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