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