1
2
3
4
5 package noder
6
7 import (
8 "errors"
9 "fmt"
10 "internal/buildcfg"
11 "os"
12 pathpkg "path"
13 "runtime"
14 "sort"
15 "strconv"
16 "strings"
17 "unicode"
18 "unicode/utf8"
19
20 "cmd/compile/internal/base"
21 "cmd/compile/internal/importer"
22 "cmd/compile/internal/ir"
23 "cmd/compile/internal/syntax"
24 "cmd/compile/internal/typecheck"
25 "cmd/compile/internal/types"
26 "cmd/compile/internal/types2"
27 "cmd/internal/archive"
28 "cmd/internal/bio"
29 "cmd/internal/goobj"
30 "cmd/internal/objabi"
31 "cmd/internal/src"
32 )
33
34
35
36
37
38
39 var haveLegacyImports = false
40
41
42
43
44
45
46 var newReadImportFunc = func(data string, pkg1 *types.Pkg, env *types2.Context, packages map[string]*types2.Package) (pkg2 *types2.Package, err error) {
47 panic("unexpected new export data payload")
48 }
49
50 type gcimports struct {
51 ctxt *types2.Context
52 packages map[string]*types2.Package
53 }
54
55 func (m *gcimports) Import(path string) (*types2.Package, error) {
56 return m.ImportFrom(path, "" , 0)
57 }
58
59 func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
60 if mode != 0 {
61 panic("mode must be 0")
62 }
63
64 _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages)
65 return pkg, err
66 }
67
68 func isDriveLetter(b byte) bool {
69 return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
70 }
71
72
73 func islocalname(name string) bool {
74 return strings.HasPrefix(name, "/") ||
75 runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
76 strings.HasPrefix(name, "./") || name == "." ||
77 strings.HasPrefix(name, "../") || name == ".."
78 }
79
80 func openPackage(path string) (*os.File, error) {
81 if islocalname(path) {
82 if base.Flag.NoLocalImports {
83 return nil, errors.New("local imports disallowed")
84 }
85
86 if base.Flag.Cfg.PackageFile != nil {
87 return os.Open(base.Flag.Cfg.PackageFile[path])
88 }
89
90
91
92
93 if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
94 return file, nil
95 }
96 if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
97 return file, nil
98 }
99 return nil, errors.New("file not found")
100 }
101
102
103
104
105 if q := pathpkg.Clean(path); q != path {
106 return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
107 }
108
109 if base.Flag.Cfg.PackageFile != nil {
110 return os.Open(base.Flag.Cfg.PackageFile[path])
111 }
112
113 for _, dir := range base.Flag.Cfg.ImportDirs {
114 if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
115 return file, nil
116 }
117 if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
118 return file, nil
119 }
120 }
121
122 if buildcfg.GOROOT != "" {
123 suffix := ""
124 if base.Flag.InstallSuffix != "" {
125 suffix = "_" + base.Flag.InstallSuffix
126 } else if base.Flag.Race {
127 suffix = "_race"
128 } else if base.Flag.MSan {
129 suffix = "_msan"
130 } else if base.Flag.ASan {
131 suffix = "_asan"
132 }
133
134 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
135 return file, nil
136 }
137 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil {
138 return file, nil
139 }
140 }
141 return nil, errors.New("file not found")
142 }
143
144
145
146 var myheight int
147
148
149
150 func resolveImportPath(path string) (string, error) {
151
152
153
154
155 if path == "main" {
156 return "", errors.New("cannot import \"main\"")
157 }
158
159 if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
160 return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
161 }
162
163 if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
164 path = mapped
165 }
166
167 if islocalname(path) {
168 if path[0] == '/' {
169 return "", errors.New("import path cannot be absolute path")
170 }
171
172 prefix := base.Flag.D
173 if prefix == "" {
174
175
176
177
178 prefix = base.Ctxt.Pathname
179 }
180 path = pathpkg.Join(prefix, path)
181
182 if err := checkImportPath(path, true); err != nil {
183 return "", err
184 }
185 }
186
187 return path, nil
188 }
189
190 func importfile(decl *syntax.ImportDecl) *types.Pkg {
191 path, err := parseImportPath(decl.Path)
192 if err != nil {
193 base.Errorf("%s", err)
194 return nil
195 }
196
197 pkg, _, err := readImportFile(path, typecheck.Target, nil, nil)
198 if err != nil {
199 base.Errorf("%s", err)
200 return nil
201 }
202
203 if pkg != types.UnsafePkg && pkg.Height >= myheight {
204 myheight = pkg.Height + 1
205 }
206 return pkg
207 }
208
209 func parseImportPath(pathLit *syntax.BasicLit) (string, error) {
210 if pathLit.Kind != syntax.StringLit {
211 return "", errors.New("import path must be a string")
212 }
213
214 path, err := strconv.Unquote(pathLit.Value)
215 if err != nil {
216 return "", errors.New("import path must be a string")
217 }
218
219 if err := checkImportPath(path, false); err != nil {
220 return "", err
221 }
222
223 return path, err
224 }
225
226
227
228
229 func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) {
230 path, err = resolveImportPath(path)
231 if err != nil {
232 return
233 }
234
235 if path == "unsafe" {
236 pkg1, pkg2 = types.UnsafePkg, types2.Unsafe
237
238
239
240 if !pkg1.Direct {
241 pkg1.Direct = true
242 target.Imports = append(target.Imports, pkg1)
243 }
244
245 return
246 }
247
248 pkg1 = types.NewPkg(path, "")
249 if packages != nil {
250 pkg2 = packages[path]
251 assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete()))
252 }
253
254 if pkg1.Direct {
255 return
256 }
257 pkg1.Direct = true
258 target.Imports = append(target.Imports, pkg1)
259
260 f, err := openPackage(path)
261 if err != nil {
262 return
263 }
264 defer f.Close()
265
266 r, end, newsize, err := findExportData(f)
267 if err != nil {
268 return
269 }
270
271 if base.Debug.Export != 0 {
272 fmt.Printf("importing %s (%s)\n", path, f.Name())
273 }
274
275 if newsize != 0 {
276
277 end -= newsize
278 var data string
279 data, err = base.MapFile(r.File(), end, newsize)
280 if err != nil {
281 return
282 }
283
284 pkg2, err = newReadImportFunc(data, pkg1, env, packages)
285 } else {
286
287 haveLegacyImports = true
288
289 var c byte
290 switch c, err = r.ReadByte(); {
291 case err != nil:
292 return
293
294 case c != 'i':
295
296
297 err = fmt.Errorf("unexpected package format byte: %v", c)
298 return
299 }
300
301 pos := r.Offset()
302
303
304
305
306 var data string
307 data, err = base.MapFile(r.File(), pos, end-pos)
308 if err != nil {
309 return
310 }
311
312 typecheck.ReadImports(pkg1, data)
313
314 if packages != nil {
315 pkg2, err = importer.ImportData(packages, data, path)
316 if err != nil {
317 return
318 }
319 }
320 }
321
322 err = addFingerprint(path, f, end)
323 return
324 }
325
326
327
328
329 func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) {
330 r = bio.NewReader(f)
331
332
333 line, err := r.ReadString('\n')
334 if err != nil {
335 return
336 }
337
338 if line == "!<arch>\n" {
339
340 sz := int64(archive.ReadHeader(r.Reader, "__.PKGDEF"))
341 if sz <= 0 {
342 err = errors.New("not a package file")
343 return
344 }
345 end = r.Offset() + sz
346 line, err = r.ReadString('\n')
347 if err != nil {
348 return
349 }
350 } else {
351
352
353 var fi os.FileInfo
354 fi, err = f.Stat()
355 if err != nil {
356 return
357 }
358 end = fi.Size()
359 }
360
361 if !strings.HasPrefix(line, "go object ") {
362 err = fmt.Errorf("not a go object file: %s", line)
363 return
364 }
365 if expect := objabi.HeaderString(); line != expect {
366 err = fmt.Errorf("object is [%s] expected [%s]", line, expect)
367 return
368 }
369
370
371 for !strings.HasPrefix(line, "$$") {
372 if strings.HasPrefix(line, "newexportsize ") {
373 fields := strings.Fields(line)
374 newsize, err = strconv.ParseInt(fields[1], 10, 64)
375 if err != nil {
376 return
377 }
378 }
379
380 line, err = r.ReadString('\n')
381 if err != nil {
382 return
383 }
384 }
385
386
387 if line != "$$B\n" {
388 err = errors.New("old export format no longer supported (recompile library)")
389 return
390 }
391
392 return
393 }
394
395
396
397 func addFingerprint(path string, f *os.File, end int64) error {
398 const eom = "\n$$\n"
399 var fingerprint goobj.FingerprintType
400
401 var buf [len(fingerprint) + len(eom)]byte
402 if _, err := f.ReadAt(buf[:], end-int64(len(buf))); err != nil {
403 return err
404 }
405
406
407
408
409
410 if s := string(buf[len(fingerprint):]); s != eom {
411 return fmt.Errorf("expected $$ marker, but found %q", s)
412 }
413
414 copy(fingerprint[:], buf[:])
415
416
417 if base.Flag.Cfg.PackageFile != nil {
418
419 base.Ctxt.AddImport(path, fingerprint)
420 } else {
421
422 file := f.Name()
423 base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
424 }
425 return nil
426 }
427
428
429
430
431
432
433 var reservedimports = []string{
434 "go",
435 "type",
436 }
437
438 func checkImportPath(path string, allowSpace bool) error {
439 if path == "" {
440 return errors.New("import path is empty")
441 }
442
443 if strings.Contains(path, "\x00") {
444 return errors.New("import path contains NUL")
445 }
446
447 for _, ri := range reservedimports {
448 if path == ri {
449 return fmt.Errorf("import path %q is reserved and cannot be used", path)
450 }
451 }
452
453 for _, r := range path {
454 switch {
455 case r == utf8.RuneError:
456 return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
457 case r < 0x20 || r == 0x7f:
458 return fmt.Errorf("import path contains control character: %q", path)
459 case r == '\\':
460 return fmt.Errorf("import path contains backslash; use slash: %q", path)
461 case !allowSpace && unicode.IsSpace(r):
462 return fmt.Errorf("import path contains space character: %q", path)
463 case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
464 return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
465 }
466 }
467
468 return nil
469 }
470
471 func pkgnotused(lineno src.XPos, path string, name string) {
472
473
474
475
476
477
478 elem := path
479 if i := strings.LastIndex(elem, "/"); i >= 0 {
480 elem = elem[i+1:]
481 }
482 if name == "" || elem == name {
483 base.ErrorfAt(lineno, "imported and not used: %q", path)
484 } else {
485 base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name)
486 }
487 }
488
489 func mkpackage(pkgname string) {
490 if types.LocalPkg.Name == "" {
491 if pkgname == "_" {
492 base.Errorf("invalid package name _")
493 }
494 types.LocalPkg.Name = pkgname
495 } else {
496 if pkgname != types.LocalPkg.Name {
497 base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name)
498 }
499 }
500 }
501
502 func clearImports() {
503 type importedPkg struct {
504 pos src.XPos
505 path string
506 name string
507 }
508 var unused []importedPkg
509
510 for _, s := range types.LocalPkg.Syms {
511 n := ir.AsNode(s.Def)
512 if n == nil {
513 continue
514 }
515 if n.Op() == ir.OPACK {
516
517
518
519
520
521 p := n.(*ir.PkgName)
522 if !p.Used && base.SyntaxErrors() == 0 {
523 unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name})
524 }
525 s.Def = nil
526 continue
527 }
528 if s.Def != nil && s.Def.Sym() != s {
529
530
531
532 s.Def = nil
533 continue
534 }
535 }
536
537 sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
538 for _, pkg := range unused {
539 pkgnotused(pkg.pos, pkg.path, pkg.name)
540 }
541 }
542
543
544 func CheckDotImports() {
545 for _, pack := range dotImports {
546 if !pack.Used {
547 base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path)
548 }
549 }
550
551
552 dotImports = nil
553 typecheck.DotImportRefs = nil
554 }
555
556
557 var dotImports []*ir.PkgName
558
559
560
561 func importDot(pack *ir.PkgName) {
562 if typecheck.DotImportRefs == nil {
563 typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName)
564 }
565
566 opkg := pack.Pkg
567 for _, s := range opkg.Syms {
568 if s.Def == nil {
569 if _, ok := typecheck.DeclImporter[s]; !ok {
570 continue
571 }
572 }
573 if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) {
574 continue
575 }
576 s1 := typecheck.Lookup(s.Name)
577 if s1.Def != nil {
578 pkgerror := fmt.Sprintf("during import %q", opkg.Path)
579 typecheck.Redeclared(base.Pos, s1, pkgerror)
580 continue
581 }
582
583 id := ir.NewIdent(src.NoXPos, s)
584 typecheck.DotImportRefs[id] = pack
585 s1.Def = id
586 s1.Block = 1
587 }
588
589 dotImports = append(dotImports, pack)
590 }
591
592
593
594 func importName(sym *types.Sym) ir.Node {
595 n := oldname(sym)
596 if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg {
597 n.SetDiag(true)
598 base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
599 }
600 return n
601 }
602
View as plain text