summaryrefslogtreecommitdiffstats
path: root/src/go/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/internal')
-rw-r--r--src/go/internal/gccgoimporter/ar.go171
-rw-r--r--src/go/internal/gccgoimporter/gccgoinstallation.go97
-rw-r--r--src/go/internal/gccgoimporter/gccgoinstallation_test.go192
-rw-r--r--src/go/internal/gccgoimporter/importer.go261
-rw-r--r--src/go/internal/gccgoimporter/importer_test.go199
-rw-r--r--src/go/internal/gccgoimporter/parser.go1283
-rw-r--r--src/go/internal/gccgoimporter/parser_test.go78
-rw-r--r--src/go/internal/gccgoimporter/testdata/aliases.go65
-rw-r--r--src/go/internal/gccgoimporter/testdata/aliases.gox33
-rw-r--r--src/go/internal/gccgoimporter/testdata/complexnums.go6
-rw-r--r--src/go/internal/gccgoimporter/testdata/complexnums.gox8
-rw-r--r--src/go/internal/gccgoimporter/testdata/conversions.go5
-rw-r--r--src/go/internal/gccgoimporter/testdata/conversions.gox6
-rw-r--r--src/go/internal/gccgoimporter/testdata/escapeinfo.go13
-rw-r--r--src/go/internal/gccgoimporter/testdata/escapeinfo.gox9
-rw-r--r--src/go/internal/gccgoimporter/testdata/imports.go5
-rw-r--r--src/go/internal/gccgoimporter/testdata/imports.gox7
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue27856.go9
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue27856.gox9
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue29198.go37
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue29198.gox86
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue30628.go18
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue30628.gox28
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue31540.go26
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue31540.gox16
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue34182.go17
-rw-r--r--src/go/internal/gccgoimporter/testdata/issue34182.gox13
-rw-r--r--src/go/internal/gccgoimporter/testdata/libimportsar.abin0 -> 9302 bytes
-rw-r--r--src/go/internal/gccgoimporter/testdata/nointerface.go12
-rw-r--r--src/go/internal/gccgoimporter/testdata/nointerface.gox8
-rw-r--r--src/go/internal/gccgoimporter/testdata/notinheap.go4
-rw-r--r--src/go/internal/gccgoimporter/testdata/notinheap.gox7
-rw-r--r--src/go/internal/gccgoimporter/testdata/pointer.go3
-rw-r--r--src/go/internal/gccgoimporter/testdata/pointer.gox4
-rw-r--r--src/go/internal/gccgoimporter/testdata/time.gox142
-rw-r--r--src/go/internal/gccgoimporter/testdata/unicode.gox273
-rw-r--r--src/go/internal/gccgoimporter/testdata/v1reflect.gox184
-rw-r--r--src/go/internal/gcimporter/exportdata.go92
-rw-r--r--src/go/internal/gcimporter/gcimporter.go248
-rw-r--r--src/go/internal/gcimporter/gcimporter_test.go752
-rw-r--r--src/go/internal/gcimporter/iimport.go817
-rw-r--r--src/go/internal/gcimporter/support.go183
-rw-r--r--src/go/internal/gcimporter/testdata/a.go14
-rw-r--r--src/go/internal/gcimporter/testdata/b.go11
-rw-r--r--src/go/internal/gcimporter/testdata/exports.go91
-rw-r--r--src/go/internal/gcimporter/testdata/g.go23
-rw-r--r--src/go/internal/gcimporter/testdata/generics.go29
-rw-r--r--src/go/internal/gcimporter/testdata/issue15920.go11
-rw-r--r--src/go/internal/gcimporter/testdata/issue20046.go9
-rw-r--r--src/go/internal/gcimporter/testdata/issue25301.go17
-rw-r--r--src/go/internal/gcimporter/testdata/issue25596.go13
-rw-r--r--src/go/internal/gcimporter/testdata/issue57015.go16
-rw-r--r--src/go/internal/gcimporter/testdata/p.go13
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test.go28
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.11_0i.abin0 -> 2420 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.11_6b.abin0 -> 2426 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.11_999b.abin0 -> 2600 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.11_999i.abin0 -> 2420 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.7_0.abin0 -> 1862 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.7_1.abin0 -> 2316 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.8_4.abin0 -> 1658 bytes
-rw-r--r--src/go/internal/gcimporter/testdata/versions/test_go1.8_5.abin0 -> 1658 bytes
-rw-r--r--src/go/internal/gcimporter/ureader.go657
-rw-r--r--src/go/internal/srcimporter/srcimporter.go269
-rw-r--r--src/go/internal/srcimporter/srcimporter_test.go252
-rw-r--r--src/go/internal/srcimporter/testdata/issue20855/issue20855.go7
-rw-r--r--src/go/internal/srcimporter/testdata/issue23092/issue23092.go5
-rw-r--r--src/go/internal/srcimporter/testdata/issue24392/issue24392.go5
-rw-r--r--src/go/internal/typeparams/typeparams.go54
69 files changed, 6950 insertions, 0 deletions
diff --git a/src/go/internal/gccgoimporter/ar.go b/src/go/internal/gccgoimporter/ar.go
new file mode 100644
index 0000000..9df7934
--- /dev/null
+++ b/src/go/internal/gccgoimporter/ar.go
@@ -0,0 +1,171 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bytes"
+ "debug/elf"
+ "errors"
+ "fmt"
+ "internal/xcoff"
+ "io"
+ "strconv"
+ "strings"
+)
+
+// Magic strings for different archive file formats.
+const (
+ armag = "!<arch>\n"
+ armagt = "!<thin>\n"
+ armagb = "<bigaf>\n"
+)
+
+// Offsets and sizes for fields in a standard archive header.
+const (
+ arNameOff = 0
+ arNameSize = 16
+ arDateOff = arNameOff + arNameSize
+ arDateSize = 12
+ arUIDOff = arDateOff + arDateSize
+ arUIDSize = 6
+ arGIDOff = arUIDOff + arUIDSize
+ arGIDSize = 6
+ arModeOff = arGIDOff + arGIDSize
+ arModeSize = 8
+ arSizeOff = arModeOff + arModeSize
+ arSizeSize = 10
+ arFmagOff = arSizeOff + arSizeSize
+ arFmagSize = 2
+
+ arHdrSize = arFmagOff + arFmagSize
+)
+
+// The contents of the fmag field of a standard archive header.
+const arfmag = "`\n"
+
+// arExportData takes an archive file and returns a ReadSeeker for the
+// export data in that file. This assumes that there is only one
+// object in the archive containing export data, which is not quite
+// what gccgo does; gccgo concatenates together all the export data
+// for all the objects in the file. In practice that case does not arise.
+func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
+ if _, err := archive.Seek(0, io.SeekStart); err != nil {
+ return nil, err
+ }
+
+ var buf [len(armag)]byte
+ if _, err := archive.Read(buf[:]); err != nil {
+ return nil, err
+ }
+
+ switch string(buf[:]) {
+ case armag:
+ return standardArExportData(archive)
+ case armagt:
+ return nil, errors.New("unsupported thin archive")
+ case armagb:
+ return aixBigArExportData(archive)
+ default:
+ return nil, fmt.Errorf("unrecognized archive file format %q", buf[:])
+ }
+}
+
+// standardArExportData returns export data from a standard archive.
+func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
+ off := int64(len(armag))
+ for {
+ var hdrBuf [arHdrSize]byte
+ if _, err := archive.Read(hdrBuf[:]); err != nil {
+ return nil, err
+ }
+ off += arHdrSize
+
+ if !bytes.Equal(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) {
+ return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:])
+ }
+
+ size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err)
+ }
+
+ fn := hdrBuf[arNameOff : arNameOff+arNameSize]
+ if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || string(fn[:8]) == "/SYM64/ ") {
+ // Archive symbol table or extended name table,
+ // which we don't care about.
+ } else {
+ archiveAt := readerAtFromSeeker(archive)
+ ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size))
+ if ret != nil || err != nil {
+ return ret, err
+ }
+ }
+
+ if size&1 != 0 {
+ size++
+ }
+ off += size
+ if _, err := archive.Seek(off, io.SeekStart); err != nil {
+ return nil, err
+ }
+ }
+}
+
+// elfFromAr tries to get export data from an archive member as an ELF file.
+// If there is no export data, this returns nil, nil.
+func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) {
+ ef, err := elf.NewFile(member)
+ if err != nil {
+ return nil, err
+ }
+ sec := ef.Section(".go_export")
+ if sec == nil {
+ return nil, nil
+ }
+ return sec.Open(), nil
+}
+
+// aixBigArExportData returns export data from an AIX big archive.
+func aixBigArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) {
+ archiveAt := readerAtFromSeeker(archive)
+ arch, err := xcoff.NewArchive(archiveAt)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, mem := range arch.Members {
+ f, err := arch.GetFile(mem.Name)
+ if err != nil {
+ return nil, err
+ }
+ sdat := f.CSect(".go_export")
+ if sdat != nil {
+ return bytes.NewReader(sdat), nil
+ }
+ }
+
+ return nil, fmt.Errorf(".go_export not found in this archive")
+}
+
+// readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt.
+// This is only safe because there won't be any concurrent seeks
+// while this code is executing.
+func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt {
+ if ret, ok := rs.(io.ReaderAt); ok {
+ return ret
+ }
+ return seekerReadAt{rs}
+}
+
+type seekerReadAt struct {
+ seeker io.ReadSeeker
+}
+
+func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) {
+ if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil {
+ return 0, err
+ }
+ return sra.seeker.Read(p)
+}
diff --git a/src/go/internal/gccgoimporter/gccgoinstallation.go b/src/go/internal/gccgoimporter/gccgoinstallation.go
new file mode 100644
index 0000000..8fc7ce3
--- /dev/null
+++ b/src/go/internal/gccgoimporter/gccgoinstallation.go
@@ -0,0 +1,97 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bufio"
+ "go/types"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+// Information about a specific installation of gccgo.
+type GccgoInstallation struct {
+ // Version of gcc (e.g. 4.8.0).
+ GccVersion string
+
+ // Target triple (e.g. x86_64-unknown-linux-gnu).
+ TargetTriple string
+
+ // Built-in library paths used by this installation.
+ LibPaths []string
+}
+
+// Ask the driver at the given path for information for this GccgoInstallation.
+// The given arguments are passed directly to the call of the driver.
+func (inst *GccgoInstallation) InitFromDriver(gccgoPath string, args ...string) (err error) {
+ argv := append([]string{"-###", "-S", "-x", "go", "-"}, args...)
+ cmd := exec.Command(gccgoPath, argv...)
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return
+ }
+
+ scanner := bufio.NewScanner(stderr)
+ for scanner.Scan() {
+ line := scanner.Text()
+ switch {
+ case strings.HasPrefix(line, "Target: "):
+ inst.TargetTriple = line[8:]
+
+ case line[0] == ' ':
+ args := strings.Fields(line)
+ for _, arg := range args[1:] {
+ if strings.HasPrefix(arg, "-L") {
+ inst.LibPaths = append(inst.LibPaths, arg[2:])
+ }
+ }
+ }
+ }
+
+ argv = append([]string{"-dumpversion"}, args...)
+ stdout, err := exec.Command(gccgoPath, argv...).Output()
+ if err != nil {
+ return
+ }
+ inst.GccVersion = strings.TrimSpace(string(stdout))
+
+ return
+}
+
+// Return the list of export search paths for this GccgoInstallation.
+func (inst *GccgoInstallation) SearchPaths() (paths []string) {
+ for _, lpath := range inst.LibPaths {
+ spath := filepath.Join(lpath, "go", inst.GccVersion)
+ fi, err := os.Stat(spath)
+ if err != nil || !fi.IsDir() {
+ continue
+ }
+ paths = append(paths, spath)
+
+ spath = filepath.Join(spath, inst.TargetTriple)
+ fi, err = os.Stat(spath)
+ if err != nil || !fi.IsDir() {
+ continue
+ }
+ paths = append(paths, spath)
+ }
+
+ paths = append(paths, inst.LibPaths...)
+
+ return
+}
+
+// Return an importer that searches incpaths followed by the gcc installation's
+// built-in search paths and the current directory.
+func (inst *GccgoInstallation) GetImporter(incpaths []string, initmap map[*types.Package]InitData) Importer {
+ return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap)
+}
diff --git a/src/go/internal/gccgoimporter/gccgoinstallation_test.go b/src/go/internal/gccgoimporter/gccgoinstallation_test.go
new file mode 100644
index 0000000..df0188a
--- /dev/null
+++ b/src/go/internal/gccgoimporter/gccgoinstallation_test.go
@@ -0,0 +1,192 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "go/types"
+ "testing"
+)
+
+// importablePackages is a list of packages that we verify that we can
+// import. This should be all standard library packages in all relevant
+// versions of gccgo. Note that since gccgo follows a different release
+// cycle, and since different systems have different versions installed,
+// we can't use the last-two-versions rule of the gc toolchain.
+var importablePackages = [...]string{
+ "archive/tar",
+ "archive/zip",
+ "bufio",
+ "bytes",
+ "compress/bzip2",
+ "compress/flate",
+ "compress/gzip",
+ "compress/lzw",
+ "compress/zlib",
+ "container/heap",
+ "container/list",
+ "container/ring",
+ "crypto/aes",
+ "crypto/cipher",
+ "crypto/des",
+ "crypto/dsa",
+ "crypto/ecdsa",
+ "crypto/elliptic",
+ "crypto",
+ "crypto/hmac",
+ "crypto/md5",
+ "crypto/rand",
+ "crypto/rc4",
+ "crypto/rsa",
+ "crypto/sha1",
+ "crypto/sha256",
+ "crypto/sha512",
+ "crypto/subtle",
+ "crypto/tls",
+ "crypto/x509",
+ "crypto/x509/pkix",
+ "database/sql/driver",
+ "database/sql",
+ "debug/dwarf",
+ "debug/elf",
+ "debug/gosym",
+ "debug/macho",
+ "debug/pe",
+ "encoding/ascii85",
+ "encoding/asn1",
+ "encoding/base32",
+ "encoding/base64",
+ "encoding/binary",
+ "encoding/csv",
+ "encoding/gob",
+ // "encoding", // Added in GCC 4.9.
+ "encoding/hex",
+ "encoding/json",
+ "encoding/pem",
+ "encoding/xml",
+ "errors",
+ "expvar",
+ "flag",
+ "fmt",
+ "go/ast",
+ "go/build",
+ "go/doc",
+ // "go/format", // Added in GCC 4.8.
+ "go/parser",
+ "go/printer",
+ "go/scanner",
+ "go/token",
+ "hash/adler32",
+ "hash/crc32",
+ "hash/crc64",
+ "hash/fnv",
+ "hash",
+ "html",
+ "html/template",
+ "image/color",
+ // "image/color/palette", // Added in GCC 4.9.
+ "image/draw",
+ "image/gif",
+ "image",
+ "image/jpeg",
+ "image/png",
+ "index/suffixarray",
+ "io",
+ "io/ioutil",
+ "log",
+ "log/syslog",
+ "math/big",
+ "math/cmplx",
+ "math",
+ "math/rand",
+ "mime",
+ "mime/multipart",
+ "net",
+ "net/http/cgi",
+ // "net/http/cookiejar", // Added in GCC 4.8.
+ "net/http/fcgi",
+ "net/http",
+ "net/http/httptest",
+ "net/http/httputil",
+ "net/http/pprof",
+ "net/mail",
+ "net/rpc",
+ "net/rpc/jsonrpc",
+ "net/smtp",
+ "net/textproto",
+ "net/url",
+ "os/exec",
+ "os",
+ "os/signal",
+ "os/user",
+ "path/filepath",
+ "path",
+ "reflect",
+ "regexp",
+ "regexp/syntax",
+ "runtime/debug",
+ "runtime",
+ "runtime/pprof",
+ "sort",
+ "strconv",
+ "strings",
+ "sync/atomic",
+ "sync",
+ "syscall",
+ "testing",
+ "testing/iotest",
+ "testing/quick",
+ "text/scanner",
+ "text/tabwriter",
+ "text/template",
+ "text/template/parse",
+ "time",
+ "unicode",
+ "unicode/utf16",
+ "unicode/utf8",
+}
+
+func TestInstallationImporter(t *testing.T) {
+ // This test relies on gccgo being around.
+ gpath := gccgoPath()
+ if gpath == "" {
+ t.Skip("This test needs gccgo")
+ }
+
+ var inst GccgoInstallation
+ err := inst.InitFromDriver(gpath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ imp := inst.GetImporter(nil, nil)
+
+ // Ensure we don't regress the number of packages we can parse. First import
+ // all packages into the same map and then each individually.
+ pkgMap := make(map[string]*types.Package)
+ for _, pkg := range importablePackages {
+ _, err = imp(pkgMap, pkg, ".", nil)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ for _, pkg := range importablePackages {
+ _, err = imp(make(map[string]*types.Package), pkg, ".", nil)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ // Test for certain specific entities in the imported data.
+ for _, test := range [...]importerTest{
+ {pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"},
+ {pkgpath: "math", name: "Pi", want: "const Pi untyped float"},
+ {pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"},
+ {pkgpath: "sort", name: "Search", want: "func Search(n int, f func(int) bool) int"},
+ {pkgpath: "unsafe", name: "Pointer", want: "type Pointer"},
+ } {
+ runImporterTest(t, imp, nil, &test)
+ }
+}
diff --git a/src/go/internal/gccgoimporter/importer.go b/src/go/internal/gccgoimporter/importer.go
new file mode 100644
index 0000000..94f2def
--- /dev/null
+++ b/src/go/internal/gccgoimporter/importer.go
@@ -0,0 +1,261 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gccgoimporter implements Import for gccgo-generated object files.
+package gccgoimporter // import "go/internal/gccgoimporter"
+
+import (
+ "bytes"
+ "debug/elf"
+ "fmt"
+ "go/types"
+ "internal/xcoff"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// A PackageInit describes an imported package that needs initialization.
+type PackageInit struct {
+ Name string // short package name
+ InitFunc string // name of init function
+ Priority int // priority of init function, see InitData.Priority
+}
+
+// The gccgo-specific init data for a package.
+type InitData struct {
+ // Initialization priority of this package relative to other packages.
+ // This is based on the maximum depth of the package's dependency graph;
+ // it is guaranteed to be greater than that of its dependencies.
+ Priority int
+
+ // The list of packages which this package depends on to be initialized,
+ // including itself if needed. This is the subset of the transitive closure of
+ // the package's dependencies that need initialization.
+ Inits []PackageInit
+}
+
+// Locate the file from which to read export data.
+// This is intended to replicate the logic in gofrontend.
+func findExportFile(searchpaths []string, pkgpath string) (string, error) {
+ for _, spath := range searchpaths {
+ pkgfullpath := filepath.Join(spath, pkgpath)
+ pkgdir, name := filepath.Split(pkgfullpath)
+
+ for _, filepath := range [...]string{
+ pkgfullpath,
+ pkgfullpath + ".gox",
+ pkgdir + "lib" + name + ".so",
+ pkgdir + "lib" + name + ".a",
+ pkgfullpath + ".o",
+ } {
+ fi, err := os.Stat(filepath)
+ if err == nil && !fi.IsDir() {
+ return filepath, nil
+ }
+ }
+ }
+
+ return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
+}
+
+const (
+ gccgov1Magic = "v1;\n"
+ gccgov2Magic = "v2;\n"
+ gccgov3Magic = "v3;\n"
+ goimporterMagic = "\n$$ "
+ archiveMagic = "!<ar"
+ aixbigafMagic = "<big"
+)
+
+// Opens the export data file at the given path. If this is an ELF file,
+// searches for and opens the .go_export section. If this is an archive,
+// reads the export data from the first member, which is assumed to be an ELF file.
+// This is intended to replicate the logic in gofrontend.
+func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
+ f, err := os.Open(fpath)
+ if err != nil {
+ return
+ }
+ closer = f
+ defer func() {
+ if err != nil && closer != nil {
+ f.Close()
+ }
+ }()
+
+ var magic [4]byte
+ _, err = f.ReadAt(magic[:], 0)
+ if err != nil {
+ return
+ }
+
+ var objreader io.ReaderAt
+ switch string(magic[:]) {
+ case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic:
+ // Raw export data.
+ reader = f
+ return
+
+ case archiveMagic, aixbigafMagic:
+ reader, err = arExportData(f)
+ return
+
+ default:
+ objreader = f
+ }
+
+ ef, err := elf.NewFile(objreader)
+ if err == nil {
+ sec := ef.Section(".go_export")
+ if sec == nil {
+ err = fmt.Errorf("%s: .go_export section not found", fpath)
+ return
+ }
+ reader = sec.Open()
+ return
+ }
+
+ xf, err := xcoff.NewFile(objreader)
+ if err == nil {
+ sdat := xf.CSect(".go_export")
+ if sdat == nil {
+ err = fmt.Errorf("%s: .go_export section not found", fpath)
+ return
+ }
+ reader = bytes.NewReader(sdat)
+ return
+ }
+
+ err = fmt.Errorf("%s: unrecognized file format", fpath)
+ return
+}
+
+// An Importer resolves import paths to Packages. The imports map records
+// packages already known, indexed by package path.
+// An importer must determine the canonical package path and check imports
+// to see if it is already present in the map. If so, the Importer can return
+// the map entry. Otherwise, the importer must load the package data for the
+// given path into a new *Package, record it in imports map, and return the
+// package.
+type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error)
+
+func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer {
+ return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
+ // TODO(gri): Use srcDir.
+ // Or not. It's possible that srcDir will fade in importance as
+ // the go command and other tools provide a translation table
+ // for relative imports (like ./foo or vendored imports).
+ if pkgpath == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ var reader io.ReadSeeker
+ var fpath string
+ var rc io.ReadCloser
+ if lookup != nil {
+ if p := imports[pkgpath]; p != nil && p.Complete() {
+ return p, nil
+ }
+ rc, err = lookup(pkgpath)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if rc != nil {
+ defer rc.Close()
+ rs, ok := rc.(io.ReadSeeker)
+ if !ok {
+ return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc)
+ }
+ reader = rs
+ fpath = "<lookup " + pkgpath + ">"
+ // Take name from Name method (like on os.File) if present.
+ if n, ok := rc.(interface{ Name() string }); ok {
+ fpath = n.Name()
+ }
+ } else {
+ fpath, err = findExportFile(searchpaths, pkgpath)
+ if err != nil {
+ return nil, err
+ }
+
+ r, closer, err := openExportFile(fpath)
+ if err != nil {
+ return nil, err
+ }
+ if closer != nil {
+ defer closer.Close()
+ }
+ reader = r
+ }
+
+ var magics string
+ magics, err = readMagic(reader)
+ if err != nil {
+ return
+ }
+
+ if magics == archiveMagic || magics == aixbigafMagic {
+ reader, err = arExportData(reader)
+ if err != nil {
+ return
+ }
+ magics, err = readMagic(reader)
+ if err != nil {
+ return
+ }
+ }
+
+ switch magics {
+ case gccgov1Magic, gccgov2Magic, gccgov3Magic:
+ var p parser
+ p.init(fpath, reader, imports)
+ pkg = p.parsePackage()
+ if initmap != nil {
+ initmap[pkg] = p.initdata
+ }
+
+ // Excluded for now: Standard gccgo doesn't support this import format currently.
+ // case goimporterMagic:
+ // var data []byte
+ // data, err = io.ReadAll(reader)
+ // if err != nil {
+ // return
+ // }
+ // var n int
+ // n, pkg, err = importer.ImportData(imports, data)
+ // if err != nil {
+ // return
+ // }
+
+ // if initmap != nil {
+ // suffixreader := bytes.NewReader(data[n:])
+ // var p parser
+ // p.init(fpath, suffixreader, nil)
+ // p.parseInitData()
+ // initmap[pkg] = p.initdata
+ // }
+
+ default:
+ err = fmt.Errorf("unrecognized magic string: %q", magics)
+ }
+
+ return
+ }
+}
+
+// readMagic reads the four bytes at the start of a ReadSeeker and
+// returns them as a string.
+func readMagic(reader io.ReadSeeker) (string, error) {
+ var magic [4]byte
+ if _, err := reader.Read(magic[:]); err != nil {
+ return "", err
+ }
+ if _, err := reader.Seek(0, io.SeekStart); err != nil {
+ return "", err
+ }
+ return string(magic[:]), nil
+}
diff --git a/src/go/internal/gccgoimporter/importer_test.go b/src/go/internal/gccgoimporter/importer_test.go
new file mode 100644
index 0000000..76b4500
--- /dev/null
+++ b/src/go/internal/gccgoimporter/importer_test.go
@@ -0,0 +1,199 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "go/types"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "testing"
+)
+
+type importerTest struct {
+ pkgpath, name, want, wantval string
+ wantinits []string
+ gccgoVersion int // minimum gccgo version (0 => any)
+}
+
+func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) {
+ pkg, err := imp(make(map[string]*types.Package), test.pkgpath, ".", nil)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if test.name != "" {
+ obj := pkg.Scope().Lookup(test.name)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ return
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+
+ if test.wantval != "" {
+ gotval := obj.(*types.Const).Val().String()
+ if gotval != test.wantval {
+ t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval)
+ }
+ }
+ }
+
+ if len(test.wantinits) > 0 {
+ initdata := initmap[pkg]
+ found := false
+ // Check that the package's own init function has the package's priority
+ for _, pkginit := range initdata.Inits {
+ if pkginit.InitFunc == test.wantinits[0] {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0])
+ }
+
+ // FIXME: the original version of this test was written against
+ // the v1 export data scheme for capturing init functions, so it
+ // verified the priority values. We moved away from the priority
+ // scheme some time ago; it is not clear how much work it would be
+ // to validate the new init export data.
+ }
+}
+
+// When adding tests to this list, be sure to set the 'gccgoVersion'
+// field if the testcases uses a "recent" Go addition (ex: aliases).
+var importerTests = [...]importerTest{
+ {pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
+ {pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1 + -1i)"},
+ {pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1 + 1i)"},
+ {pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1 + -1i)"},
+ {pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1 + 1i)"},
+ {pkgpath: "conversions", name: "Bits", want: "const Bits Units", wantval: `"bits"`},
+ {pkgpath: "time", name: "Duration", want: "type Duration int64"},
+ {pkgpath: "time", name: "Nanosecond", want: "const Nanosecond Duration", wantval: "1"},
+ {pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"},
+ {pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
+ {pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import"}},
+ {pkgpath: "importsar", name: "Hello", want: "var Hello string"},
+ {pkgpath: "aliases", name: "A14", gccgoVersion: 7, want: "type A14 = func(int, T0) chan T2"},
+ {pkgpath: "aliases", name: "C0", gccgoVersion: 7, want: "type C0 struct{f1 C1; f2 C1}"},
+ {pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"},
+ {pkgpath: "issue27856", name: "M", gccgoVersion: 7, want: "type M struct{E F}"},
+ {pkgpath: "v1reflect", name: "Type", want: "type Type interface{Align() int; AssignableTo(u Type) bool; Bits() int; ChanDir() ChanDir; Elem() Type; Field(i int) StructField; FieldAlign() int; FieldByIndex(index []int) StructField; FieldByName(name string) (StructField, bool); FieldByNameFunc(match func(string) bool) (StructField, bool); Implements(u Type) bool; In(i int) Type; IsVariadic() bool; Key() Type; Kind() Kind; Len() int; Method(int) Method; MethodByName(string) (Method, bool); Name() string; NumField() int; NumIn() int; NumMethod() int; NumOut() int; Out(i int) Type; PkgPath() string; Size() uintptr; String() string; common() *commonType; rawString() string; runtimeType() *runtimeType; uncommon() *uncommonType}"},
+ {pkgpath: "nointerface", name: "I", want: "type I int"},
+ {pkgpath: "issue29198", name: "FooServer", gccgoVersion: 7, want: "type FooServer struct{FooServer *FooServer; user string; ctx context.Context}"},
+ {pkgpath: "issue30628", name: "Apple", want: "type Apple struct{hey sync.RWMutex; x int; RQ [517]struct{Count uintptr; NumBytes uintptr; Last uintptr}}"},
+ {pkgpath: "issue31540", name: "S", gccgoVersion: 7, want: "type S struct{b int; map[Y]Z}"}, // should want "type S struct{b int; A2}" (issue #44410)
+ {pkgpath: "issue34182", name: "T1", want: "type T1 struct{f *T2}"},
+ {pkgpath: "notinheap", name: "S", want: "type S struct{}"},
+}
+
+func TestGoxImporter(t *testing.T) {
+ testenv.MustHaveExec(t)
+ initmap := make(map[*types.Package]InitData)
+ imp := GetImporter([]string{"testdata"}, initmap)
+
+ for _, test := range importerTests {
+ runImporterTest(t, imp, initmap, &test)
+ }
+}
+
+// gccgoPath returns a path to gccgo if it is present (either in
+// path or specified via GCCGO environment variable), or an
+// empty string if no gccgo is available.
+func gccgoPath() string {
+ gccgoname := os.Getenv("GCCGO")
+ if gccgoname == "" {
+ gccgoname = "gccgo"
+ }
+ if gpath, gerr := exec.LookPath(gccgoname); gerr == nil {
+ return gpath
+ }
+ return ""
+}
+
+func TestObjImporter(t *testing.T) {
+ // This test relies on gccgo being around.
+ gpath := gccgoPath()
+ if gpath == "" {
+ t.Skip("This test needs gccgo")
+ }
+
+ verout, err := testenv.Command(t, gpath, "--version").CombinedOutput()
+ if err != nil {
+ t.Logf("%s", verout)
+ t.Fatal(err)
+ }
+ vers := regexp.MustCompile(`(\d+)\.(\d+)`).FindSubmatch(verout)
+ if len(vers) == 0 {
+ t.Fatalf("could not find version number in %s", verout)
+ }
+ major, err := strconv.Atoi(string(vers[1]))
+ if err != nil {
+ t.Fatal(err)
+ }
+ minor, err := strconv.Atoi(string(vers[2]))
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("gccgo version %d.%d", major, minor)
+
+ tmpdir := t.TempDir()
+ initmap := make(map[*types.Package]InitData)
+ imp := GetImporter([]string{tmpdir}, initmap)
+
+ artmpdir := t.TempDir()
+ arinitmap := make(map[*types.Package]InitData)
+ arimp := GetImporter([]string{artmpdir}, arinitmap)
+
+ for _, test := range importerTests {
+ if major < test.gccgoVersion {
+ // Support for type aliases was added in GCC 7.
+ t.Logf("skipping %q: not supported before gccgo version %d", test.pkgpath, test.gccgoVersion)
+ continue
+ }
+
+ gofile := filepath.Join("testdata", test.pkgpath+".go")
+ if _, err := os.Stat(gofile); os.IsNotExist(err) {
+ continue
+ }
+ ofile := filepath.Join(tmpdir, test.pkgpath+".o")
+ afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a")
+
+ cmd := testenv.Command(t, gpath, "-fgo-pkgpath="+test.pkgpath, "-c", "-o", ofile, gofile)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("gccgo %s failed: %s", gofile, err)
+ }
+
+ runImporterTest(t, imp, initmap, &test)
+
+ cmd = testenv.Command(t, "ar", "cr", afile, ofile)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("ar cr %s %s failed: %s", afile, ofile, err)
+ }
+
+ runImporterTest(t, arimp, arinitmap, &test)
+
+ if err = os.Remove(ofile); err != nil {
+ t.Fatal(err)
+ }
+ if err = os.Remove(afile); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go
new file mode 100644
index 0000000..de9df0b
--- /dev/null
+++ b/src/go/internal/gccgoimporter/parser.go
@@ -0,0 +1,1283 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "errors"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "io"
+ "strconv"
+ "strings"
+ "text/scanner"
+ "unicode/utf8"
+)
+
+type parser struct {
+ scanner *scanner.Scanner
+ version string // format version
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ pkgpath string // package path of imported package
+ pkgname string // name of imported package
+ pkg *types.Package // reference to imported package
+ imports map[string]*types.Package // package path -> package object
+ typeList []types.Type // type number -> type
+ typeData []string // unparsed type data (v3 and later)
+ fixups []fixupRecord // fixups to apply at end of parsing
+ initdata InitData // package init priority data
+ aliases map[int]string // maps saved type number to alias name
+}
+
+// When reading export data it's possible to encounter a defined type
+// N1 with an underlying defined type N2 while we are still reading in
+// that defined type N2; see issues #29006 and #29198 for instances
+// of this. Example:
+//
+// type N1 N2
+// type N2 struct {
+// ...
+// p *N1
+// }
+//
+// To handle such cases, the parser generates a fixup record (below) and
+// delays setting of N1's underlying type until parsing is complete, at
+// which point fixups are applied.
+
+type fixupRecord struct {
+ toUpdate *types.Named // type to modify when fixup is processed
+ target types.Type // type that was incomplete when fixup was created
+}
+
+func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) {
+ p.scanner = new(scanner.Scanner)
+ p.initScanner(filename, src)
+ p.imports = imports
+ p.aliases = make(map[int]string)
+ p.typeList = make([]types.Type, 1 /* type numbers start at 1 */, 16)
+}
+
+func (p *parser) initScanner(filename string, src io.Reader) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings
+ p.scanner.Whitespace = 1<<'\t' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+}
+
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err any) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...any) {
+ p.error(fmt.Errorf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *parser) expectEOL() {
+ if p.version == "v1" || p.version == "v2" {
+ p.expect(';')
+ }
+ p.expect('\n')
+}
+
+func (p *parser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+func (p *parser) parseString() string {
+ str, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ return str
+}
+
+// unquotedString = { unquotedStringChar } .
+// unquotedStringChar = <neither a whitespace nor a ';' char> .
+func (p *parser) parseUnquotedString() string {
+ if p.tok == scanner.EOF {
+ p.error("unexpected EOF")
+ }
+ var b strings.Builder
+ b.WriteString(p.scanner.TokenText())
+ // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon,
+ // we need to let it be consumed by p.next().
+ for ch := p.scanner.Peek(); ch != '\n' && ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() {
+ b.WriteRune(ch)
+ p.scanner.Next()
+ }
+ p.next()
+ return b.String()
+}
+
+func (p *parser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Float, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+}
+
+func (p *parser) parseQualifiedName() (path, name string) {
+ return p.parseQualifiedNameStr(p.parseString())
+}
+
+func (p *parser) parseUnquotedQualifiedName() (path, name string) {
+ return p.parseQualifiedNameStr(p.parseUnquotedString())
+}
+
+// qualifiedName = [ ["."] unquotedString "." ] unquotedString .
+//
+// The above production uses greedy matching.
+func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name string) {
+ parts := strings.Split(unquotedName, ".")
+ if parts[0] == "" {
+ parts = parts[1:]
+ }
+
+ switch len(parts) {
+ case 0:
+ p.errorf("malformed qualified name: %q", unquotedName)
+ case 1:
+ // unqualified name
+ pkgpath = p.pkgpath
+ name = parts[0]
+ default:
+ // qualified name, which may contain periods
+ pkgpath = strings.Join(parts[0:len(parts)-1], ".")
+ name = parts[len(parts)-1]
+ }
+
+ return
+}
+
+// getPkg returns the package for a given path. If the package is
+// not found but we have a package name, create the package and
+// add it to the p.imports map.
+func (p *parser) getPkg(pkgpath, name string) *types.Package {
+ // package unsafe is not in the imports map - handle explicitly
+ if pkgpath == "unsafe" {
+ return types.Unsafe
+ }
+ pkg := p.imports[pkgpath]
+ if pkg == nil && name != "" {
+ pkg = types.NewPackage(pkgpath, name)
+ p.imports[pkgpath] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package path is resolved to an imported *types.Package.
+//
+// ExportedName = string [string] .
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+ path, name := p.parseQualifiedName()
+ var pkgname string
+ if p.tok == scanner.String {
+ pkgname = p.parseString()
+ }
+ pkg = p.getPkg(path, pkgname)
+ if pkg == nil {
+ p.errorf("package %s (path = %q) not found", name, path)
+ }
+ return
+}
+
+// Name = QualifiedName | "?" .
+func (p *parser) parseName() string {
+ if p.tok == '?' {
+ // Anonymous.
+ p.next()
+ return ""
+ }
+ // The package path is redundant for us. Don't try to parse it.
+ _, name := p.parseUnquotedQualifiedName()
+ return name
+}
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ typ = p.Elem()
+ }
+ return typ
+}
+
+// Field = Name Type [string] .
+func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
+ name := p.parseName()
+ typ, n := p.parseTypeExtended(pkg)
+ anon := false
+ if name == "" {
+ anon = true
+ // Alias?
+ if aname, ok := p.aliases[n]; ok {
+ name = aname
+ } else {
+ switch typ := deref(typ).(type) {
+ case *types.Basic:
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ p.error("embedded field expected")
+ }
+ }
+ }
+ field = types.NewField(token.NoPos, pkg, name, typ, anon)
+ if p.tok == scanner.String {
+ tag = p.parseString()
+ }
+ return
+}
+
+// Param = Name ["..."] Type .
+func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) {
+ name := p.parseName()
+ // Ignore names invented for inlinable functions.
+ if strings.HasPrefix(name, "p.") || strings.HasPrefix(name, "r.") || strings.HasPrefix(name, "$ret") {
+ name = ""
+ }
+ if p.tok == '<' && p.scanner.Peek() == 'e' {
+ // EscInfo = "<esc:" int ">" . (optional and ignored)
+ p.next()
+ p.expectKeyword("esc")
+ p.expect(':')
+ p.expect(scanner.Int)
+ p.expect('>')
+ }
+ if p.tok == '.' {
+ p.next()
+ p.expect('.')
+ p.expect('.')
+ isVariadic = true
+ }
+ typ := p.parseType(pkg)
+ if isVariadic {
+ typ = types.NewSlice(typ)
+ }
+ param = types.NewParam(token.NoPos, pkg, name, typ)
+ return
+}
+
+// Var = Name Type .
+func (p *parser) parseVar(pkg *types.Package) *types.Var {
+ name := p.parseName()
+ v := types.NewVar(token.NoPos, pkg, name, p.parseType(pkg))
+ if name[0] == '.' || name[0] == '<' {
+ // This is an unexported variable,
+ // or a variable defined in a different package.
+ // We only want to record exported variables.
+ return nil
+ }
+ return v
+}
+
+// Conversion = "convert" "(" Type "," ConstValue ")" .
+func (p *parser) parseConversion(pkg *types.Package) (val constant.Value, typ types.Type) {
+ p.expectKeyword("convert")
+ p.expect('(')
+ typ = p.parseType(pkg)
+ p.expect(',')
+ val, _ = p.parseConstValue(pkg)
+ p.expect(')')
+ return
+}
+
+// ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) | Conversion .
+// FloatOrComplex = float ["i" | ("+"|"-") float "i"] .
+func (p *parser) parseConstValue(pkg *types.Package) (val constant.Value, typ types.Type) {
+ // v3 changed to $false, $true, $convert, to avoid confusion
+ // with variable names in inline function bodies.
+ if p.tok == '$' {
+ p.next()
+ if p.tok != scanner.Ident {
+ p.errorf("expected identifier after '$', got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ }
+ }
+
+ switch p.tok {
+ case scanner.String:
+ str := p.parseString()
+ val = constant.MakeString(str)
+ typ = types.Typ[types.UntypedString]
+ return
+
+ case scanner.Ident:
+ b := false
+ switch p.lit {
+ case "false":
+ case "true":
+ b = true
+
+ case "convert":
+ return p.parseConversion(pkg)
+
+ default:
+ p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ }
+
+ p.next()
+ val = constant.MakeBool(b)
+ typ = types.Typ[types.UntypedBool]
+ return
+ }
+
+ sign := ""
+ if p.tok == '-' {
+ p.next()
+ sign = "-"
+ }
+
+ switch p.tok {
+ case scanner.Int:
+ val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0)
+ if val == nil {
+ p.error("could not parse integer literal")
+ }
+
+ p.next()
+ if p.tok == '\'' {
+ p.next()
+ typ = types.Typ[types.UntypedRune]
+ } else {
+ typ = types.Typ[types.UntypedInt]
+ }
+
+ case scanner.Float:
+ re := sign + p.lit
+ p.next()
+
+ var im string
+ switch p.tok {
+ case '+':
+ p.next()
+ im = p.expect(scanner.Float)
+
+ case '-':
+ p.next()
+ im = "-" + p.expect(scanner.Float)
+
+ case scanner.Ident:
+ // re is in fact the imaginary component. Expect "i" below.
+ im = re
+ re = "0"
+
+ default:
+ val = constant.MakeFromLiteral(re, token.FLOAT, 0)
+ if val == nil {
+ p.error("could not parse float literal")
+ }
+ typ = types.Typ[types.UntypedFloat]
+ return
+ }
+
+ p.expectKeyword("i")
+ reval := constant.MakeFromLiteral(re, token.FLOAT, 0)
+ if reval == nil {
+ p.error("could not parse real component of complex literal")
+ }
+ imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0)
+ if imval == nil {
+ p.error("could not parse imag component of complex literal")
+ }
+ val = constant.BinaryOp(reval, token.ADD, imval)
+ typ = types.Typ[types.UntypedComplex]
+
+ default:
+ p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ }
+
+ return
+}
+
+// Const = Name [Type] "=" ConstValue .
+func (p *parser) parseConst(pkg *types.Package) *types.Const {
+ name := p.parseName()
+ var typ types.Type
+ if p.tok == '<' {
+ typ = p.parseType(pkg)
+ }
+ p.expect('=')
+ val, vtyp := p.parseConstValue(pkg)
+ if typ == nil {
+ typ = vtyp
+ }
+ return types.NewConst(token.NoPos, pkg, name, typ, val)
+}
+
+// reserved is a singleton type used to fill type map slots that have
+// been reserved (i.e., for which a type number has been parsed) but
+// which don't have their actual type yet. When the type map is updated,
+// the actual type must replace a reserved entry (or we have an internal
+// error). Used for self-verification only - not required for correctness.
+var reserved = new(struct{ types.Type })
+
+// reserve reserves the type map entry n for future use.
+func (p *parser) reserve(n int) {
+ // Notes:
+ // - for pre-V3 export data, the type numbers we see are
+ // guaranteed to be in increasing order, so we append a
+ // reserved entry onto the list.
+ // - for V3+ export data, type numbers can appear in
+ // any order, however the 'types' section tells us the
+ // total number of types, hence typeList is pre-allocated.
+ if len(p.typeData) == 0 {
+ if n != len(p.typeList) {
+ p.errorf("invalid type number %d (out of sync)", n)
+ }
+ p.typeList = append(p.typeList, reserved)
+ } else {
+ if p.typeList[n] != nil {
+ p.errorf("previously visited type number %d", n)
+ }
+ p.typeList[n] = reserved
+ }
+}
+
+// update sets the type map entries for the entries in nlist to t.
+// An entry in nlist can be a type number in p.typeList,
+// used to resolve named types, or it can be a *types.Pointer,
+// used to resolve pointers to named types in case they are referenced
+// by embedded fields.
+func (p *parser) update(t types.Type, nlist []any) {
+ if t == reserved {
+ p.errorf("internal error: update(%v) invoked on reserved", nlist)
+ }
+ if t == nil {
+ p.errorf("internal error: update(%v) invoked on nil", nlist)
+ }
+ for _, n := range nlist {
+ switch n := n.(type) {
+ case int:
+ if p.typeList[n] == t {
+ continue
+ }
+ if p.typeList[n] != reserved {
+ p.errorf("internal error: update(%v): %d not reserved", nlist, n)
+ }
+ p.typeList[n] = t
+ case *types.Pointer:
+ if *n != (types.Pointer{}) {
+ elem := n.Elem()
+ if elem == t {
+ continue
+ }
+ p.errorf("internal error: update: pointer already set to %v, expected %v", elem, t)
+ }
+ *n = *types.NewPointer(t)
+ default:
+ p.errorf("internal error: %T on nlist", n)
+ }
+ }
+}
+
+// NamedType = TypeName [ "=" ] Type { Method } .
+// TypeName = ExportedName .
+// Method = "func" "(" Param ")" Name ParamList ResultList [InlineBody] ";" .
+func (p *parser) parseNamedType(nlist []any) types.Type {
+ pkg, name := p.parseExportedName()
+ scope := pkg.Scope()
+ obj := scope.Lookup(name)
+ if obj != nil && obj.Type() == nil {
+ p.errorf("%v has nil type", obj)
+ }
+
+ if p.tok == scanner.Ident && p.lit == "notinheap" {
+ p.next()
+ // The go/types package has no way of recording that
+ // this type is marked notinheap. Presumably no user
+ // of this package actually cares.
+ }
+
+ // type alias
+ if p.tok == '=' {
+ p.next()
+ p.aliases[nlist[len(nlist)-1].(int)] = name
+ if obj != nil {
+ // use the previously imported (canonical) type
+ t := obj.Type()
+ p.update(t, nlist)
+ p.parseType(pkg) // discard
+ return t
+ }
+ t := p.parseType(pkg, nlist...)
+ obj = types.NewTypeName(token.NoPos, pkg, name, t)
+ scope.Insert(obj)
+ return t
+ }
+
+ // defined type
+ if obj == nil {
+ // A named type may be referred to before the underlying type
+ // is known - set it up.
+ tname := types.NewTypeName(token.NoPos, pkg, name, nil)
+ types.NewNamed(tname, nil, nil)
+ scope.Insert(tname)
+ obj = tname
+ }
+
+ // use the previously imported (canonical), or newly created type
+ t := obj.Type()
+ p.update(t, nlist)
+
+ nt, ok := t.(*types.Named)
+ if !ok {
+ // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type.
+ pt := p.parseType(pkg)
+ if pt != t {
+ p.error("unexpected underlying type for non-named TypeName")
+ }
+ return t
+ }
+
+ underlying := p.parseType(pkg)
+ if nt.Underlying() == nil {
+ if underlying.Underlying() == nil {
+ fix := fixupRecord{toUpdate: nt, target: underlying}
+ p.fixups = append(p.fixups, fix)
+ } else {
+ nt.SetUnderlying(underlying.Underlying())
+ }
+ }
+
+ if p.tok == '\n' {
+ p.next()
+ // collect associated methods
+ for p.tok == scanner.Ident {
+ p.expectKeyword("func")
+ if p.tok == '/' {
+ // Skip a /*nointerface*/ or /*asm ID */ comment.
+ p.expect('/')
+ p.expect('*')
+ if p.expect(scanner.Ident) == "asm" {
+ p.parseUnquotedString()
+ }
+ p.expect('*')
+ p.expect('/')
+ }
+ p.expect('(')
+ receiver, _ := p.parseParam(pkg)
+ p.expect(')')
+ name := p.parseName()
+ params, isVariadic := p.parseParamList(pkg)
+ results := p.parseResultList(pkg)
+ p.skipInlineBody()
+ p.expectEOL()
+
+ sig := types.NewSignatureType(receiver, nil, nil, params, results, isVariadic)
+ nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+ }
+
+ return nt
+}
+
+func (p *parser) parseInt64() int64 {
+ lit := p.expect(scanner.Int)
+ n, err := strconv.ParseInt(lit, 10, 64)
+ if err != nil {
+ p.error(err)
+ }
+ return n
+}
+
+func (p *parser) parseInt() int {
+ lit := p.expect(scanner.Int)
+ n, err := strconv.ParseInt(lit, 10, 0 /* int */)
+ if err != nil {
+ p.error(err)
+ }
+ return int(n)
+}
+
+// ArrayOrSliceType = "[" [ int ] "]" Type .
+func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []any) types.Type {
+ p.expect('[')
+ if p.tok == ']' {
+ p.next()
+
+ t := new(types.Slice)
+ p.update(t, nlist)
+
+ *t = *types.NewSlice(p.parseType(pkg))
+ return t
+ }
+
+ t := new(types.Array)
+ p.update(t, nlist)
+
+ len := p.parseInt64()
+ p.expect(']')
+
+ *t = *types.NewArray(p.parseType(pkg), len)
+ return t
+}
+
+// MapType = "map" "[" Type "]" Type .
+func (p *parser) parseMapType(pkg *types.Package, nlist []any) types.Type {
+ p.expectKeyword("map")
+
+ t := new(types.Map)
+ p.update(t, nlist)
+
+ p.expect('[')
+ key := p.parseType(pkg)
+ p.expect(']')
+ elem := p.parseType(pkg)
+
+ *t = *types.NewMap(key, elem)
+ return t
+}
+
+// ChanType = "chan" ["<-" | "-<"] Type .
+func (p *parser) parseChanType(pkg *types.Package, nlist []any) types.Type {
+ p.expectKeyword("chan")
+
+ t := new(types.Chan)
+ p.update(t, nlist)
+
+ dir := types.SendRecv
+ switch p.tok {
+ case '-':
+ p.next()
+ p.expect('<')
+ dir = types.SendOnly
+
+ case '<':
+ // don't consume '<' if it belongs to Type
+ if p.scanner.Peek() == '-' {
+ p.next()
+ p.expect('-')
+ dir = types.RecvOnly
+ }
+ }
+
+ *t = *types.NewChan(dir, p.parseType(pkg))
+ return t
+}
+
+// StructType = "struct" "{" { Field } "}" .
+func (p *parser) parseStructType(pkg *types.Package, nlist []any) types.Type {
+ p.expectKeyword("struct")
+
+ t := new(types.Struct)
+ p.update(t, nlist)
+
+ var fields []*types.Var
+ var tags []string
+
+ p.expect('{')
+ for p.tok != '}' && p.tok != scanner.EOF {
+ field, tag := p.parseField(pkg)
+ p.expect(';')
+ fields = append(fields, field)
+ tags = append(tags, tag)
+ }
+ p.expect('}')
+
+ *t = *types.NewStruct(fields, tags)
+ return t
+}
+
+// ParamList = "(" [ { Parameter "," } Parameter ] ")" .
+func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
+ var list []*types.Var
+ isVariadic := false
+
+ p.expect('(')
+ for p.tok != ')' && p.tok != scanner.EOF {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParam(pkg)
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return types.NewTuple(list...), isVariadic
+}
+
+// ResultList = Type | ParamList .
+func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
+ switch p.tok {
+ case '<':
+ p.next()
+ if p.tok == scanner.Ident && p.lit == "inl" {
+ return nil
+ }
+ taa, _ := p.parseTypeAfterAngle(pkg)
+ return types.NewTuple(types.NewParam(token.NoPos, pkg, "", taa))
+
+ case '(':
+ params, _ := p.parseParamList(pkg)
+ return params
+
+ default:
+ return nil
+ }
+}
+
+// FunctionType = ParamList ResultList .
+func (p *parser) parseFunctionType(pkg *types.Package, nlist []any) *types.Signature {
+ t := new(types.Signature)
+ p.update(t, nlist)
+
+ params, isVariadic := p.parseParamList(pkg)
+ results := p.parseResultList(pkg)
+
+ *t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic)
+ return t
+}
+
+// Func = Name FunctionType [InlineBody] .
+func (p *parser) parseFunc(pkg *types.Package) *types.Func {
+ if p.tok == '/' {
+ // Skip an /*asm ID */ comment.
+ p.expect('/')
+ p.expect('*')
+ if p.expect(scanner.Ident) == "asm" {
+ p.parseUnquotedString()
+ }
+ p.expect('*')
+ p.expect('/')
+ }
+
+ name := p.parseName()
+ f := types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg, nil))
+ p.skipInlineBody()
+
+ if name[0] == '.' || name[0] == '<' || strings.ContainsRune(name, '$') {
+ // This is an unexported function,
+ // or a function defined in a different package,
+ // or a type$equal or type$hash function.
+ // We only want to record exported functions.
+ return nil
+ }
+
+ return f
+}
+
+// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" .
+func (p *parser) parseInterfaceType(pkg *types.Package, nlist []any) types.Type {
+ p.expectKeyword("interface")
+
+ t := new(types.Interface)
+ p.update(t, nlist)
+
+ var methods []*types.Func
+ var embeddeds []types.Type
+
+ p.expect('{')
+ for p.tok != '}' && p.tok != scanner.EOF {
+ if p.tok == '?' {
+ p.next()
+ embeddeds = append(embeddeds, p.parseType(pkg))
+ } else {
+ method := p.parseFunc(pkg)
+ if method != nil {
+ methods = append(methods, method)
+ }
+ }
+ p.expect(';')
+ }
+ p.expect('}')
+
+ *t = *types.NewInterfaceType(methods, embeddeds)
+ return t
+}
+
+// PointerType = "*" ("any" | Type) .
+func (p *parser) parsePointerType(pkg *types.Package, nlist []any) types.Type {
+ p.expect('*')
+ if p.tok == scanner.Ident {
+ p.expectKeyword("any")
+ t := types.Typ[types.UnsafePointer]
+ p.update(t, nlist)
+ return t
+ }
+
+ t := new(types.Pointer)
+ p.update(t, nlist)
+
+ *t = *types.NewPointer(p.parseType(pkg, t))
+
+ return t
+}
+
+// TypeSpec = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType .
+func (p *parser) parseTypeSpec(pkg *types.Package, nlist []any) types.Type {
+ switch p.tok {
+ case scanner.String:
+ return p.parseNamedType(nlist)
+
+ case scanner.Ident:
+ switch p.lit {
+ case "map":
+ return p.parseMapType(pkg, nlist)
+
+ case "chan":
+ return p.parseChanType(pkg, nlist)
+
+ case "struct":
+ return p.parseStructType(pkg, nlist)
+
+ case "interface":
+ return p.parseInterfaceType(pkg, nlist)
+ }
+
+ case '*':
+ return p.parsePointerType(pkg, nlist)
+
+ case '[':
+ return p.parseArrayOrSliceType(pkg, nlist)
+
+ case '(':
+ return p.parseFunctionType(pkg, nlist)
+ }
+
+ p.errorf("expected type name or literal, got %s", scanner.TokenString(p.tok))
+ return nil
+}
+
+const (
+ // From gofrontend/go/export.h
+ // Note that these values are negative in the gofrontend and have been made positive
+ // in the gccgoimporter.
+ gccgoBuiltinINT8 = 1
+ gccgoBuiltinINT16 = 2
+ gccgoBuiltinINT32 = 3
+ gccgoBuiltinINT64 = 4
+ gccgoBuiltinUINT8 = 5
+ gccgoBuiltinUINT16 = 6
+ gccgoBuiltinUINT32 = 7
+ gccgoBuiltinUINT64 = 8
+ gccgoBuiltinFLOAT32 = 9
+ gccgoBuiltinFLOAT64 = 10
+ gccgoBuiltinINT = 11
+ gccgoBuiltinUINT = 12
+ gccgoBuiltinUINTPTR = 13
+ gccgoBuiltinBOOL = 15
+ gccgoBuiltinSTRING = 16
+ gccgoBuiltinCOMPLEX64 = 17
+ gccgoBuiltinCOMPLEX128 = 18
+ gccgoBuiltinERROR = 19
+ gccgoBuiltinBYTE = 20
+ gccgoBuiltinRUNE = 21
+)
+
+func lookupBuiltinType(typ int) types.Type {
+ return [...]types.Type{
+ gccgoBuiltinINT8: types.Typ[types.Int8],
+ gccgoBuiltinINT16: types.Typ[types.Int16],
+ gccgoBuiltinINT32: types.Typ[types.Int32],
+ gccgoBuiltinINT64: types.Typ[types.Int64],
+ gccgoBuiltinUINT8: types.Typ[types.Uint8],
+ gccgoBuiltinUINT16: types.Typ[types.Uint16],
+ gccgoBuiltinUINT32: types.Typ[types.Uint32],
+ gccgoBuiltinUINT64: types.Typ[types.Uint64],
+ gccgoBuiltinFLOAT32: types.Typ[types.Float32],
+ gccgoBuiltinFLOAT64: types.Typ[types.Float64],
+ gccgoBuiltinINT: types.Typ[types.Int],
+ gccgoBuiltinUINT: types.Typ[types.Uint],
+ gccgoBuiltinUINTPTR: types.Typ[types.Uintptr],
+ gccgoBuiltinBOOL: types.Typ[types.Bool],
+ gccgoBuiltinSTRING: types.Typ[types.String],
+ gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64],
+ gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128],
+ gccgoBuiltinERROR: types.Universe.Lookup("error").Type(),
+ gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(),
+ gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(),
+ }[typ]
+}
+
+// Type = "<" "type" ( "-" int | int [ TypeSpec ] ) ">" .
+//
+// parseType updates the type map to t for all type numbers n.
+func (p *parser) parseType(pkg *types.Package, n ...any) types.Type {
+ p.expect('<')
+ t, _ := p.parseTypeAfterAngle(pkg, n...)
+ return t
+}
+
+// (*parser).Type after reading the "<".
+func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...any) (t types.Type, n1 int) {
+ p.expectKeyword("type")
+
+ n1 = 0
+ switch p.tok {
+ case scanner.Int:
+ n1 = p.parseInt()
+ if p.tok == '>' {
+ if len(p.typeData) > 0 && p.typeList[n1] == nil {
+ p.parseSavedType(pkg, n1, n)
+ }
+ t = p.typeList[n1]
+ if len(p.typeData) == 0 && t == reserved {
+ p.errorf("invalid type cycle, type %d not yet defined (nlist=%v)", n1, n)
+ }
+ p.update(t, n)
+ } else {
+ p.reserve(n1)
+ t = p.parseTypeSpec(pkg, append(n, n1))
+ }
+
+ case '-':
+ p.next()
+ n1 := p.parseInt()
+ t = lookupBuiltinType(n1)
+ p.update(t, n)
+
+ default:
+ p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil, 0
+ }
+
+ if t == nil || t == reserved {
+ p.errorf("internal error: bad return from parseType(%v)", n)
+ }
+
+ p.expect('>')
+ return
+}
+
+// parseTypeExtended is identical to parseType, but if the type in
+// question is a saved type, returns the index as well as the type
+// pointer (index returned is zero if we parsed a builtin).
+func (p *parser) parseTypeExtended(pkg *types.Package, n ...any) (t types.Type, n1 int) {
+ p.expect('<')
+ t, n1 = p.parseTypeAfterAngle(pkg, n...)
+ return
+}
+
+// InlineBody = "<inl:NN>" .{NN}
+// Reports whether a body was skipped.
+func (p *parser) skipInlineBody() {
+ // We may or may not have seen the '<' already, depending on
+ // whether the function had a result type or not.
+ if p.tok == '<' {
+ p.next()
+ p.expectKeyword("inl")
+ } else if p.tok != scanner.Ident || p.lit != "inl" {
+ return
+ } else {
+ p.next()
+ }
+
+ p.expect(':')
+ want := p.parseInt()
+ p.expect('>')
+
+ defer func(w uint64) {
+ p.scanner.Whitespace = w
+ }(p.scanner.Whitespace)
+ p.scanner.Whitespace = 0
+
+ got := 0
+ for got < want {
+ r := p.scanner.Next()
+ if r == scanner.EOF {
+ p.error("unexpected EOF")
+ }
+ got += utf8.RuneLen(r)
+ }
+}
+
+// Types = "types" maxp1 exportedp1 (offset length)* .
+func (p *parser) parseTypes(pkg *types.Package) {
+ maxp1 := p.parseInt()
+ exportedp1 := p.parseInt()
+ p.typeList = make([]types.Type, maxp1, maxp1)
+
+ type typeOffset struct {
+ offset int
+ length int
+ }
+ var typeOffsets []typeOffset
+
+ total := 0
+ for i := 1; i < maxp1; i++ {
+ len := p.parseInt()
+ typeOffsets = append(typeOffsets, typeOffset{total, len})
+ total += len
+ }
+
+ defer func(w uint64) {
+ p.scanner.Whitespace = w
+ }(p.scanner.Whitespace)
+ p.scanner.Whitespace = 0
+
+ // We should now have p.tok pointing to the final newline.
+ // The next runes from the scanner should be the type data.
+
+ var sb strings.Builder
+ for sb.Len() < total {
+ r := p.scanner.Next()
+ if r == scanner.EOF {
+ p.error("unexpected EOF")
+ }
+ sb.WriteRune(r)
+ }
+ allTypeData := sb.String()
+
+ p.typeData = []string{""} // type 0, unused
+ for _, to := range typeOffsets {
+ p.typeData = append(p.typeData, allTypeData[to.offset:to.offset+to.length])
+ }
+
+ for i := 1; i < int(exportedp1); i++ {
+ p.parseSavedType(pkg, i, nil)
+ }
+}
+
+// parseSavedType parses one saved type definition.
+func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []any) {
+ defer func(s *scanner.Scanner, tok rune, lit string) {
+ p.scanner = s
+ p.tok = tok
+ p.lit = lit
+ }(p.scanner, p.tok, p.lit)
+
+ p.scanner = new(scanner.Scanner)
+ p.initScanner(p.scanner.Filename, strings.NewReader(p.typeData[i]))
+ p.expectKeyword("type")
+ id := p.parseInt()
+ if id != i {
+ p.errorf("type ID mismatch: got %d, want %d", id, i)
+ }
+ if p.typeList[i] == reserved {
+ p.errorf("internal error: %d already reserved in parseSavedType", i)
+ }
+ if p.typeList[i] == nil {
+ p.reserve(i)
+ p.parseTypeSpec(pkg, append(nlist, i))
+ }
+ if p.typeList[i] == nil || p.typeList[i] == reserved {
+ p.errorf("internal error: parseSavedType(%d,%v) reserved/nil", i, nlist)
+ }
+}
+
+// PackageInit = unquotedString unquotedString int .
+func (p *parser) parsePackageInit() PackageInit {
+ name := p.parseUnquotedString()
+ initfunc := p.parseUnquotedString()
+ priority := -1
+ if p.version == "v1" {
+ priority = p.parseInt()
+ }
+ return PackageInit{Name: name, InitFunc: initfunc, Priority: priority}
+}
+
+// Create the package if we have parsed both the package path and package name.
+func (p *parser) maybeCreatePackage() {
+ if p.pkgname != "" && p.pkgpath != "" {
+ p.pkg = p.getPkg(p.pkgpath, p.pkgname)
+ }
+}
+
+// InitDataDirective = ( "v1" | "v2" | "v3" ) ";" |
+//
+// "priority" int ";" |
+// "init" { PackageInit } ";" |
+// "checksum" unquotedString ";" .
+func (p *parser) parseInitDataDirective() {
+ if p.tok != scanner.Ident {
+ // unexpected token kind; panic
+ p.expect(scanner.Ident)
+ }
+
+ switch p.lit {
+ case "v1", "v2", "v3":
+ p.version = p.lit
+ p.next()
+ p.expect(';')
+ p.expect('\n')
+
+ case "priority":
+ p.next()
+ p.initdata.Priority = p.parseInt()
+ p.expectEOL()
+
+ case "init":
+ p.next()
+ for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF {
+ p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit())
+ }
+ p.expectEOL()
+
+ case "init_graph":
+ p.next()
+ // The graph data is thrown away for now.
+ for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF {
+ p.parseInt64()
+ p.parseInt64()
+ }
+ p.expectEOL()
+
+ case "checksum":
+ // Don't let the scanner try to parse the checksum as a number.
+ defer func(mode uint) {
+ p.scanner.Mode = mode
+ }(p.scanner.Mode)
+ p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats
+ p.next()
+ p.parseUnquotedString()
+ p.expectEOL()
+
+ default:
+ p.errorf("unexpected identifier: %q", p.lit)
+ }
+}
+
+// Directive = InitDataDirective |
+//
+// "package" unquotedString [ unquotedString ] [ unquotedString ] ";" |
+// "pkgpath" unquotedString ";" |
+// "prefix" unquotedString ";" |
+// "import" unquotedString unquotedString string ";" |
+// "indirectimport" unquotedString unquotedstring ";" |
+// "func" Func ";" |
+// "type" Type ";" |
+// "var" Var ";" |
+// "const" Const ";" .
+func (p *parser) parseDirective() {
+ if p.tok != scanner.Ident {
+ // unexpected token kind; panic
+ p.expect(scanner.Ident)
+ }
+
+ switch p.lit {
+ case "v1", "v2", "v3", "priority", "init", "init_graph", "checksum":
+ p.parseInitDataDirective()
+
+ case "package":
+ p.next()
+ p.pkgname = p.parseUnquotedString()
+ p.maybeCreatePackage()
+ if p.version != "v1" && p.tok != '\n' && p.tok != ';' {
+ p.parseUnquotedString()
+ p.parseUnquotedString()
+ }
+ p.expectEOL()
+
+ case "pkgpath":
+ p.next()
+ p.pkgpath = p.parseUnquotedString()
+ p.maybeCreatePackage()
+ p.expectEOL()
+
+ case "prefix":
+ p.next()
+ p.pkgpath = p.parseUnquotedString()
+ p.expectEOL()
+
+ case "import":
+ p.next()
+ pkgname := p.parseUnquotedString()
+ pkgpath := p.parseUnquotedString()
+ p.getPkg(pkgpath, pkgname)
+ p.parseString()
+ p.expectEOL()
+
+ case "indirectimport":
+ p.next()
+ pkgname := p.parseUnquotedString()
+ pkgpath := p.parseUnquotedString()
+ p.getPkg(pkgpath, pkgname)
+ p.expectEOL()
+
+ case "types":
+ p.next()
+ p.parseTypes(p.pkg)
+ p.expectEOL()
+
+ case "func":
+ p.next()
+ fun := p.parseFunc(p.pkg)
+ if fun != nil {
+ p.pkg.Scope().Insert(fun)
+ }
+ p.expectEOL()
+
+ case "type":
+ p.next()
+ p.parseType(p.pkg)
+ p.expectEOL()
+
+ case "var":
+ p.next()
+ v := p.parseVar(p.pkg)
+ if v != nil {
+ p.pkg.Scope().Insert(v)
+ }
+ p.expectEOL()
+
+ case "const":
+ p.next()
+ c := p.parseConst(p.pkg)
+ p.pkg.Scope().Insert(c)
+ p.expectEOL()
+
+ default:
+ p.errorf("unexpected identifier: %q", p.lit)
+ }
+}
+
+// Package = { Directive } .
+func (p *parser) parsePackage() *types.Package {
+ for p.tok != scanner.EOF {
+ p.parseDirective()
+ }
+ for _, f := range p.fixups {
+ if f.target.Underlying() == nil {
+ p.errorf("internal error: fixup can't be applied, loop required")
+ }
+ f.toUpdate.SetUnderlying(f.target.Underlying())
+ }
+ p.fixups = nil
+ for _, typ := range p.typeList {
+ if it, ok := typ.(*types.Interface); ok {
+ it.Complete()
+ }
+ }
+ p.pkg.MarkComplete()
+ return p.pkg
+}
diff --git a/src/go/internal/gccgoimporter/parser_test.go b/src/go/internal/gccgoimporter/parser_test.go
new file mode 100644
index 0000000..00128b4
--- /dev/null
+++ b/src/go/internal/gccgoimporter/parser_test.go
@@ -0,0 +1,78 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bytes"
+ "go/types"
+ "strings"
+ "testing"
+ "text/scanner"
+)
+
+var typeParserTests = []struct {
+ id, typ, want, underlying, methods string
+}{
+ {id: "foo", typ: "<type -1>", want: "int8"},
+ {id: "foo", typ: "<type 1 *<type -19>>", want: "*error"},
+ {id: "foo", typ: "<type 1 *any>", want: "unsafe.Pointer"},
+ {id: "foo", typ: "<type 1 \"Bar\" <type 2 *<type 1>>>", want: "foo.Bar", underlying: "*foo.Bar"},
+ {id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1>\nfunc (? <type 1>) M ();\n>", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"},
+ {id: "foo", typ: "<type 1 \".bar.foo\" \"bar\" <type -1>>", want: "bar.foo", underlying: "int8"},
+ {id: "foo", typ: "<type 1 []<type -1>>", want: "[]int8"},
+ {id: "foo", typ: "<type 1 [42]<type -1>>", want: "[42]int8"},
+ {id: "foo", typ: "<type 1 map [<type -1>] <type -2>>", want: "map[int8]int16"},
+ {id: "foo", typ: "<type 1 chan <type -1>>", want: "chan int8"},
+ {id: "foo", typ: "<type 1 chan <- <type -1>>", want: "<-chan int8"},
+ {id: "foo", typ: "<type 1 chan -< <type -1>>", want: "chan<- int8"},
+ {id: "foo", typ: "<type 1 struct { I8 <type -1>; I16 <type -2> \"i16\"; }>", want: "struct{I8 int8; I16 int16 \"i16\"}"},
+ {id: "foo", typ: "<type 1 interface { Foo (a <type -1>, b <type -2>) <type -1>; Bar (? <type -2>, ? ...<type -1>) (? <type -2>, ? <type -1>); Baz (); }>", want: "interface{Bar(int16, ...int8) (int16, int8); Baz(); Foo(a int8, b int16) int8}"},
+ {id: "foo", typ: "<type 1 (? <type -1>) <type -2>>", want: "func(int8) int16"},
+}
+
+func TestTypeParser(t *testing.T) {
+ for _, test := range typeParserTests {
+ var p parser
+ p.init("test.gox", strings.NewReader(test.typ), make(map[string]*types.Package))
+ p.version = "v2"
+ p.pkgname = test.id
+ p.pkgpath = test.id
+ p.maybeCreatePackage()
+ typ := p.parseType(p.pkg)
+
+ if p.tok != scanner.EOF {
+ t.Errorf("expected full parse, stopped at %q", p.lit)
+ }
+
+ // interfaces must be explicitly completed
+ if ityp, _ := typ.(*types.Interface); ityp != nil {
+ ityp.Complete()
+ }
+
+ got := typ.String()
+ if got != test.want {
+ t.Errorf("got type %q, expected %q", got, test.want)
+ }
+
+ if test.underlying != "" {
+ underlying := typ.Underlying().String()
+ if underlying != test.underlying {
+ t.Errorf("got underlying type %q, expected %q", underlying, test.underlying)
+ }
+ }
+
+ if test.methods != "" {
+ nt := typ.(*types.Named)
+ var buf bytes.Buffer
+ for i := 0; i != nt.NumMethods(); i++ {
+ buf.WriteString(nt.Method(i).String())
+ }
+ methods := buf.String()
+ if methods != test.methods {
+ t.Errorf("got methods %q, expected %q", methods, test.methods)
+ }
+ }
+ }
+}
diff --git a/src/go/internal/gccgoimporter/testdata/aliases.go b/src/go/internal/gccgoimporter/testdata/aliases.go
new file mode 100644
index 0000000..cfb59b3
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/aliases.go
@@ -0,0 +1,65 @@
+package aliases
+
+type (
+ T0 [10]int
+ T1 []byte
+ T2 struct {
+ x int
+ }
+ T3 interface {
+ m() T2
+ }
+ T4 func(int, T0) chan T2
+)
+
+// basic aliases
+type (
+ Ai = int
+ A0 = T0
+ A1 = T1
+ A2 = T2
+ A3 = T3
+ A4 = T4
+
+ A10 = [10]int
+ A11 = []byte
+ A12 = struct {
+ x int
+ }
+ A13 = interface {
+ m() A2
+ }
+ A14 = func(int, A0) chan A2
+)
+
+// alias receiver types
+func (T0) m1() {}
+func (A0) m2() {}
+
+// alias receiver types (long type declaration chains)
+type (
+ V0 = V1
+ V1 = (V2)
+ V2 = (V3)
+ V3 = T0
+)
+
+func (V1) n() {}
+
+// cycles
+type C0 struct {
+ f1 C1
+ f2 C2
+}
+
+type (
+ C1 *C0
+ C2 = C1
+)
+
+type (
+ C5 struct {
+ f *C6
+ }
+ C6 = C5
+)
diff --git a/src/go/internal/gccgoimporter/testdata/aliases.gox b/src/go/internal/gccgoimporter/testdata/aliases.gox
new file mode 100644
index 0000000..2428c06
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/aliases.gox
@@ -0,0 +1,33 @@
+v2;
+package aliases;
+prefix go;
+package aliases go.aliases go.aliases;
+type <type 1 "A0" = <type 2 "T0" <type 3 [10 ] <type -11>>
+ func (? <esc:0x1> <type 2>) .go.aliases.m1 ();
+ func (? <esc:0x1> <type 1>) .go.aliases.m2 ();
+ func (? <esc:0x1> <type 4 "V1" = <type 5 "V2" = <type 6 "V3" = <type 2>>>>) .go.aliases.n ();
+>>;
+type <type 7 "A1" = <type 8 "T1" <type 9 [] <type -20>>>>;
+type <type 10 "A10" = <type 11 [10 ] <type -11>>>;
+type <type 12 "A11" = <type 13 [] <type -20>>>;
+type <type 14 "A12" = <type 15 struct { .go.aliases.x <type -11>; }>>;
+type <type 16 "A13" = <type 17 interface { .go.aliases.m () <type 18 "A2" = <type 19 "T2" <type 20 struct { .go.aliases.x <type -11>; }>>>; }>>;
+type <type 21 "A14" = <type 22 (? <type -11>, ? <type 1>) <type 23 chan <type 18>>>>;
+type <type 18>;
+type <type 24 "A3" = <type 25 "T3" <type 26 interface { .go.aliases.m () <type 19>; }>>>;
+type <type 27 "A4" = <type 28 "T4" <type 29 (? <type -11>, ? <type 2>) <type 30 chan <type 19>>>>>;
+type <type 31 "Ai" = <type -11>>;
+type <type 32 "C0" <type 33 struct { .go.aliases.f1 <type 34 "C1" <type 35 *<type 32>>>; .go.aliases.f2 <type 36 "C2" = <type 34>>; }>>;
+type <type 34>;
+type <type 36>;
+type <type 37 "C5" <type 38 struct { .go.aliases.f <type 39 *<type 40 "C6" = <type 37>>>; }>>;
+type <type 40>;
+type <type 2>;
+type <type 8>;
+type <type 19>;
+type <type 25>;
+type <type 28>;
+type <type 41 "V0" = <type 4>>;
+type <type 4>;
+type <type 5>;
+type <type 6>;
diff --git a/src/go/internal/gccgoimporter/testdata/complexnums.go b/src/go/internal/gccgoimporter/testdata/complexnums.go
new file mode 100644
index 0000000..a51b6b0
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/complexnums.go
@@ -0,0 +1,6 @@
+package complexnums
+
+const NN = -1 - 1i
+const NP = -1 + 1i
+const PN = 1 - 1i
+const PP = 1 + 1i
diff --git a/src/go/internal/gccgoimporter/testdata/complexnums.gox b/src/go/internal/gccgoimporter/testdata/complexnums.gox
new file mode 100644
index 0000000..b66524f
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/complexnums.gox
@@ -0,0 +1,8 @@
+v1;
+package complexnums;
+pkgpath complexnums;
+priority 1;
+const NN = -0.1E1-0.1E1i ;
+const NP = -0.1E1+0.1E1i ;
+const PN = 0.1E1-0.1E1i ;
+const PP = 0.1E1+0.1E1i ;
diff --git a/src/go/internal/gccgoimporter/testdata/conversions.go b/src/go/internal/gccgoimporter/testdata/conversions.go
new file mode 100644
index 0000000..653927a
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/conversions.go
@@ -0,0 +1,5 @@
+package conversions
+
+type Units string
+
+const Bits = Units("bits")
diff --git a/src/go/internal/gccgoimporter/testdata/conversions.gox b/src/go/internal/gccgoimporter/testdata/conversions.gox
new file mode 100644
index 0000000..7de6cda
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/conversions.gox
@@ -0,0 +1,6 @@
+v2;
+package conversions;
+prefix go;
+package conversions go.conversions go.conversions;
+const Bits <type 1 "Units" <type -16>> = convert(<type 1>, "bits");
+type <type 1>;
diff --git a/src/go/internal/gccgoimporter/testdata/escapeinfo.go b/src/go/internal/gccgoimporter/testdata/escapeinfo.go
new file mode 100644
index 0000000..103ad95
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/escapeinfo.go
@@ -0,0 +1,13 @@
+// Test case for escape info in export data. To compile and extract .gox file:
+// gccgo -fgo-optimize-allocs -c escapeinfo.go
+// objcopy -j .go_export escapeinfo.o escapeinfo.gox
+
+package escapeinfo
+
+type T struct{ data []byte }
+
+func NewT(data []byte) *T {
+ return &T{data}
+}
+
+func (*T) Read(p []byte) {}
diff --git a/src/go/internal/gccgoimporter/testdata/escapeinfo.gox b/src/go/internal/gccgoimporter/testdata/escapeinfo.gox
new file mode 100644
index 0000000..94ce039
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/escapeinfo.gox
@@ -0,0 +1,9 @@
+v2;
+package escapeinfo;
+prefix go;
+package escapeinfo go.escapeinfo go.escapeinfo;
+func NewT (data <type 1 [] <type -20>>) <type 2 *<type 3 "T" <type 4 struct { .go.escapeinfo.data <type 5 [] <type -20>>; }>
+ func (? <type 6 *<type 3>>) Read (p <esc:0x1> <type 7 [] <type -20>>);
+>>;
+type <type 3>;
+checksum 3500838130783C0059CD0C81527F60E9738E3ACE;
diff --git a/src/go/internal/gccgoimporter/testdata/imports.go b/src/go/internal/gccgoimporter/testdata/imports.go
new file mode 100644
index 0000000..7907316
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/imports.go
@@ -0,0 +1,5 @@
+package imports
+
+import "fmt"
+
+var Hello = fmt.Sprintf("Hello, world")
diff --git a/src/go/internal/gccgoimporter/testdata/imports.gox b/src/go/internal/gccgoimporter/testdata/imports.gox
new file mode 100644
index 0000000..958a4f5
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/imports.gox
@@ -0,0 +1,7 @@
+v1;
+package imports;
+pkgpath imports;
+priority 7;
+import fmt fmt "fmt";
+init imports imports..import 7 math math..import 1 runtime runtime..import 1 strconv strconv..import 2 io io..import 3 reflect reflect..import 3 syscall syscall..import 3 time time..import 4 os os..import 5 fmt fmt..import 6;
+var Hello <type -16>;
diff --git a/src/go/internal/gccgoimporter/testdata/issue27856.go b/src/go/internal/gccgoimporter/testdata/issue27856.go
new file mode 100644
index 0000000..bf361e1
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue27856.go
@@ -0,0 +1,9 @@
+package lib
+
+type M struct {
+ E E
+}
+type F struct {
+ _ *M
+}
+type E = F
diff --git a/src/go/internal/gccgoimporter/testdata/issue27856.gox b/src/go/internal/gccgoimporter/testdata/issue27856.gox
new file mode 100644
index 0000000..6665e64
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue27856.gox
@@ -0,0 +1,9 @@
+v2;
+package main;
+pkgpath main;
+import runtime runtime "runtime";
+init runtime runtime..import sys runtime_internal_sys..import;
+init_graph 0 1;
+type <type 1 "E" = <type 2 "F" <type 3 struct { .main._ <type 4 *<type 5 "M" <type 6 struct { E <type 1>; }>>>; }>>>;
+type <type 2>;
+type <type 5>;
diff --git a/src/go/internal/gccgoimporter/testdata/issue29198.go b/src/go/internal/gccgoimporter/testdata/issue29198.go
new file mode 100644
index 0000000..75c2162
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue29198.go
@@ -0,0 +1,37 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package server
+
+import (
+ "context"
+ "errors"
+)
+
+type A struct {
+ x int
+}
+
+func (a *A) AMethod(y int) *Server {
+ return nil
+}
+
+// FooServer is a server that provides Foo services
+type FooServer Server
+
+func (f *FooServer) WriteEvents(ctx context.Context, x int) error {
+ return errors.New("hey!")
+}
+
+type Server struct {
+ FooServer *FooServer
+ user string
+ ctx context.Context
+}
+
+func New(sctx context.Context, u string) (*Server, error) {
+ s := &Server{user: u, ctx: sctx}
+ s.FooServer = (*FooServer)(s)
+ return s, nil
+}
diff --git a/src/go/internal/gccgoimporter/testdata/issue29198.gox b/src/go/internal/gccgoimporter/testdata/issue29198.gox
new file mode 100644
index 0000000..905c866
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue29198.gox
@@ -0,0 +1,86 @@
+v2;
+package server;
+pkgpath issue29198;
+import context context "context";
+import errors errors "errors";
+init context context..import fmt fmt..import poll internal_poll..import testlog internal_testlog..import io io..import os os..import reflect reflect..import runtime runtime..import sys runtime_internal_sys..import strconv strconv..import sync sync..import syscall syscall..import time time..import unicode unicode..import;
+init_graph 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 0 10 0 11 0 12 0 13 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 10 1 11 1 12 1 13 2 4 2 7 2 8 2 10 2 11 2 12 4 7 4 8 4 10 5 2 5 3 5 4 5 7 5 8 5 10 5 11 5 12 6 7 6 8 6 9 6 10 6 13 7 8 9 7 9 8 10 7 10 8 11 7 11 8 11 10 12 7 12 8 12 10 12 11;
+type <type 1 "A" <type 2 struct { .issue29198.x <type -11>; }>
+ func (a <esc:0x1> <type 3 *<type 1>>) AMethod (y <type -11>) <type 4 *<type 5 "Server" <type 6 struct { FooServer <type 7 *<type 8 "FooServer" <type 5>
+ func (f <esc:0x1> <type 9 *<type 8>>) WriteEvents (ctx <esc:0x1> <type 10 "context.Context" <type 11 interface { Deadline () (deadline <type 12 "time.Time" "time" <type 13 struct { .time.wall <type -8>; .time.ext <type -4>; .time.loc <type 14 *<type 15 "time.Location" <type 16 struct { .time.name <type -16>; .time.zone <type 17 [] <type 18 ".time.zone" <type 19 struct { .time.name <type -16>; .time.offset <type -11>; .time.isDST <type -15>; }>>>; .time.tx <type 20 [] <type 21 ".time.zoneTrans" <type 22 struct { .time.when <type -4>; .time.index <type -5>; .time.isstd <type -15>; .time.isutc <type -15>; }>>>; .time.cacheStart <type -4>; .time.cacheEnd <type -4>; .time.cacheZone <type 23 *<type 18>>; }>
+ func (l <esc:0x22> <type 24 *<type 15>>) String () <type -16>;
+ func (l <esc:0x1> <type 24>) .time.lookupFirstZone () <type -11>;
+ func (l <esc:0x12> <type 24>) .time.get () <type 24>;
+ func (l <esc:0x32> <type 24>) .time.lookup (sec <type -4>) (name <type -16>, offset <type -11>, isDST <type -15>, start <type -4>, end <type -4>);
+ func (l <esc:0x1> <type 24>) .time.lookupName (name <esc:0x1> <type -16>, unix <type -4>) (offset <type -11>, ok <type -15>);
+ func (l <esc:0x1> <type 24>) .time.firstZoneUsed () <type -15>;
+>>; }>
+ func (t <esc:0x12> <type 12>) In (loc <type 14>) <type 12>;
+ func (t <esc:0x1> <type 12>) .time.date (full <type -15>) (year <type -11>, month <type 25 "time.Month" <type -11>
+ func (m <type 25>) String () <type -16>;
+>, day <type -11>, yday <type -11>);
+ func (t <esc:0x1> <type 12>) Sub (u <esc:0x1> <type 12>) <type 26 "time.Duration" <type -4>
+ func (d <type 26>) Truncate (m <type 26>) <type 26>;
+ func (d <type 26>) String () <type -16>;
+ func (d <type 26>) Round (m <type 26>) <type 26>;
+ func (d <type 26>) Seconds () <type -10>;
+ func (d <type 26>) Nanoseconds () <type -4>;
+ func (d <type 26>) Minutes () <type -10>;
+ func (d <type 26>) Hours () <type -10>;
+>;
+ func (t <esc:0x12> <type 12>) Add (d <type 26>) <type 12>;
+ func (t <esc:0x12> <type 12>) UTC () <type 12>;
+ func (t <type 12>) AddDate (years <type -11>, months <type -11>, days <type -11>) <type 12>;
+ func (t <esc:0x1> <type 12>) MarshalBinary () (? <type 27 [] <type -20>>, ? <type -19>);
+ func (t <esc:0x1> <type 12>) Nanosecond () <type -11>;
+ func (t <esc:0x12> <type 12>) Round (d <type 26>) <type 12>;
+ func (t <esc:0x1> <type 12>) Minute () <type -11>;
+ func (t <esc:0x1> <type 12>) Clock () (hour <type -11>, min <type -11>, sec <type -11>);
+ func (t <esc:0x1> <type 12>) ISOWeek () (year <type -11>, week <type -11>);
+ func (t <esc:0x1> <type 12>) Day () <type -11>;
+ func (t <esc:0x1> <type 28 *<type 12>>) .time.mono () <type -4>;
+ func (t <esc:0x1> <type 12>) UnixNano () <type -4>;
+ func (t <esc:0x1> <type 28>) .time.sec () <type -4>;
+ func (t <esc:0x1> <type 12>) Second () <type -11>;
+ func (t <esc:0x1> <type 12>) Before (u <esc:0x1> <type 12>) <type -15>;
+ func (t <esc:0x1> <type 28>) UnmarshalBinary (data <esc:0x1> <type 29 [] <type -20>>) <type -19>;
+ func (t <esc:0x1> <type 12>) Month () <type 25>;
+ func (t <esc:0x1> <type 12>) YearDay () <type -11>;
+ func (t <esc:0x12> <type 12>) Location () <type 14>;
+ func (t <esc:0x32> <type 12>) Zone () (name <type -16>, offset <type -11>);
+ func (t <esc:0x12> <type 12>) Local () <type 12>;
+ func (t <esc:0x1> <type 28>) .time.setLoc (loc <type 14>);
+ func (t <esc:0x12> <type 12>) Truncate (d <type 26>) <type 12>;
+ func (t <esc:0x1> <type 12>) MarshalJSON () (? <type 30 [] <type -20>>, ? <type -19>);
+ func (t <esc:0x1> <type 12>) AppendFormat (b <esc:0x12> <type 31 [] <type -20>>, layout <esc:0x1> <type -16>) <type 32 [] <type -20>>;
+ func (t <esc:0x1> <type 28>) GobDecode (data <esc:0x1> <type 33 [] <type -20>>) <type -19>;
+ func (t <esc:0x1> <type 28>) UnmarshalJSON (data <esc:0x1> <type 34 [] <type -20>>) <type -19>;
+ func (t <esc:0x1> <type 12>) MarshalText () (? <type 35 [] <type -20>>, ? <type -19>);
+ func (t <esc:0x1> <type 12>) GobEncode () (? <type 36 [] <type -20>>, ? <type -19>);
+ func (t <esc:0x1> <type 28>) .time.stripMono ();
+ func (t <esc:0x1> <type 12>) After (u <esc:0x1> <type 12>) <type -15>;
+ func (t <esc:0x1> <type 12>) Hour () <type -11>;
+ func (t <esc:0x1> <type 28>) UnmarshalText (data <esc:0x1> <type 37 [] <type -20>>) <type -19>;
+ func (t <esc:0x1> <type 12>) Equal (u <esc:0x1> <type 12>) <type -15>;
+ func (t <esc:0x1> <type 28>) .time.setMono (m <type -4>);
+ func (t <esc:0x1> <type 12>) Year () <type -11>;
+ func (t <esc:0x1> <type 12>) IsZero () <type -15>;
+ func (t <esc:0x1> <type 28>) .time.addSec (d <type -4>);
+ func (t <esc:0x1> <type 12>) Weekday () <type 38 "time.Weekday" <type -11>
+ func (d <type 38>) String () <type -16>;
+>;
+ func (t <esc:0x1> <type 12>) String () <type -16>;
+ func (t <esc:0x1> <type 28>) .time.nsec () <type -3>;
+ func (t <esc:0x1> <type 12>) Format (layout <esc:0x1> <type -16>) <type -16>;
+ func (t <esc:0x1> <type 28>) .time.unixSec () <type -4>;
+ func (t <esc:0x1> <type 12>) Unix () <type -4>;
+ func (t <esc:0x1> <type 12>) .time.abs () <type -8>;
+ func (t <esc:0x32> <type 12>) .time.locabs () (name <type -16>, offset <type -11>, abs <type -8>);
+ func (t <esc:0x1> <type 12>) Date () (year <type -11>, month <type 25>, day <type -11>);
+>, ok <type -15>); Done () <type 39 chan <- <type 40 struct { }>>; Err () <type -19>; Value (key <type 41 interface { }>) <type 42 interface { }>; }>>, x <type -11>) <type -19>;
+>>; .issue29198.user <type -16>; .issue29198.ctx <type 10>; }>>>;
+>;
+type <type 8>;
+func New (sctx <type 10>, u <type -16>) (? <type 43 *<type 5>>, ? <type -19>);
+type <type 5>;
+checksum 86C8D76B2582F55A8BD2CA9E00060358EC1CE214;
diff --git a/src/go/internal/gccgoimporter/testdata/issue30628.go b/src/go/internal/gccgoimporter/testdata/issue30628.go
new file mode 100644
index 0000000..8fd7c13
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue30628.go
@@ -0,0 +1,18 @@
+package issue30628
+
+import (
+ "os"
+ "sync"
+)
+
+const numR = int32(os.O_TRUNC + 5)
+
+type Apple struct {
+ hey sync.RWMutex
+ x int
+ RQ [numR]struct {
+ Count uintptr
+ NumBytes uintptr
+ Last uintptr
+ }
+}
diff --git a/src/go/internal/gccgoimporter/testdata/issue30628.gox b/src/go/internal/gccgoimporter/testdata/issue30628.gox
new file mode 100644
index 0000000..0ff6259
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue30628.gox
@@ -0,0 +1,28 @@
+v3;
+package issue30628
+pkgpath issue30628
+import os os "os"
+import sync sync "sync"
+init cpu internal..z2fcpu..import poll internal..z2fpoll..import testlog internal..z2ftestlog..import io io..import os os..import runtime runtime..import sys runtime..z2finternal..z2fsys..import sync sync..import syscall syscall..import time time..import
+init_graph 1 0 1 3 1 5 1 6 1 7 1 8 1 9 3 0 3 5 3 6 3 7 4 0 4 1 4 2 4 3 4 5 4 6 4 7 4 8 4 9 5 0 5 6 7 0 7 5 7 6 8 0 8 5 8 6 8 7 9 0 9 5 9 6 9 7 9 8
+types 13 2 24 84 208 17 30 41 147 86 17 64 25 75
+type 1 "Apple" <type 2>
+type 2 struct { .issue30628.hey <type 3>; .issue30628.x <type -11>; RQ <type 11>; }
+type 3 "sync.RWMutex" <type 7>
+ func (rw <type 4>) Lock ()
+ func (rw <esc:0x12> <type 4>) RLocker () ($ret8 <type 5>)
+ func (rw <type 4>) RUnlock ()
+ func (rw <type 4>) Unlock ()
+ func (rw <type 4>) RLock ()
+type 4 *<type 3>
+type 5 "sync.Locker" <type 6>
+type 6 interface { Lock (); Unlock (); }
+type 7 struct { .sync.w <type 8>; .sync.writerSem <type -7>; .sync.readerSem <type -7>; .sync.readerCount <type -3>; .sync.readerWait <type -3>; }
+type 8 "sync.Mutex" <type 10>
+ func (m <type 9>) Unlock ()
+ func (m <type 9>) Lock ()
+type 9 *<type 8>
+type 10 struct { .sync.state <type -3>; .sync.sema <type -7>; }
+type 11 [517 ] <type 12>
+type 12 struct { Count <type -13>; NumBytes <type -13>; Last <type -13>; }
+checksum 199DCF6D3EE2FCF39F715B4E42B5F87F5B15D3AF
diff --git a/src/go/internal/gccgoimporter/testdata/issue31540.go b/src/go/internal/gccgoimporter/testdata/issue31540.go
new file mode 100644
index 0000000..2c6799e
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue31540.go
@@ -0,0 +1,26 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue31540
+
+type Y struct {
+ q int
+}
+
+type Z map[int]int
+
+type X = map[Y]Z
+
+type A1 = X
+
+type A2 = A1
+
+type S struct {
+ b int
+ A2
+}
+
+func Hallo() S {
+ return S{}
+}
diff --git a/src/go/internal/gccgoimporter/testdata/issue31540.gox b/src/go/internal/gccgoimporter/testdata/issue31540.gox
new file mode 100644
index 0000000..abdc696
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue31540.gox
@@ -0,0 +1,16 @@
+v3;
+package issue31540
+pkgpath issue31540
+types 11 7 23 23 20 22 20 21 57 31 45 36
+type 1 "A1" = <type 4>
+type 2 "A2" = <type 1>
+type 3 "S" <type 7>
+type 4 "X" = <type 8>
+type 5 "Y" <type 9>
+type 6 "Z" <type 10>
+type 7 struct { .go.mapalias.b <type -11>; ? <type 2>; }
+type 8 map [<type 5>] <type 6>
+type 9 struct { .go.mapalias.q <type -11>; }
+type 10 map [<type -11>] <type -11>
+func Hallo () <type 3>
+checksum C3FAF2524A90BC11225EE65D059BF27DFB69134B
diff --git a/src/go/internal/gccgoimporter/testdata/issue34182.go b/src/go/internal/gccgoimporter/testdata/issue34182.go
new file mode 100644
index 0000000..2a5c333
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue34182.go
@@ -0,0 +1,17 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue34182
+
+type T1 struct {
+ f *T2
+}
+
+type T2 struct {
+ f T3
+}
+
+type T3 struct {
+ *T2
+}
diff --git a/src/go/internal/gccgoimporter/testdata/issue34182.gox b/src/go/internal/gccgoimporter/testdata/issue34182.gox
new file mode 100644
index 0000000..671a7d6
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/issue34182.gox
@@ -0,0 +1,13 @@
+v3;
+package issue34182
+pkgpath issue34182
+init issue34182 ~go.issue34182
+types 8 4 21 21 21 17 30 45 45
+type 1 "T1" <type 6>
+type 2 "T2" <type 7>
+type 3 "T3" <type 5>
+type 4 *<type 2>
+type 5 struct { ? <type 4>; }
+type 6 struct { .go.issue34182.f <type 4>; }
+type 7 struct { .go.issue34182.f <type 3>; }
+checksum FF02C49BAF44B06C087ED4E573F7CC880C79C208
diff --git a/src/go/internal/gccgoimporter/testdata/libimportsar.a b/src/go/internal/gccgoimporter/testdata/libimportsar.a
new file mode 100644
index 0000000..6f30758
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/libimportsar.a
Binary files differ
diff --git a/src/go/internal/gccgoimporter/testdata/nointerface.go b/src/go/internal/gccgoimporter/testdata/nointerface.go
new file mode 100644
index 0000000..6a545f2
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/nointerface.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package nointerface
+
+type I int
+
+//go:nointerface
+func (p *I) Get() int { return int(*p) }
+
+func (p *I) Set(v int) { *p = I(v) }
diff --git a/src/go/internal/gccgoimporter/testdata/nointerface.gox b/src/go/internal/gccgoimporter/testdata/nointerface.gox
new file mode 100644
index 0000000..7b73d17
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/nointerface.gox
@@ -0,0 +1,8 @@
+v3;
+package nointerface
+pkgpath nointerface
+types 3 2 133 17
+type 1 "I" <type -11>
+ func /*nointerface*/ (p <esc:0x1> <type 2>) Get () <type -11>
+ func (p <esc:0x1> <type 2>) Set (v <type -11>)
+type 2 *<type 1>
diff --git a/src/go/internal/gccgoimporter/testdata/notinheap.go b/src/go/internal/gccgoimporter/testdata/notinheap.go
new file mode 100644
index 0000000..b1ac967
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/notinheap.go
@@ -0,0 +1,4 @@
+package notinheap
+
+//go:notinheap
+type S struct{}
diff --git a/src/go/internal/gccgoimporter/testdata/notinheap.gox b/src/go/internal/gccgoimporter/testdata/notinheap.gox
new file mode 100644
index 0000000..cc438e7
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/notinheap.gox
@@ -0,0 +1,7 @@
+v3;
+package notinheap
+pkgpath notinheap
+init notinheap ~notinheap
+types 3 2 30 18
+type 1 "S" notinheap <type 2>
+type 2 struct { }
diff --git a/src/go/internal/gccgoimporter/testdata/pointer.go b/src/go/internal/gccgoimporter/testdata/pointer.go
new file mode 100644
index 0000000..4ebc671
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/pointer.go
@@ -0,0 +1,3 @@
+package pointer
+
+type Int8Ptr *int8
diff --git a/src/go/internal/gccgoimporter/testdata/pointer.gox b/src/go/internal/gccgoimporter/testdata/pointer.gox
new file mode 100644
index 0000000..d96ebbd
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/pointer.gox
@@ -0,0 +1,4 @@
+v1;
+package pointer;
+pkgpath pointer;
+type <type 1 "Int8Ptr" <type 2 *<type -1>>>;
diff --git a/src/go/internal/gccgoimporter/testdata/time.gox b/src/go/internal/gccgoimporter/testdata/time.gox
new file mode 100644
index 0000000..a6822ea
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/time.gox
@@ -0,0 +1,142 @@
+v2;
+package time;
+pkgpath time;
+import errors errors "errors";
+import sync sync "sync";
+import syscall syscall "syscall";
+init time time..import runtime runtime..import sync sync..import syscall syscall..import;
+init_graph 0 1 0 2 0 3 2 1 3 1 3 2;
+const ANSIC = "Mon Jan _2 15:04:05 2006";
+func After (d <type 1 "Duration" <type -4>
+ func (d <type 1>) String () <type -16>;
+ func (d <type 1>) Nanoseconds () <type -4>;
+ func (d <type 1>) Seconds () <type -10>;
+ func (d <type 1>) Minutes () <type -10>;
+ func (d <type 1>) Hours () <type -10>;
+>) <type 2 chan <- <type 3 "Time" <type 4 struct { .time.sec <type -4>; .time.nsec <type -3>; .time.loc <type 5 *<type 6 "Location" <type 7 struct { .time.name <type -16>; .time.zone <type 8 [] <type 9 ".time.zone" <type 10 struct { .time.name <type -16>; .time.offset <type -11>; .time.isDST <type -15>; }>>>; .time.tx <type 11 [] <type 12 ".time.zoneTrans" <type 13 struct { .time.when <type -4>; .time.index <type -5>; .time.isstd <type -15>; .time.isutc <type -15>; }>>>; .time.cacheStart <type -4>; .time.cacheEnd <type -4>; .time.cacheZone <type 14 *<type 9>>; }>
+ func (l <type 15 *<type 6>>) .time.get () <type 15>;
+ func (l <type 15>) String () <type -16>;
+ func (l <type 15>) .time.lookup (sec <type -4>) (name <type -16>, offset <type -11>, isDST <type -15>, start <type -4>, end <type -4>);
+ func (l <type 15>) .time.lookupFirstZone () <type -11>;
+ func (l <type 15>) .time.firstZoneUsed () <type -15>;
+ func (l <type 15>) .time.lookupName (name <type -16>, unix <type -4>) (offset <type -11>, isDST <type -15>, ok <type -15>);
+>>; }>
+ func (t <type 3>) String () <type -16>;
+ func (t <type 3>) Format (layout <type -16>) <type -16>;
+ func (t <type 3>) AppendFormat (b <type 16 [] <type -20>>, layout <type -16>) <type 17 [] <type -20>>;
+ func (t <type 3>) After (u <type 3>) <type -15>;
+ func (t <type 3>) Before (u <type 3>) <type -15>;
+ func (t <type 3>) Equal (u <type 3>) <type -15>;
+ func (t <type 3>) IsZero () <type -15>;
+ func (t <type 3>) .time.abs () <type -8>;
+ func (t <type 3>) .time.locabs () (name <type -16>, offset <type -11>, abs <type -8>);
+ func (t <type 3>) Date () (year <type -11>, month <type 18 "Month" <type -11>
+ func (m <type 18>) String () <type -16>;
+>, day <type -11>);
+ func (t <type 3>) Year () <type -11>;
+ func (t <type 3>) Month () <type 18>;
+ func (t <type 3>) Day () <type -11>;
+ func (t <type 3>) Weekday () <type 19 "Weekday" <type -11>
+ func (d <type 19>) String () <type -16>;
+>;
+ func (t <type 3>) ISOWeek () (year <type -11>, week <type -11>);
+ func (t <type 3>) Clock () (hour <type -11>, min <type -11>, sec <type -11>);
+ func (t <type 3>) Hour () <type -11>;
+ func (t <type 3>) Minute () <type -11>;
+ func (t <type 3>) Second () <type -11>;
+ func (t <type 3>) Nanosecond () <type -11>;
+ func (t <type 3>) YearDay () <type -11>;
+ func (t <type 3>) Add (d <type 1>) <type 3>;
+ func (t <type 3>) Sub (u <type 3>) <type 1>;
+ func (t <type 3>) AddDate (years <type -11>, months <type -11>, days <type -11>) <type 3>;
+ func (t <type 3>) .time.date (full <type -15>) (year <type -11>, month <type 18>, day <type -11>, yday <type -11>);
+ func (t <type 3>) UTC () <type 3>;
+ func (t <type 3>) Local () <type 3>;
+ func (t <type 3>) In (loc <type 20 *<type 6>>) <type 3>;
+ func (t <type 3>) Location () <type 21 *<type 6>>;
+ func (t <type 3>) Zone () (name <type -16>, offset <type -11>);
+ func (t <type 3>) Unix () <type -4>;
+ func (t <type 3>) UnixNano () <type -4>;
+ func (t <type 3>) MarshalBinary () (? <type 22 [] <type -20>>, ? <type -19>);
+ func (t <type 23 *<type 3>>) UnmarshalBinary (data <type 24 [] <type -20>>) <type -19>;
+ func (t <type 3>) GobEncode () (? <type 25 [] <type -20>>, ? <type -19>);
+ func (t <type 23>) GobDecode (data <type 26 [] <type -20>>) <type -19>;
+ func (t <type 3>) MarshalJSON () (? <type 27 [] <type -20>>, ? <type -19>);
+ func (t <type 23>) UnmarshalJSON (data <type 28 [] <type -20>>) <type -19>;
+ func (t <type 3>) MarshalText () (? <type 29 [] <type -20>>, ? <type -19>);
+ func (t <type 23>) UnmarshalText (data <type 30 [] <type -20>>) <type -19>;
+ func (t <type 3>) Truncate (d <type 1>) <type 3>;
+ func (t <type 3>) Round (d <type 1>) <type 3>;
+>>;
+func AfterFunc (d <type 1>, f <type 31 ()>) <type 32 *<type 33 "Timer" <type 34 struct { C <type 35 chan <- <type 3>>; .time.r <type 36 ".time.runtimeTimer" <type 37 struct { .time.i <type -11>; .time.when <type -4>; .time.period <type -4>; .time.f <type 38 (? <type 39 interface { }>, ? <type -13>)>; .time.arg <type 40 interface { }>; .time.seq <type -13>; }>>; }>
+ func (t <type 41 *<type 33>>) Stop () <type -15>;
+ func (t <type 41>) Reset (d <type 1>) <type -15>;
+>>;
+const April <type 18> = 4 ;
+const August <type 18> = 8 ;
+func Date (year <type -11>, month <type 18>, day <type -11>, hour <type -11>, min <type -11>, sec <type -11>, nsec <type -11>, loc <type 42 *<type 6>>) <type 3>;
+const December <type 18> = 12 ;
+type <type 1>;
+const February <type 18> = 2 ;
+func FixedZone (name <type -16>, offset <type -11>) <type 15>;
+const Friday <type 19> = 5 ;
+const Hour <type 1> = 3600000000000 ;
+const January <type 18> = 1 ;
+const July <type 18> = 7 ;
+const June <type 18> = 6 ;
+const Kitchen = "3:04PM";
+func LoadLocation (name <type -16>) (? <type 15>, ? <type -19>);
+var Local <type 15>;
+type <type 6>;
+var LocationSource <type 43 (name <type -16>) (? <type 44 [] <type -20>>, ? <type -19>)>;
+const March <type 18> = 3 ;
+const May <type 18> = 5 ;
+const Microsecond <type 1> = 1000 ;
+const Millisecond <type 1> = 1000000 ;
+const Minute <type 1> = 60000000000 ;
+const Monday <type 19> = 1 ;
+type <type 18>;
+const Nanosecond <type 1> = 1 ;
+func NewTicker (d <type 1>) <type 45 *<type 46 "Ticker" <type 47 struct { C <type 48 chan <- <type 3>>; .time.r <type 36>; }>
+ func (t <type 49 *<type 46>>) Stop ();
+>>;
+func NewTimer (d <type 1>) <type 32>;
+const November <type 18> = 11 ;
+func Now () <type 3>;
+const October <type 18> = 10 ;
+func Parse (layout <type -16>, value <type -16>) (? <type 3>, ? <type -19>);
+func ParseDuration (s <type -16>) (? <type 1>, ? <type -19>);
+type <type 50 "ParseError" <type 51 struct { Layout <type -16>; Value <type -16>; LayoutElem <type -16>; ValueElem <type -16>; Message <type -16>; }>
+ func (e <type 52 *<type 50>>) Error () <type -16>;
+>;
+func ParseInLocation (layout <type -16>, value <type -16>, loc <type 53 *<type 6>>) (? <type 3>, ? <type -19>);
+const RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST";
+const RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700";
+const RFC3339 = "2006-01-02T15:04:05Z07:00";
+const RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00";
+const RFC822 = "02 Jan 06 15:04 MST";
+const RFC822Z = "02 Jan 06 15:04 -0700";
+const RFC850 = "Monday, 02-Jan-06 15:04:05 MST";
+const RubyDate = "Mon Jan 02 15:04:05 -0700 2006";
+const Saturday <type 19> = 6 ;
+const Second <type 1> = 1000000000 ;
+const September <type 18> = 9 ;
+func Since (t <type 3>) <type 1>;
+func Sleep (d <type 1>);
+const Stamp = "Jan _2 15:04:05";
+const StampMicro = "Jan _2 15:04:05.000000";
+const StampMilli = "Jan _2 15:04:05.000";
+const StampNano = "Jan _2 15:04:05.000000000";
+const Sunday <type 19> = 0 ;
+const Thursday <type 19> = 4 ;
+func Tick (d <type 1>) <type 54 chan <- <type 3>>;
+type <type 46>;
+type <type 3>;
+type <type 33>;
+const Tuesday <type 19> = 2 ;
+var UTC <type 15>;
+func Unix (sec <type -4>, nsec <type -4>) <type 3>;
+const UnixDate = "Mon Jan _2 15:04:05 MST 2006";
+const Wednesday <type 19> = 3 ;
+type <type 19>;
+checksum C04E7F45B20C621A25DC74E9B13EA4FF6732E226;
diff --git a/src/go/internal/gccgoimporter/testdata/unicode.gox b/src/go/internal/gccgoimporter/testdata/unicode.gox
new file mode 100644
index 0000000..ae1a6f7
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/unicode.gox
@@ -0,0 +1,273 @@
+v2;
+package unicode;
+pkgpath unicode;
+init unicode unicode..import;
+var ASCII_Hex_Digit <type 1 *<type 2 "RangeTable" <type 3 struct { R16 <type 4 [] <type 5 "Range16" <type 6 struct { Lo <type -6>; Hi <type -6>; Stride <type -6>; }>>>; R32 <type 7 [] <type 8 "Range32" <type 9 struct { Lo <type -7>; Hi <type -7>; Stride <type -7>; }>>>; LatinOffset <type -11>; }>>>;
+var Adlam <type 1>;
+var Ahom <type 1>;
+var Anatolian_Hieroglyphs <type 1>;
+var Arabic <type 1>;
+var Armenian <type 1>;
+var Avestan <type 1>;
+var AzeriCase <type 10 "SpecialCase" <type 11 [] <type 12 "CaseRange" <type 13 struct { Lo <type -7>; Hi <type -7>; Delta <type 14 ".unicode.d" <type 15 [3 ] <type -21>>>; }>>>
+ func (special <type 10>) ToUpper (r <type -21>) <type -21>;
+ func (special <type 10>) ToTitle (r <type -21>) <type -21>;
+ func (special <type 10>) ToLower (r <type -21>) <type -21>;
+>;
+var Balinese <type 1>;
+var Bamum <type 1>;
+var Bassa_Vah <type 1>;
+var Batak <type 1>;
+var Bengali <type 1>;
+var Bhaiksuki <type 1>;
+var Bidi_Control <type 1>;
+var Bopomofo <type 1>;
+var Brahmi <type 1>;
+var Braille <type 1>;
+var Buginese <type 1>;
+var Buhid <type 1>;
+var C <type 1>;
+var Canadian_Aboriginal <type 1>;
+var Carian <type 1>;
+type <type 12>;
+var CaseRanges <type 16 [] <type 12>>;
+var Categories <type 17 map [<type -16>] <type 1>>;
+var Caucasian_Albanian <type 1>;
+var Cc <type 1>;
+var Cf <type 1>;
+var Chakma <type 1>;
+var Cham <type 1>;
+var Cherokee <type 1>;
+var Co <type 1>;
+var Common <type 1>;
+var Coptic <type 1>;
+var Cs <type 1>;
+var Cuneiform <type 1>;
+var Cypriot <type 1>;
+var Cyrillic <type 1>;
+var Dash <type 1>;
+var Deprecated <type 1>;
+var Deseret <type 1>;
+var Devanagari <type 1>;
+var Diacritic <type 1>;
+var Digit <type 1>;
+var Duployan <type 1>;
+var Egyptian_Hieroglyphs <type 1>;
+var Elbasan <type 1>;
+var Ethiopic <type 1>;
+var Extender <type 1>;
+var FoldCategory <type 18 map [<type -16>] <type 1>>;
+var FoldScript <type 19 map [<type -16>] <type 1>>;
+var Georgian <type 1>;
+var Glagolitic <type 1>;
+var Gothic <type 1>;
+var Grantha <type 1>;
+var GraphicRanges <type 20 [] <type 21 *<type 2>>>;
+var Greek <type 1>;
+var Gujarati <type 1>;
+var Gurmukhi <type 1>;
+var Han <type 1>;
+var Hangul <type 1>;
+var Hanunoo <type 1>;
+var Hatran <type 1>;
+var Hebrew <type 1>;
+var Hex_Digit <type 1>;
+var Hiragana <type 1>;
+var Hyphen <type 1>;
+var IDS_Binary_Operator <type 1>;
+var IDS_Trinary_Operator <type 1>;
+var Ideographic <type 1>;
+var Imperial_Aramaic <type 1>;
+func In (r <type -21>, ranges ...<type 22 *<type 2>>) <type -15>;
+var Inherited <type 1>;
+var Inscriptional_Pahlavi <type 1>;
+var Inscriptional_Parthian <type 1>;
+func Is (rangeTab <type 1>, r <type -21>) <type -15>;
+func IsControl (r <type -21>) <type -15>;
+func IsDigit (r <type -21>) <type -15>;
+func IsGraphic (r <type -21>) <type -15>;
+func IsLetter (r <type -21>) <type -15>;
+func IsLower (r <type -21>) <type -15>;
+func IsMark (r <type -21>) <type -15>;
+func IsNumber (r <type -21>) <type -15>;
+func IsOneOf (ranges <type 23 [] <type 24 *<type 2>>>, r <type -21>) <type -15>;
+func IsPrint (r <type -21>) <type -15>;
+func IsPunct (r <type -21>) <type -15>;
+func IsSpace (r <type -21>) <type -15>;
+func IsSymbol (r <type -21>) <type -15>;
+func IsTitle (r <type -21>) <type -15>;
+func IsUpper (r <type -21>) <type -15>;
+var Javanese <type 1>;
+var Join_Control <type 1>;
+var Kaithi <type 1>;
+var Kannada <type 1>;
+var Katakana <type 1>;
+var Kayah_Li <type 1>;
+var Kharoshthi <type 1>;
+var Khmer <type 1>;
+var Khojki <type 1>;
+var Khudawadi <type 1>;
+var L <type 1>;
+var Lao <type 1>;
+var Latin <type 1>;
+var Lepcha <type 1>;
+var Letter <type 1>;
+var Limbu <type 1>;
+var Linear_A <type 1>;
+var Linear_B <type 1>;
+var Lisu <type 1>;
+var Ll <type 1>;
+var Lm <type 1>;
+var Lo <type 1>;
+var Logical_Order_Exception <type 1>;
+var Lower <type 1>;
+const LowerCase = 1 ;
+var Lt <type 1>;
+var Lu <type 1>;
+var Lycian <type 1>;
+var Lydian <type 1>;
+var M <type 1>;
+var Mahajani <type 1>;
+var Malayalam <type 1>;
+var Mandaic <type 1>;
+var Manichaean <type 1>;
+var Marchen <type 1>;
+var Mark <type 1>;
+const MaxASCII = 127' ;
+const MaxCase = 3 ;
+const MaxLatin1 = 255' ;
+const MaxRune = 1114111' ;
+var Mc <type 1>;
+var Me <type 1>;
+var Meetei_Mayek <type 1>;
+var Mende_Kikakui <type 1>;
+var Meroitic_Cursive <type 1>;
+var Meroitic_Hieroglyphs <type 1>;
+var Miao <type 1>;
+var Mn <type 1>;
+var Modi <type 1>;
+var Mongolian <type 1>;
+var Mro <type 1>;
+var Multani <type 1>;
+var Myanmar <type 1>;
+var N <type 1>;
+var Nabataean <type 1>;
+var Nd <type 1>;
+var New_Tai_Lue <type 1>;
+var Newa <type 1>;
+var Nko <type 1>;
+var Nl <type 1>;
+var No <type 1>;
+var Noncharacter_Code_Point <type 1>;
+var Number <type 1>;
+var Ogham <type 1>;
+var Ol_Chiki <type 1>;
+var Old_Hungarian <type 1>;
+var Old_Italic <type 1>;
+var Old_North_Arabian <type 1>;
+var Old_Permic <type 1>;
+var Old_Persian <type 1>;
+var Old_South_Arabian <type 1>;
+var Old_Turkic <type 1>;
+var Oriya <type 1>;
+var Osage <type 1>;
+var Osmanya <type 1>;
+var Other <type 1>;
+var Other_Alphabetic <type 1>;
+var Other_Default_Ignorable_Code_Point <type 1>;
+var Other_Grapheme_Extend <type 1>;
+var Other_ID_Continue <type 1>;
+var Other_ID_Start <type 1>;
+var Other_Lowercase <type 1>;
+var Other_Math <type 1>;
+var Other_Uppercase <type 1>;
+var P <type 1>;
+var Pahawh_Hmong <type 1>;
+var Palmyrene <type 1>;
+var Pattern_Syntax <type 1>;
+var Pattern_White_Space <type 1>;
+var Pau_Cin_Hau <type 1>;
+var Pc <type 1>;
+var Pd <type 1>;
+var Pe <type 1>;
+var Pf <type 1>;
+var Phags_Pa <type 1>;
+var Phoenician <type 1>;
+var Pi <type 1>;
+var Po <type 1>;
+var Prepended_Concatenation_Mark <type 1>;
+var PrintRanges <type 25 [] <type 26 *<type 2>>>;
+var Properties <type 27 map [<type -16>] <type 1>>;
+var Ps <type 1>;
+var Psalter_Pahlavi <type 1>;
+var Punct <type 1>;
+var Quotation_Mark <type 1>;
+var Radical <type 1>;
+type <type 5>;
+type <type 8>;
+type <type 2>;
+var Rejang <type 1>;
+const ReplacementChar = 65533' ;
+var Runic <type 1>;
+var S <type 1>;
+var STerm <type 1>;
+var Samaritan <type 1>;
+var Saurashtra <type 1>;
+var Sc <type 1>;
+var Scripts <type 28 map [<type -16>] <type 1>>;
+var Sentence_Terminal <type 1>;
+var Sharada <type 1>;
+var Shavian <type 1>;
+var Siddham <type 1>;
+var SignWriting <type 1>;
+func SimpleFold (r <type -21>) <type -21>;
+var Sinhala <type 1>;
+var Sk <type 1>;
+var Sm <type 1>;
+var So <type 1>;
+var Soft_Dotted <type 1>;
+var Sora_Sompeng <type 1>;
+var Space <type 1>;
+type <type 10>;
+var Sundanese <type 1>;
+var Syloti_Nagri <type 1>;
+var Symbol <type 1>;
+var Syriac <type 1>;
+var Tagalog <type 1>;
+var Tagbanwa <type 1>;
+var Tai_Le <type 1>;
+var Tai_Tham <type 1>;
+var Tai_Viet <type 1>;
+var Takri <type 1>;
+var Tamil <type 1>;
+var Tangut <type 1>;
+var Telugu <type 1>;
+var Terminal_Punctuation <type 1>;
+var Thaana <type 1>;
+var Thai <type 1>;
+var Tibetan <type 1>;
+var Tifinagh <type 1>;
+var Tirhuta <type 1>;
+var Title <type 1>;
+const TitleCase = 2 ;
+func To (_case <type -11>, r <type -21>) <type -21>;
+func ToLower (r <type -21>) <type -21>;
+func ToTitle (r <type -21>) <type -21>;
+func ToUpper (r <type -21>) <type -21>;
+var TurkishCase <type 10>;
+var Ugaritic <type 1>;
+var Unified_Ideograph <type 1>;
+var Upper <type 1>;
+const UpperCase = 0 ;
+const UpperLower = 1114112' ;
+var Vai <type 1>;
+var Variation_Selector <type 1>;
+const Version = "9.0.0";
+var Warang_Citi <type 1>;
+var White_Space <type 1>;
+var Yi <type 1>;
+var Z <type 1>;
+var Zl <type 1>;
+var Zp <type 1>;
+var Zs <type 1>;
+checksum 7643975C0BE2732C7557F1B2A70796673C11DF4A;
diff --git a/src/go/internal/gccgoimporter/testdata/v1reflect.gox b/src/go/internal/gccgoimporter/testdata/v1reflect.gox
new file mode 100644
index 0000000..d693fe6
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/v1reflect.gox
@@ -0,0 +1,184 @@
+v1;
+package reflect;
+pkgpath reflect;
+priority 3;
+import math math "math";
+import runtime runtime "runtime";
+import strconv strconv "strconv";
+import sync sync "sync";
+import unsafe unsafe "unsafe";
+init reflect reflect..import 3 math math..import 1 runtime runtime..import 1 strconv strconv..import 2;
+func Append (s <type 1 "Value" <type 2 struct { .reflect.typ <type 3 *<type 4 ".reflect.commonType" <type 5 struct { .reflect.kind <type -5>; .reflect.align <type -1>; .reflect.fieldAlign <type -5>; .reflect._ <type -5>; .reflect.size <type -13>; .reflect.hash <type -7>; .reflect.hashfn <type 6 (? <type 7 "unsafe.Pointer" <type 8 *any>>, ? <type -13>)>; .reflect.equalfn <type 9 (? <type 7>, ? <type 7>, ? <type -13>)>; .reflect.string <type 10 *<type -16>>; ? <type 11 *<type 12 ".reflect.uncommonType" <type 13 struct { .reflect.name <type 14 *<type -16>>; .reflect.pkgPath <type 15 *<type -16>>; .reflect.methods <type 16 [] <type 17 ".reflect.method" <type 18 struct { .reflect.name <type 19 *<type -16>>; .reflect.pkgPath <type 20 *<type -16>>; .reflect.mtyp <type 21 *<type 22 ".reflect.runtimeType" <type 4>>>; .reflect.typ <type 21>; .reflect.tfn <type 7>; }>>>; }>
+ func (t <type 23 *<type 12>>) .reflect.uncommon () <type 23>;
+ func (t <type 23>) PkgPath () <type -16>;
+ func (t <type 23>) Name () <type -16>;
+ func (t <type 23>) Method (i <type -11>) (m <type 24 "Method" <type 25 struct { Name <type -16>; PkgPath <type -16>; Type <type 26 "Type" <type 27 interface { Align () <type -11>; FieldAlign () <type -11>; Method (? <type -11>) <type 24>; MethodByName (? <type -16>) (? <type 24>, ? <type -15>); NumMethod () <type -11>; Name () <type -16>; PkgPath () <type -16>; Size () <type -13>; String () <type -16>; .reflect.rawString () <type -16>; Kind () <type 28 "Kind" <type -12>
+ func (k <type 28>) String () <type -16>;
+>; Implements (u <type 26>) <type -15>; AssignableTo (u <type 26>) <type -15>; Bits () <type -11>; ChanDir () <type 29 "ChanDir" <type -11>
+ func (d <type 29>) String () <type -16>;
+>; IsVariadic () <type -15>; Elem () <type 26>; Field (i <type -11>) <type 30 "StructField" <type 31 struct { Name <type -16>; PkgPath <type -16>; Type <type 26>; Tag <type 32 "StructTag" <type -16>
+ func (tag <type 32>) Get (key <type -16>) <type -16>;
+>; Offset <type -13>; Index <type 33 [] <type -11>>; Anonymous <type -15>; }>>; FieldByIndex (index <type 34 [] <type -11>>) <type 30>; FieldByName (name <type -16>) (? <type 30>, ? <type -15>); FieldByNameFunc (match <type 35 (? <type -16>) <type -15>>) (? <type 30>, ? <type -15>); In (i <type -11>) <type 26>; Key () <type 26>; Len () <type -11>; NumField () <type -11>; NumIn () <type -11>; NumOut () <type -11>; Out (i <type -11>) <type 26>; .reflect.runtimeType () <type 36 *<type 22>>; .reflect.common () <type 37 *<type 4>>; .reflect.uncommon () <type 38 *<type 12>>; }>>; Func <type 1>; Index <type -11>; }>>);
+ func (t <type 23>) NumMethod () <type -11>;
+ func (t <type 23>) MethodByName (name <type -16>) (m <type 24>, ok <type -15>);
+>>; .reflect.ptrToThis <type 21>; }>
+ func (t <type 39 *<type 4>>) .reflect.toType () <type 26>;
+ func (t <type 39>) .reflect.rawString () <type -16>;
+ func (t <type 39>) String () <type -16>;
+ func (t <type 39>) Size () <type -13>;
+ func (t <type 39>) Bits () <type -11>;
+ func (t <type 39>) Align () <type -11>;
+ func (t <type 39>) FieldAlign () <type -11>;
+ func (t <type 39>) Kind () <type 28>;
+ func (t <type 39>) .reflect.common () <type 39>;
+ func (t <type 39>) NumMethod () <type -11>;
+ func (t <type 39>) Method (i <type -11>) (m <type 24>);
+ func (t <type 39>) MethodByName (name <type -16>) (m <type 24>, ok <type -15>);
+ func (t <type 39>) PkgPath () <type -16>;
+ func (t <type 39>) Name () <type -16>;
+ func (t <type 39>) ChanDir () <type 29>;
+ func (t <type 39>) IsVariadic () <type -15>;
+ func (t <type 39>) Elem () <type 26>;
+ func (t <type 39>) Field (i <type -11>) <type 30>;
+ func (t <type 39>) FieldByIndex (index <type 40 [] <type -11>>) <type 30>;
+ func (t <type 39>) FieldByName (name <type -16>) (? <type 30>, ? <type -15>);
+ func (t <type 39>) FieldByNameFunc (match <type 41 (? <type -16>) <type -15>>) (? <type 30>, ? <type -15>);
+ func (t <type 39>) In (i <type -11>) <type 26>;
+ func (t <type 39>) Key () <type 26>;
+ func (t <type 39>) Len () <type -11>;
+ func (t <type 39>) NumField () <type -11>;
+ func (t <type 39>) NumIn () <type -11>;
+ func (t <type 39>) NumOut () <type -11>;
+ func (t <type 39>) Out (i <type -11>) <type 26>;
+ func (t <type 39>) .reflect.runtimeType () <type 21>;
+ func (ct <type 39>) .reflect.ptrTo () <type 39>;
+ func (t <type 39>) Implements (u <type 26>) <type -15>;
+ func (t <type 39>) AssignableTo (u <type 26>) <type -15>;
+>>; .reflect.val <type 7>; ? <type 42 ".reflect.flag" <type -13>
+ func (f <type 42>) .reflect.kind () <type 28>;
+ func (f <type 42>) .reflect.mustBe (expected <type 28>);
+ func (f <type 42>) .reflect.mustBeExported ();
+ func (f <type 42>) .reflect.mustBeAssignable ();
+>; }>
+ func (v <type 1>) .reflect.iword () <type 43 ".reflect.iword" <type 7>>;
+ func (v <type 1>) Addr () <type 1>;
+ func (v <type 1>) Bool () <type -15>;
+ func (v <type 1>) Bytes () <type 44 [] <type -20>>;
+ func (v <type 1>) CanAddr () <type -15>;
+ func (v <type 1>) CanSet () <type -15>;
+ func (v <type 1>) Call (in <type 45 [] <type 1>>) <type 46 [] <type 1>>;
+ func (v <type 1>) CallSlice (in <type 47 [] <type 1>>) <type 48 [] <type 1>>;
+ func (v <type 1>) .reflect.call (method <type -16>, in <type 49 [] <type 1>>) <type 50 [] <type 1>>;
+ func (v <type 1>) Cap () <type -11>;
+ func (v <type 1>) Close ();
+ func (v <type 1>) Complex () <type -18>;
+ func (v <type 1>) Elem () <type 1>;
+ func (v <type 1>) Field (i <type -11>) <type 1>;
+ func (v <type 1>) FieldByIndex (index <type 51 [] <type -11>>) <type 1>;
+ func (v <type 1>) FieldByName (name <type -16>) <type 1>;
+ func (v <type 1>) FieldByNameFunc (match <type 52 (? <type -16>) <type -15>>) <type 1>;
+ func (v <type 1>) Float () <type -10>;
+ func (v <type 1>) Index (i <type -11>) <type 1>;
+ func (v <type 1>) Int () <type -4>;
+ func (v <type 1>) CanInterface () <type -15>;
+ func (v <type 1>) Interface () (i <type 53 interface { }>);
+ func (v <type 1>) InterfaceData () <type 54 [2 ] <type -13>>;
+ func (v <type 1>) IsNil () <type -15>;
+ func (v <type 1>) IsValid () <type -15>;
+ func (v <type 1>) Kind () <type 28>;
+ func (v <type 1>) Len () <type -11>;
+ func (v <type 1>) MapIndex (key <type 1>) <type 1>;
+ func (v <type 1>) MapKeys () <type 55 [] <type 1>>;
+ func (v <type 1>) Method (i <type -11>) <type 1>;
+ func (v <type 1>) NumMethod () <type -11>;
+ func (v <type 1>) MethodByName (name <type -16>) <type 1>;
+ func (v <type 1>) NumField () <type -11>;
+ func (v <type 1>) OverflowComplex (x <type -18>) <type -15>;
+ func (v <type 1>) OverflowFloat (x <type -10>) <type -15>;
+ func (v <type 1>) OverflowInt (x <type -4>) <type -15>;
+ func (v <type 1>) OverflowUint (x <type -8>) <type -15>;
+ func (v <type 1>) Pointer () <type -13>;
+ func (v <type 1>) Recv () (x <type 1>, ok <type -15>);
+ func (v <type 1>) .reflect.recv (nb <type -15>) (val <type 1>, ok <type -15>);
+ func (v <type 1>) Send (x <type 1>);
+ func (v <type 1>) .reflect.send (x <type 1>, nb <type -15>) (selected <type -15>);
+ func (v <type 1>) Set (x <type 1>);
+ func (v <type 1>) SetBool (x <type -15>);
+ func (v <type 1>) SetBytes (x <type 56 [] <type -20>>);
+ func (v <type 1>) SetComplex (x <type -18>);
+ func (v <type 1>) SetFloat (x <type -10>);
+ func (v <type 1>) SetInt (x <type -4>);
+ func (v <type 1>) SetLen (n <type -11>);
+ func (v <type 1>) SetMapIndex (key <type 1>, val <type 1>);
+ func (v <type 1>) SetUint (x <type -8>);
+ func (v <type 1>) SetPointer (x <type 7>);
+ func (v <type 1>) SetString (x <type -16>);
+ func (v <type 1>) Slice (beg <type -11>, end <type -11>) <type 1>;
+ func (v <type 1>) String () <type -16>;
+ func (v <type 1>) TryRecv () (x <type 1>, ok <type -15>);
+ func (v <type 1>) TrySend (x <type 1>) <type -15>;
+ func (v <type 1>) Type () <type 26>;
+ func (v <type 1>) Uint () <type -8>;
+ func (v <type 1>) UnsafeAddr () <type -13>;
+ func (v <type 1>) .reflect.assignTo (context <type -16>, dst <type 3>, target <type 57 *<type 58 interface { }>>) <type 1>;
+>, x ...<type 1>) <type 1>;
+func AppendSlice (s <type 1>, t <type 1>) <type 1>;
+const Array <type 28> = 17 ;
+const Bool <type 28> = 1 ;
+const BothDir <type 29> = 3 ;
+const Chan <type 28> = 18 ;
+type <type 29>;
+const Complex128 <type 28> = 16 ;
+const Complex64 <type 28> = 15 ;
+func Copy (dst <type 1>, src <type 1>) <type -11>;
+func DeepEqual (a1 <type 59 interface { }>, a2 <type 59>) <type -15>;
+const Float32 <type 28> = 13 ;
+const Float64 <type 28> = 14 ;
+const Func <type 28> = 19 ;
+func Indirect (v <type 1>) <type 1>;
+const Int <type 28> = 2 ;
+const Int16 <type 28> = 4 ;
+const Int32 <type 28> = 5 ;
+const Int64 <type 28> = 6 ;
+const Int8 <type 28> = 3 ;
+const Interface <type 28> = 20 ;
+const Invalid <type 28> = 0 ;
+type <type 28>;
+func MakeChan (typ <type 26>, buffer <type -11>) <type 1>;
+func MakeMap (typ <type 26>) <type 1>;
+func MakeSlice (typ <type 26>, len <type -11>, cap <type -11>) <type 1>;
+const Map <type 28> = 21 ;
+type <type 24>;
+func Method$equal (key1 <type 8>, key2 <type 8>, key_size <type -13>) <type -15>;
+func Method$hash (key <type 8>, key_size <type -13>) <type -13>;
+func New (typ <type 26>) <type 1>;
+func NewAt (typ <type 26>, p <type 7>) <type 1>;
+const Ptr <type 28> = 22 ;
+func PtrTo (t <type 26>) <type 26>;
+const RecvDir <type 29> = 1 ;
+const SendDir <type 29> = 2 ;
+const Slice <type 28> = 23 ;
+type <type 60 "SliceHeader" <type 61 struct { Data <type -13>; Len <type -11>; Cap <type -11>; }>>;
+const String <type 28> = 24 ;
+type <type 62 "StringHeader" <type 63 struct { Data <type -13>; Len <type -11>; }>>;
+const Struct <type 28> = 25 ;
+type <type 30>;
+type <type 32>;
+type <type 26>;
+func TypeOf (i <type 64 interface { }>) <type 26>;
+const Uint <type 28> = 7 ;
+const Uint16 <type 28> = 9 ;
+const Uint32 <type 28> = 10 ;
+const Uint64 <type 28> = 11 ;
+const Uint8 <type 28> = 8 ;
+const Uintptr <type 28> = 12 ;
+const UnsafePointer <type 28> = 26 ;
+type <type 1>;
+type <type 65 "ValueError" <type 66 struct { Method <type -16>; Kind <type 28>; }>
+ func (e <type 67 *<type 65>>) Error () <type -16>;
+>;
+func ValueError$equal (key1 <type 8>, key2 <type 8>, key_size <type -13>) <type -15>;
+func ValueError$hash (key <type 8>, key_size <type -13>) <type -13>;
+func ValueOf (i <type 68 interface { }>) <type 1>;
+func Zero (typ <type 26>) <type 1>;
+checksum DD7720796E91D9D24ED12B75BDEA2A714D47B095;
diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go
new file mode 100644
index 0000000..4aa22d7
--- /dev/null
+++ b/src/go/internal/gcimporter/exportdata.go
@@ -0,0 +1,92 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements FindExportData.
+
+package gcimporter
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = fmt.Errorf("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function. The hdr result
+// is the string before the export data, either "$$" or "$$B".
+func FindExportData(r *bufio.Reader) (hdr string, size int, err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF.
+ var name string
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+
+ // First entry should be __.PKGDEF.
+ if name != "__.PKGDEF" {
+ err = fmt.Errorf("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+ }
+
+ // Now at __.PKGDEF in archive or still at beginning of file.
+ // Either way, line should begin with "go object ".
+ if !strings.HasPrefix(string(line), "go object ") {
+ err = fmt.Errorf("not a Go object file")
+ return
+ }
+ size -= len(line)
+
+ // Skip over object header to export data.
+ // Begins after first line starting with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ err = fmt.Errorf("can't find export data (%v)", err)
+ return
+ }
+ size -= len(line)
+ }
+ hdr = string(line)
+
+ return
+}
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
new file mode 100644
index 0000000..93b33d1
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -0,0 +1,248 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gcimporter implements Import for gc-generated object files.
+package gcimporter // import "go/internal/gcimporter"
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "go/build"
+ "go/token"
+ "go/types"
+ "internal/pkgbits"
+ "internal/saferio"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync"
+)
+
+// debugging/development support
+const debug = false
+
+var exportMap sync.Map // package dir → func() (string, bool)
+
+// lookupGorootExport returns the location of the export data
+// (normally found in the build cache, but located in GOROOT/pkg
+// in prior Go releases) for the package located in pkgDir.
+//
+// (We use the package's directory instead of its import path
+// mainly to simplify handling of the packages in src/vendor
+// and cmd/vendor.)
+func lookupGorootExport(pkgDir string) (string, bool) {
+ f, ok := exportMap.Load(pkgDir)
+ if !ok {
+ var (
+ listOnce sync.Once
+ exportPath string
+ )
+ f, _ = exportMap.LoadOrStore(pkgDir, func() (string, bool) {
+ listOnce.Do(func() {
+ cmd := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", pkgDir)
+ cmd.Dir = build.Default.GOROOT
+ cmd.Env = append(cmd.Environ(), "GOROOT="+build.Default.GOROOT)
+ var output []byte
+ output, err := cmd.Output()
+ if err != nil {
+ return
+ }
+
+ exports := strings.Split(string(bytes.TrimSpace(output)), "\n")
+ if len(exports) != 1 {
+ return
+ }
+
+ exportPath = exports[0]
+ })
+
+ return exportPath, exportPath != ""
+ })
+ }
+
+ return f.(func() (string, bool))()
+}
+
+var pkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extension
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context). A relative srcDir is interpreted
+// relative to the current working directory.
+// If no file was found, an empty filename is returned.
+func FindPkg(path, srcDir string) (filename, id string) {
+ if path == "" {
+ return
+ }
+
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282
+ srcDir = abs
+ }
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ var ok bool
+ if bp.Goroot && bp.Dir != "" {
+ filename, ok = lookupGorootExport(bp.Dir)
+ }
+ if !ok {
+ id = path // make sure we have an id to print in error message
+ return
+ }
+ } else {
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+ }
+ id = bp.ImportPath
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ id = path
+ }
+
+ if false { // for debugging
+ if path != id {
+ fmt.Printf("%s -> %s\n", path, id)
+ }
+ }
+
+ if filename != "" {
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// Import imports a gc-generated package given its import path and srcDir, adds
+// the corresponding package object to the packages map, and returns the object.
+// The packages map must contain all packages already imported.
+func Import(fset *token.FileSet, packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) {
+ var rc io.ReadCloser
+ var id string
+ if lookup != nil {
+ // With custom lookup specified, assume that caller has
+ // converted path to a canonical import path for use in the map.
+ if path == "unsafe" {
+ return types.Unsafe, nil
+ }
+ id = path
+
+ // No need to re-import if the package was imported completely before.
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+ f, err := lookup(path)
+ if err != nil {
+ return nil, err
+ }
+ rc = f
+ } else {
+ var filename string
+ filename, id = FindPkg(path, srcDir)
+ if filename == "" {
+ if path == "unsafe" {
+ return types.Unsafe, nil
+ }
+ return nil, fmt.Errorf("can't find import: %q", id)
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("%s: %v", filename, err)
+ }
+ }()
+ rc = f
+ }
+ defer rc.Close()
+
+ buf := bufio.NewReader(rc)
+ hdr, size, err := FindExportData(buf)
+ if err != nil {
+ return
+ }
+
+ switch hdr {
+ case "$$\n":
+ err = fmt.Errorf("import %q: old textual export format no longer supported (recompile library)", path)
+
+ case "$$B\n":
+ var exportFormat byte
+ if exportFormat, err = buf.ReadByte(); err != nil {
+ return
+ }
+ size--
+
+ // The unified export format starts with a 'u'; the indexed export
+ // format starts with an 'i'; and the older binary export format
+ // starts with a 'c', 'd', or 'v' (from "version"). Select
+ // appropriate importer.
+ switch exportFormat {
+ case 'u':
+ var data []byte
+ var r io.Reader = buf
+ if size >= 0 {
+ if data, err = saferio.ReadData(r, uint64(size)); err != nil {
+ return
+ }
+ } else if data, err = io.ReadAll(r); err != nil {
+ return
+ }
+ s := string(data)
+ s = s[:strings.LastIndex(s, "\n$$\n")]
+
+ input := pkgbits.NewPkgDecoder(id, s)
+ pkg = readUnifiedPackage(fset, nil, packages, input)
+ case 'i':
+ pkg, err = iImportData(fset, packages, buf, id)
+ default:
+ err = fmt.Errorf("import %q: old binary export format no longer supported (recompile library)", path)
+ }
+
+ default:
+ err = fmt.Errorf("import %q: unknown export data header: %q", path, hdr)
+ }
+
+ return
+}
+
+type byPath []*types.Package
+
+func (a byPath) Len() int { return len(a) }
+func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
new file mode 100644
index 0000000..25ff402
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -0,0 +1,752 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gcimporter_test
+
+import (
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "go/ast"
+ "go/build"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "go/types"
+
+ . "go/internal/gcimporter"
+)
+
+func TestMain(m *testing.M) {
+ build.Default.GOROOT = testenv.GOROOT(nil)
+ os.Exit(m.Run())
+}
+
+// compile runs the compiler on filename, with dirname as the working directory,
+// and writes the output file to outdirname.
+// compile gives the resulting package a packagepath of testdata/<filebasename>.
+func compile(t *testing.T, dirname, filename, outdirname string, packageFiles map[string]string, pkgImports ...string) string {
+ // filename must end with ".go"
+ basename, ok := strings.CutSuffix(filepath.Base(filename), ".go")
+ if !ok {
+ t.Fatalf("filename doesn't end in .go: %s", filename)
+ }
+ objname := basename + ".o"
+ outname := filepath.Join(outdirname, objname)
+
+ importcfgfile := os.DevNull
+ if len(packageFiles) > 0 || len(pkgImports) > 0 {
+ importcfgfile = filepath.Join(outdirname, basename) + ".importcfg"
+ testenv.WriteImportcfg(t, importcfgfile, packageFiles, pkgImports...)
+ }
+
+ pkgpath := path.Join("testdata", basename)
+ cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p", pkgpath, "-D", "testdata", "-importcfg", importcfgfile, "-o", outname, filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("go tool compile %s failed: %s", filename, err)
+ }
+ return outname
+}
+
+func testPath(t *testing.T, path, srcDir string) *types.Package {
+ t0 := time.Now()
+ fset := token.NewFileSet()
+ pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return nil
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return pkg
+}
+
+var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go
+
+func mktmpdir(t *testing.T) string {
+ tmpdir, err := os.MkdirTemp("", "gcimporter_test")
+ if err != nil {
+ t.Fatal("mktmpdir:", err)
+ }
+ if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
+ os.RemoveAll(tmpdir)
+ t.Fatal("mktmpdir:", err)
+ }
+ return tmpdir
+}
+
+func TestImportTestdata(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ testenv.MustHaveGoBuild(t)
+
+ testfiles := map[string][]string{
+ "exports.go": {"go/ast", "go/token"},
+ "generics.go": nil,
+ }
+ if true /* was goexperiment.Unified */ {
+ // TODO(mdempsky): Fix test below to flatten the transitive
+ // Package.Imports graph. Unified IR is more precise about
+ // recreating the package import graph.
+ testfiles["exports.go"] = []string{"go/ast"}
+ }
+
+ for testfile, wantImports := range testfiles {
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil, wantImports...)
+ path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
+
+ if pkg := testPath(t, path, tmpdir); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by testfile, plus all packages
+ // referenced indirectly via exported objects in testfile.
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range wantImports {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
+ }
+ }
+ }
+}
+
+func TestImportTypeparamTests(t *testing.T) {
+ if testing.Short() {
+ t.Skipf("in short mode, skipping test that requires export data for all of std")
+ }
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // cmd/distpack removes the GOROOT/test directory, so skip if it isn't there.
+ // cmd/distpack also requires the presence of GOROOT/VERSION, so use that to
+ // avoid false-positive skips.
+ gorootTest := filepath.Join(testenv.GOROOT(t), "test")
+ if _, err := os.Stat(gorootTest); os.IsNotExist(err) {
+ if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil {
+ t.Skipf("skipping: GOROOT/test not present")
+ }
+ }
+
+ testenv.MustHaveGoBuild(t)
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ // Check go files in test/typeparam, except those that fail for a known
+ // reason.
+ rootDir := filepath.Join(gorootTest, "typeparam")
+ list, err := os.ReadDir(rootDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, entry := range list {
+ if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
+ // For now, only consider standalone go files.
+ continue
+ }
+
+ t.Run(entry.Name(), func(t *testing.T) {
+ filename := filepath.Join(rootDir, entry.Name())
+ src, err := os.ReadFile(filename)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
+ // We're bypassing the logic of run.go here, so be conservative about
+ // the files we consider in an attempt to make this test more robust to
+ // changes in test/typeparams.
+ t.Skipf("not detected as a run test")
+ }
+
+ // Compile and import, and compare the resulting package with the package
+ // that was type-checked directly.
+ compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), nil, filename)
+ pkgName := strings.TrimSuffix(entry.Name(), ".go")
+ imported := importPkg(t, "./testdata/"+pkgName, tmpdir)
+ checked := checkFile(t, filename, src)
+
+ seen := make(map[string]bool)
+ for _, name := range imported.Scope().Names() {
+ if !token.IsExported(name) {
+ continue // ignore synthetic names like .inittask and .dict.*
+ }
+ seen[name] = true
+
+ importedObj := imported.Scope().Lookup(name)
+ got := types.ObjectString(importedObj, types.RelativeTo(imported))
+ got = sanitizeObjectString(got)
+
+ checkedObj := checked.Scope().Lookup(name)
+ if checkedObj == nil {
+ t.Fatalf("imported object %q was not type-checked", name)
+ }
+ want := types.ObjectString(checkedObj, types.RelativeTo(checked))
+ want = sanitizeObjectString(want)
+
+ if got != want {
+ t.Errorf("imported %q as %q, want %q", name, got, want)
+ }
+ }
+
+ for _, name := range checked.Scope().Names() {
+ if !token.IsExported(name) || seen[name] {
+ continue
+ }
+ t.Errorf("did not import object %q", name)
+ }
+ })
+ }
+}
+
+// sanitizeObjectString removes type parameter debugging markers from an object
+// string, to normalize it for comparison.
+// TODO(rfindley): this should not be necessary.
+func sanitizeObjectString(s string) string {
+ var runes []rune
+ for _, r := range s {
+ if 'â‚€' <= r && r < 'â‚€'+10 {
+ continue // trim type parameter subscripts
+ }
+ runes = append(runes, r)
+ }
+ return string(runes)
+}
+
+func checkFile(t *testing.T, filename string, src []byte) *types.Package {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ config := types.Config{
+ Importer: importer.Default(),
+ }
+ pkg, err := config.Check("", fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return pkg
+}
+
+func TestVersionHandling(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ const dir = "./testdata/versions"
+ list, err := os.ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ corruptdir := filepath.Join(tmpdir, "testdata", "versions")
+ if err := os.Mkdir(corruptdir, 0700); err != nil {
+ t.Fatal(err)
+ }
+
+ fset := token.NewFileSet()
+
+ for _, f := range list {
+ name := f.Name()
+ if !strings.HasSuffix(name, ".a") {
+ continue // not a package file
+ }
+ if strings.Contains(name, "corrupted") {
+ continue // don't process a leftover corrupted file
+ }
+ pkgpath := "./" + name[:len(name)-2]
+
+ if testing.Verbose() {
+ t.Logf("importing %s", name)
+ }
+
+ // test that export data can be imported
+ _, err := Import(fset, make(map[string]*types.Package), pkgpath, dir, nil)
+ if err != nil {
+ // ok to fail if it fails with a no longer supported error for select files
+ if strings.Contains(err.Error(), "no longer supported") {
+ switch name {
+ case "test_go1.7_0.a", "test_go1.7_1.a",
+ "test_go1.8_4.a", "test_go1.8_5.a",
+ "test_go1.11_6b.a", "test_go1.11_999b.a":
+ continue
+ }
+ // fall through
+ }
+ // ok to fail if it fails with a newer version error for select files
+ if strings.Contains(err.Error(), "newer version") {
+ switch name {
+ case "test_go1.11_999i.a":
+ continue
+ }
+ // fall through
+ }
+ t.Errorf("import %q failed: %v", pkgpath, err)
+ continue
+ }
+
+ // create file with corrupted export data
+ // 1) read file
+ data, err := os.ReadFile(filepath.Join(dir, name))
+ if err != nil {
+ t.Fatal(err)
+ }
+ // 2) find export data
+ i := bytes.Index(data, []byte("\n$$B\n")) + 5
+ j := bytes.Index(data[i:], []byte("\n$$\n")) + i
+ if i < 0 || j < 0 || i > j {
+ t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
+ }
+ // 3) corrupt the data (increment every 7th byte)
+ for k := j - 13; k >= i; k -= 7 {
+ data[k]++
+ }
+ // 4) write the file
+ pkgpath += "_corrupted"
+ filename := filepath.Join(corruptdir, pkgpath) + ".a"
+ os.WriteFile(filename, data, 0666)
+
+ // test that importing the corrupted file results in an error
+ _, err = Import(fset, make(map[string]*types.Package), pkgpath, corruptdir, nil)
+ if err == nil {
+ t.Errorf("import corrupted %q succeeded", pkgpath)
+ } else if msg := err.Error(); !strings.Contains(msg, "version skew") {
+ t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
+ }
+ }
+}
+
+func TestImportStdLib(t *testing.T) {
+ if testing.Short() {
+ t.Skip("the imports can be expensive, and this test is especially slow when the build cache is empty")
+ }
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // Get list of packages in stdlib. Filter out test-only packages with {{if .GoFiles}} check.
+ var stderr bytes.Buffer
+ cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std")
+ cmd.Stderr = &stderr
+ out, err := cmd.Output()
+ if err != nil {
+ t.Fatalf("failed to run go list to determine stdlib packages: %v\nstderr:\n%v", err, stderr.String())
+ }
+ pkgs := strings.Fields(string(out))
+
+ var nimports int
+ for _, pkg := range pkgs {
+ t.Run(pkg, func(t *testing.T) {
+ if testPath(t, pkg, filepath.Join(testenv.GOROOT(t), "src", path.Dir(pkg))) != nil {
+ nimports++
+ }
+ })
+ }
+ const minPkgs = 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more.
+ if len(pkgs) < minPkgs {
+ t.Fatalf("too few packages (%d) were imported", nimports)
+ }
+
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ // non-interfaces
+ {"crypto.Hash", "type Hash uint"},
+ {"go/ast.ObjKind", "type ObjKind int"},
+ {"go/types.Qualifier", "type Qualifier func(*Package) string"},
+ {"go/types.Comparable", "func Comparable(T Type) bool"},
+ {"math.Pi", "const Pi untyped float"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ {"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
+ {"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
+
+ // interfaces
+ {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
+ {"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
+ {"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
+ {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
+ {"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
+ {"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
+}
+
+func TestImportedTypes(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ fset := token.NewFileSet()
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := Import(fset, make(map[string]*types.Package), importPath, ".", nil)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+
+ if named, _ := obj.Type().(*types.Named); named != nil {
+ verifyInterfaceMethodRecvs(t, named, 0)
+ }
+ }
+}
+
+// verifyInterfaceMethodRecvs verifies that method receiver types
+// are named if the methods belong to a named interface type.
+func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
+ // avoid endless recursion in case of an embedding bug that lead to a cycle
+ if level > 10 {
+ t.Errorf("%s: embeds itself", named)
+ return
+ }
+
+ iface, _ := named.Underlying().(*types.Interface)
+ if iface == nil {
+ return // not an interface
+ }
+
+ // check explicitly declared methods
+ for i := 0; i < iface.NumExplicitMethods(); i++ {
+ m := iface.ExplicitMethod(i)
+ recv := m.Type().(*types.Signature).Recv()
+ if recv == nil {
+ t.Errorf("%s: missing receiver type", m)
+ continue
+ }
+ if recv.Type() != named {
+ t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
+ }
+ }
+
+ // check embedded interfaces (if they are named, too)
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
+ // embedding of interfaces cannot have cycles; recursion will terminate
+ if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
+ verifyInterfaceMethodRecvs(t, etype, level+1)
+ }
+ }
+}
+
+func TestIssue5815(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ pkg := importPkg(t, "strings", ".")
+
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Pkg() == nil {
+ t.Errorf("no pkg for %s", obj)
+ }
+ if tname, _ := obj.(*types.TypeName); tname != nil {
+ named := tname.Type().(*types.Named)
+ for i := 0; i < named.NumMethods(); i++ {
+ m := named.Method(i)
+ if m.Pkg() == nil {
+ t.Errorf("no pkg for %s", m)
+ }
+ }
+ }
+ }
+}
+
+// Smoke test to ensure that imported methods get the correct package.
+func TestCorrectMethodPackage(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ imports := make(map[string]*types.Package)
+ fset := token.NewFileSet()
+ _, err := Import(fset, imports, "net/http", ".", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
+ mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
+ sel := mset.Lookup(nil, "Lock")
+ lock := sel.Obj().(*types.Func)
+ if got, want := lock.Pkg().Path(), "sync"; got != want {
+ t.Errorf("got package path %q; want %q", got, want)
+ }
+}
+
+func TestIssue13566(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ testoutdir := filepath.Join(tmpdir, "testdata")
+
+ // b.go needs to be compiled from the output directory so that the compiler can
+ // find the compiled package a. We pass the full path to compile() so that we
+ // don't have to copy the file to that directory.
+ bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ compile(t, "testdata", "a.go", testoutdir, nil, "encoding/json")
+ compile(t, testoutdir, bpath, testoutdir, map[string]string{"testdata/a": filepath.Join(testoutdir, "a.o")}, "encoding/json")
+
+ // import must succeed (test for issue at hand)
+ pkg := importPkg(t, "./testdata/b", tmpdir)
+
+ // make sure all indirectly imported packages have names
+ for _, imp := range pkg.Imports() {
+ if imp.Name() == "" {
+ t.Errorf("no name for %s package", imp.Path())
+ }
+ }
+}
+
+func TestTypeNamingOrder(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ testoutdir := filepath.Join(tmpdir, "testdata")
+
+ compile(t, "testdata", "g.go", testoutdir, nil)
+
+ // import must succeed (test for issue at hand)
+ _ = importPkg(t, "./testdata/g", tmpdir)
+}
+
+func TestIssue13898(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // import go/internal/gcimporter which imports go/types partially
+ fset := token.NewFileSet()
+ imports := make(map[string]*types.Package)
+ _, err := Import(fset, imports, "go/internal/gcimporter", ".", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // look for go/types package
+ var goTypesPkg *types.Package
+ for path, pkg := range imports {
+ if path == "go/types" {
+ goTypesPkg = pkg
+ break
+ }
+ }
+ if goTypesPkg == nil {
+ t.Fatal("go/types not found")
+ }
+
+ // look for go/types.Object type
+ obj := lookupObj(t, goTypesPkg.Scope(), "Object")
+ typ, ok := obj.Type().(*types.Named)
+ if !ok {
+ t.Fatalf("go/types.Object type is %v; wanted named type", typ)
+ }
+
+ // lookup go/types.Object.Pkg method
+ m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
+ if m == nil {
+ t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
+ }
+
+ // the method must belong to go/types
+ if m.Pkg().Path() != "go/types" {
+ t.Fatalf("found %v; want go/types", m.Pkg())
+ }
+}
+
+func TestIssue15517(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil)
+
+ // Multiple imports of p must succeed without redeclaration errors.
+ // We use an import path that's not cleaned up so that the eventual
+ // file path for the package is different from the package path; this
+ // will expose the error if it is present.
+ //
+ // (Issue: Both the textual and the binary importer used the file path
+ // of the package to be imported as key into the shared packages map.
+ // However, the binary importer then used the package path to identify
+ // the imported package to mark it as complete; effectively marking the
+ // wrong package as complete. By using an "unclean" package path, the
+ // file and package path are different, exposing the problem if present.
+ // The same issue occurs with vendoring.)
+ imports := make(map[string]*types.Package)
+ fset := token.NewFileSet()
+ for i := 0; i < 3; i++ {
+ if _, err := Import(fset, imports, "./././testdata/p", tmpdir, nil); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+func TestIssue15920(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ compileAndImportPkg(t, "issue15920")
+}
+
+func TestIssue20046(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // "./issue20046".V.M must exist
+ pkg := compileAndImportPkg(t, "issue20046")
+ obj := lookupObj(t, pkg.Scope(), "V")
+ if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
+ t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
+ }
+}
+func TestIssue25301(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ compileAndImportPkg(t, "issue25301")
+}
+
+func TestIssue25596(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ compileAndImportPkg(t, "issue25596")
+}
+
+func TestIssue57015(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ compileAndImportPkg(t, "issue57015")
+}
+
+func importPkg(t *testing.T, path, srcDir string) *types.Package {
+ fset := token.NewFileSet()
+ pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
+ if err != nil {
+ t.Helper()
+ t.Fatal(err)
+ }
+ return pkg
+}
+
+func compileAndImportPkg(t *testing.T, name string) *types.Package {
+ t.Helper()
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil)
+ return importPkg(t, "./testdata/"+name, tmpdir)
+}
+
+func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
+ if obj := scope.Lookup(name); obj != nil {
+ return obj
+ }
+ t.Helper()
+ t.Fatalf("%s not found", name)
+ return nil
+}
diff --git a/src/go/internal/gcimporter/iimport.go b/src/go/internal/gcimporter/iimport.go
new file mode 100644
index 0000000..9e3c945
--- /dev/null
+++ b/src/go/internal/gcimporter/iimport.go
@@ -0,0 +1,817 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Indexed package import.
+// See cmd/compile/internal/gc/iexport.go for the export data format.
+
+package gcimporter
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "internal/saferio"
+ "io"
+ "math"
+ "math/big"
+ "sort"
+ "strings"
+)
+
+type intReader struct {
+ *bufio.Reader
+ path string
+}
+
+func (r *intReader) int64() int64 {
+ i, err := binary.ReadVarint(r.Reader)
+ if err != nil {
+ errorf("import %q: read varint error: %v", r.path, err)
+ }
+ return i
+}
+
+func (r *intReader) uint64() uint64 {
+ i, err := binary.ReadUvarint(r.Reader)
+ if err != nil {
+ errorf("import %q: read varint error: %v", r.path, err)
+ }
+ return i
+}
+
+// Keep this in sync with constants in iexport.go.
+const (
+ iexportVersionGo1_11 = 0
+ iexportVersionPosCol = 1
+ iexportVersionGenerics = 2
+ iexportVersionGo1_18 = 2
+
+ iexportVersionCurrent = 2
+)
+
+type ident struct {
+ pkg *types.Package
+ name string
+}
+
+const predeclReserved = 32
+
+type itag uint64
+
+const (
+ // Types
+ definedType itag = iota
+ pointerType
+ sliceType
+ arrayType
+ chanType
+ mapType
+ signatureType
+ structType
+ interfaceType
+ typeParamType
+ instanceType
+ unionType
+)
+
+// iImportData imports a package from the serialized package data
+// and returns the number of bytes consumed and a reference to the package.
+// If the export data version is not recognized or the format is otherwise
+// compromised, an error is returned.
+func iImportData(fset *token.FileSet, imports map[string]*types.Package, dataReader *bufio.Reader, path string) (pkg *types.Package, err error) {
+ const currentVersion = iexportVersionCurrent
+ version := int64(-1)
+ defer func() {
+ if e := recover(); e != nil {
+ if version > currentVersion {
+ err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
+ } else {
+ err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
+ }
+ }
+ }()
+
+ r := &intReader{dataReader, path}
+
+ version = int64(r.uint64())
+ switch version {
+ case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11:
+ default:
+ errorf("unknown iexport format version %d", version)
+ }
+
+ sLen := r.uint64()
+ dLen := r.uint64()
+
+ if sLen > math.MaxUint64-dLen {
+ errorf("lengths out of range (%d, %d)", sLen, dLen)
+ }
+
+ data, err := saferio.ReadData(r, sLen+dLen)
+ if err != nil {
+ errorf("cannot read %d bytes of stringData and declData: %s", sLen+dLen, err)
+ }
+ stringData := data[:sLen]
+ declData := data[sLen:]
+
+ p := iimporter{
+ exportVersion: version,
+ ipath: path,
+ version: int(version),
+
+ stringData: stringData,
+ stringCache: make(map[uint64]string),
+ pkgCache: make(map[uint64]*types.Package),
+
+ declData: declData,
+ pkgIndex: make(map[*types.Package]map[string]uint64),
+ typCache: make(map[uint64]types.Type),
+ // Separate map for typeparams, keyed by their package and unique
+ // name (name with subscript).
+ tparamIndex: make(map[ident]*types.TypeParam),
+
+ fake: fakeFileSet{
+ fset: fset,
+ files: make(map[string]*fileInfo),
+ },
+ }
+ defer p.fake.setLines() // set lines for files in fset
+
+ for i, pt := range predeclared {
+ p.typCache[uint64(i)] = pt
+ }
+
+ pkgList := make([]*types.Package, r.uint64())
+ for i := range pkgList {
+ pkgPathOff := r.uint64()
+ pkgPath := p.stringAt(pkgPathOff)
+ pkgName := p.stringAt(r.uint64())
+ _ = r.uint64() // package height; unused by go/types
+
+ if pkgPath == "" {
+ pkgPath = path
+ }
+ pkg := imports[pkgPath]
+ if pkg == nil {
+ pkg = types.NewPackage(pkgPath, pkgName)
+ imports[pkgPath] = pkg
+ } else if pkg.Name() != pkgName {
+ errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
+ }
+
+ p.pkgCache[pkgPathOff] = pkg
+
+ nameIndex := make(map[string]uint64)
+ for nSyms := r.uint64(); nSyms > 0; nSyms-- {
+ name := p.stringAt(r.uint64())
+ nameIndex[name] = r.uint64()
+ }
+
+ p.pkgIndex[pkg] = nameIndex
+ pkgList[i] = pkg
+ }
+
+ localpkg := pkgList[0]
+
+ names := make([]string, 0, len(p.pkgIndex[localpkg]))
+ for name := range p.pkgIndex[localpkg] {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, name := range names {
+ p.doDecl(localpkg, name)
+ }
+
+ // SetConstraint can't be called if the constraint type is not yet complete.
+ // When type params are created in the 'P' case of (*importReader).obj(),
+ // the associated constraint type may not be complete due to recursion.
+ // Therefore, we defer calling SetConstraint there, and call it here instead
+ // after all types are complete.
+ for _, d := range p.later {
+ d.t.SetConstraint(d.constraint)
+ }
+
+ for _, typ := range p.interfaceList {
+ typ.Complete()
+ }
+
+ // record all referenced packages as imports
+ list := append(([]*types.Package)(nil), pkgList[1:]...)
+ sort.Sort(byPath(list))
+ localpkg.SetImports(list)
+
+ // package was imported completely and without errors
+ localpkg.MarkComplete()
+ return localpkg, nil
+}
+
+type setConstraintArgs struct {
+ t *types.TypeParam
+ constraint types.Type
+}
+
+type iimporter struct {
+ exportVersion int64
+ ipath string
+ version int
+
+ stringData []byte
+ stringCache map[uint64]string
+ pkgCache map[uint64]*types.Package
+
+ declData []byte
+ pkgIndex map[*types.Package]map[string]uint64
+ typCache map[uint64]types.Type
+ tparamIndex map[ident]*types.TypeParam
+
+ fake fakeFileSet
+ interfaceList []*types.Interface
+
+ // Arguments for calls to SetConstraint that are deferred due to recursive types
+ later []setConstraintArgs
+}
+
+func (p *iimporter) doDecl(pkg *types.Package, name string) {
+ // See if we've already imported this declaration.
+ if obj := pkg.Scope().Lookup(name); obj != nil {
+ return
+ }
+
+ off, ok := p.pkgIndex[pkg][name]
+ if !ok {
+ errorf("%v.%v not in index", pkg, name)
+ }
+
+ r := &importReader{p: p, currPkg: pkg}
+ r.declReader.Reset(p.declData[off:])
+
+ r.obj(name)
+}
+
+func (p *iimporter) stringAt(off uint64) string {
+ if s, ok := p.stringCache[off]; ok {
+ return s
+ }
+
+ slen, n := binary.Uvarint(p.stringData[off:])
+ if n <= 0 {
+ errorf("varint failed")
+ }
+ spos := off + uint64(n)
+ s := string(p.stringData[spos : spos+slen])
+ p.stringCache[off] = s
+ return s
+}
+
+func (p *iimporter) pkgAt(off uint64) *types.Package {
+ if pkg, ok := p.pkgCache[off]; ok {
+ return pkg
+ }
+ path := p.stringAt(off)
+ errorf("missing package %q in %q", path, p.ipath)
+ return nil
+}
+
+func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
+ if t, ok := p.typCache[off]; ok && canReuse(base, t) {
+ return t
+ }
+
+ if off < predeclReserved {
+ errorf("predeclared type missing from cache: %v", off)
+ }
+
+ r := &importReader{p: p}
+ r.declReader.Reset(p.declData[off-predeclReserved:])
+ t := r.doType(base)
+
+ if canReuse(base, t) {
+ p.typCache[off] = t
+ }
+ return t
+}
+
+// canReuse reports whether the type rhs on the RHS of the declaration for def
+// may be re-used.
+//
+// Specifically, if def is non-nil and rhs is an interface type with methods, it
+// may not be re-used because we have a convention of setting the receiver type
+// for interface methods to def.
+func canReuse(def *types.Named, rhs types.Type) bool {
+ if def == nil {
+ return true
+ }
+ iface, _ := rhs.(*types.Interface)
+ if iface == nil {
+ return true
+ }
+ // Don't use iface.Empty() here as iface may not be complete.
+ return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
+}
+
+type importReader struct {
+ p *iimporter
+ declReader bytes.Reader
+ currPkg *types.Package
+ prevFile string
+ prevLine int64
+ prevColumn int64
+}
+
+func (r *importReader) obj(name string) {
+ tag := r.byte()
+ pos := r.pos()
+
+ switch tag {
+ case 'A':
+ typ := r.typ()
+
+ r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
+
+ case 'C':
+ typ, val := r.value()
+
+ r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
+
+ case 'F', 'G':
+ var tparams []*types.TypeParam
+ if tag == 'G' {
+ tparams = r.tparamList()
+ }
+ sig := r.signature(nil, nil, tparams)
+ r.declare(types.NewFunc(pos, r.currPkg, name, sig))
+
+ case 'T', 'U':
+ // Types can be recursive. We need to setup a stub
+ // declaration before recurring.
+ obj := types.NewTypeName(pos, r.currPkg, name, nil)
+ named := types.NewNamed(obj, nil, nil)
+ // Declare obj before calling r.tparamList, so the new type name is recognized
+ // if used in the constraint of one of its own typeparams (see #48280).
+ r.declare(obj)
+ if tag == 'U' {
+ tparams := r.tparamList()
+ named.SetTypeParams(tparams)
+ }
+
+ underlying := r.p.typAt(r.uint64(), named).Underlying()
+ named.SetUnderlying(underlying)
+
+ if !isInterface(underlying) {
+ for n := r.uint64(); n > 0; n-- {
+ mpos := r.pos()
+ mname := r.ident()
+ recv := r.param()
+
+ // If the receiver has any targs, set those as the
+ // rparams of the method (since those are the
+ // typeparams being used in the method sig/body).
+ targs := baseType(recv.Type()).TypeArgs()
+ var rparams []*types.TypeParam
+ if targs.Len() > 0 {
+ rparams = make([]*types.TypeParam, targs.Len())
+ for i := range rparams {
+ rparams[i], _ = targs.At(i).(*types.TypeParam)
+ }
+ }
+ msig := r.signature(recv, rparams, nil)
+
+ named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
+ }
+ }
+
+ case 'P':
+ // We need to "declare" a typeparam in order to have a name that
+ // can be referenced recursively (if needed) in the type param's
+ // bound.
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ // Remove the "path" from the type param name that makes it unique,
+ // and revert any unique name used for blank typeparams.
+ name0 := tparamName(name)
+ tn := types.NewTypeName(pos, r.currPkg, name0, nil)
+ t := types.NewTypeParam(tn, nil)
+ // To handle recursive references to the typeparam within its
+ // bound, save the partial type in tparamIndex before reading the bounds.
+ id := ident{r.currPkg, name}
+ r.p.tparamIndex[id] = t
+
+ var implicit bool
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ implicit = r.bool()
+ }
+ constraint := r.typ()
+ if implicit {
+ iface, _ := constraint.(*types.Interface)
+ if iface == nil {
+ errorf("non-interface constraint marked implicit")
+ }
+ iface.MarkImplicit()
+ }
+ // The constraint type may not be complete, if we
+ // are in the middle of a type recursion involving type
+ // constraints. So, we defer SetConstraint until we have
+ // completely set up all types in ImportData.
+ r.p.later = append(r.p.later, setConstraintArgs{t: t, constraint: constraint})
+
+ case 'V':
+ typ := r.typ()
+
+ r.declare(types.NewVar(pos, r.currPkg, name, typ))
+
+ default:
+ errorf("unexpected tag: %v", tag)
+ }
+}
+
+func (r *importReader) declare(obj types.Object) {
+ obj.Pkg().Scope().Insert(obj)
+}
+
+func (r *importReader) value() (typ types.Type, val constant.Value) {
+ typ = r.typ()
+ if r.p.exportVersion >= iexportVersionGo1_18 {
+ // TODO: add support for using the kind
+ _ = constant.Kind(r.int64())
+ }
+
+ switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
+ case types.IsBoolean:
+ val = constant.MakeBool(r.bool())
+
+ case types.IsString:
+ val = constant.MakeString(r.string())
+
+ case types.IsInteger:
+ var x big.Int
+ r.mpint(&x, b)
+ val = constant.Make(&x)
+
+ case types.IsFloat:
+ val = r.mpfloat(b)
+
+ case types.IsComplex:
+ re := r.mpfloat(b)
+ im := r.mpfloat(b)
+ val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+
+ default:
+ errorf("unexpected type %v", typ) // panics
+ panic("unreachable")
+ }
+
+ return
+}
+
+func intSize(b *types.Basic) (signed bool, maxBytes uint) {
+ if (b.Info() & types.IsUntyped) != 0 {
+ return true, 64
+ }
+
+ switch b.Kind() {
+ case types.Float32, types.Complex64:
+ return true, 3
+ case types.Float64, types.Complex128:
+ return true, 7
+ }
+
+ signed = (b.Info() & types.IsUnsigned) == 0
+ switch b.Kind() {
+ case types.Int8, types.Uint8:
+ maxBytes = 1
+ case types.Int16, types.Uint16:
+ maxBytes = 2
+ case types.Int32, types.Uint32:
+ maxBytes = 4
+ default:
+ maxBytes = 8
+ }
+
+ return
+}
+
+func (r *importReader) mpint(x *big.Int, typ *types.Basic) {
+ signed, maxBytes := intSize(typ)
+
+ maxSmall := 256 - maxBytes
+ if signed {
+ maxSmall = 256 - 2*maxBytes
+ }
+ if maxBytes == 1 {
+ maxSmall = 256
+ }
+
+ n, _ := r.declReader.ReadByte()
+ if uint(n) < maxSmall {
+ v := int64(n)
+ if signed {
+ v >>= 1
+ if n&1 != 0 {
+ v = ^v
+ }
+ }
+ x.SetInt64(v)
+ return
+ }
+
+ v := -n
+ if signed {
+ v = -(n &^ 1) >> 1
+ }
+ if v < 1 || uint(v) > maxBytes {
+ errorf("weird decoding: %v, %v => %v", n, signed, v)
+ }
+ b := make([]byte, v)
+ io.ReadFull(&r.declReader, b)
+ x.SetBytes(b)
+ if signed && n&1 != 0 {
+ x.Neg(x)
+ }
+}
+
+func (r *importReader) mpfloat(typ *types.Basic) constant.Value {
+ var mant big.Int
+ r.mpint(&mant, typ)
+ var f big.Float
+ f.SetInt(&mant)
+ if f.Sign() != 0 {
+ f.SetMantExp(&f, int(r.int64()))
+ }
+ return constant.Make(&f)
+}
+
+func (r *importReader) ident() string {
+ return r.string()
+}
+
+func (r *importReader) qualifiedIdent() (*types.Package, string) {
+ name := r.string()
+ pkg := r.pkg()
+ return pkg, name
+}
+
+func (r *importReader) pos() token.Pos {
+ if r.p.version >= 1 {
+ r.posv1()
+ } else {
+ r.posv0()
+ }
+
+ if r.prevFile == "" && r.prevLine == 0 && r.prevColumn == 0 {
+ return token.NoPos
+ }
+ return r.p.fake.pos(r.prevFile, int(r.prevLine), int(r.prevColumn))
+}
+
+func (r *importReader) posv0() {
+ delta := r.int64()
+ if delta != deltaNewFile {
+ r.prevLine += delta
+ } else if l := r.int64(); l == -1 {
+ r.prevLine += deltaNewFile
+ } else {
+ r.prevFile = r.string()
+ r.prevLine = l
+ }
+}
+
+func (r *importReader) posv1() {
+ delta := r.int64()
+ r.prevColumn += delta >> 1
+ if delta&1 != 0 {
+ delta = r.int64()
+ r.prevLine += delta >> 1
+ if delta&1 != 0 {
+ r.prevFile = r.string()
+ }
+ }
+}
+
+func (r *importReader) typ() types.Type {
+ return r.p.typAt(r.uint64(), nil)
+}
+
+func isInterface(t types.Type) bool {
+ _, ok := t.(*types.Interface)
+ return ok
+}
+
+func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
+func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
+
+func (r *importReader) doType(base *types.Named) types.Type {
+ switch k := r.kind(); k {
+ default:
+ errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
+ return nil
+
+ case definedType:
+ pkg, name := r.qualifiedIdent()
+ r.p.doDecl(pkg, name)
+ return pkg.Scope().Lookup(name).(*types.TypeName).Type()
+ case pointerType:
+ return types.NewPointer(r.typ())
+ case sliceType:
+ return types.NewSlice(r.typ())
+ case arrayType:
+ n := r.uint64()
+ return types.NewArray(r.typ(), int64(n))
+ case chanType:
+ dir := chanDir(int(r.uint64()))
+ return types.NewChan(dir, r.typ())
+ case mapType:
+ return types.NewMap(r.typ(), r.typ())
+ case signatureType:
+ r.currPkg = r.pkg()
+ return r.signature(nil, nil, nil)
+
+ case structType:
+ r.currPkg = r.pkg()
+
+ fields := make([]*types.Var, r.uint64())
+ tags := make([]string, len(fields))
+ for i := range fields {
+ fpos := r.pos()
+ fname := r.ident()
+ ftyp := r.typ()
+ emb := r.bool()
+ tag := r.string()
+
+ fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
+ tags[i] = tag
+ }
+ return types.NewStruct(fields, tags)
+
+ case interfaceType:
+ r.currPkg = r.pkg()
+
+ embeddeds := make([]types.Type, r.uint64())
+ for i := range embeddeds {
+ _ = r.pos()
+ embeddeds[i] = r.typ()
+ }
+
+ methods := make([]*types.Func, r.uint64())
+ for i := range methods {
+ mpos := r.pos()
+ mname := r.ident()
+
+ // TODO(mdempsky): Matches bimport.go, but I
+ // don't agree with this.
+ var recv *types.Var
+ if base != nil {
+ recv = types.NewVar(token.NoPos, r.currPkg, "", base)
+ }
+
+ msig := r.signature(recv, nil, nil)
+ methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
+ }
+
+ typ := types.NewInterfaceType(methods, embeddeds)
+ r.p.interfaceList = append(r.p.interfaceList, typ)
+ return typ
+
+ case typeParamType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected type param type")
+ }
+ pkg, name := r.qualifiedIdent()
+ id := ident{pkg, name}
+ if t, ok := r.p.tparamIndex[id]; ok {
+ // We're already in the process of importing this typeparam.
+ return t
+ }
+ // Otherwise, import the definition of the typeparam now.
+ r.p.doDecl(pkg, name)
+ return r.p.tparamIndex[id]
+
+ case instanceType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ // pos does not matter for instances: they are positioned on the original
+ // type.
+ _ = r.pos()
+ len := r.uint64()
+ targs := make([]types.Type, len)
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+ baseType := r.typ()
+ // The imported instantiated type doesn't include any methods, so
+ // we must always use the methods of the base (orig) type.
+ // TODO provide a non-nil *Context
+ t, _ := types.Instantiate(nil, baseType, targs, false)
+ return t
+
+ case unionType:
+ if r.p.exportVersion < iexportVersionGenerics {
+ errorf("unexpected instantiation type")
+ }
+ terms := make([]*types.Term, r.uint64())
+ for i := range terms {
+ terms[i] = types.NewTerm(r.bool(), r.typ())
+ }
+ return types.NewUnion(terms)
+ }
+}
+
+func (r *importReader) kind() itag {
+ return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types.Var, rparams, tparams []*types.TypeParam) *types.Signature {
+ params := r.paramList()
+ results := r.paramList()
+ variadic := params.Len() > 0 && r.bool()
+ return types.NewSignatureType(recv, rparams, tparams, params, results, variadic)
+}
+
+func (r *importReader) tparamList() []*types.TypeParam {
+ n := r.uint64()
+ if n == 0 {
+ return nil
+ }
+ xs := make([]*types.TypeParam, n)
+ for i := range xs {
+ xs[i], _ = r.typ().(*types.TypeParam)
+ }
+ return xs
+}
+
+func (r *importReader) paramList() *types.Tuple {
+ xs := make([]*types.Var, r.uint64())
+ for i := range xs {
+ xs[i] = r.param()
+ }
+ return types.NewTuple(xs...)
+}
+
+func (r *importReader) param() *types.Var {
+ pos := r.pos()
+ name := r.ident()
+ typ := r.typ()
+ return types.NewParam(pos, r.currPkg, name, typ)
+}
+
+func (r *importReader) bool() bool {
+ return r.uint64() != 0
+}
+
+func (r *importReader) int64() int64 {
+ n, err := binary.ReadVarint(&r.declReader)
+ if err != nil {
+ errorf("readVarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) uint64() uint64 {
+ n, err := binary.ReadUvarint(&r.declReader)
+ if err != nil {
+ errorf("readUvarint: %v", err)
+ }
+ return n
+}
+
+func (r *importReader) byte() byte {
+ x, err := r.declReader.ReadByte()
+ if err != nil {
+ errorf("declReader.ReadByte: %v", err)
+ }
+ return x
+}
+
+func baseType(typ types.Type) *types.Named {
+ // pointer receivers are never types.Named types
+ if p, _ := typ.(*types.Pointer); p != nil {
+ typ = p.Elem()
+ }
+ // receiver base types are always (possibly generic) types.Named types
+ n, _ := typ.(*types.Named)
+ return n
+}
+
+const blankMarker = "$"
+
+// tparamName returns the real name of a type parameter, after stripping its
+// qualifying prefix and reverting blank-name encoding. See tparamExportName
+// for details.
+func tparamName(exportName string) string {
+ // Remove the "path" from the type param name that makes it unique.
+ ix := strings.LastIndex(exportName, ".")
+ if ix < 0 {
+ errorf("malformed type parameter export name %s: missing prefix", exportName)
+ }
+ name := exportName[ix+1:]
+ if strings.HasPrefix(name, blankMarker) {
+ return "_"
+ }
+ return name
+}
diff --git a/src/go/internal/gcimporter/support.go b/src/go/internal/gcimporter/support.go
new file mode 100644
index 0000000..7ed8c9a
--- /dev/null
+++ b/src/go/internal/gcimporter/support.go
@@ -0,0 +1,183 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements support functionality for iimport.go.
+
+package gcimporter
+
+import (
+ "fmt"
+ "go/token"
+ "go/types"
+ "internal/pkgbits"
+ "sync"
+)
+
+func assert(b bool) {
+ if !b {
+ panic("assertion failed")
+ }
+}
+
+func errorf(format string, args ...any) {
+ panic(fmt.Sprintf(format, args...))
+}
+
+// deltaNewFile is a magic line delta offset indicating a new file.
+// We use -64 because it is rare; see issue 20080 and CL 41619.
+// -64 is the smallest int that fits in a single byte as a varint.
+const deltaNewFile = -64
+
+// Synthesize a token.Pos
+type fakeFileSet struct {
+ fset *token.FileSet
+ files map[string]*fileInfo
+}
+
+type fileInfo struct {
+ file *token.File
+ lastline int
+}
+
+const maxlines = 64 * 1024
+
+func (s *fakeFileSet) pos(file string, line, column int) token.Pos {
+ // TODO(mdempsky): Make use of column.
+
+ // Since we don't know the set of needed file positions, we reserve
+ // maxlines positions per file. We delay calling token.File.SetLines until
+ // all positions have been calculated (by way of fakeFileSet.setLines), so
+ // that we can avoid setting unnecessary lines. See also golang/go#46586.
+ f := s.files[file]
+ if f == nil {
+ f = &fileInfo{file: s.fset.AddFile(file, -1, maxlines)}
+ s.files[file] = f
+ }
+
+ if line > maxlines {
+ line = 1
+ }
+ if line > f.lastline {
+ f.lastline = line
+ }
+
+ // Return a fake position assuming that f.file consists only of newlines.
+ return token.Pos(f.file.Base() + line - 1)
+}
+
+func (s *fakeFileSet) setLines() {
+ fakeLinesOnce.Do(func() {
+ fakeLines = make([]int, maxlines)
+ for i := range fakeLines {
+ fakeLines[i] = i
+ }
+ })
+ for _, f := range s.files {
+ f.file.SetLines(fakeLines[:f.lastline])
+ }
+}
+
+var (
+ fakeLines []int
+ fakeLinesOnce sync.Once
+)
+
+func chanDir(d int) types.ChanDir {
+ // tag values must match the constants in cmd/compile/internal/gc/go.go
+ switch d {
+ case 1 /* Crecv */ :
+ return types.RecvOnly
+ case 2 /* Csend */ :
+ return types.SendOnly
+ case 3 /* Cboth */ :
+ return types.SendRecv
+ default:
+ errorf("unexpected channel dir %d", d)
+ return 0
+ }
+}
+
+var predeclared = []types.Type{
+ // basic types
+ types.Typ[types.Bool],
+ types.Typ[types.Int],
+ types.Typ[types.Int8],
+ types.Typ[types.Int16],
+ types.Typ[types.Int32],
+ types.Typ[types.Int64],
+ types.Typ[types.Uint],
+ types.Typ[types.Uint8],
+ types.Typ[types.Uint16],
+ types.Typ[types.Uint32],
+ types.Typ[types.Uint64],
+ types.Typ[types.Uintptr],
+ types.Typ[types.Float32],
+ types.Typ[types.Float64],
+ types.Typ[types.Complex64],
+ types.Typ[types.Complex128],
+ types.Typ[types.String],
+
+ // basic type aliases
+ types.Universe.Lookup("byte").Type(),
+ types.Universe.Lookup("rune").Type(),
+
+ // error
+ types.Universe.Lookup("error").Type(),
+
+ // untyped types
+ types.Typ[types.UntypedBool],
+ types.Typ[types.UntypedInt],
+ types.Typ[types.UntypedRune],
+ types.Typ[types.UntypedFloat],
+ types.Typ[types.UntypedComplex],
+ types.Typ[types.UntypedString],
+ types.Typ[types.UntypedNil],
+
+ // package unsafe
+ types.Typ[types.UnsafePointer],
+
+ // invalid type
+ types.Typ[types.Invalid], // only appears in packages with errors
+
+ // used internally by gc; never used by this package or in .a files
+ // not to be confused with the universe any
+ anyType{},
+
+ // comparable
+ types.Universe.Lookup("comparable").Type(),
+
+ // any
+ types.Universe.Lookup("any").Type(),
+}
+
+type anyType struct{}
+
+func (t anyType) Underlying() types.Type { return t }
+func (t anyType) String() string { return "any" }
+
+// See cmd/compile/internal/noder.derivedInfo.
+type derivedInfo struct {
+ idx pkgbits.Index
+ needed bool
+}
+
+// See cmd/compile/internal/noder.typeInfo.
+type typeInfo struct {
+ idx pkgbits.Index
+ derived bool
+}
+
+// See cmd/compile/internal/types.SplitVargenSuffix.
+func splitVargenSuffix(name string) (base, suffix string) {
+ i := len(name)
+ for i > 0 && name[i-1] >= '0' && name[i-1] <= '9' {
+ i--
+ }
+ const dot = "·"
+ if i >= len(dot) && name[i-len(dot):i] == dot {
+ i -= len(dot)
+ return name[:i], name[i:]
+ }
+ return name, ""
+}
diff --git a/src/go/internal/gcimporter/testdata/a.go b/src/go/internal/gcimporter/testdata/a.go
new file mode 100644
index 0000000..56e4292
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/a.go
@@ -0,0 +1,14 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestIssue13566
+
+package a
+
+import "encoding/json"
+
+type A struct {
+ a *A
+ json json.RawMessage
+}
diff --git a/src/go/internal/gcimporter/testdata/b.go b/src/go/internal/gcimporter/testdata/b.go
new file mode 100644
index 0000000..4196678
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/b.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestIssue13566
+
+package b
+
+import "./a"
+
+type A a.A
diff --git a/src/go/internal/gcimporter/testdata/exports.go b/src/go/internal/gcimporter/testdata/exports.go
new file mode 100644
index 0000000..3d5a8c9
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/exports.go
@@ -0,0 +1,91 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import "go/ast"
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+const (
+ C0 int = 0
+ C1 = 3.14159265
+ C2 = 2.718281828i
+ C3 = -123.456e-789
+ C4 = +123.456e+789
+ C5 = 1234i
+ C6 = "foo\n"
+ C7 = `bar\n`
+ C8 = 42
+ C9 int = 42
+ C10 float64 = 42
+)
+
+type (
+ T1 int
+ T2 [10]int
+ T3 []int
+ T4 *int
+ T5 chan int
+ T6a chan<- int
+ T6b chan (<-chan int)
+ T6c chan<- (chan int)
+ T7 <-chan *ast.File
+ T8 struct{}
+ T9 struct {
+ a int
+ b, c float32
+ d []string `go:"tag"`
+ }
+ T10 struct {
+ T8
+ T9
+ _ *T10
+ }
+ T11 map[int]string
+ T12 any
+ T13 interface {
+ m1()
+ m2(int) float32
+ }
+ T14 interface {
+ T12
+ T13
+ m3(x ...struct{}) []T9
+ }
+ T15 func()
+ T16 func(int)
+ T17 func(x int)
+ T18 func() float32
+ T19 func() (x float32)
+ T20 func(...any)
+ T21 struct{ next *T21 }
+ T22 struct{ link *T23 }
+ T23 struct{ link *T22 }
+ T24 *T24
+ T25 *T26
+ T26 *T27
+ T27 *T25
+ T28 func(T28) T28
+)
+
+var (
+ V0 int
+ V1 = -991.0
+ V2 float32 = 1.2
+)
+
+func F1() {}
+func F2(x int) {}
+func F3() int { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...any) (p, q, r chan<- T10)
+
+func (p *T1) M1()
diff --git a/src/go/internal/gcimporter/testdata/g.go b/src/go/internal/gcimporter/testdata/g.go
new file mode 100644
index 0000000..301c142
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/g.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestTypeNamingOrder
+
+// ensures that the order in which "type A B" declarations are
+// processed is correct; this was a problem for unified IR imports.
+
+package g
+
+type Client struct {
+ common service
+ A *AService
+ B *BService
+}
+
+type service struct {
+ client *Client
+}
+
+type AService service
+type BService service
diff --git a/src/go/internal/gcimporter/testdata/generics.go b/src/go/internal/gcimporter/testdata/generics.go
new file mode 100644
index 0000000..00bf040
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/generics.go
@@ -0,0 +1,29 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is used to generate an object file which
+// serves as test file for gcimporter_test.go.
+
+package generics
+
+type Any any
+
+var x any
+
+type T[A, B any] struct {
+ Left A
+ Right B
+}
+
+var X T[int, string] = T[int, string]{1, "hi"}
+
+func ToInt[P interface{ ~int }](p P) int { return int(p) }
+
+var IntID = ToInt[int]
+
+type G[C comparable] int
+
+func ImplicitFunc[T ~int]() {}
+
+type ImplicitType[T ~int] int
diff --git a/src/go/internal/gcimporter/testdata/issue15920.go b/src/go/internal/gcimporter/testdata/issue15920.go
new file mode 100644
index 0000000..c70f7d8
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/issue15920.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+// The underlying type of Error is the underlying type of error.
+// Make sure we can import this again without problems.
+type Error error
+
+func F() Error { return nil }
diff --git a/src/go/internal/gcimporter/testdata/issue20046.go b/src/go/internal/gcimporter/testdata/issue20046.go
new file mode 100644
index 0000000..c63ee82
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/issue20046.go
@@ -0,0 +1,9 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+var V interface {
+ M()
+}
diff --git a/src/go/internal/gcimporter/testdata/issue25301.go b/src/go/internal/gcimporter/testdata/issue25301.go
new file mode 100644
index 0000000..e3dc98b
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/issue25301.go
@@ -0,0 +1,17 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue25301
+
+type (
+ A = interface {
+ M()
+ }
+ T interface {
+ A
+ }
+ S struct{}
+)
+
+func (S) M() { println("m") }
diff --git a/src/go/internal/gcimporter/testdata/issue25596.go b/src/go/internal/gcimporter/testdata/issue25596.go
new file mode 100644
index 0000000..8923373
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/issue25596.go
@@ -0,0 +1,13 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue25596
+
+type E interface {
+ M() T
+}
+
+type T interface {
+ E
+}
diff --git a/src/go/internal/gcimporter/testdata/issue57015.go b/src/go/internal/gcimporter/testdata/issue57015.go
new file mode 100644
index 0000000..b6be811
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/issue57015.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue57015
+
+type E error
+
+type X[T any] struct {}
+
+func F() X[interface {
+ E
+}] {
+ panic(0)
+}
+
diff --git a/src/go/internal/gcimporter/testdata/p.go b/src/go/internal/gcimporter/testdata/p.go
new file mode 100644
index 0000000..9e2e705
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/p.go
@@ -0,0 +1,13 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Input for TestIssue15517
+
+package p
+
+const C = 0
+
+var V int
+
+func F() {}
diff --git a/src/go/internal/gcimporter/testdata/versions/test.go b/src/go/internal/gcimporter/testdata/versions/test.go
new file mode 100644
index 0000000..227fc09
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test.go
@@ -0,0 +1,28 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// To create a test case for a new export format version,
+// build this package with the latest compiler and store
+// the resulting .a file appropriately named in the versions
+// directory. The VersionHandling test will pick it up.
+//
+// In the testdata/versions:
+//
+// go build -o test_go1.$X_$Y.a test.go
+//
+// with $X = Go version and $Y = export format version
+// (add 'b' or 'i' to distinguish between binary and
+// indexed format starting with 1.11 as long as both
+// formats are supported).
+//
+// Make sure this source is extended such that it exercises
+// whatever export format change has taken place.
+
+package test
+
+// Any release before and including Go 1.7 didn't encode
+// the package for a blank struct field.
+type BlankField struct {
+ _ int
+}
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.11_0i.a b/src/go/internal/gcimporter/testdata/versions/test_go1.11_0i.a
new file mode 100644
index 0000000..b00fefe
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.11_0i.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a b/src/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
new file mode 100644
index 0000000..c0a211e
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a b/src/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
new file mode 100644
index 0000000..c35d22d
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.11_999i.a b/src/go/internal/gcimporter/testdata/versions/test_go1.11_999i.a
new file mode 100644
index 0000000..99401d7
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.11_999i.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a b/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
new file mode 100644
index 0000000..edb6c3f
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a b/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
new file mode 100644
index 0000000..554d04a
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.8_4.a b/src/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
new file mode 100644
index 0000000..26b8531
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
Binary files differ
diff --git a/src/go/internal/gcimporter/testdata/versions/test_go1.8_5.a b/src/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
new file mode 100644
index 0000000..60e52ef
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
Binary files differ
diff --git a/src/go/internal/gcimporter/ureader.go b/src/go/internal/gcimporter/ureader.go
new file mode 100644
index 0000000..ac85a41
--- /dev/null
+++ b/src/go/internal/gcimporter/ureader.go
@@ -0,0 +1,657 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gcimporter
+
+import (
+ "go/token"
+ "go/types"
+ "internal/pkgbits"
+ "sort"
+)
+
+// A pkgReader holds the shared state for reading a unified IR package
+// description.
+type pkgReader struct {
+ pkgbits.PkgDecoder
+
+ fake fakeFileSet
+
+ ctxt *types.Context
+ imports map[string]*types.Package // previously imported packages, indexed by path
+
+ // lazily initialized arrays corresponding to the unified IR
+ // PosBase, Pkg, and Type sections, respectively.
+ posBases []string // position bases (i.e., file names)
+ pkgs []*types.Package
+ typs []types.Type
+
+ // laterFns holds functions that need to be invoked at the end of
+ // import reading.
+ laterFns []func()
+
+ // ifaces holds a list of constructed Interfaces, which need to have
+ // Complete called after importing is done.
+ ifaces []*types.Interface
+}
+
+// later adds a function to be invoked at the end of import reading.
+func (pr *pkgReader) later(fn func()) {
+ pr.laterFns = append(pr.laterFns, fn)
+}
+
+// readUnifiedPackage reads a package description from the given
+// unified IR export data decoder.
+func readUnifiedPackage(fset *token.FileSet, ctxt *types.Context, imports map[string]*types.Package, input pkgbits.PkgDecoder) *types.Package {
+ pr := pkgReader{
+ PkgDecoder: input,
+
+ fake: fakeFileSet{
+ fset: fset,
+ files: make(map[string]*fileInfo),
+ },
+
+ ctxt: ctxt,
+ imports: imports,
+
+ posBases: make([]string, input.NumElems(pkgbits.RelocPosBase)),
+ pkgs: make([]*types.Package, input.NumElems(pkgbits.RelocPkg)),
+ typs: make([]types.Type, input.NumElems(pkgbits.RelocType)),
+ }
+ defer pr.fake.setLines()
+
+ r := pr.newReader(pkgbits.RelocMeta, pkgbits.PublicRootIdx, pkgbits.SyncPublic)
+ pkg := r.pkg()
+ r.Bool() // TODO(mdempsky): Remove; was "has init"
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ // As if r.obj(), but avoiding the Scope.Lookup call,
+ // to avoid eager loading of imports.
+ r.Sync(pkgbits.SyncObject)
+ assert(!r.Bool())
+ r.p.objIdx(r.Reloc(pkgbits.RelocObj))
+ assert(r.Len() == 0)
+ }
+
+ r.Sync(pkgbits.SyncEOF)
+
+ for _, fn := range pr.laterFns {
+ fn()
+ }
+
+ for _, iface := range pr.ifaces {
+ iface.Complete()
+ }
+
+ // Imports() of pkg are all of the transitive packages that were loaded.
+ var imps []*types.Package
+ for _, imp := range pr.pkgs {
+ if imp != nil && imp != pkg {
+ imps = append(imps, imp)
+ }
+ }
+ sort.Sort(byPath(imps))
+ pkg.SetImports(imps)
+
+ pkg.MarkComplete()
+ return pkg
+}
+
+// A reader holds the state for reading a single unified IR element
+// within a package.
+type reader struct {
+ pkgbits.Decoder
+
+ p *pkgReader
+
+ dict *readerDict
+}
+
+// A readerDict holds the state for type parameters that parameterize
+// the current unified IR element.
+type readerDict struct {
+ // bounds is a slice of typeInfos corresponding to the underlying
+ // bounds of the element's type parameters.
+ bounds []typeInfo
+
+ // tparams is a slice of the constructed TypeParams for the element.
+ tparams []*types.TypeParam
+
+ // devived is a slice of types derived from tparams, which may be
+ // instantiated while reading the current element.
+ derived []derivedInfo
+ derivedTypes []types.Type // lazily instantiated from derived
+}
+
+func (pr *pkgReader) newReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
+ return &reader{
+ Decoder: pr.NewDecoder(k, idx, marker),
+ p: pr,
+ }
+}
+
+func (pr *pkgReader) tempReader(k pkgbits.RelocKind, idx pkgbits.Index, marker pkgbits.SyncMarker) *reader {
+ return &reader{
+ Decoder: pr.TempDecoder(k, idx, marker),
+ p: pr,
+ }
+}
+
+func (pr *pkgReader) retireReader(r *reader) {
+ pr.RetireDecoder(&r.Decoder)
+}
+
+// @@@ Positions
+
+func (r *reader) pos() token.Pos {
+ r.Sync(pkgbits.SyncPos)
+ if !r.Bool() {
+ return token.NoPos
+ }
+
+ // TODO(mdempsky): Delta encoding.
+ posBase := r.posBase()
+ line := r.Uint()
+ col := r.Uint()
+ return r.p.fake.pos(posBase, int(line), int(col))
+}
+
+func (r *reader) posBase() string {
+ return r.p.posBaseIdx(r.Reloc(pkgbits.RelocPosBase))
+}
+
+func (pr *pkgReader) posBaseIdx(idx pkgbits.Index) string {
+ if b := pr.posBases[idx]; b != "" {
+ return b
+ }
+
+ var filename string
+ {
+ r := pr.tempReader(pkgbits.RelocPosBase, idx, pkgbits.SyncPosBase)
+
+ // Within types2, position bases have a lot more details (e.g.,
+ // keeping track of where //line directives appeared exactly).
+ //
+ // For go/types, we just track the file name.
+
+ filename = r.String()
+
+ if r.Bool() { // file base
+ // Was: "b = token.NewTrimmedFileBase(filename, true)"
+ } else { // line base
+ pos := r.pos()
+ line := r.Uint()
+ col := r.Uint()
+
+ // Was: "b = token.NewLineBase(pos, filename, true, line, col)"
+ _, _, _ = pos, line, col
+ }
+ pr.retireReader(r)
+ }
+ b := filename
+ pr.posBases[idx] = b
+ return b
+}
+
+// @@@ Packages
+
+func (r *reader) pkg() *types.Package {
+ r.Sync(pkgbits.SyncPkg)
+ return r.p.pkgIdx(r.Reloc(pkgbits.RelocPkg))
+}
+
+func (pr *pkgReader) pkgIdx(idx pkgbits.Index) *types.Package {
+ // TODO(mdempsky): Consider using some non-nil pointer to indicate
+ // the universe scope, so we don't need to keep re-reading it.
+ if pkg := pr.pkgs[idx]; pkg != nil {
+ return pkg
+ }
+
+ pkg := pr.newReader(pkgbits.RelocPkg, idx, pkgbits.SyncPkgDef).doPkg()
+ pr.pkgs[idx] = pkg
+ return pkg
+}
+
+func (r *reader) doPkg() *types.Package {
+ path := r.String()
+ switch path {
+ case "":
+ path = r.p.PkgPath()
+ case "builtin":
+ return nil // universe
+ case "unsafe":
+ return types.Unsafe
+ }
+
+ if pkg := r.p.imports[path]; pkg != nil {
+ return pkg
+ }
+
+ name := r.String()
+
+ pkg := types.NewPackage(path, name)
+ r.p.imports[path] = pkg
+
+ return pkg
+}
+
+// @@@ Types
+
+func (r *reader) typ() types.Type {
+ return r.p.typIdx(r.typInfo(), r.dict)
+}
+
+func (r *reader) typInfo() typeInfo {
+ r.Sync(pkgbits.SyncType)
+ if r.Bool() {
+ return typeInfo{idx: pkgbits.Index(r.Len()), derived: true}
+ }
+ return typeInfo{idx: r.Reloc(pkgbits.RelocType), derived: false}
+}
+
+func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) types.Type {
+ idx := info.idx
+ var where *types.Type
+ if info.derived {
+ where = &dict.derivedTypes[idx]
+ idx = dict.derived[idx].idx
+ } else {
+ where = &pr.typs[idx]
+ }
+
+ if typ := *where; typ != nil {
+ return typ
+ }
+
+ var typ types.Type
+ {
+ r := pr.tempReader(pkgbits.RelocType, idx, pkgbits.SyncTypeIdx)
+ r.dict = dict
+
+ typ = r.doTyp()
+ assert(typ != nil)
+ pr.retireReader(r)
+ }
+ // See comment in pkgReader.typIdx explaining how this happens.
+ if prev := *where; prev != nil {
+ return prev
+ }
+
+ *where = typ
+ return typ
+}
+
+func (r *reader) doTyp() (res types.Type) {
+ switch tag := pkgbits.CodeType(r.Code(pkgbits.SyncType)); tag {
+ default:
+ errorf("unhandled type tag: %v", tag)
+ panic("unreachable")
+
+ case pkgbits.TypeBasic:
+ return types.Typ[r.Len()]
+
+ case pkgbits.TypeNamed:
+ obj, targs := r.obj()
+ name := obj.(*types.TypeName)
+ if len(targs) != 0 {
+ t, _ := types.Instantiate(r.p.ctxt, name.Type(), targs, false)
+ return t
+ }
+ return name.Type()
+
+ case pkgbits.TypeTypeParam:
+ return r.dict.tparams[r.Len()]
+
+ case pkgbits.TypeArray:
+ len := int64(r.Uint64())
+ return types.NewArray(r.typ(), len)
+ case pkgbits.TypeChan:
+ dir := types.ChanDir(r.Len())
+ return types.NewChan(dir, r.typ())
+ case pkgbits.TypeMap:
+ return types.NewMap(r.typ(), r.typ())
+ case pkgbits.TypePointer:
+ return types.NewPointer(r.typ())
+ case pkgbits.TypeSignature:
+ return r.signature(nil, nil, nil)
+ case pkgbits.TypeSlice:
+ return types.NewSlice(r.typ())
+ case pkgbits.TypeStruct:
+ return r.structType()
+ case pkgbits.TypeInterface:
+ return r.interfaceType()
+ case pkgbits.TypeUnion:
+ return r.unionType()
+ }
+}
+
+func (r *reader) structType() *types.Struct {
+ fields := make([]*types.Var, r.Len())
+ var tags []string
+ for i := range fields {
+ pos := r.pos()
+ pkg, name := r.selector()
+ ftyp := r.typ()
+ tag := r.String()
+ embedded := r.Bool()
+
+ fields[i] = types.NewField(pos, pkg, name, ftyp, embedded)
+ if tag != "" {
+ for len(tags) < i {
+ tags = append(tags, "")
+ }
+ tags = append(tags, tag)
+ }
+ }
+ return types.NewStruct(fields, tags)
+}
+
+func (r *reader) unionType() *types.Union {
+ terms := make([]*types.Term, r.Len())
+ for i := range terms {
+ terms[i] = types.NewTerm(r.Bool(), r.typ())
+ }
+ return types.NewUnion(terms)
+}
+
+func (r *reader) interfaceType() *types.Interface {
+ methods := make([]*types.Func, r.Len())
+ embeddeds := make([]types.Type, r.Len())
+ implicit := len(methods) == 0 && len(embeddeds) == 1 && r.Bool()
+
+ for i := range methods {
+ pos := r.pos()
+ pkg, name := r.selector()
+ mtyp := r.signature(nil, nil, nil)
+ methods[i] = types.NewFunc(pos, pkg, name, mtyp)
+ }
+
+ for i := range embeddeds {
+ embeddeds[i] = r.typ()
+ }
+
+ iface := types.NewInterfaceType(methods, embeddeds)
+ if implicit {
+ iface.MarkImplicit()
+ }
+
+ // We need to call iface.Complete(), but if there are any embedded
+ // defined types, then we may not have set their underlying
+ // interface type yet. So we need to defer calling Complete until
+ // after we've called SetUnderlying everywhere.
+ //
+ // TODO(mdempsky): After CL 424876 lands, it should be safe to call
+ // iface.Complete() immediately.
+ r.p.ifaces = append(r.p.ifaces, iface)
+
+ return iface
+}
+
+func (r *reader) signature(recv *types.Var, rtparams, tparams []*types.TypeParam) *types.Signature {
+ r.Sync(pkgbits.SyncSignature)
+
+ params := r.params()
+ results := r.params()
+ variadic := r.Bool()
+
+ return types.NewSignatureType(recv, rtparams, tparams, params, results, variadic)
+}
+
+func (r *reader) params() *types.Tuple {
+ r.Sync(pkgbits.SyncParams)
+
+ params := make([]*types.Var, r.Len())
+ for i := range params {
+ params[i] = r.param()
+ }
+
+ return types.NewTuple(params...)
+}
+
+func (r *reader) param() *types.Var {
+ r.Sync(pkgbits.SyncParam)
+
+ pos := r.pos()
+ pkg, name := r.localIdent()
+ typ := r.typ()
+
+ return types.NewParam(pos, pkg, name, typ)
+}
+
+// @@@ Objects
+
+func (r *reader) obj() (types.Object, []types.Type) {
+ r.Sync(pkgbits.SyncObject)
+
+ assert(!r.Bool())
+
+ pkg, name := r.p.objIdx(r.Reloc(pkgbits.RelocObj))
+ obj := pkgScope(pkg).Lookup(name)
+
+ targs := make([]types.Type, r.Len())
+ for i := range targs {
+ targs[i] = r.typ()
+ }
+
+ return obj, targs
+}
+
+func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) {
+
+ var objPkg *types.Package
+ var objName string
+ var tag pkgbits.CodeObj
+ {
+ rname := pr.tempReader(pkgbits.RelocName, idx, pkgbits.SyncObject1)
+
+ objPkg, objName = rname.qualifiedIdent()
+ assert(objName != "")
+
+ tag = pkgbits.CodeObj(rname.Code(pkgbits.SyncCodeObj))
+ pr.retireReader(rname)
+ }
+
+ if tag == pkgbits.ObjStub {
+ assert(objPkg == nil || objPkg == types.Unsafe)
+ return objPkg, objName
+ }
+
+ // Ignore local types promoted to global scope (#55110).
+ if _, suffix := splitVargenSuffix(objName); suffix != "" {
+ return objPkg, objName
+ }
+
+ if objPkg.Scope().Lookup(objName) == nil {
+ dict := pr.objDictIdx(idx)
+
+ r := pr.newReader(pkgbits.RelocObj, idx, pkgbits.SyncObject1)
+ r.dict = dict
+
+ declare := func(obj types.Object) {
+ objPkg.Scope().Insert(obj)
+ }
+
+ switch tag {
+ default:
+ panic("weird")
+
+ case pkgbits.ObjAlias:
+ pos := r.pos()
+ typ := r.typ()
+ declare(types.NewTypeName(pos, objPkg, objName, typ))
+
+ case pkgbits.ObjConst:
+ pos := r.pos()
+ typ := r.typ()
+ val := r.Value()
+ declare(types.NewConst(pos, objPkg, objName, typ, val))
+
+ case pkgbits.ObjFunc:
+ pos := r.pos()
+ tparams := r.typeParamNames()
+ sig := r.signature(nil, nil, tparams)
+ declare(types.NewFunc(pos, objPkg, objName, sig))
+
+ case pkgbits.ObjType:
+ pos := r.pos()
+
+ obj := types.NewTypeName(pos, objPkg, objName, nil)
+ named := types.NewNamed(obj, nil, nil)
+ declare(obj)
+
+ named.SetTypeParams(r.typeParamNames())
+
+ underlying := r.typ().Underlying()
+
+ // If the underlying type is an interface, we need to
+ // duplicate its methods so we can replace the receiver
+ // parameter's type (#49906).
+ if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 {
+ methods := make([]*types.Func, iface.NumExplicitMethods())
+ for i := range methods {
+ fn := iface.ExplicitMethod(i)
+ sig := fn.Type().(*types.Signature)
+
+ recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
+ methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignature(recv, sig.Params(), sig.Results(), sig.Variadic()))
+ }
+
+ embeds := make([]types.Type, iface.NumEmbeddeds())
+ for i := range embeds {
+ embeds[i] = iface.EmbeddedType(i)
+ }
+
+ newIface := types.NewInterfaceType(methods, embeds)
+ r.p.ifaces = append(r.p.ifaces, newIface)
+ underlying = newIface
+ }
+
+ named.SetUnderlying(underlying)
+
+ for i, n := 0, r.Len(); i < n; i++ {
+ named.AddMethod(r.method())
+ }
+
+ case pkgbits.ObjVar:
+ pos := r.pos()
+ typ := r.typ()
+ declare(types.NewVar(pos, objPkg, objName, typ))
+ }
+ }
+
+ return objPkg, objName
+}
+
+func (pr *pkgReader) objDictIdx(idx pkgbits.Index) *readerDict {
+
+ var dict readerDict
+
+ {
+ r := pr.tempReader(pkgbits.RelocObjDict, idx, pkgbits.SyncObject1)
+ if implicits := r.Len(); implicits != 0 {
+ errorf("unexpected object with %v implicit type parameter(s)", implicits)
+ }
+
+ dict.bounds = make([]typeInfo, r.Len())
+ for i := range dict.bounds {
+ dict.bounds[i] = r.typInfo()
+ }
+
+ dict.derived = make([]derivedInfo, r.Len())
+ dict.derivedTypes = make([]types.Type, len(dict.derived))
+ for i := range dict.derived {
+ dict.derived[i] = derivedInfo{r.Reloc(pkgbits.RelocType), r.Bool()}
+ }
+
+ pr.retireReader(r)
+ }
+ // function references follow, but reader doesn't need those
+
+ return &dict
+}
+
+func (r *reader) typeParamNames() []*types.TypeParam {
+ r.Sync(pkgbits.SyncTypeParamNames)
+
+ // Note: This code assumes it only processes objects without
+ // implement type parameters. This is currently fine, because
+ // reader is only used to read in exported declarations, which are
+ // always package scoped.
+
+ if len(r.dict.bounds) == 0 {
+ return nil
+ }
+
+ // Careful: Type parameter lists may have cycles. To allow for this,
+ // we construct the type parameter list in two passes: first we
+ // create all the TypeNames and TypeParams, then we construct and
+ // set the bound type.
+
+ r.dict.tparams = make([]*types.TypeParam, len(r.dict.bounds))
+ for i := range r.dict.bounds {
+ pos := r.pos()
+ pkg, name := r.localIdent()
+
+ tname := types.NewTypeName(pos, pkg, name, nil)
+ r.dict.tparams[i] = types.NewTypeParam(tname, nil)
+ }
+
+ typs := make([]types.Type, len(r.dict.bounds))
+ for i, bound := range r.dict.bounds {
+ typs[i] = r.p.typIdx(bound, r.dict)
+ }
+
+ // TODO(mdempsky): This is subtle, elaborate further.
+ //
+ // We have to save tparams outside of the closure, because
+ // typeParamNames() can be called multiple times with the same
+ // dictionary instance.
+ //
+ // Also, this needs to happen later to make sure SetUnderlying has
+ // been called.
+ //
+ // TODO(mdempsky): Is it safe to have a single "later" slice or do
+ // we need to have multiple passes? See comments on CL 386002 and
+ // go.dev/issue/52104.
+ tparams := r.dict.tparams
+ r.p.later(func() {
+ for i, typ := range typs {
+ tparams[i].SetConstraint(typ)
+ }
+ })
+
+ return r.dict.tparams
+}
+
+func (r *reader) method() *types.Func {
+ r.Sync(pkgbits.SyncMethod)
+ pos := r.pos()
+ pkg, name := r.selector()
+
+ rparams := r.typeParamNames()
+ sig := r.signature(r.param(), rparams, nil)
+
+ _ = r.pos() // TODO(mdempsky): Remove; this is a hacker for linker.go.
+ return types.NewFunc(pos, pkg, name, sig)
+}
+
+func (r *reader) qualifiedIdent() (*types.Package, string) { return r.ident(pkgbits.SyncSym) }
+func (r *reader) localIdent() (*types.Package, string) { return r.ident(pkgbits.SyncLocalIdent) }
+func (r *reader) selector() (*types.Package, string) { return r.ident(pkgbits.SyncSelector) }
+
+func (r *reader) ident(marker pkgbits.SyncMarker) (*types.Package, string) {
+ r.Sync(marker)
+ return r.pkg(), r.String()
+}
+
+// pkgScope returns pkg.Scope().
+// If pkg is nil, it returns types.Universe instead.
+//
+// TODO(mdempsky): Remove after x/tools can depend on Go 1.19.
+func pkgScope(pkg *types.Package) *types.Scope {
+ if pkg != nil {
+ return pkg.Scope()
+ }
+ return types.Universe
+}
diff --git a/src/go/internal/srcimporter/srcimporter.go b/src/go/internal/srcimporter/srcimporter.go
new file mode 100644
index 0000000..c964274
--- /dev/null
+++ b/src/go/internal/srcimporter/srcimporter.go
@@ -0,0 +1,269 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package srcimporter implements importing directly
+// from source files rather than installed packages.
+package srcimporter // import "go/internal/srcimporter"
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync"
+ _ "unsafe" // for go:linkname
+)
+
+// An Importer provides the context for importing packages from source code.
+type Importer struct {
+ ctxt *build.Context
+ fset *token.FileSet
+ sizes types.Sizes
+ packages map[string]*types.Package
+}
+
+// New returns a new Importer for the given context, file set, and map
+// of packages. The context is used to resolve import paths to package paths,
+// and identifying the files belonging to the package. If the context provides
+// non-nil file system functions, they are used instead of the regular package
+// os functions. The file set is used to track position information of package
+// files; and imported packages are added to the packages map.
+func New(ctxt *build.Context, fset *token.FileSet, packages map[string]*types.Package) *Importer {
+ return &Importer{
+ ctxt: ctxt,
+ fset: fset,
+ sizes: types.SizesFor(ctxt.Compiler, ctxt.GOARCH), // uses go/types default if GOARCH not found
+ packages: packages,
+ }
+}
+
+// Importing is a sentinel taking the place in Importer.packages
+// for a package that is in the process of being imported.
+var importing types.Package
+
+// Import(path) is a shortcut for ImportFrom(path, ".", 0).
+func (p *Importer) Import(path string) (*types.Package, error) {
+ return p.ImportFrom(path, ".", 0) // use "." rather than "" (see issue #24441)
+}
+
+// ImportFrom imports the package with the given import path resolved from the given srcDir,
+// adds the new package to the set of packages maintained by the importer, and returns the
+// package. Package path resolution and file system operations are controlled by the context
+// maintained with the importer. The import mode must be zero but is otherwise ignored.
+// Packages that are not comprised entirely of pure Go files may fail to import because the
+// type checker may not be able to determine all exported entities (e.g. due to cgo dependencies).
+func (p *Importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) {
+ if mode != 0 {
+ panic("non-zero import mode")
+ }
+
+ if abs, err := p.absPath(srcDir); err == nil { // see issue #14282
+ srcDir = abs
+ }
+ bp, err := p.ctxt.Import(path, srcDir, 0)
+ if err != nil {
+ return nil, err // err may be *build.NoGoError - return as is
+ }
+
+ // package unsafe is known to the type checker
+ if bp.ImportPath == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ // no need to re-import if the package was imported completely before
+ pkg := p.packages[bp.ImportPath]
+ if pkg != nil {
+ if pkg == &importing {
+ return nil, fmt.Errorf("import cycle through package %q", bp.ImportPath)
+ }
+ if !pkg.Complete() {
+ // Package exists but is not complete - we cannot handle this
+ // at the moment since the source importer replaces the package
+ // wholesale rather than augmenting it (see #19337 for details).
+ // Return incomplete package with error (see #16088).
+ return pkg, fmt.Errorf("reimported partially imported package %q", bp.ImportPath)
+ }
+ return pkg, nil
+ }
+
+ p.packages[bp.ImportPath] = &importing
+ defer func() {
+ // clean up in case of error
+ // TODO(gri) Eventually we may want to leave a (possibly empty)
+ // package in the map in all cases (and use that package to
+ // identify cycles). See also issue 16088.
+ if p.packages[bp.ImportPath] == &importing {
+ p.packages[bp.ImportPath] = nil
+ }
+ }()
+
+ var filenames []string
+ filenames = append(filenames, bp.GoFiles...)
+ filenames = append(filenames, bp.CgoFiles...)
+
+ files, err := p.parseFiles(bp.Dir, filenames)
+ if err != nil {
+ return nil, err
+ }
+
+ // type-check package files
+ var firstHardErr error
+ conf := types.Config{
+ IgnoreFuncBodies: true,
+ // continue type-checking after the first error
+ Error: func(err error) {
+ if firstHardErr == nil && !err.(types.Error).Soft {
+ firstHardErr = err
+ }
+ },
+ Importer: p,
+ Sizes: p.sizes,
+ }
+ if len(bp.CgoFiles) > 0 {
+ if p.ctxt.OpenFile != nil {
+ // cgo, gcc, pkg-config, etc. do not support
+ // build.Context's VFS.
+ conf.FakeImportC = true
+ } else {
+ setUsesCgo(&conf)
+ file, err := p.cgo(bp)
+ if err != nil {
+ return nil, fmt.Errorf("error processing cgo for package %q: %w", bp.ImportPath, err)
+ }
+ files = append(files, file)
+ }
+ }
+
+ pkg, err = conf.Check(bp.ImportPath, p.fset, files, nil)
+ if err != nil {
+ // If there was a hard error it is possibly unsafe
+ // to use the package as it may not be fully populated.
+ // Do not return it (see also #20837, #20855).
+ if firstHardErr != nil {
+ pkg = nil
+ err = firstHardErr // give preference to first hard error over any soft error
+ }
+ return pkg, fmt.Errorf("type-checking package %q failed (%v)", bp.ImportPath, err)
+ }
+ if firstHardErr != nil {
+ // this can only happen if we have a bug in go/types
+ panic("package is not safe yet no error was returned")
+ }
+
+ p.packages[bp.ImportPath] = pkg
+ return pkg, nil
+}
+
+func (p *Importer) parseFiles(dir string, filenames []string) ([]*ast.File, error) {
+ // use build.Context's OpenFile if there is one
+ open := p.ctxt.OpenFile
+ if open == nil {
+ open = func(name string) (io.ReadCloser, error) { return os.Open(name) }
+ }
+
+ files := make([]*ast.File, len(filenames))
+ errors := make([]error, len(filenames))
+
+ var wg sync.WaitGroup
+ wg.Add(len(filenames))
+ for i, filename := range filenames {
+ go func(i int, filepath string) {
+ defer wg.Done()
+ src, err := open(filepath)
+ if err != nil {
+ errors[i] = err // open provides operation and filename in error
+ return
+ }
+ files[i], errors[i] = parser.ParseFile(p.fset, filepath, src, parser.SkipObjectResolution)
+ src.Close() // ignore Close error - parsing may have succeeded which is all we need
+ }(i, p.joinPath(dir, filename))
+ }
+ wg.Wait()
+
+ // if there are errors, return the first one for deterministic results
+ for _, err := range errors {
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return files, nil
+}
+
+func (p *Importer) cgo(bp *build.Package) (*ast.File, error) {
+ tmpdir, err := os.MkdirTemp("", "srcimporter")
+ if err != nil {
+ return nil, err
+ }
+ defer os.RemoveAll(tmpdir)
+
+ goCmd := "go"
+ if p.ctxt.GOROOT != "" {
+ goCmd = filepath.Join(p.ctxt.GOROOT, "bin", "go")
+ }
+ args := []string{goCmd, "tool", "cgo", "-objdir", tmpdir}
+ if bp.Goroot {
+ switch bp.ImportPath {
+ case "runtime/cgo":
+ args = append(args, "-import_runtime_cgo=false", "-import_syscall=false")
+ case "runtime/race":
+ args = append(args, "-import_syscall=false")
+ }
+ }
+ args = append(args, "--")
+ args = append(args, strings.Fields(os.Getenv("CGO_CPPFLAGS"))...)
+ args = append(args, bp.CgoCPPFLAGS...)
+ if len(bp.CgoPkgConfig) > 0 {
+ cmd := exec.Command("pkg-config", append([]string{"--cflags"}, bp.CgoPkgConfig...)...)
+ out, err := cmd.Output()
+ if err != nil {
+ return nil, fmt.Errorf("pkg-config --cflags: %w", err)
+ }
+ args = append(args, strings.Fields(string(out))...)
+ }
+ args = append(args, "-I", tmpdir)
+ args = append(args, strings.Fields(os.Getenv("CGO_CFLAGS"))...)
+ args = append(args, bp.CgoCFLAGS...)
+ args = append(args, bp.CgoFiles...)
+
+ cmd := exec.Command(args[0], args[1:]...)
+ cmd.Dir = bp.Dir
+ if err := cmd.Run(); err != nil {
+ return nil, fmt.Errorf("go tool cgo: %w", err)
+ }
+
+ return parser.ParseFile(p.fset, filepath.Join(tmpdir, "_cgo_gotypes.go"), nil, parser.SkipObjectResolution)
+}
+
+// context-controlled file system operations
+
+func (p *Importer) absPath(path string) (string, error) {
+ // TODO(gri) This should be using p.ctxt.AbsPath which doesn't
+ // exist but probably should. See also issue #14282.
+ return filepath.Abs(path)
+}
+
+func (p *Importer) isAbsPath(path string) bool {
+ if f := p.ctxt.IsAbsPath; f != nil {
+ return f(path)
+ }
+ return filepath.IsAbs(path)
+}
+
+func (p *Importer) joinPath(elem ...string) string {
+ if f := p.ctxt.JoinPath; f != nil {
+ return f(elem...)
+ }
+ return filepath.Join(elem...)
+}
+
+//go:linkname setUsesCgo go/types.srcimporter_setUsesCgo
+func setUsesCgo(conf *types.Config)
diff --git a/src/go/internal/srcimporter/srcimporter_test.go b/src/go/internal/srcimporter/srcimporter_test.go
new file mode 100644
index 0000000..61ae0c1
--- /dev/null
+++ b/src/go/internal/srcimporter/srcimporter_test.go
@@ -0,0 +1,252 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package srcimporter
+
+import (
+ "flag"
+ "go/build"
+ "go/token"
+ "go/types"
+ "internal/testenv"
+ "os"
+ "path"
+ "path/filepath"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ build.Default.GOROOT = testenv.GOROOT(nil)
+ os.Exit(m.Run())
+}
+
+const maxTime = 2 * time.Second
+
+var importer = New(&build.Default, token.NewFileSet(), make(map[string]*types.Package))
+
+func doImport(t *testing.T, path, srcDir string) {
+ t0 := time.Now()
+ if _, err := importer.ImportFrom(path, srcDir, 0); err != nil {
+ // don't report an error if there's no buildable Go files
+ if _, nogo := err.(*build.NoGoError); !nogo {
+ t.Errorf("import %q failed (%v)", path, err)
+ }
+ return
+ }
+ t.Logf("import %q: %v", path, time.Since(t0))
+}
+
+// walkDir imports the all the packages with the given path
+// prefix recursively. It returns the number of packages
+// imported and whether importing was aborted because time
+// has passed endTime.
+func walkDir(t *testing.T, path string, endTime time.Time) (int, bool) {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return 0, true
+ }
+
+ // ignore fake packages and testdata directories
+ if path == "builtin" || path == "unsafe" || strings.HasSuffix(path, "testdata") {
+ return 0, false
+ }
+
+ list, err := os.ReadDir(filepath.Join(testenv.GOROOT(t), "src", path))
+ if err != nil {
+ t.Fatalf("walkDir %s failed (%v)", path, err)
+ }
+
+ nimports := 0
+ hasGoFiles := false
+ for _, f := range list {
+ if f.IsDir() {
+ n, abort := walkDir(t, filepath.Join(path, f.Name()), endTime)
+ nimports += n
+ if abort {
+ return nimports, true
+ }
+ } else if strings.HasSuffix(f.Name(), ".go") {
+ hasGoFiles = true
+ }
+ }
+
+ if hasGoFiles {
+ doImport(t, path, "")
+ nimports++
+ }
+
+ return nimports, false
+}
+
+func TestImportStdLib(t *testing.T) {
+ if !testenv.HasSrc() {
+ t.Skip("no source code available")
+ }
+
+ if testing.Short() && testenv.Builder() == "" {
+ t.Skip("skipping in -short mode")
+ }
+ dt := maxTime
+ nimports, _ := walkDir(t, "", time.Now().Add(dt)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ {"flag.Bool", "func Bool(name string, value bool, usage string) *bool"},
+ {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"}, // go/types.gcCompatibilityMode is off => interface not flattened
+ {"math.Pi", "const Pi untyped float"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ {"math/big.Int", "type Int struct{neg bool; abs nat}"},
+ {"golang.org/x/text/unicode/norm.MaxSegmentSize", "const MaxSegmentSize untyped int"},
+}
+
+func TestImportedTypes(t *testing.T) {
+ if !testenv.HasSrc() {
+ t.Skip("no source code available")
+ }
+
+ for _, test := range importedObjectTests {
+ i := strings.LastIndex(test.name, ".")
+ if i < 0 {
+ t.Fatal("invalid test data format")
+ }
+ importPath := test.name[:i]
+ objName := test.name[i+1:]
+
+ pkg, err := importer.ImportFrom(importPath, ".", 0)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+
+ if named, _ := obj.Type().(*types.Named); named != nil {
+ verifyInterfaceMethodRecvs(t, named, 0)
+ }
+ }
+}
+
+// verifyInterfaceMethodRecvs verifies that method receiver types
+// are named if the methods belong to a named interface type.
+func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
+ // avoid endless recursion in case of an embedding bug that lead to a cycle
+ if level > 10 {
+ t.Errorf("%s: embeds itself", named)
+ return
+ }
+
+ iface, _ := named.Underlying().(*types.Interface)
+ if iface == nil {
+ return // not an interface
+ }
+
+ // check explicitly declared methods
+ for i := 0; i < iface.NumExplicitMethods(); i++ {
+ m := iface.ExplicitMethod(i)
+ recv := m.Type().(*types.Signature).Recv()
+ if recv == nil {
+ t.Errorf("%s: missing receiver type", m)
+ continue
+ }
+ if recv.Type() != named {
+ t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
+ }
+ }
+
+ // check embedded interfaces (they are named, too)
+ for i := 0; i < iface.NumEmbeddeds(); i++ {
+ // embedding of interfaces cannot have cycles; recursion will terminate
+ verifyInterfaceMethodRecvs(t, iface.Embedded(i), level+1)
+ }
+}
+
+func TestReimport(t *testing.T) {
+ if !testenv.HasSrc() {
+ t.Skip("no source code available")
+ }
+
+ // Reimporting a partially imported (incomplete) package is not supported (see issue #19337).
+ // Make sure we recognize the situation and report an error.
+
+ mathPkg := types.NewPackage("math", "math") // incomplete package
+ importer := New(&build.Default, token.NewFileSet(), map[string]*types.Package{mathPkg.Path(): mathPkg})
+ _, err := importer.ImportFrom("math", ".", 0)
+ if err == nil || !strings.HasPrefix(err.Error(), "reimport") {
+ t.Errorf("got %v; want reimport error", err)
+ }
+}
+
+func TestIssue20855(t *testing.T) {
+ if !testenv.HasSrc() {
+ t.Skip("no source code available")
+ }
+
+ pkg, err := importer.ImportFrom("go/internal/srcimporter/testdata/issue20855", ".", 0)
+ if err == nil || !strings.Contains(err.Error(), "missing function body") {
+ t.Fatalf("got unexpected or no error: %v", err)
+ }
+ if pkg == nil {
+ t.Error("got no package despite no hard errors")
+ }
+}
+
+func testImportPath(t *testing.T, pkgPath string) {
+ if !testenv.HasSrc() {
+ t.Skip("no source code available")
+ }
+
+ pkgName := path.Base(pkgPath)
+
+ pkg, err := importer.Import(pkgPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if pkg.Name() != pkgName {
+ t.Errorf("got %q; want %q", pkg.Name(), pkgName)
+ }
+
+ if pkg.Path() != pkgPath {
+ t.Errorf("got %q; want %q", pkg.Path(), pkgPath)
+ }
+}
+
+// TestIssue23092 tests relative imports.
+func TestIssue23092(t *testing.T) {
+ testImportPath(t, "./testdata/issue23092")
+}
+
+// TestIssue24392 tests imports against a path containing 'testdata'.
+func TestIssue24392(t *testing.T) {
+ testImportPath(t, "go/internal/srcimporter/testdata/issue24392")
+}
+
+func TestCgo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ buildCtx := build.Default
+ importer := New(&buildCtx, token.NewFileSet(), make(map[string]*types.Package))
+ _, err := importer.ImportFrom("cmd/cgo/internal/test", buildCtx.Dir, 0)
+ if err != nil {
+ t.Fatalf("Import failed: %v", err)
+ }
+}
diff --git a/src/go/internal/srcimporter/testdata/issue20855/issue20855.go b/src/go/internal/srcimporter/testdata/issue20855/issue20855.go
new file mode 100644
index 0000000..d55448b
--- /dev/null
+++ b/src/go/internal/srcimporter/testdata/issue20855/issue20855.go
@@ -0,0 +1,7 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue20855
+
+func init() // "missing function body" is a soft error
diff --git a/src/go/internal/srcimporter/testdata/issue23092/issue23092.go b/src/go/internal/srcimporter/testdata/issue23092/issue23092.go
new file mode 100644
index 0000000..608698b
--- /dev/null
+++ b/src/go/internal/srcimporter/testdata/issue23092/issue23092.go
@@ -0,0 +1,5 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue23092
diff --git a/src/go/internal/srcimporter/testdata/issue24392/issue24392.go b/src/go/internal/srcimporter/testdata/issue24392/issue24392.go
new file mode 100644
index 0000000..8ad5221
--- /dev/null
+++ b/src/go/internal/srcimporter/testdata/issue24392/issue24392.go
@@ -0,0 +1,5 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue24392
diff --git a/src/go/internal/typeparams/typeparams.go b/src/go/internal/typeparams/typeparams.go
new file mode 100644
index 0000000..3f84f2f
--- /dev/null
+++ b/src/go/internal/typeparams/typeparams.go
@@ -0,0 +1,54 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package typeparams
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func PackIndexExpr(x ast.Expr, lbrack token.Pos, exprs []ast.Expr, rbrack token.Pos) ast.Expr {
+ switch len(exprs) {
+ case 0:
+ panic("internal error: PackIndexExpr with empty expr slice")
+ case 1:
+ return &ast.IndexExpr{
+ X: x,
+ Lbrack: lbrack,
+ Index: exprs[0],
+ Rbrack: rbrack,
+ }
+ default:
+ return &ast.IndexListExpr{
+ X: x,
+ Lbrack: lbrack,
+ Indices: exprs,
+ Rbrack: rbrack,
+ }
+ }
+}
+
+// IndexExpr wraps an ast.IndexExpr or ast.IndexListExpr.
+//
+// Orig holds the original ast.Expr from which this IndexExpr was derived.
+type IndexExpr struct {
+ Orig ast.Expr // the wrapped expr, which may be distinct from the IndexListExpr below.
+ *ast.IndexListExpr
+}
+
+func UnpackIndexExpr(n ast.Node) *IndexExpr {
+ switch e := n.(type) {
+ case *ast.IndexExpr:
+ return &IndexExpr{e, &ast.IndexListExpr{
+ X: e.X,
+ Lbrack: e.Lbrack,
+ Indices: []ast.Expr{e.Index},
+ Rbrack: e.Rbrack,
+ }}
+ case *ast.IndexListExpr:
+ return &IndexExpr{e, e}
+ }
+ return nil
+}