| | |
| | |
| | |
| |
|
| | package noder |
| |
|
| | import ( |
| | "errors" |
| | "fmt" |
| | "internal/buildcfg" |
| | "internal/exportdata" |
| | "internal/pkgbits" |
| | "os" |
| | pathpkg "path" |
| | "runtime" |
| | "strings" |
| | "unicode" |
| | "unicode/utf8" |
| |
|
| | "cmd/compile/internal/base" |
| | "cmd/compile/internal/importer" |
| | "cmd/compile/internal/ir" |
| | "cmd/compile/internal/typecheck" |
| | "cmd/compile/internal/types" |
| | "cmd/compile/internal/types2" |
| | "cmd/internal/bio" |
| | "cmd/internal/goobj" |
| | "cmd/internal/objabi" |
| | ) |
| |
|
| | type gcimports struct { |
| | ctxt *types2.Context |
| | packages map[string]*types2.Package |
| | } |
| |
|
| | func (m *gcimports) Import(path string) (*types2.Package, error) { |
| | return m.ImportFrom(path, "" , 0) |
| | } |
| |
|
| | func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) { |
| | if mode != 0 { |
| | panic("mode must be 0") |
| | } |
| |
|
| | _, pkg, err := readImportFile(path, typecheck.Target, m.ctxt, m.packages) |
| | return pkg, err |
| | } |
| |
|
| | func isDriveLetter(b byte) bool { |
| | return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z' |
| | } |
| |
|
| | |
| | func islocalname(name string) bool { |
| | return strings.HasPrefix(name, "/") || |
| | runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' || |
| | strings.HasPrefix(name, "./") || name == "." || |
| | strings.HasPrefix(name, "../") || name == ".." |
| | } |
| |
|
| | func openPackage(path string) (*os.File, error) { |
| | if islocalname(path) { |
| | if base.Flag.NoLocalImports { |
| | return nil, errors.New("local imports disallowed") |
| | } |
| |
|
| | if base.Flag.Cfg.PackageFile != nil { |
| | return os.Open(base.Flag.Cfg.PackageFile[path]) |
| | } |
| |
|
| | |
| | |
| | |
| | if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil { |
| | return file, nil |
| | } |
| | if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil { |
| | return file, nil |
| | } |
| | return nil, errors.New("file not found") |
| | } |
| |
|
| | |
| | |
| | |
| | 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 { |
| | return os.Open(base.Flag.Cfg.PackageFile[path]) |
| | } |
| |
|
| | for _, dir := range base.Flag.Cfg.ImportDirs { |
| | if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil { |
| | return file, nil |
| | } |
| | if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil { |
| | return file, nil |
| | } |
| | } |
| |
|
| | if buildcfg.GOROOT != "" { |
| | suffix := "" |
| | if base.Flag.InstallSuffix != "" { |
| | suffix = "_" + base.Flag.InstallSuffix |
| | } else if base.Flag.Race { |
| | suffix = "_race" |
| | } else if base.Flag.MSan { |
| | suffix = "_msan" |
| | } else if base.Flag.ASan { |
| | suffix = "_asan" |
| | } |
| |
|
| | if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { |
| | return file, nil |
| | } |
| | if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", buildcfg.GOROOT, buildcfg.GOOS, buildcfg.GOARCH, suffix, path)); err == nil { |
| | return file, nil |
| | } |
| | } |
| | return nil, errors.New("file not found") |
| | } |
| |
|
| | |
| | |
| | func resolveImportPath(path string) (string, error) { |
| | |
| | |
| | |
| | |
| | if path == "main" { |
| | return "", errors.New("cannot import \"main\"") |
| | } |
| |
|
| | if base.Ctxt.Pkgpath == "" { |
| | panic("missing pkgpath") |
| | } |
| | if path == base.Ctxt.Pkgpath { |
| | return "", fmt.Errorf("import %q while compiling that package (import cycle)", path) |
| | } |
| |
|
| | if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok { |
| | path = mapped |
| | } |
| |
|
| | if islocalname(path) { |
| | if path[0] == '/' { |
| | return "", errors.New("import path cannot be absolute path") |
| | } |
| |
|
| | prefix := base.Flag.D |
| | if prefix == "" { |
| | |
| | |
| | |
| | |
| | prefix = base.Ctxt.Pathname |
| | } |
| | path = pathpkg.Join(prefix, path) |
| |
|
| | if err := checkImportPath(path, true); err != nil { |
| | return "", err |
| | } |
| | } |
| |
|
| | return path, nil |
| | } |
| |
|
| | |
| | |
| | |
| | func readImportFile(path string, target *ir.Package, env *types2.Context, packages map[string]*types2.Package) (pkg1 *types.Pkg, pkg2 *types2.Package, err error) { |
| | path, err = resolveImportPath(path) |
| | if err != nil { |
| | return |
| | } |
| |
|
| | if path == "unsafe" { |
| | pkg1, pkg2 = types.UnsafePkg, types2.Unsafe |
| |
|
| | |
| | |
| | if !pkg1.Direct { |
| | pkg1.Direct = true |
| | target.Imports = append(target.Imports, pkg1) |
| | } |
| |
|
| | return |
| | } |
| |
|
| | pkg1 = types.NewPkg(path, "") |
| | if packages != nil { |
| | pkg2 = packages[path] |
| | assert(pkg1.Direct == (pkg2 != nil && pkg2.Complete())) |
| | } |
| |
|
| | if pkg1.Direct { |
| | return |
| | } |
| | pkg1.Direct = true |
| | target.Imports = append(target.Imports, pkg1) |
| |
|
| | f, err := openPackage(path) |
| | if err != nil { |
| | return |
| | } |
| | defer f.Close() |
| |
|
| | data, err := readExportData(f) |
| | if err != nil { |
| | return |
| | } |
| |
|
| | if base.Debug.Export != 0 { |
| | fmt.Printf("importing %s (%s)\n", path, f.Name()) |
| | } |
| |
|
| | pr := pkgbits.NewPkgDecoder(pkg1.Path, data) |
| |
|
| | |
| | readPackage(newPkgReader(pr), pkg1, false) |
| | pkg2 = importer.ReadPackage(env, packages, pr) |
| |
|
| | err = addFingerprint(path, data) |
| | return |
| | } |
| |
|
| | |
| | func readExportData(f *os.File) (data string, err error) { |
| | r := bio.NewReader(f) |
| |
|
| | sz, err := exportdata.FindPackageDefinition(r.Reader) |
| | if err != nil { |
| | return |
| | } |
| | end := r.Offset() + int64(sz) |
| |
|
| | abihdr, _, err := exportdata.ReadObjectHeaders(r.Reader) |
| | if err != nil { |
| | return |
| | } |
| |
|
| | if expect := objabi.HeaderString(); abihdr != expect { |
| | err = fmt.Errorf("object is [%s] expected [%s]", abihdr, expect) |
| | return |
| | } |
| |
|
| | _, err = exportdata.ReadExportDataHeader(r.Reader) |
| | if err != nil { |
| | return |
| | } |
| |
|
| | pos := r.Offset() |
| |
|
| | |
| | |
| | |
| | var mapped string |
| | mapped, err = base.MapFile(r.File(), pos, end-pos) |
| | if err != nil { |
| | return |
| | } |
| |
|
| | |
| | const marker = "\n$$\n" |
| |
|
| | var ok bool |
| | data, ok = strings.CutSuffix(mapped, marker) |
| | if !ok { |
| | cutoff := data |
| | if len(cutoff) >= 10 { |
| | cutoff = cutoff[len(cutoff)-10:] |
| | } |
| | err = fmt.Errorf("expected $$ marker, but found %q (recompile package)", cutoff) |
| | return |
| | } |
| |
|
| | return |
| | } |
| |
|
| | |
| | |
| | func addFingerprint(path string, data string) error { |
| | var fingerprint goobj.FingerprintType |
| |
|
| | pos := len(data) - len(fingerprint) |
| | if pos < 0 { |
| | return fmt.Errorf("missing linker fingerprint in exportdata, but found %q", data) |
| | } |
| | buf := []byte(data[pos:]) |
| |
|
| | copy(fingerprint[:], buf) |
| | base.Ctxt.AddImport(path, fingerprint) |
| |
|
| | return nil |
| | } |
| |
|
| | func checkImportPath(path string, allowSpace bool) error { |
| | if path == "" { |
| | return errors.New("import path is empty") |
| | } |
| |
|
| | if strings.Contains(path, "\x00") { |
| | return errors.New("import path contains NUL") |
| | } |
| |
|
| | for ri := range base.ReservedImports { |
| | if path == ri { |
| | return fmt.Errorf("import path %q is reserved and cannot be used", path) |
| | } |
| | } |
| |
|
| | for _, r := range path { |
| | 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 nil |
| | } |
| |
|