1// Copyright 2013 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 ssa_test
6
7import (
8	"bytes"
9	"go/ast"
10	"go/importer"
11	"go/parser"
12	"go/token"
13	"go/types"
14	"os"
15	"reflect"
16	"sort"
17	"strings"
18	"testing"
19
20	"golang.org/x/tools/go/loader"
21	"golang.org/x/tools/go/ssa"
22	"golang.org/x/tools/go/ssa/ssautil"
23)
24
25func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
26
27// Tests that programs partially loaded from gc object files contain
28// functions with no code for the external portions, but are otherwise ok.
29func TestBuildPackage(t *testing.T) {
30	input := `
31package main
32
33import (
34	"bytes"
35	"io"
36	"testing"
37)
38
39func main() {
40        var t testing.T
41	t.Parallel()    // static call to external declared method
42        t.Fail()        // static call to promoted external declared method
43        testing.Short() // static call to external package-level function
44
45        var w io.Writer = new(bytes.Buffer)
46        w.Write(nil)    // interface invoke of external declared method
47}
48`
49
50	// Parse the file.
51	fset := token.NewFileSet()
52	f, err := parser.ParseFile(fset, "input.go", input, 0)
53	if err != nil {
54		t.Error(err)
55		return
56	}
57
58	// Build an SSA program from the parsed file.
59	// Load its dependencies from gc binary export data.
60	mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
61		types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
62	if err != nil {
63		t.Error(err)
64		return
65	}
66
67	// The main package, its direct and indirect dependencies are loaded.
68	deps := []string{
69		// directly imported dependencies:
70		"bytes", "io", "testing",
71		// indirect dependencies mentioned by
72		// the direct imports' export data
73		"sync", "unicode", "time",
74	}
75
76	prog := mainPkg.Prog
77	all := prog.AllPackages()
78	if len(all) <= len(deps) {
79		t.Errorf("unexpected set of loaded packages: %q", all)
80	}
81	for _, path := range deps {
82		pkg := prog.ImportedPackage(path)
83		if pkg == nil {
84			t.Errorf("package not loaded: %q", path)
85			continue
86		}
87
88		// External packages should have no function bodies (except for wrappers).
89		isExt := pkg != mainPkg
90
91		// init()
92		if isExt && !isEmpty(pkg.Func("init")) {
93			t.Errorf("external package %s has non-empty init", pkg)
94		} else if !isExt && isEmpty(pkg.Func("init")) {
95			t.Errorf("main package %s has empty init", pkg)
96		}
97
98		for _, mem := range pkg.Members {
99			switch mem := mem.(type) {
100			case *ssa.Function:
101				// Functions at package level.
102				if isExt && !isEmpty(mem) {
103					t.Errorf("external function %s is non-empty", mem)
104				} else if !isExt && isEmpty(mem) {
105					t.Errorf("function %s is empty", mem)
106				}
107
108			case *ssa.Type:
109				// Methods of named types T.
110				// (In this test, all exported methods belong to *T not T.)
111				if !isExt {
112					t.Fatalf("unexpected name type in main package: %s", mem)
113				}
114				mset := prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
115				for i, n := 0, mset.Len(); i < n; i++ {
116					m := prog.MethodValue(mset.At(i))
117					// For external types, only synthetic wrappers have code.
118					expExt := !strings.Contains(m.Synthetic, "wrapper")
119					if expExt && !isEmpty(m) {
120						t.Errorf("external method %s is non-empty: %s",
121							m, m.Synthetic)
122					} else if !expExt && isEmpty(m) {
123						t.Errorf("method function %s is empty: %s",
124							m, m.Synthetic)
125					}
126				}
127			}
128		}
129	}
130
131	expectedCallee := []string{
132		"(*testing.T).Parallel",
133		"(*testing.common).Fail",
134		"testing.Short",
135		"N/A",
136	}
137	callNum := 0
138	for _, b := range mainPkg.Func("main").Blocks {
139		for _, instr := range b.Instrs {
140			switch instr := instr.(type) {
141			case ssa.CallInstruction:
142				call := instr.Common()
143				if want := expectedCallee[callNum]; want != "N/A" {
144					got := call.StaticCallee().String()
145					if want != got {
146						t.Errorf("call #%d from main.main: got callee %s, want %s",
147							callNum, got, want)
148					}
149				}
150				callNum++
151			}
152		}
153	}
154	if callNum != 4 {
155		t.Errorf("in main.main: got %d calls, want %d", callNum, 4)
156	}
157}
158
159// TestRuntimeTypes tests that (*Program).RuntimeTypes() includes all necessary types.
160func TestRuntimeTypes(t *testing.T) {
161	tests := []struct {
162		input string
163		want  []string
164	}{
165		// An exported package-level type is needed.
166		{`package A; type T struct{}; func (T) f() {}`,
167			[]string{"*p.T", "p.T"},
168		},
169		// An unexported package-level type is not needed.
170		{`package B; type t struct{}; func (t) f() {}`,
171			nil,
172		},
173		// Subcomponents of type of exported package-level var are needed.
174		{`package C; import "bytes"; var V struct {*bytes.Buffer}`,
175			[]string{"*bytes.Buffer", "*struct{*bytes.Buffer}", "struct{*bytes.Buffer}"},
176		},
177		// Subcomponents of type of unexported package-level var are not needed.
178		{`package D; import "bytes"; var v struct {*bytes.Buffer}`,
179			nil,
180		},
181		// Subcomponents of type of exported package-level function are needed.
182		{`package E; import "bytes"; func F(struct {*bytes.Buffer}) {}`,
183			[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
184		},
185		// Subcomponents of type of unexported package-level function are not needed.
186		{`package F; import "bytes"; func f(struct {*bytes.Buffer}) {}`,
187			nil,
188		},
189		// Subcomponents of type of exported method of uninstantiated unexported type are not needed.
190		{`package G; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v x`,
191			nil,
192		},
193		// ...unless used by MakeInterface.
194		{`package G2; import "bytes"; type x struct{}; func (x) G(struct {*bytes.Buffer}) {}; var v interface{} = x{}`,
195			[]string{"*bytes.Buffer", "*p.x", "p.x", "struct{*bytes.Buffer}"},
196		},
197		// Subcomponents of type of unexported method are not needed.
198		{`package I; import "bytes"; type X struct{}; func (X) G(struct {*bytes.Buffer}) {}`,
199			[]string{"*bytes.Buffer", "*p.X", "p.X", "struct{*bytes.Buffer}"},
200		},
201		// Local types aren't needed.
202		{`package J; import "bytes"; func f() { type T struct {*bytes.Buffer}; var t T; _ = t }`,
203			nil,
204		},
205		// ...unless used by MakeInterface.
206		{`package K; import "bytes"; func f() { type T struct {*bytes.Buffer}; _ = interface{}(T{}) }`,
207			[]string{"*bytes.Buffer", "*p.T", "p.T"},
208		},
209		// Types used as operand of MakeInterface are needed.
210		{`package L; import "bytes"; func f() { _ = interface{}(struct{*bytes.Buffer}{}) }`,
211			[]string{"*bytes.Buffer", "struct{*bytes.Buffer}"},
212		},
213		// MakeInterface is optimized away when storing to a blank.
214		{`package M; import "bytes"; var _ interface{} = struct{*bytes.Buffer}{}`,
215			nil,
216		},
217	}
218	for _, test := range tests {
219		// Parse the file.
220		fset := token.NewFileSet()
221		f, err := parser.ParseFile(fset, "input.go", test.input, 0)
222		if err != nil {
223			t.Errorf("test %q: %s", test.input[:15], err)
224			continue
225		}
226
227		// Create a single-file main package.
228		// Load dependencies from gc binary export data.
229		ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
230			types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
231		if err != nil {
232			t.Errorf("test %q: %s", test.input[:15], err)
233			continue
234		}
235
236		var typstrs []string
237		for _, T := range ssapkg.Prog.RuntimeTypes() {
238			typstrs = append(typstrs, T.String())
239		}
240		sort.Strings(typstrs)
241
242		if !reflect.DeepEqual(typstrs, test.want) {
243			t.Errorf("test 'package %s': got %q, want %q",
244				f.Name.Name, typstrs, test.want)
245		}
246	}
247}
248
249// TestInit tests that synthesized init functions are correctly formed.
250// Bare init functions omit calls to dependent init functions and the use of
251// an init guard. They are useful in cases where the client uses a different
252// calling convention for init functions, or cases where it is easier for a
253// client to analyze bare init functions. Both of these aspects are used by
254// the llgo compiler for simpler integration with gccgo's runtime library,
255// and to simplify the analysis whereby it deduces which stores to globals
256// can be lowered to global initializers.
257func TestInit(t *testing.T) {
258	tests := []struct {
259		mode        ssa.BuilderMode
260		input, want string
261	}{
262		{0, `package A; import _ "errors"; var i int = 42`,
263			`# Name: A.init
264# Package: A
265# Synthetic: package initializer
266func init():
2670:                                                                entry P:0 S:2
268	t0 = *init$guard                                                   bool
269	if t0 goto 2 else 1
2701:                                                           init.start P:1 S:1
271	*init$guard = true:bool
272	t1 = errors.init()                                                   ()
273	*i = 42:int
274	jump 2
2752:                                                            init.done P:2 S:0
276	return
277
278`},
279		{ssa.BareInits, `package B; import _ "errors"; var i int = 42`,
280			`# Name: B.init
281# Package: B
282# Synthetic: package initializer
283func init():
2840:                                                                entry P:0 S:0
285	*i = 42:int
286	return
287
288`},
289	}
290	for _, test := range tests {
291		// Create a single-file main package.
292		var conf loader.Config
293		f, err := conf.ParseFile("<input>", test.input)
294		if err != nil {
295			t.Errorf("test %q: %s", test.input[:15], err)
296			continue
297		}
298		conf.CreateFromFiles(f.Name.Name, f)
299
300		lprog, err := conf.Load()
301		if err != nil {
302			t.Errorf("test 'package %s': Load: %s", f.Name.Name, err)
303			continue
304		}
305		prog := ssautil.CreateProgram(lprog, test.mode)
306		mainPkg := prog.Package(lprog.Created[0].Pkg)
307		prog.Build()
308		initFunc := mainPkg.Func("init")
309		if initFunc == nil {
310			t.Errorf("test 'package %s': no init function", f.Name.Name)
311			continue
312		}
313
314		var initbuf bytes.Buffer
315		_, err = initFunc.WriteTo(&initbuf)
316		if err != nil {
317			t.Errorf("test 'package %s': WriteTo: %s", f.Name.Name, err)
318			continue
319		}
320
321		if initbuf.String() != test.want {
322			t.Errorf("test 'package %s': got %s, want %s", f.Name.Name, initbuf.String(), test.want)
323		}
324	}
325}
326
327// TestSyntheticFuncs checks that the expected synthetic functions are
328// created, reachable, and not duplicated.
329func TestSyntheticFuncs(t *testing.T) {
330	const input = `package P
331type T int
332func (T) f() int
333func (*T) g() int
334var (
335	// thunks
336	a = T.f
337	b = T.f
338	c = (struct{T}).f
339	d = (struct{T}).f
340	e = (*T).g
341	f = (*T).g
342	g = (struct{*T}).g
343	h = (struct{*T}).g
344
345	// bounds
346	i = T(0).f
347	j = T(0).f
348	k = new(T).g
349	l = new(T).g
350
351	// wrappers
352	m interface{} = struct{T}{}
353	n interface{} = struct{T}{}
354	o interface{} = struct{*T}{}
355	p interface{} = struct{*T}{}
356	q interface{} = new(struct{T})
357	r interface{} = new(struct{T})
358	s interface{} = new(struct{*T})
359	t interface{} = new(struct{*T})
360)
361`
362	// Parse
363	var conf loader.Config
364	f, err := conf.ParseFile("<input>", input)
365	if err != nil {
366		t.Fatalf("parse: %v", err)
367	}
368	conf.CreateFromFiles(f.Name.Name, f)
369
370	// Load
371	lprog, err := conf.Load()
372	if err != nil {
373		t.Fatalf("Load: %v", err)
374	}
375
376	// Create and build SSA
377	prog := ssautil.CreateProgram(lprog, 0)
378	prog.Build()
379
380	// Enumerate reachable synthetic functions
381	want := map[string]string{
382		"(*P.T).g$bound": "bound method wrapper for func (*P.T).g() int",
383		"(P.T).f$bound":  "bound method wrapper for func (P.T).f() int",
384
385		"(*P.T).g$thunk":         "thunk for func (*P.T).g() int",
386		"(P.T).f$thunk":          "thunk for func (P.T).f() int",
387		"(struct{*P.T}).g$thunk": "thunk for func (*P.T).g() int",
388		"(struct{P.T}).f$thunk":  "thunk for func (P.T).f() int",
389
390		"(*P.T).f":          "wrapper for func (P.T).f() int",
391		"(*struct{*P.T}).f": "wrapper for func (P.T).f() int",
392		"(*struct{*P.T}).g": "wrapper for func (*P.T).g() int",
393		"(*struct{P.T}).f":  "wrapper for func (P.T).f() int",
394		"(*struct{P.T}).g":  "wrapper for func (*P.T).g() int",
395		"(struct{*P.T}).f":  "wrapper for func (P.T).f() int",
396		"(struct{*P.T}).g":  "wrapper for func (*P.T).g() int",
397		"(struct{P.T}).f":   "wrapper for func (P.T).f() int",
398
399		"P.init": "package initializer",
400	}
401	for fn := range ssautil.AllFunctions(prog) {
402		if fn.Synthetic == "" {
403			continue
404		}
405		name := fn.String()
406		wantDescr, ok := want[name]
407		if !ok {
408			t.Errorf("got unexpected/duplicate func: %q: %q", name, fn.Synthetic)
409			continue
410		}
411		delete(want, name)
412
413		if wantDescr != fn.Synthetic {
414			t.Errorf("(%s).Synthetic = %q, want %q", name, fn.Synthetic, wantDescr)
415		}
416	}
417	for fn, descr := range want {
418		t.Errorf("want func: %q: %q", fn, descr)
419	}
420}
421
422// TestPhiElimination ensures that dead phis, including those that
423// participate in a cycle, are properly eliminated.
424func TestPhiElimination(t *testing.T) {
425	const input = `
426package p
427
428func f() error
429
430func g(slice []int) {
431	for {
432		for range slice {
433			// e should not be lifted to a dead φ-node.
434			e := f()
435			h(e)
436		}
437	}
438}
439
440func h(error)
441`
442	// The SSA code for this function should look something like this:
443	// 0:
444	//         jump 1
445	// 1:
446	//         t0 = len(slice)
447	//         jump 2
448	// 2:
449	//         t1 = phi [1: -1:int, 3: t2]
450	//         t2 = t1 + 1:int
451	//         t3 = t2 < t0
452	//         if t3 goto 3 else 1
453	// 3:
454	//         t4 = f()
455	//         t5 = h(t4)
456	//         jump 2
457	//
458	// But earlier versions of the SSA construction algorithm would
459	// additionally generate this cycle of dead phis:
460	//
461	// 1:
462	//         t7 = phi [0: nil:error, 2: t8] #e
463	//         ...
464	// 2:
465	//         t8 = phi [1: t7, 3: t4] #e
466	//         ...
467
468	// Parse
469	var conf loader.Config
470	f, err := conf.ParseFile("<input>", input)
471	if err != nil {
472		t.Fatalf("parse: %v", err)
473	}
474	conf.CreateFromFiles("p", f)
475
476	// Load
477	lprog, err := conf.Load()
478	if err != nil {
479		t.Fatalf("Load: %v", err)
480	}
481
482	// Create and build SSA
483	prog := ssautil.CreateProgram(lprog, 0)
484	p := prog.Package(lprog.Package("p").Pkg)
485	p.Build()
486	g := p.Func("g")
487
488	phis := 0
489	for _, b := range g.Blocks {
490		for _, instr := range b.Instrs {
491			if _, ok := instr.(*ssa.Phi); ok {
492				phis++
493			}
494		}
495	}
496	if phis != 1 {
497		g.WriteTo(os.Stderr)
498		t.Errorf("expected a single Phi (for the range index), got %d", phis)
499	}
500}
501