Home | History | Annotate | Download | only in gc
      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 	"unicode"
     13 	"unicode/utf8"
     14 )
     15 
     16 var (
     17 	Debug_export int // if set, print debugging information about export data
     18 	exportsize   int
     19 )
     20 
     21 func exportf(format string, args ...interface{}) {
     22 	n, _ := fmt.Fprintf(bout, format, args...)
     23 	exportsize += n
     24 	if Debug_export != 0 {
     25 		fmt.Printf(format, args...)
     26 	}
     27 }
     28 
     29 var asmlist []*Node
     30 
     31 // Mark n's symbol as exported
     32 func exportsym(n *Node) {
     33 	if n == nil || n.Sym == nil {
     34 		return
     35 	}
     36 	if n.Sym.Flags&(SymExport|SymPackage) != 0 {
     37 		if n.Sym.Flags&SymPackage != 0 {
     38 			yyerror("export/package mismatch: %v", n.Sym)
     39 		}
     40 		return
     41 	}
     42 
     43 	n.Sym.Flags |= SymExport
     44 	if Debug['E'] != 0 {
     45 		fmt.Printf("export symbol %v\n", n.Sym)
     46 	}
     47 
     48 	// Ensure original object is on exportlist before aliases.
     49 	if n.Sym.Flags&SymAlias != 0 {
     50 		exportlist = append(exportlist, n.Sym.Def)
     51 	}
     52 
     53 	exportlist = append(exportlist, n)
     54 }
     55 
     56 func exportname(s string) bool {
     57 	if r := s[0]; r < utf8.RuneSelf {
     58 		return 'A' <= r && r <= 'Z'
     59 	}
     60 	r, _ := utf8.DecodeRuneInString(s)
     61 	return unicode.IsUpper(r)
     62 }
     63 
     64 func initname(s string) bool {
     65 	return s == "init"
     66 }
     67 
     68 // exportedsym reports whether a symbol will be visible
     69 // to files that import our package.
     70 func exportedsym(sym *Sym) bool {
     71 	// Builtins are visible everywhere.
     72 	if sym.Pkg == builtinpkg || sym.Origpkg == builtinpkg {
     73 		return true
     74 	}
     75 
     76 	return sym.Pkg == localpkg && exportname(sym.Name)
     77 }
     78 
     79 func autoexport(n *Node, ctxt Class) {
     80 	if n == nil || n.Sym == nil {
     81 		return
     82 	}
     83 	if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
     84 		return
     85 	}
     86 	if n.Name.Param != nil && n.Name.Param.Ntype != nil && n.Name.Param.Ntype.Op == OTFUNC && n.Name.Param.Ntype.Left != nil { // method
     87 		return
     88 	}
     89 
     90 	if exportname(n.Sym.Name) || initname(n.Sym.Name) {
     91 		exportsym(n)
     92 	}
     93 	if asmhdr != "" && n.Sym.Pkg == localpkg && n.Sym.Flags&SymAsm == 0 {
     94 		n.Sym.Flags |= SymAsm
     95 		asmlist = append(asmlist, n)
     96 	}
     97 }
     98 
     99 // Look for anything we need for the inline body
    100 func reexportdeplist(ll Nodes) {
    101 	for _, n := range ll.Slice() {
    102 		reexportdep(n)
    103 	}
    104 }
    105 
    106 func reexportdep(n *Node) {
    107 	if n == nil {
    108 		return
    109 	}
    110 
    111 	//print("reexportdep %+hN\n", n);
    112 	switch n.Op {
    113 	case ONAME:
    114 		switch n.Class {
    115 		// methods will be printed along with their type
    116 		// nodes for T.Method expressions
    117 		case PFUNC:
    118 			if n.Left != nil && n.Left.Op == OTYPE {
    119 				break
    120 			}
    121 
    122 			// nodes for method calls.
    123 			if n.Type == nil || n.IsMethod() {
    124 				break
    125 			}
    126 			fallthrough
    127 
    128 		case PEXTERN:
    129 			if n.Sym != nil && !exportedsym(n.Sym) {
    130 				if Debug['E'] != 0 {
    131 					fmt.Printf("reexport name %v\n", n.Sym)
    132 				}
    133 				exportlist = append(exportlist, n)
    134 			}
    135 		}
    136 
    137 	// Local variables in the bodies need their type.
    138 	case ODCL:
    139 		t := n.Left.Type
    140 
    141 		if t != Types[t.Etype] && t != idealbool && t != idealstring {
    142 			if t.IsPtr() {
    143 				t = t.Elem()
    144 			}
    145 			if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
    146 				if Debug['E'] != 0 {
    147 					fmt.Printf("reexport type %v from declaration\n", t.Sym)
    148 				}
    149 				exportlist = append(exportlist, t.Sym.Def)
    150 			}
    151 		}
    152 
    153 	case OLITERAL:
    154 		t := n.Type
    155 		if t != Types[n.Type.Etype] && t != idealbool && t != idealstring {
    156 			if t.IsPtr() {
    157 				t = t.Elem()
    158 			}
    159 			if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
    160 				if Debug['E'] != 0 {
    161 					fmt.Printf("reexport literal type %v\n", t.Sym)
    162 				}
    163 				exportlist = append(exportlist, t.Sym.Def)
    164 			}
    165 		}
    166 		fallthrough
    167 
    168 	case OTYPE:
    169 		if n.Sym != nil && n.Sym.Def != nil && !exportedsym(n.Sym) {
    170 			if Debug['E'] != 0 {
    171 				fmt.Printf("reexport literal/type %v\n", n.Sym)
    172 			}
    173 			exportlist = append(exportlist, n)
    174 		}
    175 
    176 	// for operations that need a type when rendered, put the type on the export list.
    177 	case OCONV,
    178 		OCONVIFACE,
    179 		OCONVNOP,
    180 		ORUNESTR,
    181 		OARRAYBYTESTR,
    182 		OARRAYRUNESTR,
    183 		OSTRARRAYBYTE,
    184 		OSTRARRAYRUNE,
    185 		ODOTTYPE,
    186 		ODOTTYPE2,
    187 		OSTRUCTLIT,
    188 		OARRAYLIT,
    189 		OSLICELIT,
    190 		OPTRLIT,
    191 		OMAKEMAP,
    192 		OMAKESLICE,
    193 		OMAKECHAN:
    194 		t := n.Type
    195 
    196 		switch t.Etype {
    197 		case TARRAY, TCHAN, TPTR32, TPTR64, TSLICE:
    198 			if t.Sym == nil {
    199 				t = t.Elem()
    200 			}
    201 		}
    202 		if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
    203 			if Debug['E'] != 0 {
    204 				fmt.Printf("reexport type for expression %v\n", t.Sym)
    205 			}
    206 			exportlist = append(exportlist, t.Sym.Def)
    207 		}
    208 	}
    209 
    210 	reexportdep(n.Left)
    211 	reexportdep(n.Right)
    212 	reexportdeplist(n.List)
    213 	reexportdeplist(n.Rlist)
    214 	reexportdeplist(n.Ninit)
    215 	reexportdeplist(n.Nbody)
    216 }
    217 
    218 // methodbyname sorts types by symbol name.
    219 type methodbyname []*Field
    220 
    221 func (x methodbyname) Len() int           { return len(x) }
    222 func (x methodbyname) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
    223 func (x methodbyname) Less(i, j int) bool { return x[i].Sym.Name < x[j].Sym.Name }
    224 
    225 func dumpexport() {
    226 	if buildid != "" {
    227 		exportf("build id %q\n", buildid)
    228 	}
    229 
    230 	size := 0 // size of export section without enclosing markers
    231 	// The linker also looks for the $$ marker - use char after $$ to distinguish format.
    232 	exportf("\n$$B\n") // indicate binary export format
    233 	if debugFormat {
    234 		// save a copy of the export data
    235 		var copy bytes.Buffer
    236 		bcopy := bufio.NewWriter(&copy)
    237 		size = export(bcopy, Debug_export != 0)
    238 		bcopy.Flush() // flushing to bytes.Buffer cannot fail
    239 		if n, err := bout.Write(copy.Bytes()); n != size || err != nil {
    240 			Fatalf("error writing export data: got %d bytes, want %d bytes, err = %v", n, size, err)
    241 		}
    242 		// export data must contain no '$' so that we can find the end by searching for "$$"
    243 		// TODO(gri) is this still needed?
    244 		if bytes.IndexByte(copy.Bytes(), '$') >= 0 {
    245 			Fatalf("export data contains $")
    246 		}
    247 
    248 		// verify that we can read the copied export data back in
    249 		// (use empty package map to avoid collisions)
    250 		savedPkgMap := pkgMap
    251 		savedPkgs := pkgs
    252 		pkgMap = make(map[string]*Pkg)
    253 		pkgs = nil
    254 		importpkg = mkpkg("")
    255 		Import(bufio.NewReader(&copy)) // must not die
    256 		importpkg = nil
    257 		pkgs = savedPkgs
    258 		pkgMap = savedPkgMap
    259 	} else {
    260 		size = export(bout.Writer, Debug_export != 0)
    261 	}
    262 	exportf("\n$$\n")
    263 
    264 	if Debug_export != 0 {
    265 		fmt.Printf("export data size = %d bytes\n", size)
    266 	}
    267 }
    268 
    269 // importsym declares symbol s as an imported object representable by op.
    270 func importsym(s *Sym, op Op) {
    271 	if s.Def != nil && s.Def.Op != op {
    272 		pkgstr := fmt.Sprintf("during import %q", importpkg.Path)
    273 		redeclare(s, pkgstr)
    274 	}
    275 
    276 	// mark the symbol so it is not reexported
    277 	if s.Def == nil {
    278 		if exportname(s.Name) || initname(s.Name) {
    279 			s.Flags |= SymExport
    280 		} else {
    281 			s.Flags |= SymPackage // package scope
    282 		}
    283 	}
    284 }
    285 
    286 // pkgtype returns the named type declared by symbol s.
    287 // If no such type has been declared yet, a forward declaration is returned.
    288 func pkgtype(s *Sym) *Type {
    289 	importsym(s, OTYPE)
    290 	if s.Def == nil || s.Def.Op != OTYPE {
    291 		t := typ(TFORW)
    292 		t.Sym = s
    293 		s.Def = typenod(t)
    294 		s.Def.Name = new(Name)
    295 	}
    296 
    297 	if s.Def.Type == nil {
    298 		yyerror("pkgtype %v", s)
    299 	}
    300 	return s.Def.Type
    301 }
    302 
    303 // importconst declares symbol s as an imported constant with type t and value n.
    304 func importconst(s *Sym, t *Type, n *Node) {
    305 	importsym(s, OLITERAL)
    306 	n = convlit(n, t)
    307 
    308 	if s.Def != nil { // TODO: check if already the same.
    309 		return
    310 	}
    311 
    312 	if n.Op != OLITERAL {
    313 		yyerror("expression must be a constant")
    314 		return
    315 	}
    316 
    317 	if n.Sym != nil {
    318 		n1 := *n
    319 		n = &n1
    320 	}
    321 
    322 	n.Orig = newname(s)
    323 	n.Sym = s
    324 	declare(n, PEXTERN)
    325 
    326 	if Debug['E'] != 0 {
    327 		fmt.Printf("import const %v\n", s)
    328 	}
    329 }
    330 
    331 // importvar declares symbol s as an imported variable with type t.
    332 func importvar(s *Sym, t *Type) {
    333 	importsym(s, ONAME)
    334 	if s.Def != nil && s.Def.Op == ONAME {
    335 		if eqtype(t, s.Def.Type) {
    336 			return
    337 		}
    338 		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)
    339 	}
    340 
    341 	n := newname(s)
    342 	s.Importdef = importpkg
    343 	n.Type = t
    344 	declare(n, PEXTERN)
    345 
    346 	if Debug['E'] != 0 {
    347 		fmt.Printf("import var %v %L\n", s, t)
    348 	}
    349 }
    350 
    351 func dumpasmhdr() {
    352 	b, err := bio.Create(asmhdr)
    353 	if err != nil {
    354 		Fatalf("%v", err)
    355 	}
    356 	fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", localpkg.Name)
    357 	for _, n := range asmlist {
    358 		if isblanksym(n.Sym) {
    359 			continue
    360 		}
    361 		switch n.Op {
    362 		case OLITERAL:
    363 			fmt.Fprintf(b, "#define const_%s %#v\n", n.Sym.Name, n.Val())
    364 
    365 		case OTYPE:
    366 			t := n.Type
    367 			if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() {
    368 				break
    369 			}
    370 			fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width))
    371 			for _, t := range t.Fields().Slice() {
    372 				if !isblanksym(t.Sym) {
    373 					fmt.Fprintf(b, "#define %s_%s %d\n", n.Sym.Name, t.Sym.Name, int(t.Offset))
    374 				}
    375 			}
    376 		}
    377 	}
    378 
    379 	b.Close()
    380 }
    381