aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/noder/import.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/noder/import.go')
-rw-r--r--src/cmd/compile/internal/noder/import.go260
1 files changed, 135 insertions, 125 deletions
diff --git a/src/cmd/compile/internal/noder/import.go b/src/cmd/compile/internal/noder/import.go
index 08f19a4028..ac7bc8bbf0 100644
--- a/src/cmd/compile/internal/noder/import.go
+++ b/src/cmd/compile/internal/noder/import.go
@@ -5,20 +5,25 @@
package noder
import (
+ "errors"
"fmt"
- "go/constant"
+ "io"
"os"
- "path"
+ pathpkg "path"
"runtime"
"sort"
+ "strconv"
"strings"
"unicode"
"unicode/utf8"
"cmd/compile/internal/base"
+ "cmd/compile/internal/importer"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/syntax"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "cmd/compile/internal/types2"
"cmd/internal/archive"
"cmd/internal/bio"
"cmd/internal/goobj"
@@ -26,6 +31,29 @@ import (
"cmd/internal/src"
)
+// Temporary import helper to get type2-based type-checking going.
+type gcimports struct {
+ packages map[string]*types2.Package
+}
+
+func (m *gcimports) Import(path string) (*types2.Package, error) {
+ return m.ImportFrom(path, "" /* no vendoring */, 0)
+}
+
+func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
+ if mode != 0 {
+ panic("mode must be 0")
+ }
+
+ path, err := resolveImportPath(path)
+ if err != nil {
+ return nil, err
+ }
+
+ lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
+ return importer.Import(m.packages, path, srcDir, lookup)
+}
+
func isDriveLetter(b byte) bool {
return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
}
@@ -38,160 +66,152 @@ func islocalname(name string) bool {
strings.HasPrefix(name, "../") || name == ".."
}
-func findpkg(name string) (file string, ok bool) {
- if islocalname(name) {
+func openPackage(path string) (*os.File, error) {
+ if islocalname(path) {
if base.Flag.NoLocalImports {
- return "", false
+ return nil, errors.New("local imports disallowed")
}
if base.Flag.Cfg.PackageFile != nil {
- file, ok = base.Flag.Cfg.PackageFile[name]
- return file, ok
+ return os.Open(base.Flag.Cfg.PackageFile[path])
}
- // try .a before .6. important for building libraries:
- // if there is an array.6 in the array.a library,
- // want to find all of array.a, not just array.6.
- file = fmt.Sprintf("%s.a", name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ // try .a before .o. important for building libraries:
+ // if there is an array.o in the array.a library,
+ // want to find all of array.a, not just array.o.
+ if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
+ return file, nil
}
- file = fmt.Sprintf("%s.o", name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
+ return file, nil
}
- return "", false
+ return nil, errors.New("file not found")
}
// local imports should be canonicalized already.
// don't want to see "encoding/../encoding/base64"
// as different from "encoding/base64".
- if q := path.Clean(name); q != name {
- base.Errorf("non-canonical import path %q (should be %q)", name, q)
- return "", false
+ if q := pathpkg.Clean(path); q != path {
+ return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
}
if base.Flag.Cfg.PackageFile != nil {
- file, ok = base.Flag.Cfg.PackageFile[name]
- return file, ok
+ return os.Open(base.Flag.Cfg.PackageFile[path])
}
for _, dir := range base.Flag.Cfg.ImportDirs {
- file = fmt.Sprintf("%s/%s.a", dir, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
+ return file, nil
}
- file = fmt.Sprintf("%s/%s.o", dir, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
+ return file, nil
}
}
if objabi.GOROOT != "" {
suffix := ""
- suffixsep := ""
if base.Flag.InstallSuffix != "" {
- suffixsep = "_"
- suffix = base.Flag.InstallSuffix
+ suffix = "_" + base.Flag.InstallSuffix
} else if base.Flag.Race {
- suffixsep = "_"
- suffix = "race"
+ suffix = "_race"
} else if base.Flag.MSan {
- suffixsep = "_"
- suffix = "msan"
+ suffix = "_msan"
}
- file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+ return file, nil
}
- file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
- if _, err := os.Stat(file); err == nil {
- return file, true
+ if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+ return file, nil
}
}
-
- return "", false
+ return nil, errors.New("file not found")
}
// myheight tracks the local package's height based on packages
// imported so far.
var myheight int
-func importfile(f constant.Value) *types.Pkg {
- if f.Kind() != constant.String {
- base.Errorf("import path must be a string")
- return nil
- }
-
- path_ := constant.StringVal(f)
- if len(path_) == 0 {
- base.Errorf("import path is empty")
- return nil
- }
-
- if isbadimport(path_, false) {
- return nil
- }
-
+// resolveImportPath resolves an import path as it appears in a Go
+// source file to the package's full path.
+func resolveImportPath(path string) (string, error) {
// The package name main is no longer reserved,
// but we reserve the import path "main" to identify
// the main package, just as we reserve the import
// path "math" to identify the standard math package.
- if path_ == "main" {
- base.Errorf("cannot import \"main\"")
- base.ErrorExit()
- }
-
- if base.Ctxt.Pkgpath != "" && path_ == base.Ctxt.Pkgpath {
- base.Errorf("import %q while compiling that package (import cycle)", path_)
- base.ErrorExit()
+ if path == "main" {
+ return "", errors.New("cannot import \"main\"")
}
- if mapped, ok := base.Flag.Cfg.ImportMap[path_]; ok {
- path_ = mapped
+ if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
+ return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
}
- if path_ == "unsafe" {
- return ir.Pkgs.Unsafe
+ if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
+ path = mapped
}
- if islocalname(path_) {
- if path_[0] == '/' {
- base.Errorf("import path cannot be absolute path")
- return nil
+ if islocalname(path) {
+ if path[0] == '/' {
+ return "", errors.New("import path cannot be absolute path")
}
- prefix := base.Ctxt.Pathname
- if base.Flag.D != "" {
- prefix = base.Flag.D
+ prefix := base.Flag.D
+ if prefix == "" {
+ // Questionable, but when -D isn't specified, historically we
+ // resolve local import paths relative to the directory the
+ // compiler's current directory, not the respective source
+ // file's directory.
+ prefix = base.Ctxt.Pathname
}
- path_ = path.Join(prefix, path_)
+ path = pathpkg.Join(prefix, path)
- if isbadimport(path_, true) {
- return nil
+ if err := checkImportPath(path, true); err != nil {
+ return "", err
}
}
- file, found := findpkg(path_)
- if !found {
- base.Errorf("can't find import: %q", path_)
- base.ErrorExit()
+ return path, nil
+}
+
+// TODO(mdempsky): Return an error instead.
+func importfile(decl *syntax.ImportDecl) *types.Pkg {
+ path, err := strconv.Unquote(decl.Path.Value)
+ if err != nil {
+ base.Errorf("import path must be a string")
+ return nil
}
- importpkg := types.NewPkg(path_, "")
- if importpkg.Imported {
- return importpkg
+ if err := checkImportPath(path, false); err != nil {
+ base.Errorf("%s", err.Error())
+ return nil
+ }
+
+ path, err = resolveImportPath(path)
+ if err != nil {
+ base.Errorf("%s", err)
+ return nil
}
- importpkg.Imported = true
+ importpkg := types.NewPkg(path, "")
+ if importpkg.Direct {
+ return importpkg // already fully loaded
+ }
+ importpkg.Direct = true
+ typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
- imp, err := bio.Open(file)
+ if path == "unsafe" {
+ return importpkg // initialized with universe
+ }
+
+ f, err := openPackage(path)
if err != nil {
- base.Errorf("can't open import: %q: %v", path_, err)
+ base.Errorf("could not import %q: %v", path, err)
base.ErrorExit()
}
+ imp := bio.NewReader(f)
defer imp.Close()
+ file := f.Name()
// check object header
p, err := imp.ReadString('\n')
@@ -261,12 +281,12 @@ func importfile(f constant.Value) *types.Pkg {
var fingerprint goobj.FingerprintType
switch c {
case '\n':
- base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path_)
+ base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
return nil
case 'B':
if base.Debug.Export != 0 {
- fmt.Printf("importing %s (%s)\n", path_, file)
+ fmt.Printf("importing %s (%s)\n", path, file)
}
imp.ReadByte() // skip \n after $$B
@@ -285,17 +305,17 @@ func importfile(f constant.Value) *types.Pkg {
fingerprint = typecheck.ReadImports(importpkg, imp)
default:
- base.Errorf("no import in %q", path_)
+ base.Errorf("no import in %q", path)
base.ErrorExit()
}
// assume files move (get installed) so don't record the full path
if base.Flag.Cfg.PackageFile != nil {
// If using a packageFile map, assume path_ can be recorded directly.
- base.Ctxt.AddImport(path_, fingerprint)
+ base.Ctxt.AddImport(path, fingerprint)
} else {
// For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
- base.Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint)
+ base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
}
if importpkg.Height >= myheight {
@@ -315,47 +335,37 @@ var reservedimports = []string{
"type",
}
-func isbadimport(path string, allowSpace bool) bool {
+func checkImportPath(path string, allowSpace bool) error {
+ if path == "" {
+ return errors.New("import path is empty")
+ }
+
if strings.Contains(path, "\x00") {
- base.Errorf("import path contains NUL")
- return true
+ return errors.New("import path contains NUL")
}
for _, ri := range reservedimports {
if path == ri {
- base.Errorf("import path %q is reserved and cannot be used", path)
- return true
+ return fmt.Errorf("import path %q is reserved and cannot be used", path)
}
}
for _, r := range path {
- if r == utf8.RuneError {
- base.Errorf("import path contains invalid UTF-8 sequence: %q", path)
- return true
- }
-
- if r < 0x20 || r == 0x7f {
- base.Errorf("import path contains control character: %q", path)
- return true
- }
-
- if r == '\\' {
- base.Errorf("import path contains backslash; use slash: %q", path)
- return true
- }
-
- if !allowSpace && unicode.IsSpace(r) {
- base.Errorf("import path contains space character: %q", path)
- return true
- }
-
- if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) {
- base.Errorf("import path contains invalid character '%c': %q", r, path)
- return true
+ switch {
+ case r == utf8.RuneError:
+ return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
+ case r < 0x20 || r == 0x7f:
+ return fmt.Errorf("import path contains control character: %q", path)
+ case r == '\\':
+ return fmt.Errorf("import path contains backslash; use slash: %q", path)
+ case !allowSpace && unicode.IsSpace(r):
+ return fmt.Errorf("import path contains space character: %q", path)
+ case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
+ return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
}
}
- return false
+ return nil
}
func pkgnotused(lineno src.XPos, path string, name string) {