github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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  var numImport = make(map[string]int)
   483  
   484  func importimport(s *Sym, path string) {
   485  	// Informational: record package name
   486  	// associated with import path, for use in
   487  	// human-readable messages.
   488  
   489  	if isbadimport(path) {
   490  		errorexit()
   491  	}
   492  	p := mkpkg(path)
   493  	if p.Name == "" {
   494  		p.Name = s.Name
   495  		numImport[s.Name]++
   496  	} else if p.Name != s.Name {
   497  		Yyerror("conflicting names %s and %s for package %q", p.Name, s.Name, p.Path)
   498  	}
   499  
   500  	if incannedimport == 0 && myimportpath != "" && path == myimportpath {
   501  		Yyerror("import %q: package depends on %q (import cycle)", importpkg.Path, path)
   502  		errorexit()
   503  	}
   504  }
   505  
   506  // importconst declares symbol s as an imported constant with type t and value n.
   507  func importconst(s *Sym, t *Type, n *Node) {
   508  	importsym(s, OLITERAL)
   509  	n = convlit(n, t)
   510  
   511  	if s.Def != nil { // TODO: check if already the same.
   512  		return
   513  	}
   514  
   515  	if n.Op != OLITERAL {
   516  		Yyerror("expression must be a constant")
   517  		return
   518  	}
   519  
   520  	if n.Sym != nil {
   521  		n1 := *n
   522  		n = &n1
   523  	}
   524  
   525  	n.Orig = newname(s)
   526  	n.Sym = s
   527  	declare(n, PEXTERN)
   528  
   529  	if Debug['E'] != 0 {
   530  		fmt.Printf("import const %v\n", s)
   531  	}
   532  }
   533  
   534  // importvar declares symbol s as an imported variable with type t.
   535  func importvar(s *Sym, t *Type) {
   536  	importsym(s, ONAME)
   537  	if s.Def != nil && s.Def.Op == ONAME {
   538  		if Eqtype(t, s.Def.Type) {
   539  			return
   540  		}
   541  		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)
   542  	}
   543  
   544  	n := newname(s)
   545  	s.Importdef = importpkg
   546  	n.Type = t
   547  	declare(n, PEXTERN)
   548  
   549  	if Debug['E'] != 0 {
   550  		fmt.Printf("import var %v %v\n", s, Tconv(t, FmtLong))
   551  	}
   552  }
   553  
   554  // importtype and importer.importtype (bimport.go) need to remain in sync.
   555  func importtype(pt *Type, t *Type) {
   556  	// override declaration in unsafe.go for Pointer.
   557  	// there is no way in Go code to define unsafe.Pointer
   558  	// so we have to supply it.
   559  	if incannedimport != 0 && importpkg.Name == "unsafe" && pt.Nod.Sym.Name == "Pointer" {
   560  		t = Types[TUNSAFEPTR]
   561  	}
   562  
   563  	if pt.Etype == TFORW {
   564  		n := pt.Nod
   565  		copytype(pt.Nod, t)
   566  		pt.Nod = n // unzero nod
   567  		pt.Sym.Importdef = importpkg
   568  		pt.Sym.Lastlineno = lineno
   569  		declare(n, PEXTERN)
   570  		checkwidth(pt)
   571  	} else if !Eqtype(pt.Orig, t) {
   572  		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)
   573  	}
   574  
   575  	if Debug['E'] != 0 {
   576  		fmt.Printf("import type %v %v\n", pt, Tconv(t, FmtLong))
   577  	}
   578  }
   579  
   580  func dumpasmhdr() {
   581  	b, err := bio.Create(asmhdr)
   582  	if err != nil {
   583  		Fatalf("%v", err)
   584  	}
   585  	fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", localpkg.Name)
   586  	for _, n := range asmlist {
   587  		if isblanksym(n.Sym) {
   588  			continue
   589  		}
   590  		switch n.Op {
   591  		case OLITERAL:
   592  			fmt.Fprintf(b, "#define const_%s %v\n", n.Sym.Name, vconv(n.Val(), FmtSharp))
   593  
   594  		case OTYPE:
   595  			t := n.Type
   596  			if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
   597  				break
   598  			}
   599  			fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width))
   600  			for _, t := range t.Fields().Slice() {
   601  				if !isblanksym(t.Sym) {
   602  					fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Offset))
   603  				}
   604  			}
   605  		}
   606  	}
   607  
   608  	b.Close()
   609  }