]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/noder/import.go
e6e3fe1834253de8babdf64c3537b37be512eb93
[gostls13.git] / src / cmd / compile / internal / noder / import.go
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 noder
6
7 import (
8         "errors"
9         "fmt"
10         "io"
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 // Temporary import helper to get type2-based type-checking going.
35 type gcimports struct {
36         packages map[string]*types2.Package
37 }
38
39 func (m *gcimports) Import(path string) (*types2.Package, error) {
40         return m.ImportFrom(path, "" /* no vendoring */, 0)
41 }
42
43 func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
44         if mode != 0 {
45                 panic("mode must be 0")
46         }
47
48         path, err := resolveImportPath(path)
49         if err != nil {
50                 return nil, err
51         }
52
53         lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
54         return importer.Import(m.packages, path, srcDir, lookup)
55 }
56
57 func isDriveLetter(b byte) bool {
58         return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
59 }
60
61 // is this path a local name? begins with ./ or ../ or /
62 func islocalname(name string) bool {
63         return strings.HasPrefix(name, "/") ||
64                 runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
65                 strings.HasPrefix(name, "./") || name == "." ||
66                 strings.HasPrefix(name, "../") || name == ".."
67 }
68
69 func openPackage(path string) (*os.File, error) {
70         if islocalname(path) {
71                 if base.Flag.NoLocalImports {
72                         return nil, errors.New("local imports disallowed")
73                 }
74
75                 if base.Flag.Cfg.PackageFile != nil {
76                         return os.Open(base.Flag.Cfg.PackageFile[path])
77                 }
78
79                 // try .a before .o.  important for building libraries:
80                 // if there is an array.o in the array.a library,
81                 // want to find all of array.a, not just array.o.
82                 if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
83                         return file, nil
84                 }
85                 if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
86                         return file, nil
87                 }
88                 return nil, errors.New("file not found")
89         }
90
91         // local imports should be canonicalized already.
92         // don't want to see "encoding/../encoding/base64"
93         // as different from "encoding/base64".
94         if q := pathpkg.Clean(path); q != path {
95                 return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
96         }
97
98         if base.Flag.Cfg.PackageFile != nil {
99                 return os.Open(base.Flag.Cfg.PackageFile[path])
100         }
101
102         for _, dir := range base.Flag.Cfg.ImportDirs {
103                 if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
104                         return file, nil
105                 }
106                 if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
107                         return file, nil
108                 }
109         }
110
111         if objabi.GOROOT != "" {
112                 suffix := ""
113                 if base.Flag.InstallSuffix != "" {
114                         suffix = "_" + base.Flag.InstallSuffix
115                 } else if base.Flag.Race {
116                         suffix = "_race"
117                 } else if base.Flag.MSan {
118                         suffix = "_msan"
119                 }
120
121                 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
122                         return file, nil
123                 }
124                 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
125                         return file, nil
126                 }
127         }
128         return nil, errors.New("file not found")
129 }
130
131 // myheight tracks the local package's height based on packages
132 // imported so far.
133 var myheight int
134
135 // resolveImportPath resolves an import path as it appears in a Go
136 // source file to the package's full path.
137 func resolveImportPath(path string) (string, error) {
138         // The package name main is no longer reserved,
139         // but we reserve the import path "main" to identify
140         // the main package, just as we reserve the import
141         // path "math" to identify the standard math package.
142         if path == "main" {
143                 return "", errors.New("cannot import \"main\"")
144         }
145
146         if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
147                 return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
148         }
149
150         if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
151                 path = mapped
152         }
153
154         if islocalname(path) {
155                 if path[0] == '/' {
156                         return "", errors.New("import path cannot be absolute path")
157                 }
158
159                 prefix := base.Flag.D
160                 if prefix == "" {
161                         // Questionable, but when -D isn't specified, historically we
162                         // resolve local import paths relative to the directory the
163                         // compiler's current directory, not the respective source
164                         // file's directory.
165                         prefix = base.Ctxt.Pathname
166                 }
167                 path = pathpkg.Join(prefix, path)
168
169                 if err := checkImportPath(path, true); err != nil {
170                         return "", err
171                 }
172         }
173
174         return path, nil
175 }
176
177 // TODO(mdempsky): Return an error instead.
178 func importfile(decl *syntax.ImportDecl) *types.Pkg {
179         if decl.Path.Kind != syntax.StringLit {
180                 base.Errorf("import path must be a string")
181                 return nil
182         }
183
184         path, err := strconv.Unquote(decl.Path.Value)
185         if err != nil {
186                 base.Errorf("import path must be a string")
187                 return nil
188         }
189
190         if err := checkImportPath(path, false); err != nil {
191                 base.Errorf("%s", err.Error())
192                 return nil
193         }
194
195         path, err = resolveImportPath(path)
196         if err != nil {
197                 base.Errorf("%s", err)
198                 return nil
199         }
200
201         importpkg := types.NewPkg(path, "")
202         if importpkg.Direct {
203                 return importpkg // already fully loaded
204         }
205         importpkg.Direct = true
206         typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
207
208         if path == "unsafe" {
209                 return importpkg // initialized with universe
210         }
211
212         f, err := openPackage(path)
213         if err != nil {
214                 base.Errorf("could not import %q: %v", path, err)
215                 base.ErrorExit()
216         }
217         imp := bio.NewReader(f)
218         defer imp.Close()
219         file := f.Name()
220
221         // check object header
222         p, err := imp.ReadString('\n')
223         if err != nil {
224                 base.Errorf("import %s: reading input: %v", file, err)
225                 base.ErrorExit()
226         }
227
228         if p == "!<arch>\n" { // package archive
229                 // package export block should be first
230                 sz := archive.ReadHeader(imp.Reader, "__.PKGDEF")
231                 if sz <= 0 {
232                         base.Errorf("import %s: not a package file", file)
233                         base.ErrorExit()
234                 }
235                 p, err = imp.ReadString('\n')
236                 if err != nil {
237                         base.Errorf("import %s: reading input: %v", file, err)
238                         base.ErrorExit()
239                 }
240         }
241
242         if !strings.HasPrefix(p, "go object ") {
243                 base.Errorf("import %s: not a go object file: %s", file, p)
244                 base.ErrorExit()
245         }
246         q := objabi.HeaderString()
247         if p != q {
248                 base.Errorf("import %s: object is [%s] expected [%s]", file, p, q)
249                 base.ErrorExit()
250         }
251
252         // process header lines
253         for {
254                 p, err = imp.ReadString('\n')
255                 if err != nil {
256                         base.Errorf("import %s: reading input: %v", file, err)
257                         base.ErrorExit()
258                 }
259                 if p == "\n" {
260                         break // header ends with blank line
261                 }
262         }
263
264         // Expect $$B\n to signal binary import format.
265
266         // look for $$
267         var c byte
268         for {
269                 c, err = imp.ReadByte()
270                 if err != nil {
271                         break
272                 }
273                 if c == '$' {
274                         c, err = imp.ReadByte()
275                         if c == '$' || err != nil {
276                                 break
277                         }
278                 }
279         }
280
281         // get character after $$
282         if err == nil {
283                 c, _ = imp.ReadByte()
284         }
285
286         var fingerprint goobj.FingerprintType
287         switch c {
288         case '\n':
289                 base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
290                 return nil
291
292         case 'B':
293                 if base.Debug.Export != 0 {
294                         fmt.Printf("importing %s (%s)\n", path, file)
295                 }
296                 imp.ReadByte() // skip \n after $$B
297
298                 c, err = imp.ReadByte()
299                 if err != nil {
300                         base.Errorf("import %s: reading input: %v", file, err)
301                         base.ErrorExit()
302                 }
303
304                 // Indexed format is distinguished by an 'i' byte,
305                 // whereas previous export formats started with 'c', 'd', or 'v'.
306                 if c != 'i' {
307                         base.Errorf("import %s: unexpected package format byte: %v", file, c)
308                         base.ErrorExit()
309                 }
310                 fingerprint = typecheck.ReadImports(importpkg, imp)
311
312         default:
313                 base.Errorf("no import in %q", path)
314                 base.ErrorExit()
315         }
316
317         // assume files move (get installed) so don't record the full path
318         if base.Flag.Cfg.PackageFile != nil {
319                 // If using a packageFile map, assume path_ can be recorded directly.
320                 base.Ctxt.AddImport(path, fingerprint)
321         } else {
322                 // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
323                 base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
324         }
325
326         if importpkg.Height >= myheight {
327                 myheight = importpkg.Height + 1
328         }
329
330         return importpkg
331 }
332
333 // The linker uses the magic symbol prefixes "go." and "type."
334 // Avoid potential confusion between import paths and symbols
335 // by rejecting these reserved imports for now. Also, people
336 // "can do weird things in GOPATH and we'd prefer they didn't
337 // do _that_ weird thing" (per rsc). See also #4257.
338 var reservedimports = []string{
339         "go",
340         "type",
341 }
342
343 func checkImportPath(path string, allowSpace bool) error {
344         if path == "" {
345                 return errors.New("import path is empty")
346         }
347
348         if strings.Contains(path, "\x00") {
349                 return errors.New("import path contains NUL")
350         }
351
352         for _, ri := range reservedimports {
353                 if path == ri {
354                         return fmt.Errorf("import path %q is reserved and cannot be used", path)
355                 }
356         }
357
358         for _, r := range path {
359                 switch {
360                 case r == utf8.RuneError:
361                         return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
362                 case r < 0x20 || r == 0x7f:
363                         return fmt.Errorf("import path contains control character: %q", path)
364                 case r == '\\':
365                         return fmt.Errorf("import path contains backslash; use slash: %q", path)
366                 case !allowSpace && unicode.IsSpace(r):
367                         return fmt.Errorf("import path contains space character: %q", path)
368                 case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
369                         return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
370                 }
371         }
372
373         return nil
374 }
375
376 func pkgnotused(lineno src.XPos, path string, name string) {
377         // If the package was imported with a name other than the final
378         // import path element, show it explicitly in the error message.
379         // Note that this handles both renamed imports and imports of
380         // packages containing unconventional package declarations.
381         // Note that this uses / always, even on Windows, because Go import
382         // paths always use forward slashes.
383         elem := path
384         if i := strings.LastIndex(elem, "/"); i >= 0 {
385                 elem = elem[i+1:]
386         }
387         if name == "" || elem == name {
388                 base.ErrorfAt(lineno, "imported and not used: %q", path)
389         } else {
390                 base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name)
391         }
392 }
393
394 func mkpackage(pkgname string) {
395         if types.LocalPkg.Name == "" {
396                 if pkgname == "_" {
397                         base.Errorf("invalid package name _")
398                 }
399                 types.LocalPkg.Name = pkgname
400         } else {
401                 if pkgname != types.LocalPkg.Name {
402                         base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name)
403                 }
404         }
405 }
406
407 func clearImports() {
408         type importedPkg struct {
409                 pos  src.XPos
410                 path string
411                 name string
412         }
413         var unused []importedPkg
414
415         for _, s := range types.LocalPkg.Syms {
416                 n := ir.AsNode(s.Def)
417                 if n == nil {
418                         continue
419                 }
420                 if n.Op() == ir.OPACK {
421                         // throw away top-level package name left over
422                         // from previous file.
423                         // leave s->block set to cause redeclaration
424                         // errors if a conflicting top-level name is
425                         // introduced by a different file.
426                         p := n.(*ir.PkgName)
427                         if !p.Used && base.SyntaxErrors() == 0 {
428                                 unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name})
429                         }
430                         s.Def = nil
431                         continue
432                 }
433                 if types.IsDotAlias(s) {
434                         // throw away top-level name left over
435                         // from previous import . "x"
436                         // We'll report errors after type checking in CheckDotImports.
437                         s.Def = nil
438                         continue
439                 }
440         }
441
442         sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
443         for _, pkg := range unused {
444                 pkgnotused(pkg.pos, pkg.path, pkg.name)
445         }
446 }
447
448 // CheckDotImports reports errors for any unused dot imports.
449 func CheckDotImports() {
450         for _, pack := range dotImports {
451                 if !pack.Used {
452                         base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path)
453                 }
454         }
455
456         // No longer needed; release memory.
457         dotImports = nil
458         typecheck.DotImportRefs = nil
459 }
460
461 // dotImports tracks all PkgNames that have been dot-imported.
462 var dotImports []*ir.PkgName
463
464 // find all the exported symbols in package referenced by PkgName,
465 // and make them available in the current package
466 func importDot(pack *ir.PkgName) {
467         if typecheck.DotImportRefs == nil {
468                 typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName)
469         }
470
471         opkg := pack.Pkg
472         for _, s := range opkg.Syms {
473                 if s.Def == nil {
474                         if _, ok := typecheck.DeclImporter[s]; !ok {
475                                 continue
476                         }
477                 }
478                 if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
479                         continue
480                 }
481                 s1 := typecheck.Lookup(s.Name)
482                 if s1.Def != nil {
483                         pkgerror := fmt.Sprintf("during import %q", opkg.Path)
484                         typecheck.Redeclared(base.Pos, s1, pkgerror)
485                         continue
486                 }
487
488                 id := ir.NewIdent(src.NoXPos, s)
489                 typecheck.DotImportRefs[id] = pack
490                 s1.Def = id
491                 s1.Block = 1
492         }
493
494         dotImports = append(dotImports, pack)
495 }
496
497 // importName is like oldname,
498 // but it reports an error if sym is from another package and not exported.
499 func importName(sym *types.Sym) ir.Node {
500         n := oldname(sym)
501         if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg {
502                 n.SetDiag(true)
503                 base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
504         }
505         return n
506 }