1
2
3
4
5 package importer
6
7 import (
8 "bytes"
9 "cmd/compile/internal/types2"
10 "fmt"
11 "internal/goexperiment"
12 "internal/testenv"
13 "os"
14 "os/exec"
15 "path/filepath"
16 "runtime"
17 "strings"
18 "testing"
19 "time"
20 )
21
22
23
24
25 func skipSpecialPlatforms(t *testing.T) {
26 switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
27 case "darwin-arm64":
28 t.Skipf("no compiled packages available for import on %s", platform)
29 }
30 }
31
32
33
34 func compile(t *testing.T, dirname, filename, outdirname string) string {
35
36 if !strings.HasSuffix(filename, ".go") {
37 t.Fatalf("filename doesn't end in .go: %s", filename)
38 }
39 basename := filepath.Base(filename)
40 outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
41 cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-o", outname, filename)
42 cmd.Dir = dirname
43 out, err := cmd.CombinedOutput()
44 if err != nil {
45 t.Logf("%s", out)
46 t.Fatalf("go tool compile %s failed: %s", filename, err)
47 }
48 return outname
49 }
50
51 func testPath(t *testing.T, path, srcDir string) *types2.Package {
52 t0 := time.Now()
53 pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
54 if err != nil {
55 t.Errorf("testPath(%s): %s", path, err)
56 return nil
57 }
58 t.Logf("testPath(%s): %v", path, time.Since(t0))
59 return pkg
60 }
61
62 const maxTime = 30 * time.Second
63
64 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
65 dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
66 list, err := os.ReadDir(dirname)
67 if err != nil {
68 t.Fatalf("testDir(%s): %s", dirname, err)
69 }
70 for _, f := range list {
71 if time.Now().After(endTime) {
72 t.Log("testing time used up")
73 return
74 }
75 switch {
76 case !f.IsDir():
77
78 for _, ext := range pkgExts {
79 if strings.HasSuffix(f.Name(), ext) {
80 name := f.Name()[0 : len(f.Name())-len(ext)]
81 if testPath(t, filepath.Join(dir, name), dir) != nil {
82 nimports++
83 }
84 }
85 }
86 case f.IsDir():
87 nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
88 }
89 }
90 return
91 }
92
93 func mktmpdir(t *testing.T) string {
94 tmpdir, err := os.MkdirTemp("", "gcimporter_test")
95 if err != nil {
96 t.Fatal("mktmpdir:", err)
97 }
98 if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
99 os.RemoveAll(tmpdir)
100 t.Fatal("mktmpdir:", err)
101 }
102 return tmpdir
103 }
104
105 func TestImportTestdata(t *testing.T) {
106
107 if runtime.Compiler != "gc" {
108 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
109 }
110
111 testfiles := map[string][]string{
112 "exports.go": {"go/ast", "go/token"},
113 }
114 if !goexperiment.Unified {
115 testfiles["generics.go"] = nil
116 }
117
118 for testfile, wantImports := range testfiles {
119 tmpdir := mktmpdir(t)
120 defer os.RemoveAll(tmpdir)
121
122 compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"))
123 path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
124
125 if pkg := testPath(t, path, tmpdir); pkg != nil {
126
127
128
129 got := fmt.Sprint(pkg.Imports())
130 for _, want := range wantImports {
131 if !strings.Contains(got, want) {
132 t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
133 }
134 }
135 }
136 }
137 }
138
139 func TestVersionHandling(t *testing.T) {
140 skipSpecialPlatforms(t)
141
142
143 if runtime.Compiler != "gc" {
144 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
145 }
146
147 const dir = "./testdata/versions"
148 list, err := os.ReadDir(dir)
149 if err != nil {
150 t.Fatal(err)
151 }
152
153 tmpdir := mktmpdir(t)
154 defer os.RemoveAll(tmpdir)
155 corruptdir := filepath.Join(tmpdir, "testdata", "versions")
156 if err := os.Mkdir(corruptdir, 0700); err != nil {
157 t.Fatal(err)
158 }
159
160 for _, f := range list {
161 name := f.Name()
162 if !strings.HasSuffix(name, ".a") {
163 continue
164 }
165 if strings.Contains(name, "corrupted") {
166 continue
167 }
168 pkgpath := "./" + name[:len(name)-2]
169
170 if testing.Verbose() {
171 t.Logf("importing %s", name)
172 }
173
174
175 _, err := Import(make(map[string]*types2.Package), pkgpath, dir, nil)
176 if err != nil {
177
178 if strings.Contains(err.Error(), "no longer supported") {
179 switch name {
180 case "test_go1.7_0.a", "test_go1.7_1.a",
181 "test_go1.8_4.a", "test_go1.8_5.a",
182 "test_go1.11_6b.a", "test_go1.11_999b.a":
183 continue
184 }
185
186 }
187
188 if strings.Contains(err.Error(), "newer version") {
189 switch name {
190 case "test_go1.11_999i.a":
191 continue
192 }
193
194 }
195 t.Errorf("import %q failed: %v", pkgpath, err)
196 continue
197 }
198
199
200
201 data, err := os.ReadFile(filepath.Join(dir, name))
202 if err != nil {
203 t.Fatal(err)
204 }
205
206 i := bytes.Index(data, []byte("\n$$B\n")) + 5
207 j := bytes.Index(data[i:], []byte("\n$$\n")) + i
208 if i < 0 || j < 0 || i > j {
209 t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
210 }
211
212 for k := j - 13; k >= i; k -= 7 {
213 data[k]++
214 }
215
216 pkgpath += "_corrupted"
217 filename := filepath.Join(corruptdir, pkgpath) + ".a"
218 os.WriteFile(filename, data, 0666)
219
220
221 _, err = Import(make(map[string]*types2.Package), pkgpath, corruptdir, nil)
222 if err == nil {
223 t.Errorf("import corrupted %q succeeded", pkgpath)
224 } else if msg := err.Error(); !strings.Contains(msg, "version skew") {
225 t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
226 }
227 }
228 }
229
230 func TestImportStdLib(t *testing.T) {
231 skipSpecialPlatforms(t)
232
233
234 if runtime.Compiler != "gc" {
235 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
236 }
237
238 dt := maxTime
239 if testing.Short() && testenv.Builder() == "" {
240 dt = 10 * time.Millisecond
241 }
242 nimports := testDir(t, "", time.Now().Add(dt))
243 t.Logf("tested %d imports", nimports)
244 }
245
246 var importedObjectTests = []struct {
247 name string
248 want string
249 }{
250
251 {"crypto.Hash", "type Hash uint"},
252 {"go/ast.ObjKind", "type ObjKind int"},
253 {"go/types.Qualifier", "type Qualifier func(*Package) string"},
254 {"go/types.Comparable", "func Comparable(T Type) bool"},
255 {"math.Pi", "const Pi untyped float"},
256 {"math.Sin", "func Sin(x float64) float64"},
257 {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
258 {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
259
260
261 {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
262 {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
263 {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
264 {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
265 {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
266 {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
267 {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
268 }
269
270 func TestImportedTypes(t *testing.T) {
271 skipSpecialPlatforms(t)
272
273
274 if runtime.Compiler != "gc" {
275 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
276 }
277
278 for _, test := range importedObjectTests {
279 s := strings.Split(test.name, ".")
280 if len(s) != 2 {
281 t.Fatal("inconsistent test data")
282 }
283 importPath := s[0]
284 objName := s[1]
285
286 pkg, err := Import(make(map[string]*types2.Package), importPath, ".", nil)
287 if err != nil {
288 t.Error(err)
289 continue
290 }
291
292 obj := pkg.Scope().Lookup(objName)
293 if obj == nil {
294 t.Errorf("%s: object not found", test.name)
295 continue
296 }
297
298 got := types2.ObjectString(obj, types2.RelativeTo(pkg))
299 if got != test.want {
300 t.Errorf("%s: got %q; want %q", test.name, got, test.want)
301 }
302
303 if named, _ := obj.Type().(*types2.Named); named != nil {
304 verifyInterfaceMethodRecvs(t, named, 0)
305 }
306 }
307 }
308
309
310
311 func verifyInterfaceMethodRecvs(t *testing.T, named *types2.Named, level int) {
312
313 if level > 10 {
314 t.Errorf("%s: embeds itself", named)
315 return
316 }
317
318 iface, _ := named.Underlying().(*types2.Interface)
319 if iface == nil {
320 return
321 }
322
323
324 for i := 0; i < iface.NumExplicitMethods(); i++ {
325 m := iface.ExplicitMethod(i)
326 recv := m.Type().(*types2.Signature).Recv()
327 if recv == nil {
328 t.Errorf("%s: missing receiver type", m)
329 continue
330 }
331 if recv.Type() != named {
332 t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
333 }
334 }
335
336
337 for i := 0; i < iface.NumEmbeddeds(); i++ {
338
339 if etype, _ := iface.EmbeddedType(i).(*types2.Named); etype != nil {
340 verifyInterfaceMethodRecvs(t, etype, level+1)
341 }
342 }
343 }
344
345 func TestIssue5815(t *testing.T) {
346 skipSpecialPlatforms(t)
347
348
349 if runtime.Compiler != "gc" {
350 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
351 }
352
353 pkg := importPkg(t, "strings", ".")
354
355 scope := pkg.Scope()
356 for _, name := range scope.Names() {
357 obj := scope.Lookup(name)
358 if obj.Pkg() == nil {
359 t.Errorf("no pkg for %s", obj)
360 }
361 if tname, _ := obj.(*types2.TypeName); tname != nil {
362 named := tname.Type().(*types2.Named)
363 for i := 0; i < named.NumMethods(); i++ {
364 m := named.Method(i)
365 if m.Pkg() == nil {
366 t.Errorf("no pkg for %s", m)
367 }
368 }
369 }
370 }
371 }
372
373
374 func TestCorrectMethodPackage(t *testing.T) {
375 skipSpecialPlatforms(t)
376
377
378 if runtime.Compiler != "gc" {
379 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
380 }
381
382 imports := make(map[string]*types2.Package)
383 _, err := Import(imports, "net/http", ".", nil)
384 if err != nil {
385 t.Fatal(err)
386 }
387
388 mutex := imports["sync"].Scope().Lookup("Mutex").(*types2.TypeName).Type()
389 obj, _, _ := types2.LookupFieldOrMethod(types2.NewPointer(mutex), false, nil, "Lock")
390 lock := obj.(*types2.Func)
391 if got, want := lock.Pkg().Path(), "sync"; got != want {
392 t.Errorf("got package path %q; want %q", got, want)
393 }
394 }
395
396 func TestIssue13566(t *testing.T) {
397 skipSpecialPlatforms(t)
398
399
400 if runtime.Compiler != "gc" {
401 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
402 }
403
404
405
406 if runtime.GOOS == "windows" {
407 t.Skip("avoid dealing with relative paths/drive letters on windows")
408 }
409
410 tmpdir := mktmpdir(t)
411 defer os.RemoveAll(tmpdir)
412 testoutdir := filepath.Join(tmpdir, "testdata")
413
414
415
416
417 bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
418 if err != nil {
419 t.Fatal(err)
420 }
421 compile(t, "testdata", "a.go", testoutdir)
422 compile(t, testoutdir, bpath, testoutdir)
423
424
425 pkg := importPkg(t, "./testdata/b", tmpdir)
426
427
428 for _, imp := range pkg.Imports() {
429 if imp.Name() == "" {
430 t.Errorf("no name for %s package", imp.Path())
431 }
432 }
433 }
434
435 func TestIssue13898(t *testing.T) {
436 skipSpecialPlatforms(t)
437
438
439 if runtime.Compiler != "gc" {
440 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
441 }
442
443
444 imports := make(map[string]*types2.Package)
445 _, err := Import(imports, "go/internal/gcimporter", ".", nil)
446 if err != nil {
447 t.Fatal(err)
448 }
449
450
451 var goTypesPkg *types2.Package
452 for path, pkg := range imports {
453 if path == "go/types" {
454 goTypesPkg = pkg
455 break
456 }
457 }
458 if goTypesPkg == nil {
459 t.Fatal("go/types not found")
460 }
461
462
463 obj := lookupObj(t, goTypesPkg.Scope(), "Object")
464 typ, ok := obj.Type().(*types2.Named)
465 if !ok {
466 t.Fatalf("go/types.Object type is %v; wanted named type", typ)
467 }
468
469
470 m, index, indirect := types2.LookupFieldOrMethod(typ, false, nil, "Pkg")
471 if m == nil {
472 t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
473 }
474
475
476 if m.Pkg().Path() != "go/types" {
477 t.Fatalf("found %v; want go/types", m.Pkg())
478 }
479 }
480
481 func TestIssue15517(t *testing.T) {
482 skipSpecialPlatforms(t)
483
484
485 if runtime.Compiler != "gc" {
486 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
487 }
488
489
490
491 if runtime.GOOS == "windows" {
492 t.Skip("avoid dealing with relative paths/drive letters on windows")
493 }
494
495 tmpdir := mktmpdir(t)
496 defer os.RemoveAll(tmpdir)
497
498 compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
499
500
501
502
503
504
505
506
507
508
509
510
511
512 imports := make(map[string]*types2.Package)
513 for i := 0; i < 3; i++ {
514 if _, err := Import(imports, "./././testdata/p", tmpdir, nil); err != nil {
515 t.Fatal(err)
516 }
517 }
518 }
519
520 func TestIssue15920(t *testing.T) {
521 skipSpecialPlatforms(t)
522
523
524 if runtime.Compiler != "gc" {
525 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
526 }
527
528
529
530 if runtime.GOOS == "windows" {
531 t.Skip("avoid dealing with relative paths/drive letters on windows")
532 }
533
534 compileAndImportPkg(t, "issue15920")
535 }
536
537 func TestIssue20046(t *testing.T) {
538 skipSpecialPlatforms(t)
539
540
541 if runtime.Compiler != "gc" {
542 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
543 }
544
545
546
547 if runtime.GOOS == "windows" {
548 t.Skip("avoid dealing with relative paths/drive letters on windows")
549 }
550
551
552 pkg := compileAndImportPkg(t, "issue20046")
553 obj := lookupObj(t, pkg.Scope(), "V")
554 if m, index, indirect := types2.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
555 t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
556 }
557 }
558 func TestIssue25301(t *testing.T) {
559 skipSpecialPlatforms(t)
560
561
562 if runtime.Compiler != "gc" {
563 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
564 }
565
566
567
568 if runtime.GOOS == "windows" {
569 t.Skip("avoid dealing with relative paths/drive letters on windows")
570 }
571
572 compileAndImportPkg(t, "issue25301")
573 }
574
575 func TestIssue25596(t *testing.T) {
576 skipSpecialPlatforms(t)
577
578
579 if runtime.Compiler != "gc" {
580 t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
581 }
582
583
584
585 if runtime.GOOS == "windows" {
586 t.Skip("avoid dealing with relative paths/drive letters on windows")
587 }
588
589 compileAndImportPkg(t, "issue25596")
590 }
591
592 func importPkg(t *testing.T, path, srcDir string) *types2.Package {
593 pkg, err := Import(make(map[string]*types2.Package), path, srcDir, nil)
594 if err != nil {
595 t.Fatal(err)
596 }
597 return pkg
598 }
599
600 func compileAndImportPkg(t *testing.T, name string) *types2.Package {
601 tmpdir := mktmpdir(t)
602 defer os.RemoveAll(tmpdir)
603 compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
604 return importPkg(t, "./testdata/"+name, tmpdir)
605 }
606
607 func lookupObj(t *testing.T, scope *types2.Scope, name string) types2.Object {
608 if obj := scope.Lookup(name); obj != nil {
609 return obj
610 }
611 t.Fatalf("%s not found", name)
612 return nil
613 }
614
View as plain text