Source file 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  	"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  // haveLegacyImports records whether we've imported any packages
    35  // without a new export data section. This is useful for experimenting
    36  // with new export data format designs, when you need to support
    37  // existing tests that manually compile files with inconsistent
    38  // compiler flags.
    39  var haveLegacyImports = false
    40  
    41  // newReadImportFunc is an extension hook for experimenting with new
    42  // export data formats. If a new export data payload was written out
    43  // for an imported package by overloading writeNewExportFunc, then
    44  // that payload will be mapped into memory and passed to
    45  // newReadImportFunc.
    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, "" /* no vendoring */, 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  // is this path a local name? begins with ./ or ../ or /
    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  		// try .a before .o.  important for building libraries:
    91  		// if there is an array.o in the array.a library,
    92  		// want to find all of array.a, not just array.o.
    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  	// local imports should be canonicalized already.
   103  	// don't want to see "encoding/../encoding/base64"
   104  	// as different from "encoding/base64".
   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  // myheight tracks the local package's height based on packages
   145  // imported so far.
   146  var myheight int
   147  
   148  // resolveImportPath resolves an import path as it appears in a Go
   149  // source file to the package's full path.
   150  func resolveImportPath(path string) (string, error) {
   151  	// The package name main is no longer reserved,
   152  	// but we reserve the import path "main" to identify
   153  	// the main package, just as we reserve the import
   154  	// path "math" to identify the standard math package.
   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  			// Questionable, but when -D isn't specified, historically we
   175  			// resolve local import paths relative to the directory the
   176  			// compiler's current directory, not the respective source
   177  			// file's directory.
   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  // readImportFile reads the import file for the given package path and
   227  // returns its types.Pkg representation. If packages is non-nil, the
   228  // types2.Package representation is also returned.
   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  		// TODO(mdempsky): Investigate if this actually matters. Why would
   239  		// the linker or runtime care whether a package imported unsafe?
   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  		// We have unified IR data. Map it, and feed to the importers.
   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  		// We only have old data. Oh well, fall back to the legacy importers.
   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  			// Indexed format is distinguished by an 'i' byte,
   296  			// whereas previous export formats started with 'c', 'd', or 'v'.
   297  			err = fmt.Errorf("unexpected package format byte: %v", c)
   298  			return
   299  		}
   300  
   301  		pos := r.Offset()
   302  
   303  		// Map string (and data) section into memory as a single large
   304  		// string. This reduces heap fragmentation and allows
   305  		// returning individual substrings very efficiently.
   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  // findExportData returns a *bio.Reader positioned at the start of the
   327  // binary export data section, and a file offset for where to stop
   328  // reading.
   329  func findExportData(f *os.File) (r *bio.Reader, end, newsize int64, err error) {
   330  	r = bio.NewReader(f)
   331  
   332  	// check object header
   333  	line, err := r.ReadString('\n')
   334  	if err != nil {
   335  		return
   336  	}
   337  
   338  	if line == "!<arch>\n" { // package archive
   339  		// package export block should be first
   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  		// Not an archive; provide end of file instead.
   352  		// TODO(mdempsky): I don't think this happens anymore.
   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  	// process header lines
   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  	// Expect $$B\n to signal binary import format.
   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  // addFingerprint reads the linker fingerprint included at the end of
   396  // the exportdata.
   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  	// Caller should have given us the end position of the export data,
   407  	// which should end with the "\n$$\n" marker. As a consistency check
   408  	// to make sure we're reading at the right offset, make sure we
   409  	// found the marker.
   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  	// assume files move (get installed) so don't record the full path
   417  	if base.Flag.Cfg.PackageFile != nil {
   418  		// If using a packageFile map, assume path_ can be recorded directly.
   419  		base.Ctxt.AddImport(path, fingerprint)
   420  	} else {
   421  		// For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
   422  		file := f.Name()
   423  		base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
   424  	}
   425  	return nil
   426  }
   427  
   428  // The linker uses the magic symbol prefixes "go." and "type."
   429  // Avoid potential confusion between import paths and symbols
   430  // by rejecting these reserved imports for now. Also, people
   431  // "can do weird things in GOPATH and we'd prefer they didn't
   432  // do _that_ weird thing" (per rsc). See also #4257.
   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  	// If the package was imported with a name other than the final
   473  	// import path element, show it explicitly in the error message.
   474  	// Note that this handles both renamed imports and imports of
   475  	// packages containing unconventional package declarations.
   476  	// Note that this uses / always, even on Windows, because Go import
   477  	// paths always use forward slashes.
   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  			// throw away top-level package name left over
   517  			// from previous file.
   518  			// leave s->block set to cause redeclaration
   519  			// errors if a conflicting top-level name is
   520  			// introduced by a different file.
   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  			// throw away top-level name left over
   530  			// from previous import . "x"
   531  			// We'll report errors after type checking in CheckDotImports.
   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  // CheckDotImports reports errors for any unused dot imports.
   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  	// No longer needed; release memory.
   552  	dotImports = nil
   553  	typecheck.DotImportRefs = nil
   554  }
   555  
   556  // dotImports tracks all PkgNames that have been dot-imported.
   557  var dotImports []*ir.PkgName
   558  
   559  // find all the exported symbols in package referenced by PkgName,
   560  // and make them available in the current package
   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) { // 0xb7 = center dot
   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  // importName is like oldname,
   593  // but it reports an error if sym is from another package and not exported.
   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