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.go1284
-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.goxbin0 -> 824 bytes
-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.goxbin0 -> 7977 bytes
-rw-r--r--src/go/internal/gccgoimporter/testdata/unicode.goxbin0 -> 7945 bytes
-rw-r--r--src/go/internal/gccgoimporter/testdata/v1reflect.goxbin0 -> 10872 bytes
-rw-r--r--src/go/internal/gcimporter/exportdata.go91
-rw-r--r--src/go/internal/gcimporter/gcimporter.go171
-rw-r--r--src/go/internal/gcimporter/gcimporter_test.go619
-rw-r--r--src/go/internal/gcimporter/iimport.go607
-rw-r--r--src/go/internal/gcimporter/support.go130
-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.go88
-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/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/srcimporter/srcimporter.go265
-rw-r--r--src/go/internal/srcimporter/srcimporter_test.go254
-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/common.go13
-rw-r--r--src/go/internal/typeparams/notypeparams.go40
-rw-r--r--src/go/internal/typeparams/typeparams.go69
67 files changed, 5207 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..443aa26
--- /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.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 {
+ 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] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) {
+ // 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..e90a3cc
--- /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"
+ exec "internal/execabs"
+ "os"
+ "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..8a7ee1a
--- /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 := exec.Command(gpath, "--version").CombinedOutput()
+ if err != nil {
+ t.Logf("%s", verout)
+ t.Fatal(err)
+ }
+ vers := regexp.MustCompile(`([0-9]+)\.([0-9]+)`).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 := exec.Command(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 = exec.Command("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..1b1d07d
--- /dev/null
+++ b/src/go/internal/gccgoimporter/parser.go
@@ -0,0 +1,1284 @@
+// 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"
+ "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 interface{}) {
+ 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 ...interface{}) {
+ 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 buf bytes.Buffer
+ buf.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() {
+ buf.WriteRune(ch)
+ p.scanner.Next()
+ }
+ p.next()
+ return buf.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 []interface{}) {
+ 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 []interface{}) 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.NewSignature(receiver, 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 []interface{}) 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 []interface{}) 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 []interface{}) 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 []interface{}) 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 []interface{}) *types.Signature {
+ t := new(types.Signature)
+ p.update(t, nlist)
+
+ params, isVariadic := p.parseParamList(pkg)
+ results := p.parseResultList(pkg)
+
+ *t = *types.NewSignature(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 []interface{}) 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 []interface{}) 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 []interface{}) 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 ...interface{}) types.Type {
+ p.expect('<')
+ t, _ := p.parseTypeAfterAngle(pkg, n...)
+ return t
+}
+
+// (*parser).Type after reading the "<".
+func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (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 ...interface{}) (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 []interface{}) {
+ 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..1db8156
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/escapeinfo.gox
Binary files differ
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..80c2dbc
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/time.gox
Binary files differ
diff --git a/src/go/internal/gccgoimporter/testdata/unicode.gox b/src/go/internal/gccgoimporter/testdata/unicode.gox
new file mode 100644
index 0000000..e70e539
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/unicode.gox
Binary files differ
diff --git a/src/go/internal/gccgoimporter/testdata/v1reflect.gox b/src/go/internal/gccgoimporter/testdata/v1reflect.gox
new file mode 100644
index 0000000..ea46841
--- /dev/null
+++ b/src/go/internal/gccgoimporter/testdata/v1reflect.gox
Binary files differ
diff --git a/src/go/internal/gcimporter/exportdata.go b/src/go/internal/gcimporter/exportdata.go
new file mode 100644
index 0000000..c12e459
--- /dev/null
+++ b/src/go/internal/gcimporter/exportdata.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 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, 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, _, 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
+ }
+
+ // 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
+ }
+ }
+ 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..73cf633
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -0,0 +1,171 @@
+// 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"
+ "fmt"
+ "go/build"
+ "go/token"
+ "go/types"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".o"}
+
+// 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 == "" {
+ id = path // make sure we have an id to print in error message
+ return
+ }
+ 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)
+ }
+ }
+
+ // 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()
+
+ var hdr string
+ buf := bufio.NewReader(rc)
+ if hdr, err = FindExportData(buf); 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
+ exportFormat, err = buf.ReadByte()
+
+ // The indexed export format starts with an 'i'; the older
+ // binary export format starts with a 'c', 'd', or 'v'
+ // (from "version"). Select appropriate importer.
+ if err == nil && exportFormat == 'i' {
+ pkg, err = iImportData(fset, packages, buf, id)
+ } else {
+ 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..3c76aaf
--- /dev/null
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -0,0 +1,619 @@
+// 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
+
+import (
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "go/token"
+ "go/types"
+)
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+// compile runs the compiler on filename, with dirname as the working directory,
+// and writes the output file to outdirname.
+func compile(t *testing.T, dirname, filename, outdirname string) string {
+ // filename must end with ".go"
+ if !strings.HasSuffix(filename, ".go") {
+ t.Fatalf("filename doesn't end in .go: %s", filename)
+ }
+ basename := filepath.Base(filename)
+ outname := filepath.Join(outdirname, basename[:len(basename)-2]+"o")
+ cmd := exec.Command(testenv.GoToolPath(t), "tool", "compile", "-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
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := os.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // try extensions
+ for _, ext := range pkgExts {
+ if strings.HasSuffix(f.Name(), ext) {
+ name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
+ if testPath(t, filepath.Join(dir, name), dir) != nil {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+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)
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", "exports.go", filepath.Join(tmpdir, "testdata"))
+
+ if pkg := testPath(t, "./testdata/exports", tmpdir); pkg != nil {
+ // The package's Imports list must include all packages
+ // explicitly imported by exports.go, plus all packages
+ // referenced indirectly via exported objects in exports.go.
+ // With the textual export format, the list may also include
+ // additional packages that are not strictly required for
+ // import processing alone (they are exported to err "on
+ // the safe side").
+ // TODO(gri) update the want list to be precise, now that
+ // the textual export data is gone.
+ got := fmt.Sprint(pkg.Imports())
+ for _, want := range []string{"go/ast", "go/token"} {
+ if !strings.Contains(got, want) {
+ t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
+ }
+ }
+ }
+}
+
+func TestVersionHandling(t *testing.T) {
+ skipSpecialPlatforms(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) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ dt := maxTime
+ if testing.Short() && testenv.Builder() == "" {
+ dt = 10 * time.Millisecond
+ }
+ nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
+ 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 interface{}) interface{}}"},
+ {"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) {
+ skipSpecialPlatforms(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) {
+ skipSpecialPlatforms(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) {
+ skipSpecialPlatforms(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) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ 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)
+ compile(t, testoutdir, bpath, testoutdir)
+
+ // 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 TestIssue13898(t *testing.T) {
+ skipSpecialPlatforms(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) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+
+ compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"))
+
+ // 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) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue15920")
+}
+
+func TestIssue20046(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ // "./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) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue25301")
+}
+
+func TestIssue25596(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ }
+
+ // On windows, we have to set the -D option for the compiler to avoid having a drive
+ // letter and an illegal ':' in the import path - just skip it (see also issue #3483).
+ if runtime.GOOS == "windows" {
+ t.Skip("avoid dealing with relative paths/drive letters on windows")
+ }
+
+ compileAndImportPkg(t, "issue25596")
+}
+
+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.Fatal(err)
+ }
+ return pkg
+}
+
+func compileAndImportPkg(t *testing.T, name string) *types.Package {
+ tmpdir := mktmpdir(t)
+ defer os.RemoveAll(tmpdir)
+ compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"))
+ 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.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..76d47d0
--- /dev/null
+++ b/src/go/internal/gcimporter/iimport.go
@@ -0,0 +1,607 @@
+// 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"
+ "io"
+ "math/big"
+ "sort"
+)
+
+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
+}
+
+const predeclReserved = 32
+
+type itag uint64
+
+const (
+ // Types
+ definedType itag = iota
+ pointerType
+ sliceType
+ arrayType
+ chanType
+ mapType
+ signatureType
+ structType
+ interfaceType
+)
+
+// 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 = 1
+ 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 currentVersion, 0:
+ default:
+ errorf("unknown iexport format version %d", version)
+ }
+
+ sLen := int64(r.uint64())
+ dLen := int64(r.uint64())
+
+ data := make([]byte, sLen+dLen)
+ if _, err := io.ReadFull(r, data); err != nil {
+ errorf("cannot read %d bytes of stringData and declData: %s", len(data), err)
+ }
+ stringData := data[:sLen]
+ declData := data[sLen:]
+
+ p := iimporter{
+ 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),
+
+ fake: fakeFileSet{
+ fset: fset,
+ files: make(map[string]*token.File),
+ },
+ }
+
+ 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)
+ }
+
+ 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 iimporter struct {
+ 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
+
+ fake fakeFileSet
+ interfaceList []*types.Interface
+}
+
+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 && (base == nil || !isInterface(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 base == nil || !isInterface(t) {
+ p.typCache[off] = t
+ }
+ return t
+}
+
+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':
+ sig := r.signature(nil)
+
+ r.declare(types.NewFunc(pos, r.currPkg, name, sig))
+
+ case 'T':
+ // Types can be recursive. We need to setup a stub
+ // declaration before recursing.
+ obj := types.NewTypeName(pos, r.currPkg, name, nil)
+ named := types.NewNamed(obj, nil, nil)
+ r.declare(obj)
+
+ 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()
+ msig := r.signature(recv)
+
+ named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
+ }
+ }
+
+ 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()
+
+ 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)
+
+ 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)
+ methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
+ }
+
+ typ := types.NewInterfaceType(methods, embeddeds)
+ r.p.interfaceList = append(r.p.interfaceList, typ)
+ return typ
+ }
+}
+
+func (r *importReader) kind() itag {
+ return itag(r.uint64())
+}
+
+func (r *importReader) signature(recv *types.Var) *types.Signature {
+ params := r.paramList()
+ results := r.paramList()
+ variadic := params.Len() > 0 && r.bool()
+ return types.NewSignature(recv, params, results, variadic)
+}
+
+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
+}
diff --git a/src/go/internal/gcimporter/support.go b/src/go/internal/gcimporter/support.go
new file mode 100644
index 0000000..b8bb14d
--- /dev/null
+++ b/src/go/internal/gcimporter/support.go
@@ -0,0 +1,130 @@
+// 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"
+ "sync"
+)
+
+func errorf(format string, args ...interface{}) {
+ 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]*token.File
+}
+
+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.
+ const maxlines = 64 * 1024
+ f := s.files[file]
+ if f == nil {
+ f = s.fset.AddFile(file, -1, maxlines)
+ s.files[file] = f
+ // Allocate the fake linebreak indices on first use.
+ // TODO(adonovan): opt: save ~512KB using a more complex scheme?
+ fakeLinesOnce.Do(func() {
+ fakeLines = make([]int, maxlines)
+ for i := range fakeLines {
+ fakeLines[i] = i
+ }
+ })
+ f.SetLines(fakeLines)
+ }
+
+ if line > maxlines {
+ line = 1
+ }
+
+ // Treat the file as if it contained only newlines
+ // and column=1: use the line number as the offset.
+ return f.Pos(line - 1)
+}
+
+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
+ anyType{},
+}
+
+type anyType struct{}
+
+func (t anyType) Underlying() types.Type { return t }
+func (t anyType) String() string { return "any" }
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..8ba3242
--- /dev/null
+++ b/src/go/internal/gcimporter/testdata/exports.go
@@ -0,0 +1,88 @@
+// 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`
+)
+
+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 interface{}
+ 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(...interface{})
+ 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 ...interface{}) (p, q, r chan<- T10)
+
+func (p *T1) M1()
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/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/srcimporter/srcimporter.go b/src/go/internal/srcimporter/srcimporter.go
new file mode 100644
index 0000000..e4225eb
--- /dev/null
+++ b/src/go/internal/srcimporter/srcimporter.go
@@ -0,0 +1,265 @@
+// 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"
+ exec "internal/execabs"
+ "io"
+ "os"
+ "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, 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, 0)
+ 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)
+
+ args := []string{"go", "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.CombinedOutput()
+ if err != nil {
+ return nil, 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, err
+ }
+
+ return parser.ParseFile(p.fset, filepath.Join(tmpdir, "_cgo_gotypes.go"), nil, 0)
+}
+
+// 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..05b12f1
--- /dev/null
+++ b/src/go/internal/srcimporter/srcimporter_test.go
@@ -0,0 +1,254 @@
+// 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"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ if goTool, err := testenv.GoTool(); err == nil {
+ os.Setenv("PATH", filepath.Dir(goTool)+string(os.PathListSeparator)+os.Getenv("PATH"))
+ }
+ 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(runtime.GOROOT(), "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)
+
+ importer := New(&build.Default, token.NewFileSet(), make(map[string]*types.Package))
+ _, err := importer.ImportFrom("./misc/cgo/test", runtime.GOROOT(), 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/common.go b/src/go/internal/typeparams/common.go
new file mode 100644
index 0000000..47b8f7c
--- /dev/null
+++ b/src/go/internal/typeparams/common.go
@@ -0,0 +1,13 @@
+// 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 provides functions to work with type parameter data
+// stored in the AST, while these AST changes are guarded by a build
+// constraint.
+package typeparams
+
+// DisallowParsing is the numeric value of a parsing mode that disallows type
+// parameters. This only matters if the typeparams experiment is active, and
+// may be used for running tests that disallow generics.
+const DisallowParsing = 1 << 30
diff --git a/src/go/internal/typeparams/notypeparams.go b/src/go/internal/typeparams/notypeparams.go
new file mode 100644
index 0000000..2ceafaa
--- /dev/null
+++ b/src/go/internal/typeparams/notypeparams.go
@@ -0,0 +1,40 @@
+// 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.
+
+//go:build !typeparams
+// +build !typeparams
+
+package typeparams
+
+import (
+ "go/ast"
+)
+
+const Enabled = false
+
+func PackExpr(list []ast.Expr) ast.Expr {
+ switch len(list) {
+ case 1:
+ return list[0]
+ default:
+ // The parser should not attempt to pack multiple expressions into an
+ // IndexExpr if type params are disabled.
+ panic("multiple index expressions are unsupported without type params")
+ }
+}
+
+func UnpackExpr(expr ast.Expr) []ast.Expr {
+ return []ast.Expr{expr}
+}
+
+func IsListExpr(n ast.Node) bool {
+ return false
+}
+
+func Get(ast.Node) *ast.FieldList {
+ return nil
+}
+
+func Set(node ast.Node, params *ast.FieldList) {
+}
diff --git a/src/go/internal/typeparams/typeparams.go b/src/go/internal/typeparams/typeparams.go
new file mode 100644
index 0000000..871e95d
--- /dev/null
+++ b/src/go/internal/typeparams/typeparams.go
@@ -0,0 +1,69 @@
+// 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.
+
+//go:build typeparams
+// +build typeparams
+
+package typeparams
+
+import (
+ "fmt"
+ "go/ast"
+)
+
+const Enabled = true
+
+func PackExpr(list []ast.Expr) ast.Expr {
+ switch len(list) {
+ case 0:
+ // Return an empty ListExpr here, rather than nil, as IndexExpr.Index must
+ // never be nil.
+ // TODO(rFindley) would a BadExpr be more appropriate here?
+ return &ast.ListExpr{}
+ case 1:
+ return list[0]
+ default:
+ return &ast.ListExpr{ElemList: list}
+ }
+}
+
+// TODO(gri) Should find a more efficient solution that doesn't
+// require introduction of a new slice for simple
+// expressions.
+func UnpackExpr(x ast.Expr) []ast.Expr {
+ if x, _ := x.(*ast.ListExpr); x != nil {
+ return x.ElemList
+ }
+ if x != nil {
+ return []ast.Expr{x}
+ }
+ return nil
+}
+
+func IsListExpr(n ast.Node) bool {
+ _, ok := n.(*ast.ListExpr)
+ return ok
+}
+
+func Get(n ast.Node) *ast.FieldList {
+ switch n := n.(type) {
+ case *ast.TypeSpec:
+ return n.TParams
+ case *ast.FuncType:
+ return n.TParams
+ default:
+ panic(fmt.Sprintf("node type %T has no type parameters", n))
+ }
+}
+
+func Set(n ast.Node, params *ast.FieldList) {
+ switch n := n.(type) {
+ case *ast.TypeSpec:
+ n.TParams = params
+ case *ast.FuncType:
+ n.TParams = params
+ default:
+ panic(fmt.Sprintf("node type %T has no type parameters", n))
+ }
+}