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