github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/cmd/compile/internal/gc/export.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gc
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"cmd/internal/bio"
    11  	"fmt"
    12  	"sort"
    13  	"unicode"
    14  	"unicode/utf8"
    15  )
    16  
    17  var (
    18  	newexport    bool // if set, use new export format
    19  	Debug_export int  // if set, print debugging information about export data
    20  	exportsize   int
    21  )
    22  
    23  func exportf(format string, args ...interface{}) {
    24  	n, _ := fmt.Fprintf(bout, format, args...)
    25  	exportsize += n
    26  	if Debug_export != 0 {
    27  		fmt.Printf(format, args...)
    28  	}
    29  }
    30  
    31  var asmlist []*Node
    32  
    33  // Mark n's symbol as exported
    34  func exportsym(n *Node) {
    35  	if n == nil || n.Sym == nil {
    36  		return
    37  	}
    38  	if n.Sym.Flags&(SymExport|SymPackage) != 0 {
    39  		if n.Sym.Flags&SymPackage != 0 {
    40  			Yyerror("export/package mismatch: %v", n.Sym)
    41  		}
    42  		return
    43  	}
    44  
    45  	n.Sym.Flags |= SymExport
    46  
    47  	if Debug['E'] != 0 {
    48  		fmt.Printf("export symbol %v\n", n.Sym)
    49  	}
    50  	exportlist = append(exportlist, n)
    51  }
    52  
    53  func exportname(s string) bool {
    54  	if r := s[0]; r < utf8.RuneSelf {
    55  		return 'A' <= r && r <= 'Z'
    56  	}
    57  	r, _ := utf8.DecodeRuneInString(s)
    58  	return unicode.IsUpper(r)
    59  }
    60  
    61  func initname(s string) bool {
    62  	return s == "init"
    63  }
    64  
    65  // exportedsym reports whether a symbol will be visible
    66  // to files that import our package.
    67  func exportedsym(sym *Sym) bool {
    68  	// Builtins are visible everywhere.
    69  	if sym.Pkg == builtinpkg || sym.Origpkg == builtinpkg {
    70  		return true
    71  	}
    72  
    73  	return sym.Pkg == localpkg && exportname(sym.Name)
    74  }
    75  
    76  func autoexport(n *Node, ctxt Class) {
    77  	if n == nil || n.Sym == nil {
    78  		return
    79  	}
    80  	if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
    81  		return
    82  	}
    83  	if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method
    84  		return
    85  	}
    86  
    87  	// -A is for cmd/gc/mkbuiltin script, so export everything
    88  	if Debug['A'] != 0 || exportname(n.Sym.Name) || initname(n.Sym.Name) {
    89  		exportsym(n)
    90  	}
    91  	if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 {
    92  		n.Sym.Flags |= SymAsm
    93  		asmlist = append(asmlist, n)
    94  	}
    95  }
    96  
    97  func dumppkg(p *Pkg) {
    98  	if p == nil || p == localpkg || p.Exported || p == builtinpkg {
    99  		return
   100  	}
   101  	p.Exported = true
   102  	suffix := ""
   103  	if !p.Direct {
   104  		suffix = " // indirect"
   105  	}
   106  	exportf("\timport %s %q%s\n", p.Name, p.Path, suffix)
   107  }
   108  
   109  // Look for anything we need for the inline body
   110  func reexportdeplist(ll Nodes) {
   111  	for _, n := range ll.Slice() {
   112  		reexportdep(n)
   113  	}
   114  }
   115  
   116  func reexportdep(n *Node) {
   117  	if n == nil {
   118  		return
   119  	}
   120  
   121  	//print("reexportdep %+hN\n", n);
   122  	switch n.Op {
   123  	case ONAME:
   124  		switch n.Class {
   125  		// methods will be printed along with their type
   126  		// nodes for T.Method expressions
   127  		case PFUNC:
   128  			if n.Left != nil && n.Left.Op == OTYPE {
   129  				break
   130  			}
   131  
   132  			// nodes for method calls.
   133  			if n.Type == nil || n.Type.Recv() != nil {
   134  				break
   135  			}
   136  			fallthrough
   137  
   138  		case PEXTERN:
   139  			if n.Sym != nil && !exportedsym(n.Sym) {
   140  				if Debug['E'] != 0 {
   141  					fmt.Printf("reexport name %v\n", n.Sym)
   142  				}
   143  				exportlist = append(exportlist, n)
   144  			}
   145  		}
   146  
   147  	// Local variables in the bodies need their type.
   148  	case ODCL:
   149  		t := n.Left.Type
   150  
   151  		if t != Types[t.Etype] && t != idealbool && t != idealstring {
   152  			if t.IsPtr() {
   153  				t = t.Elem()
   154  			}
   155  			if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
   156  				if Debug['E'] != 0 {
   157  					fmt.Printf("reexport type %v from declaration\n", t.Sym)
   158  				}
   159  				exportlist = append(exportlist, t.Sym.Def)
   160  			}
   161  		}
   162  
   163  	case OLITERAL:
   164  		t := n.Type
   165  		if t != Types[n.Type.Etype] && t != idealbool && t != idealstring {
   166  			if t.IsPtr() {
   167  				t = t.Elem()
   168  			}
   169  			if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
   170  				if Debug['E'] != 0 {
   171  					fmt.Printf("reexport literal type %v\n", t.Sym)
   172  				}
   173  				exportlist = append(exportlist, t.Sym.Def)
   174  			}
   175  		}
   176  		fallthrough
   177  
   178  	case OTYPE:
   179  		if n.Sym != nil && n.Sym.Def != nil && !exportedsym(n.Sym) {
   180  			if Debug['E'] != 0 {
   181  				fmt.Printf("reexport literal/type %v\n", n.Sym)
   182  			}
   183  			exportlist = append(exportlist, n)
   184  		}
   185  
   186  	// for operations that need a type when rendered, put the type on the export list.
   187  	case OCONV,
   188  		OCONVIFACE,
   189  		OCONVNOP,
   190  		ORUNESTR,
   191  		OARRAYBYTESTR,
   192  		OARRAYRUNESTR,
   193  		OSTRARRAYBYTE,
   194  		OSTRARRAYRUNE,
   195  		ODOTTYPE,
   196  		ODOTTYPE2,
   197  		OSTRUCTLIT,
   198  		OARRAYLIT,
   199  		OPTRLIT,
   200  		OMAKEMAP,
   201  		OMAKESLICE,
   202  		OMAKECHAN:
   203  		t := n.Type
   204  
   205  		switch t.Etype {
   206  		case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
   207  			if t.Sym == nil {
   208  				t = t.Elem()
   209  			}
   210  		}
   211  		if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
   212  			if Debug['E'] != 0 {
   213  				fmt.Printf("reexport type for expression %v\n", t.Sym)
   214  			}
   215  			exportlist = append(exportlist, t.Sym.Def)
   216  		}
   217  	}
   218  
   219  	reexportdep(n.Left)
   220  	reexportdep(n.Right)
   221  	reexportdeplist(n.List)
   222  	reexportdeplist(n.Rlist)
   223  	reexportdeplist(n.Ninit)
   224  	reexportdeplist(n.Nbody)
   225  }
   226  
   227  func dumpexportconst(s *Sym) {
   228  	n := typecheck(s.Def, Erv)
   229  	if n == nil || n.Op != OLITERAL {
   230  		Fatalf("dumpexportconst: oconst nil: %v", s)
   231  	}
   232  
   233  	t := n.Type // may or may not be specified
   234  	dumpexporttype(t)
   235  
   236  	if t != nil && !t.IsUntyped() {
   237  		exportf("\tconst %v %v = %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp), vconv(n.Val(), FmtSharp))
   238  	} else {
   239  		exportf("\tconst %v = %v\n", sconv(s, FmtSharp), vconv(n.Val(), FmtSharp))
   240  	}
   241  }
   242  
   243  func dumpexportvar(s *Sym) {
   244  	n := s.Def
   245  	n = typecheck(n, Erv|Ecall)
   246  	if n == nil || n.Type == nil {
   247  		Yyerror("variable exported but not defined: %v", s)
   248  		return
   249  	}
   250  
   251  	t := n.Type
   252  	dumpexporttype(t)
   253  
   254  	if t.Etype == TFUNC && n.Class == PFUNC {
   255  		if n.Func != nil && n.Func.Inl.Len() != 0 {
   256  			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
   257  			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
   258  			if Debug['l'] < 2 {
   259  				typecheckinl(n)
   260  			}
   261  
   262  			// NOTE: The space after %#S here is necessary for ld's export data parser.
   263  			exportf("\tfunc %v %v { %v }\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp), hconv(n.Func.Inl, FmtSharp|FmtBody))
   264  
   265  			reexportdeplist(n.Func.Inl)
   266  		} else {
   267  			exportf("\tfunc %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtShort|FmtSharp))
   268  		}
   269  	} else {
   270  		exportf("\tvar %v %v\n", sconv(s, FmtSharp), Tconv(t, FmtSharp))
   271  	}
   272  }
   273  
   274  // methodbyname sorts types by symbol name.
   275  type methodbyname []*Field
   276  
   277  func (x methodbyname) Len() int           { return len(x) }
   278  func (x methodbyname) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
   279  func (x methodbyname) Less(i, j int) bool { return x[i].Sym.Name < x[j].Sym.Name }
   280  
   281  func dumpexporttype(t *Type) {
   282  	if t == nil {
   283  		return
   284  	}
   285  	if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
   286  		return
   287  	}
   288  	t.Printed = true
   289  
   290  	if t.Sym != nil {
   291  		dumppkg(t.Sym.Pkg)
   292  	}
   293  
   294  	switch t.Etype {
   295  	case TSTRUCT, TINTER:
   296  		for _, f := range t.Fields().Slice() {
   297  			dumpexporttype(f.Type)
   298  		}
   299  	case TFUNC:
   300  		dumpexporttype(t.Recvs())
   301  		dumpexporttype(t.Results())
   302  		dumpexporttype(t.Params())
   303  	case TMAP:
   304  		dumpexporttype(t.Val())
   305  		dumpexporttype(t.Key())
   306  	case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
   307  		dumpexporttype(t.Elem())
   308  	}
   309  
   310  	if t.Sym == nil {
   311  		return
   312  	}
   313  
   314  	var m []*Field
   315  	for _, f := range t.Methods().Slice() {
   316  		dumpexporttype(f.Type)
   317  		m = append(m, f)
   318  	}
   319  	sort.Sort(methodbyname(m))
   320  
   321  	exportf("\ttype %v %v\n", sconv(t.Sym, FmtSharp), Tconv(t, FmtSharp|FmtLong))
   322  	for _, f := range m {
   323  		if f.Nointerface {
   324  			exportf("\t//go:nointerface\n")
   325  		}
   326  		if f.Type.Nname() != nil && f.Type.Nname().Func.Inl.Len() != 0 { // nname was set by caninl
   327  
   328  			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
   329  			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
   330  			if Debug['l'] < 2 {
   331  				typecheckinl(f.Type.Nname())
   332  			}
   333  			exportf("\tfunc %v %v %v { %v }\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp), hconv(f.Type.Nname().Func.Inl, FmtSharp|FmtBody))
   334  			reexportdeplist(f.Type.Nname().Func.Inl)
   335  		} else {
   336  			exportf("\tfunc %v %v %v\n", Tconv(f.Type.Recvs(), FmtSharp), sconv(f.Sym, FmtShort|FmtByte|FmtSharp), Tconv(f.Type, FmtShort|FmtSharp))
   337  		}
   338  	}
   339  }
   340  
   341  func dumpsym(s *Sym) {
   342  	if s.Flags&SymExported != 0 {
   343  		return
   344  	}
   345  	s.Flags |= SymExported
   346  
   347  	if s.Def == nil {
   348  		Yyerror("unknown export symbol: %v", s)
   349  		return
   350  	}
   351  
   352  	//	print("dumpsym %O %+S\n", s->def->op, s);
   353  	dumppkg(s.Pkg)
   354  
   355  	switch s.Def.Op {
   356  	default:
   357  		Yyerror("unexpected export symbol: %v %v", s.Def.Op, s)
   358  
   359  	case OLITERAL:
   360  		dumpexportconst(s)
   361  
   362  	case OTYPE:
   363  		if s.Def.Type.Etype == TFORW {
   364  			Yyerror("export of incomplete type %v", s)
   365  		} else {
   366  			dumpexporttype(s.Def.Type)
   367  		}
   368  
   369  	case ONAME:
   370  		dumpexportvar(s)
   371  	}
   372  }
   373  
   374  func dumpexport() {
   375  	if buildid != "" {
   376  		exportf("build id %q\n", buildid)
   377  	}
   378  
   379  	size := 0 // size of export section without enclosing markers
   380  	if newexport {
   381  		// binary export
   382  		// The linker also looks for the $$ marker - use char after $$ to distinguish format.
   383  		exportf("\n$$B\n") // indicate binary format
   384  		if debugFormat {
   385  			// save a copy of the export data
   386  			var copy bytes.Buffer
   387  			bcopy := bufio.NewWriter(&copy)
   388  			size = export(bcopy, Debug_export != 0)
   389  			bcopy.Flush() // flushing to bytes.Buffer cannot fail
   390  			if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
   391  				Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
   392  			}
   393  			// export data must contain no '$' so that we can find the end by searching for "$$"
   394  			if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
   395  				Fatalf("export data contains $")
   396  			}
   397  
   398  			// verify that we can read the copied export data back in
   399  			// (use empty package map to avoid collisions)
   400  			savedPkgMap := pkgMap
   401  			savedPkgs := pkgs
   402  			pkgMap = make(map[string]*Pkg)
   403  			pkgs = nil
   404  			importpkg = mkpkg("")
   405  			Import(bufio.NewReader(&copy)) // must not die
   406  			importpkg = nil
   407  			pkgs = savedPkgs
   408  			pkgMap = savedPkgMap
   409  		} else {
   410  			size = export(bout.Writer, Debug_export != 0)
   411  		}
   412  		exportf("\n$$\n")
   413  	} else {
   414  		// textual export
   415  		lno := lineno
   416  
   417  		exportf("\n$$\n") // indicate textual format
   418  		exportsize = 0
   419  		exportf("package %s", localpkg.Name)
   420  		if safemode {
   421  			exportf(" safe")
   422  		}
   423  		exportf("\n")
   424  
   425  		for _, p := range pkgs {
   426  			if p.Direct {
   427  				dumppkg(p)
   428  			}
   429  		}
   430  
   431  		// exportlist grows during iteration - cannot use range
   432  		for i := 0; i < len(exportlist); i++ {
   433  			n := exportlist[i]
   434  			lineno = n.Lineno
   435  			dumpsym(n.Sym)
   436  		}
   437  
   438  		size = exportsize
   439  		exportf("\n$$\n")
   440  		lineno = lno
   441  	}
   442  
   443  	if Debug_export != 0 {
   444  		fmt.Printf("export data size = %d bytes\n", size)
   445  	}
   446  }
   447  
   448  // importsym declares symbol s as an imported object representable by op.
   449  func importsym(s *Sym, op Op) {
   450  	if s.Def != nil && s.Def.Op != op {
   451  		pkgstr := fmt.Sprintf("during import %q", importpkg.Path)
   452  		redeclare(s, pkgstr)
   453  	}
   454  
   455  	// mark the symbol so it is not reexported
   456  	if s.Def == nil {
   457  		if Debug['A'] != 0 || exportname(s.Name) || initname(s.Name) {
   458  			s.Flags |= SymExport
   459  		} else {
   460  			s.Flags |= SymPackage // package scope
   461  		}
   462  	}
   463  }
   464  
   465  // pkgtype returns the named type declared by symbol s.
   466  // If no such type has been declared yet, a forward declaration is returned.
   467  func pkgtype(s *Sym) *Type {
   468  	importsym(s, OTYPE)
   469  	if s.Def == nil || s.Def.Op != OTYPE {
   470  		t := typ(TFORW)
   471  		t.Sym = s
   472  		s.Def = typenod(t)
   473  		s.Def.Name = new(Name)
   474  	}
   475  
   476  	if s.Def.Type == nil {
   477  		Yyerror("pkgtype %v", s)
   478  	}
   479  	return s.Def.Type
   480  }
   481  
   482  // numImport tracks how often a package with a given name is imported.
   483  // It is used to provide a better error message (by using the package
   484  // path to disambiguate) if a package that appears multiple times with
   485  // the same name appears in an error message.
   486  var numImport = make(map[string]int)
   487  
   488  func importimport(s *Sym, path string) {
   489  	// Informational: record package name
   490  	// associated with import path, for use in
   491  	// human-readable messages.
   492  
   493  	if isbadimport(path) {
   494  		errorexit()
   495  	}
   496  	p := mkpkg(path)
   497  	if p.Name == "" {
   498  		p.Name = s.Name
   499  		numImport[s.Name]++
   500  	} else if p.Name != s.Name {
   501  		Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path)
   502  	}
   503  
   504  	if incannedimport == 0 && myimportpath != "" && path == myimportpath {
   505  		Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
   506  		errorexit()
   507  	}
   508  }
   509  
   510  // importconst declares symbol s as an imported constant with type t and value n.
   511  func importconst(s *Sym, t *Type, n *Node) {
   512  	importsym(s, OLITERAL)
   513  	n = convlit(n, t)
   514  
   515  	if s.Def != nil { // TODO: check if already the same.
   516  		return
   517  	}
   518  
   519  	if n.Op != OLITERAL {
   520  		Yyerror("expression must be a constant")
   521  		return
   522  	}
   523  
   524  	if n.Sym != nil {
   525  		n1 := *n
   526  		n = &n1
   527  	}
   528  
   529  	n.Orig = newname(s)
   530  	n.Sym = s
   531  	declare(n, PEXTERN)
   532  
   533  	if Debug['E'] != 0 {
   534  		fmt.Printf("import const %v\n", s)
   535  	}
   536  }
   537  
   538  // importvar declares symbol s as an imported variable with type t.
   539  func importvar(s *Sym, t *Type) {
   540  	importsym(s, ONAME)
   541  	if s.Def != nil && s.Def.Op == ONAME {
   542  		if Eqtype(t, s.Def.Type) {
   543  			return
   544  		}
   545  		Yyerror("inconsistent definition for var %v during import\n\t%v (in %q)\n\t%v (in %q)", s, s.Def.Type, s.Importdef.Path, t, importpkg.Path)
   546  	}
   547  
   548  	n := newname(s)
   549  	s.Importdef = importpkg
   550  	n.Type = t
   551  	declare(n, PEXTERN)
   552  
   553  	if Debug['E'] != 0 {
   554  		fmt.Printf("import var %v %v\n", s, Tconv(t, FmtLong))
   555  	}
   556  }
   557  
   558  // importtype and importer.importtype (bimport.go) need to remain in sync.
   559  func importtype(pt *Type, t *Type) {
   560  	// override declaration in unsafe.go for Pointer.
   561  	// there is no way in Go code to define unsafe.Pointer
   562  	// so we have to supply it.
   563  	if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" {
   564  		t = Types[TUNSAFEPTR]
   565  	}
   566  
   567  	if pt.Etype == TFORW {
   568  		n := pt.Nod
   569  		copytype(pt.Nod, t)
   570  		pt.Nod = n // unzero nod
   571  		pt.Sym.Importdef = importpkg
   572  		pt.Sym.Lastlineno = lineno
   573  		declare(n, PEXTERN)
   574  		checkwidth(pt)
   575  	} else if !Eqtype(pt.Orig, t) {
   576  		Yyerror("inconsistent definition for type %v during import\n\t%v (in %q)\n\t%v (in %q)", pt.Sym, Tconv(pt, FmtLong), pt.Sym.Importdef.Path, Tconv(t, FmtLong), importpkg.Path)
   577  	}
   578  
   579  	if Debug['E'] != 0 {
   580  		fmt.Printf("import type %v %v\n", pt, Tconv(t, FmtLong))
   581  	}
   582  }
   583  
   584  func dumpasmhdr() {
   585  	b, err := bio.Create(asmhdr)
   586  	if err != nil {
   587  		Fatalf("%v", err)
   588  	}
   589  	fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", localpkg.Name)
   590  	for _, n := range asmlist {
   591  		if isblanksym(n.Sym) {
   592  			continue
   593  		}
   594  		switch n.Op {
   595  		case OLITERAL:
   596  			fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, vconv(n.Val(), FmtSharp))
   597  
   598  		case OTYPE:
   599  			t := n.Type
   600  			if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
   601  				break
   602  			}
   603  			fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width))
   604  			for _, t := range t.Fields().Slice() {
   605  				if !isblanksym(t.Sym) {
   606  					fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Offset))
   607  				}
   608  			}
   609  		}
   610  	}
   611  
   612  	b.Close()
   613  }