Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/compile/internal/noder/import.go

Documentation: cmd/compile/internal/noder

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

View as plain text