mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
The C version of the compiler had just one hash table, indexed by a (name string, pkg *Pkg) pair. Because we always know the pkg during a lookup, replace the one table with a per-Pkg map[string]*Sym. This also lets us do non-allocating []byte key lookups. This CL *does* change the generated object files. In the old code, export data and init calls were emitted in "hash table order". Now they are emitted in the order in which they were added to the table. Change-Id: I5a48d5c9add996dc43ad04a905641d901522de0b Reviewed-on: https://go-review.googlesource.com/6600 Reviewed-by: Rob Pike <r@golang.org>
562 lines
12 KiB
Go
562 lines
12 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package gc
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"fmt"
|
|
"sort"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
var asmlist *NodeList
|
|
|
|
// Mark n's symbol as exported
|
|
func exportsym(n *Node) {
|
|
if n == nil || n.Sym == nil {
|
|
return
|
|
}
|
|
if n.Sym.Flags&(SymExport|SymPackage) != 0 {
|
|
if n.Sym.Flags&SymPackage != 0 {
|
|
Yyerror("export/package mismatch: %v", Sconv(n.Sym, 0))
|
|
}
|
|
return
|
|
}
|
|
|
|
n.Sym.Flags |= SymExport
|
|
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("export symbol %v\n", Sconv(n.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, n)
|
|
}
|
|
|
|
func exportname(s string) bool {
|
|
if s[0] < utf8.RuneSelf {
|
|
return 'A' <= s[0] && s[0] <= 'Z'
|
|
}
|
|
r, _ := utf8.DecodeRuneInString(s)
|
|
return unicode.IsUpper(r)
|
|
}
|
|
|
|
func initname(s string) bool {
|
|
return s == "init"
|
|
}
|
|
|
|
// exportedsym reports whether a symbol will be visible
|
|
// to files that import our package.
|
|
func exportedsym(sym *Sym) bool {
|
|
// Builtins are visible everywhere.
|
|
if sym.Pkg == builtinpkg || sym.Origpkg == builtinpkg {
|
|
return true
|
|
}
|
|
|
|
return sym.Pkg == localpkg && exportname(sym.Name)
|
|
}
|
|
|
|
func autoexport(n *Node, ctxt int) {
|
|
if n == nil || n.Sym == nil {
|
|
return
|
|
}
|
|
if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
|
|
return
|
|
}
|
|
if n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method
|
|
return
|
|
}
|
|
|
|
// -A is for cmd/gc/mkbuiltin script, so export everything
|
|
if Debug['A'] != 0 || exportname(n.Sym.Name) || initname(n.Sym.Name) {
|
|
exportsym(n)
|
|
}
|
|
if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 {
|
|
n.Sym.Flags |= SymAsm
|
|
asmlist = list(asmlist, n)
|
|
}
|
|
}
|
|
|
|
func dumppkg(p *Pkg) {
|
|
if p == nil || p == localpkg || p.Exported != 0 || p == builtinpkg {
|
|
return
|
|
}
|
|
p.Exported = 1
|
|
suffix := ""
|
|
if p.Direct == 0 {
|
|
suffix = " // indirect"
|
|
}
|
|
fmt.Fprintf(bout, "\timport %s %q%s\n", p.Name, p.Path, suffix)
|
|
}
|
|
|
|
// Look for anything we need for the inline body
|
|
func reexportdeplist(ll *NodeList) {
|
|
for ; ll != nil; ll = ll.Next {
|
|
reexportdep(ll.N)
|
|
}
|
|
}
|
|
|
|
func reexportdep(n *Node) {
|
|
if n == nil {
|
|
return
|
|
}
|
|
|
|
//print("reexportdep %+hN\n", n);
|
|
switch n.Op {
|
|
case ONAME:
|
|
switch n.Class &^ PHEAP {
|
|
// methods will be printed along with their type
|
|
// nodes for T.Method expressions
|
|
case PFUNC:
|
|
if n.Left != nil && n.Left.Op == OTYPE {
|
|
break
|
|
}
|
|
|
|
// nodes for method calls.
|
|
if n.Type == nil || n.Type.Thistuple > 0 {
|
|
break
|
|
}
|
|
fallthrough
|
|
|
|
// fallthrough
|
|
case PEXTERN:
|
|
if n.Sym != nil && !exportedsym(n.Sym) {
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("reexport name %v\n", Sconv(n.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, n)
|
|
}
|
|
}
|
|
|
|
// Local variables in the bodies need their type.
|
|
case ODCL:
|
|
t := n.Left.Type
|
|
|
|
if t != Types[t.Etype] && t != idealbool && t != idealstring {
|
|
if Isptr[t.Etype] {
|
|
t = t.Type
|
|
}
|
|
if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("reexport type %v from declaration\n", Sconv(t.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, t.Sym.Def)
|
|
}
|
|
}
|
|
|
|
case OLITERAL:
|
|
t := n.Type
|
|
if t != Types[n.Type.Etype] && t != idealbool && t != idealstring {
|
|
if Isptr[t.Etype] {
|
|
t = t.Type
|
|
}
|
|
if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("reexport literal type %v\n", Sconv(t.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, t.Sym.Def)
|
|
}
|
|
}
|
|
fallthrough
|
|
|
|
// fallthrough
|
|
case OTYPE:
|
|
if n.Sym != nil && !exportedsym(n.Sym) {
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("reexport literal/type %v\n", Sconv(n.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, n)
|
|
}
|
|
|
|
// for operations that need a type when rendered, put the type on the export list.
|
|
case OCONV,
|
|
OCONVIFACE,
|
|
OCONVNOP,
|
|
ORUNESTR,
|
|
OARRAYBYTESTR,
|
|
OARRAYRUNESTR,
|
|
OSTRARRAYBYTE,
|
|
OSTRARRAYRUNE,
|
|
ODOTTYPE,
|
|
ODOTTYPE2,
|
|
OSTRUCTLIT,
|
|
OARRAYLIT,
|
|
OPTRLIT,
|
|
OMAKEMAP,
|
|
OMAKESLICE,
|
|
OMAKECHAN:
|
|
t := n.Type
|
|
|
|
if t.Sym == nil && t.Type != nil {
|
|
t = t.Type
|
|
}
|
|
if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("reexport type for expression %v\n", Sconv(t.Sym, 0))
|
|
}
|
|
exportlist = list(exportlist, t.Sym.Def)
|
|
}
|
|
}
|
|
|
|
reexportdep(n.Left)
|
|
reexportdep(n.Right)
|
|
reexportdeplist(n.List)
|
|
reexportdeplist(n.Rlist)
|
|
reexportdeplist(n.Ninit)
|
|
reexportdep(n.Ntest)
|
|
reexportdep(n.Nincr)
|
|
reexportdeplist(n.Nbody)
|
|
reexportdeplist(n.Nelse)
|
|
}
|
|
|
|
func dumpexportconst(s *Sym) {
|
|
n := s.Def
|
|
typecheck(&n, Erv)
|
|
if n == nil || n.Op != OLITERAL {
|
|
Fatal("dumpexportconst: oconst nil: %v", Sconv(s, 0))
|
|
}
|
|
|
|
t := n.Type // may or may not be specified
|
|
dumpexporttype(t)
|
|
|
|
if t != nil && !isideal(t) {
|
|
fmt.Fprintf(bout, "\tconst %v %v = %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp))
|
|
} else {
|
|
fmt.Fprintf(bout, "\tconst %v = %v\n", Sconv(s, obj.FmtSharp), Vconv(&n.Val, obj.FmtSharp))
|
|
}
|
|
}
|
|
|
|
func dumpexportvar(s *Sym) {
|
|
n := s.Def
|
|
typecheck(&n, Erv|Ecall)
|
|
if n == nil || n.Type == nil {
|
|
Yyerror("variable exported but not defined: %v", Sconv(s, 0))
|
|
return
|
|
}
|
|
|
|
t := n.Type
|
|
dumpexporttype(t)
|
|
|
|
if t.Etype == TFUNC && n.Class == PFUNC {
|
|
if n.Inl != nil {
|
|
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
|
|
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
|
|
if Debug['l'] < 2 {
|
|
typecheckinl(n)
|
|
}
|
|
|
|
// NOTE: The space after %#S here is necessary for ld's export data parser.
|
|
fmt.Fprintf(bout, "\tfunc %v %v { %v }\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp), Hconv(n.Inl, obj.FmtSharp))
|
|
|
|
reexportdeplist(n.Inl)
|
|
} else {
|
|
fmt.Fprintf(bout, "\tfunc %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtShort|obj.FmtSharp))
|
|
}
|
|
} else {
|
|
fmt.Fprintf(bout, "\tvar %v %v\n", Sconv(s, obj.FmtSharp), Tconv(t, obj.FmtSharp))
|
|
}
|
|
}
|
|
|
|
type methodbyname []*Type
|
|
|
|
func (x methodbyname) Len() int {
|
|
return len(x)
|
|
}
|
|
|
|
func (x methodbyname) Swap(i, j int) {
|
|
x[i], x[j] = x[j], x[i]
|
|
}
|
|
|
|
func (x methodbyname) Less(i, j int) bool {
|
|
a := x[i]
|
|
b := x[j]
|
|
return stringsCompare(a.Sym.Name, b.Sym.Name) < 0
|
|
}
|
|
|
|
func dumpexporttype(t *Type) {
|
|
if t == nil {
|
|
return
|
|
}
|
|
if t.Printed != 0 || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
|
|
return
|
|
}
|
|
t.Printed = 1
|
|
|
|
if t.Sym != nil && t.Etype != TFIELD {
|
|
dumppkg(t.Sym.Pkg)
|
|
}
|
|
|
|
dumpexporttype(t.Type)
|
|
dumpexporttype(t.Down)
|
|
|
|
if t.Sym == nil || t.Etype == TFIELD {
|
|
return
|
|
}
|
|
|
|
n := 0
|
|
for f := t.Method; f != nil; f = f.Down {
|
|
dumpexporttype(f)
|
|
n++
|
|
}
|
|
|
|
m := make([]*Type, n)
|
|
i := 0
|
|
for f := t.Method; f != nil; f = f.Down {
|
|
m[i] = f
|
|
i++
|
|
}
|
|
sort.Sort(methodbyname(m[:n]))
|
|
|
|
fmt.Fprintf(bout, "\ttype %v %v\n", Sconv(t.Sym, obj.FmtSharp), Tconv(t, obj.FmtSharp|obj.FmtLong))
|
|
var f *Type
|
|
for i := 0; i < n; i++ {
|
|
f = m[i]
|
|
if f.Nointerface {
|
|
fmt.Fprintf(bout, "\t//go:nointerface\n")
|
|
}
|
|
if f.Type.Nname != nil && f.Type.Nname.Inl != nil { // nname was set by caninl
|
|
|
|
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
|
|
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
|
|
if Debug['l'] < 2 {
|
|
typecheckinl(f.Type.Nname)
|
|
}
|
|
fmt.Fprintf(bout, "\tfunc (%v) %v %v { %v }\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp), Hconv(f.Type.Nname.Inl, obj.FmtSharp))
|
|
reexportdeplist(f.Type.Nname.Inl)
|
|
} else {
|
|
fmt.Fprintf(bout, "\tfunc (%v) %v %v\n", Tconv(getthisx(f.Type).Type, obj.FmtSharp), Sconv(f.Sym, obj.FmtShort|obj.FmtByte|obj.FmtSharp), Tconv(f.Type, obj.FmtShort|obj.FmtSharp))
|
|
}
|
|
}
|
|
}
|
|
|
|
func dumpsym(s *Sym) {
|
|
if s.Flags&SymExported != 0 {
|
|
return
|
|
}
|
|
s.Flags |= SymExported
|
|
|
|
if s.Def == nil {
|
|
Yyerror("unknown export symbol: %v", Sconv(s, 0))
|
|
return
|
|
}
|
|
|
|
// print("dumpsym %O %+S\n", s->def->op, s);
|
|
dumppkg(s.Pkg)
|
|
|
|
switch s.Def.Op {
|
|
default:
|
|
Yyerror("unexpected export symbol: %v %v", Oconv(int(s.Def.Op), 0), Sconv(s, 0))
|
|
|
|
case OLITERAL:
|
|
dumpexportconst(s)
|
|
|
|
case OTYPE:
|
|
if s.Def.Type.Etype == TFORW {
|
|
Yyerror("export of incomplete type %v", Sconv(s, 0))
|
|
} else {
|
|
dumpexporttype(s.Def.Type)
|
|
}
|
|
|
|
case ONAME:
|
|
dumpexportvar(s)
|
|
}
|
|
}
|
|
|
|
func dumpexport() {
|
|
lno := lineno
|
|
|
|
fmt.Fprintf(bout, "\n$$\npackage %s", localpkg.Name)
|
|
if safemode != 0 {
|
|
fmt.Fprintf(bout, " safe")
|
|
}
|
|
fmt.Fprintf(bout, "\n")
|
|
|
|
for _, p := range pkgs {
|
|
if p.Direct != 0 {
|
|
dumppkg(p)
|
|
}
|
|
}
|
|
|
|
for l := exportlist; l != nil; l = l.Next {
|
|
lineno = l.N.Lineno
|
|
dumpsym(l.N.Sym)
|
|
}
|
|
|
|
fmt.Fprintf(bout, "\n$$\n")
|
|
lineno = lno
|
|
}
|
|
|
|
/*
|
|
* import
|
|
*/
|
|
|
|
/*
|
|
* return the sym for ss, which should match lexical
|
|
*/
|
|
func importsym(s *Sym, op int) *Sym {
|
|
if s.Def != nil && int(s.Def.Op) != op {
|
|
pkgstr := fmt.Sprintf("during import %q", importpkg.Path)
|
|
redeclare(s, pkgstr)
|
|
}
|
|
|
|
// mark the symbol so it is not reexported
|
|
if s.Def == nil {
|
|
if exportname(s.Name) || initname(s.Name) {
|
|
s.Flags |= SymExport
|
|
} else {
|
|
s.Flags |= SymPackage // package scope
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
/*
|
|
* return the type pkg.name, forward declaring if needed
|
|
*/
|
|
func pkgtype(s *Sym) *Type {
|
|
importsym(s, OTYPE)
|
|
if s.Def == nil || s.Def.Op != OTYPE {
|
|
t := typ(TFORW)
|
|
t.Sym = s
|
|
s.Def = typenod(t)
|
|
}
|
|
|
|
if s.Def.Type == nil {
|
|
Yyerror("pkgtype %v", Sconv(s, 0))
|
|
}
|
|
return s.Def.Type
|
|
}
|
|
|
|
var numImport = make(map[string]int)
|
|
|
|
func importimport(s *Sym, path string) {
|
|
// Informational: record package name
|
|
// associated with import path, for use in
|
|
// human-readable messages.
|
|
|
|
if isbadimport(path) {
|
|
errorexit()
|
|
}
|
|
p := mkpkg(path)
|
|
if p.Name == "" {
|
|
p.Name = s.Name
|
|
numImport[s.Name]++
|
|
} else if p.Name != s.Name {
|
|
Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path)
|
|
}
|
|
|
|
if incannedimport == 0 && myimportpath != "" && path == myimportpath {
|
|
Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
|
|
errorexit()
|
|
}
|
|
}
|
|
|
|
func importconst(s *Sym, t *Type, n *Node) {
|
|
importsym(s, OLITERAL)
|
|
Convlit(&n, t)
|
|
|
|
if s.Def != nil { // TODO: check if already the same.
|
|
return
|
|
}
|
|
|
|
if n.Op != OLITERAL {
|
|
Yyerror("expression must be a constant")
|
|
return
|
|
}
|
|
|
|
if n.Sym != nil {
|
|
n1 := Nod(OXXX, nil, nil)
|
|
*n1 = *n
|
|
n = n1
|
|
}
|
|
|
|
n.Orig = newname(s)
|
|
n.Sym = s
|
|
declare(n, PEXTERN)
|
|
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("import const %v\n", Sconv(s, 0))
|
|
}
|
|
}
|
|
|
|
func importvar(s *Sym, t *Type) {
|
|
importsym(s, ONAME)
|
|
if s.Def != nil && s.Def.Op == ONAME {
|
|
if Eqtype(t, s.Def.Type) {
|
|
return
|
|
}
|
|
Yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", Sconv(s, 0), Tconv(s.Def.Type, 0), s.Importdef.Path, Tconv(t, 0), importpkg.Path)
|
|
}
|
|
|
|
n := newname(s)
|
|
s.Importdef = importpkg
|
|
n.Type = t
|
|
declare(n, PEXTERN)
|
|
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("import var %v %v\n", Sconv(s, 0), Tconv(t, obj.FmtLong))
|
|
}
|
|
}
|
|
|
|
func importtype(pt *Type, t *Type) {
|
|
// override declaration in unsafe.go for Pointer.
|
|
// there is no way in Go code to define unsafe.Pointer
|
|
// so we have to supply it.
|
|
if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" {
|
|
t = Types[TUNSAFEPTR]
|
|
}
|
|
|
|
if pt.Etype == TFORW {
|
|
n := pt.Nod
|
|
copytype(pt.Nod, t)
|
|
pt.Nod = n // unzero nod
|
|
pt.Sym.Importdef = importpkg
|
|
pt.Sym.Lastlineno = int32(parserline())
|
|
declare(n, PEXTERN)
|
|
checkwidth(pt)
|
|
} else if !Eqtype(pt.Orig, t) {
|
|
Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", Sconv(pt.Sym, 0), Tconv(pt, obj.FmtLong), pt.Sym.Importdef.Path, Tconv(t, obj.FmtLong), importpkg.Path)
|
|
}
|
|
|
|
if Debug['E'] != 0 {
|
|
fmt.Printf("import type %v %v\n", Tconv(pt, 0), Tconv(t, obj.FmtLong))
|
|
}
|
|
}
|
|
|
|
func dumpasmhdr() {
|
|
var b *obj.Biobuf
|
|
|
|
b, err := obj.Bopenw(asmhdr)
|
|
if err != nil {
|
|
Fatal("%v", err)
|
|
}
|
|
fmt.Fprintf(b, "// generated by %cg -asmhdr from package %s\n\n", Thearch.Thechar, localpkg.Name)
|
|
var n *Node
|
|
var t *Type
|
|
for l := asmlist; l != nil; l = l.Next {
|
|
n = l.N
|
|
if isblanksym(n.Sym) {
|
|
continue
|
|
}
|
|
switch n.Op {
|
|
case OLITERAL:
|
|
fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, Vconv(&n.Val, obj.FmtSharp))
|
|
|
|
case OTYPE:
|
|
t = n.Type
|
|
if t.Etype != TSTRUCT || t.Map != nil || t.Funarg != 0 {
|
|
break
|
|
}
|
|
fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width))
|
|
for t = t.Type; t != nil; t = t.Down {
|
|
if !isblanksym(t.Sym) {
|
|
fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Width))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obj.Bterm(b)
|
|
}
|