diff options
Diffstat (limited to 'src/go/internal/gccgoimporter')
37 files changed, 2737 insertions, 0 deletions
diff --git a/src/go/internal/gccgoimporter/ar.go b/src/go/internal/gccgoimporter/ar.go new file mode 100644 index 0000000..443aa26 --- /dev/null +++ b/src/go/internal/gccgoimporter/ar.go @@ -0,0 +1,171 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gccgoimporter + +import ( + "bytes" + "debug/elf" + "errors" + "fmt" + "internal/xcoff" + "io" + "strconv" + "strings" +) + +// Magic strings for different archive file formats. +const ( + armag = "!<arch>\n" + armagt = "!<thin>\n" + armagb = "<bigaf>\n" +) + +// Offsets and sizes for fields in a standard archive header. +const ( + arNameOff = 0 + arNameSize = 16 + arDateOff = arNameOff + arNameSize + arDateSize = 12 + arUIDOff = arDateOff + arDateSize + arUIDSize = 6 + arGIDOff = arUIDOff + arUIDSize + arGIDSize = 6 + arModeOff = arGIDOff + arGIDSize + arModeSize = 8 + arSizeOff = arModeOff + arModeSize + arSizeSize = 10 + arFmagOff = arSizeOff + arSizeSize + arFmagSize = 2 + + arHdrSize = arFmagOff + arFmagSize +) + +// The contents of the fmag field of a standard archive header. +const arfmag = "`\n" + +// arExportData takes an archive file and returns a ReadSeeker for the +// export data in that file. This assumes that there is only one +// object in the archive containing export data, which is not quite +// what gccgo does; gccgo concatenates together all the export data +// for all the objects in the file. In practice that case does not arise. +func arExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { + if _, err := archive.Seek(0, io.SeekStart); err != nil { + return nil, err + } + + var buf [len(armag)]byte + if _, err := archive.Read(buf[:]); err != nil { + return nil, err + } + + switch string(buf[:]) { + case armag: + return standardArExportData(archive) + case armagt: + return nil, errors.New("unsupported thin archive") + case armagb: + return aixBigArExportData(archive) + default: + return nil, fmt.Errorf("unrecognized archive file format %q", buf[:]) + } +} + +// standardArExportData returns export data from a standard archive. +func standardArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { + off := int64(len(armag)) + for { + var hdrBuf [arHdrSize]byte + if _, err := archive.Read(hdrBuf[:]); err != nil { + return nil, err + } + off += arHdrSize + + if bytes.Compare(hdrBuf[arFmagOff:arFmagOff+arFmagSize], []byte(arfmag)) != 0 { + return nil, fmt.Errorf("archive header format header (%q)", hdrBuf[:]) + } + + size, err := strconv.ParseInt(strings.TrimSpace(string(hdrBuf[arSizeOff:arSizeOff+arSizeSize])), 10, 64) + if err != nil { + return nil, fmt.Errorf("error parsing size in archive header (%q): %v", hdrBuf[:], err) + } + + fn := hdrBuf[arNameOff : arNameOff+arNameSize] + if fn[0] == '/' && (fn[1] == ' ' || fn[1] == '/' || bytes.Compare(fn[:8], []byte("/SYM64/ ")) == 0) { + // Archive symbol table or extended name table, + // which we don't care about. + } else { + archiveAt := readerAtFromSeeker(archive) + ret, err := elfFromAr(io.NewSectionReader(archiveAt, off, size)) + if ret != nil || err != nil { + return ret, err + } + } + + if size&1 != 0 { + size++ + } + off += size + if _, err := archive.Seek(off, io.SeekStart); err != nil { + return nil, err + } + } +} + +// elfFromAr tries to get export data from an archive member as an ELF file. +// If there is no export data, this returns nil, nil. +func elfFromAr(member *io.SectionReader) (io.ReadSeeker, error) { + ef, err := elf.NewFile(member) + if err != nil { + return nil, err + } + sec := ef.Section(".go_export") + if sec == nil { + return nil, nil + } + return sec.Open(), nil +} + +// aixBigArExportData returns export data from an AIX big archive. +func aixBigArExportData(archive io.ReadSeeker) (io.ReadSeeker, error) { + archiveAt := readerAtFromSeeker(archive) + arch, err := xcoff.NewArchive(archiveAt) + if err != nil { + return nil, err + } + + for _, mem := range arch.Members { + f, err := arch.GetFile(mem.Name) + if err != nil { + return nil, err + } + sdat := f.CSect(".go_export") + if sdat != nil { + return bytes.NewReader(sdat), nil + } + } + + return nil, fmt.Errorf(".go_export not found in this archive") +} + +// readerAtFromSeeker turns an io.ReadSeeker into an io.ReaderAt. +// This is only safe because there won't be any concurrent seeks +// while this code is executing. +func readerAtFromSeeker(rs io.ReadSeeker) io.ReaderAt { + if ret, ok := rs.(io.ReaderAt); ok { + return ret + } + return seekerReadAt{rs} +} + +type seekerReadAt struct { + seeker io.ReadSeeker +} + +func (sra seekerReadAt) ReadAt(p []byte, off int64) (int, error) { + if _, err := sra.seeker.Seek(off, io.SeekStart); err != nil { + return 0, err + } + return sra.seeker.Read(p) +} diff --git a/src/go/internal/gccgoimporter/gccgoinstallation.go b/src/go/internal/gccgoimporter/gccgoinstallation.go new file mode 100644 index 0000000..e90a3cc --- /dev/null +++ b/src/go/internal/gccgoimporter/gccgoinstallation.go @@ -0,0 +1,97 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gccgoimporter + +import ( + "bufio" + "go/types" + exec "internal/execabs" + "os" + "path/filepath" + "strings" +) + +// Information about a specific installation of gccgo. +type GccgoInstallation struct { + // Version of gcc (e.g. 4.8.0). + GccVersion string + + // Target triple (e.g. x86_64-unknown-linux-gnu). + TargetTriple string + + // Built-in library paths used by this installation. + LibPaths []string +} + +// Ask the driver at the given path for information for this GccgoInstallation. +// The given arguments are passed directly to the call of the driver. +func (inst *GccgoInstallation) InitFromDriver(gccgoPath string, args ...string) (err error) { + argv := append([]string{"-###", "-S", "-x", "go", "-"}, args...) + cmd := exec.Command(gccgoPath, argv...) + stderr, err := cmd.StderrPipe() + if err != nil { + return + } + + err = cmd.Start() + if err != nil { + return + } + + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + line := scanner.Text() + switch { + case strings.HasPrefix(line, "Target: "): + inst.TargetTriple = line[8:] + + case line[0] == ' ': + args := strings.Fields(line) + for _, arg := range args[1:] { + if strings.HasPrefix(arg, "-L") { + inst.LibPaths = append(inst.LibPaths, arg[2:]) + } + } + } + } + + argv = append([]string{"-dumpversion"}, args...) + stdout, err := exec.Command(gccgoPath, argv...).Output() + if err != nil { + return + } + inst.GccVersion = strings.TrimSpace(string(stdout)) + + return +} + +// Return the list of export search paths for this GccgoInstallation. +func (inst *GccgoInstallation) SearchPaths() (paths []string) { + for _, lpath := range inst.LibPaths { + spath := filepath.Join(lpath, "go", inst.GccVersion) + fi, err := os.Stat(spath) + if err != nil || !fi.IsDir() { + continue + } + paths = append(paths, spath) + + spath = filepath.Join(spath, inst.TargetTriple) + fi, err = os.Stat(spath) + if err != nil || !fi.IsDir() { + continue + } + paths = append(paths, spath) + } + + paths = append(paths, inst.LibPaths...) + + return +} + +// Return an importer that searches incpaths followed by the gcc installation's +// built-in search paths and the current directory. +func (inst *GccgoInstallation) GetImporter(incpaths []string, initmap map[*types.Package]InitData) Importer { + return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap) +} diff --git a/src/go/internal/gccgoimporter/gccgoinstallation_test.go b/src/go/internal/gccgoimporter/gccgoinstallation_test.go new file mode 100644 index 0000000..df0188a --- /dev/null +++ b/src/go/internal/gccgoimporter/gccgoinstallation_test.go @@ -0,0 +1,192 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gccgoimporter + +import ( + "go/types" + "testing" +) + +// importablePackages is a list of packages that we verify that we can +// import. This should be all standard library packages in all relevant +// versions of gccgo. Note that since gccgo follows a different release +// cycle, and since different systems have different versions installed, +// we can't use the last-two-versions rule of the gc toolchain. +var importablePackages = [...]string{ + "archive/tar", + "archive/zip", + "bufio", + "bytes", + "compress/bzip2", + "compress/flate", + "compress/gzip", + "compress/lzw", + "compress/zlib", + "container/heap", + "container/list", + "container/ring", + "crypto/aes", + "crypto/cipher", + "crypto/des", + "crypto/dsa", + "crypto/ecdsa", + "crypto/elliptic", + "crypto", + "crypto/hmac", + "crypto/md5", + "crypto/rand", + "crypto/rc4", + "crypto/rsa", + "crypto/sha1", + "crypto/sha256", + "crypto/sha512", + "crypto/subtle", + "crypto/tls", + "crypto/x509", + "crypto/x509/pkix", + "database/sql/driver", + "database/sql", + "debug/dwarf", + "debug/elf", + "debug/gosym", + "debug/macho", + "debug/pe", + "encoding/ascii85", + "encoding/asn1", + "encoding/base32", + "encoding/base64", + "encoding/binary", + "encoding/csv", + "encoding/gob", + // "encoding", // Added in GCC 4.9. + "encoding/hex", + "encoding/json", + "encoding/pem", + "encoding/xml", + "errors", + "expvar", + "flag", + "fmt", + "go/ast", + "go/build", + "go/doc", + // "go/format", // Added in GCC 4.8. + "go/parser", + "go/printer", + "go/scanner", + "go/token", + "hash/adler32", + "hash/crc32", + "hash/crc64", + "hash/fnv", + "hash", + "html", + "html/template", + "image/color", + // "image/color/palette", // Added in GCC 4.9. + "image/draw", + "image/gif", + "image", + "image/jpeg", + "image/png", + "index/suffixarray", + "io", + "io/ioutil", + "log", + "log/syslog", + "math/big", + "math/cmplx", + "math", + "math/rand", + "mime", + "mime/multipart", + "net", + "net/http/cgi", + // "net/http/cookiejar", // Added in GCC 4.8. + "net/http/fcgi", + "net/http", + "net/http/httptest", + "net/http/httputil", + "net/http/pprof", + "net/mail", + "net/rpc", + "net/rpc/jsonrpc", + "net/smtp", + "net/textproto", + "net/url", + "os/exec", + "os", + "os/signal", + "os/user", + "path/filepath", + "path", + "reflect", + "regexp", + "regexp/syntax", + "runtime/debug", + "runtime", + "runtime/pprof", + "sort", + "strconv", + "strings", + "sync/atomic", + "sync", + "syscall", + "testing", + "testing/iotest", + "testing/quick", + "text/scanner", + "text/tabwriter", + "text/template", + "text/template/parse", + "time", + "unicode", + "unicode/utf16", + "unicode/utf8", +} + +func TestInstallationImporter(t *testing.T) { + // This test relies on gccgo being around. + gpath := gccgoPath() + if gpath == "" { + t.Skip("This test needs gccgo") + } + + var inst GccgoInstallation + err := inst.InitFromDriver(gpath) + if err != nil { + t.Fatal(err) + } + imp := inst.GetImporter(nil, nil) + + // Ensure we don't regress the number of packages we can parse. First import + // all packages into the same map and then each individually. + pkgMap := make(map[string]*types.Package) + for _, pkg := range importablePackages { + _, err = imp(pkgMap, pkg, ".", nil) + if err != nil { + t.Error(err) + } + } + + for _, pkg := range importablePackages { + _, err = imp(make(map[string]*types.Package), pkg, ".", nil) + if err != nil { + t.Error(err) + } + } + + // Test for certain specific entities in the imported data. + for _, test := range [...]importerTest{ + {pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []byte) (n int, err error)}"}, + {pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"}, + {pkgpath: "math", name: "Pi", want: "const Pi untyped float"}, + {pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"}, + {pkgpath: "sort", name: "Search", want: "func Search(n int, f func(int) bool) int"}, + {pkgpath: "unsafe", name: "Pointer", want: "type Pointer"}, + } { + runImporterTest(t, imp, nil, &test) + } +} diff --git a/src/go/internal/gccgoimporter/importer.go b/src/go/internal/gccgoimporter/importer.go new file mode 100644 index 0000000..94f2def --- /dev/null +++ b/src/go/internal/gccgoimporter/importer.go @@ -0,0 +1,261 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gccgoimporter implements Import for gccgo-generated object files. +package gccgoimporter // import "go/internal/gccgoimporter" + +import ( + "bytes" + "debug/elf" + "fmt" + "go/types" + "internal/xcoff" + "io" + "os" + "path/filepath" + "strings" +) + +// A PackageInit describes an imported package that needs initialization. +type PackageInit struct { + Name string // short package name + InitFunc string // name of init function + Priority int // priority of init function, see InitData.Priority +} + +// The gccgo-specific init data for a package. +type InitData struct { + // Initialization priority of this package relative to other packages. + // This is based on the maximum depth of the package's dependency graph; + // it is guaranteed to be greater than that of its dependencies. + Priority int + + // The list of packages which this package depends on to be initialized, + // including itself if needed. This is the subset of the transitive closure of + // the package's dependencies that need initialization. + Inits []PackageInit +} + +// Locate the file from which to read export data. +// This is intended to replicate the logic in gofrontend. +func findExportFile(searchpaths []string, pkgpath string) (string, error) { + for _, spath := range searchpaths { + pkgfullpath := filepath.Join(spath, pkgpath) + pkgdir, name := filepath.Split(pkgfullpath) + + for _, filepath := range [...]string{ + pkgfullpath, + pkgfullpath + ".gox", + pkgdir + "lib" + name + ".so", + pkgdir + "lib" + name + ".a", + pkgfullpath + ".o", + } { + fi, err := os.Stat(filepath) + if err == nil && !fi.IsDir() { + return filepath, nil + } + } + } + + return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) +} + +const ( + gccgov1Magic = "v1;\n" + gccgov2Magic = "v2;\n" + gccgov3Magic = "v3;\n" + goimporterMagic = "\n$$ " + archiveMagic = "!<ar" + aixbigafMagic = "<big" +) + +// Opens the export data file at the given path. If this is an ELF file, +// searches for and opens the .go_export section. If this is an archive, +// reads the export data from the first member, which is assumed to be an ELF file. +// This is intended to replicate the logic in gofrontend. +func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) { + f, err := os.Open(fpath) + if err != nil { + return + } + closer = f + defer func() { + if err != nil && closer != nil { + f.Close() + } + }() + + var magic [4]byte + _, err = f.ReadAt(magic[:], 0) + if err != nil { + return + } + + var objreader io.ReaderAt + switch string(magic[:]) { + case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: + // Raw export data. + reader = f + return + + case archiveMagic, aixbigafMagic: + reader, err = arExportData(f) + return + + default: + objreader = f + } + + ef, err := elf.NewFile(objreader) + if err == nil { + sec := ef.Section(".go_export") + if sec == nil { + err = fmt.Errorf("%s: .go_export section not found", fpath) + return + } + reader = sec.Open() + return + } + + xf, err := xcoff.NewFile(objreader) + if err == nil { + sdat := xf.CSect(".go_export") + if sdat == nil { + err = fmt.Errorf("%s: .go_export section not found", fpath) + return + } + reader = bytes.NewReader(sdat) + return + } + + err = fmt.Errorf("%s: unrecognized file format", fpath) + return +} + +// An Importer resolves import paths to Packages. The imports map records +// packages already known, indexed by package path. +// An importer must determine the canonical package path and check imports +// to see if it is already present in the map. If so, the Importer can return +// the map entry. Otherwise, the importer must load the package data for the +// given path into a new *Package, record it in imports map, and return the +// package. +type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error) + +func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { + return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) { + // TODO(gri): Use srcDir. + // Or not. It's possible that srcDir will fade in importance as + // the go command and other tools provide a translation table + // for relative imports (like ./foo or vendored imports). + if pkgpath == "unsafe" { + return types.Unsafe, nil + } + + var reader io.ReadSeeker + var fpath string + var rc io.ReadCloser + if lookup != nil { + if p := imports[pkgpath]; p != nil && p.Complete() { + return p, nil + } + rc, err = lookup(pkgpath) + if err != nil { + return nil, err + } + } + if rc != nil { + defer rc.Close() + rs, ok := rc.(io.ReadSeeker) + if !ok { + return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) + } + reader = rs + fpath = "<lookup " + pkgpath + ">" + // Take name from Name method (like on os.File) if present. + if n, ok := rc.(interface{ Name() string }); ok { + fpath = n.Name() + } + } else { + fpath, err = findExportFile(searchpaths, pkgpath) + if err != nil { + return nil, err + } + + r, closer, err := openExportFile(fpath) + if err != nil { + return nil, err + } + if closer != nil { + defer closer.Close() + } + reader = r + } + + var magics string + magics, err = readMagic(reader) + if err != nil { + return + } + + if magics == archiveMagic || magics == aixbigafMagic { + reader, err = arExportData(reader) + if err != nil { + return + } + magics, err = readMagic(reader) + if err != nil { + return + } + } + + switch magics { + case gccgov1Magic, gccgov2Magic, gccgov3Magic: + var p parser + p.init(fpath, reader, imports) + pkg = p.parsePackage() + if initmap != nil { + initmap[pkg] = p.initdata + } + + // Excluded for now: Standard gccgo doesn't support this import format currently. + // case goimporterMagic: + // var data []byte + // data, err = io.ReadAll(reader) + // if err != nil { + // return + // } + // var n int + // n, pkg, err = importer.ImportData(imports, data) + // if err != nil { + // return + // } + + // if initmap != nil { + // suffixreader := bytes.NewReader(data[n:]) + // var p parser + // p.init(fpath, suffixreader, nil) + // p.parseInitData() + // initmap[pkg] = p.initdata + // } + + default: + err = fmt.Errorf("unrecognized magic string: %q", magics) + } + + return + } +} + +// readMagic reads the four bytes at the start of a ReadSeeker and +// returns them as a string. +func readMagic(reader io.ReadSeeker) (string, error) { + var magic [4]byte + if _, err := reader.Read(magic[:]); err != nil { + return "", err + } + if _, err := reader.Seek(0, io.SeekStart); err != nil { + return "", err + } + return string(magic[:]), nil +} diff --git a/src/go/internal/gccgoimporter/importer_test.go b/src/go/internal/gccgoimporter/importer_test.go new file mode 100644 index 0000000..35240c8 --- /dev/null +++ b/src/go/internal/gccgoimporter/importer_test.go @@ -0,0 +1,209 @@ +// 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}"}, + {pkgpath: "issue34182", name: "T1", want: "type T1 struct{f *T2}"}, + {pkgpath: "notinheap", name: "S", want: "type S struct{}"}, +} + +func TestGoxImporter(t *testing.T) { + testenv.MustHaveExec(t) + initmap := make(map[*types.Package]InitData) + imp := GetImporter([]string{"testdata"}, initmap) + + for _, test := range importerTests { + runImporterTest(t, imp, initmap, &test) + } +} + +// gccgoPath returns a path to gccgo if it is present (either in +// path or specified via GCCGO environment variable), or an +// empty string if no gccgo is available. +func gccgoPath() string { + gccgoname := os.Getenv("GCCGO") + if gccgoname == "" { + gccgoname = "gccgo" + } + if gpath, gerr := exec.LookPath(gccgoname); gerr == nil { + return gpath + } + return "" +} + +func TestObjImporter(t *testing.T) { + // This test relies on gccgo being around. + gpath := gccgoPath() + if gpath == "" { + t.Skip("This test needs gccgo") + } + + verout, err := exec.Command(gpath, "--version").CombinedOutput() + if err != nil { + t.Logf("%s", verout) + t.Fatal(err) + } + vers := regexp.MustCompile(`([0-9]+)\.([0-9]+)`).FindSubmatch(verout) + if len(vers) == 0 { + t.Fatalf("could not find version number in %s", verout) + } + major, err := strconv.Atoi(string(vers[1])) + if err != nil { + t.Fatal(err) + } + minor, err := strconv.Atoi(string(vers[2])) + if err != nil { + t.Fatal(err) + } + t.Logf("gccgo version %d.%d", major, minor) + + tmpdir, err := os.MkdirTemp("", "TestObjImporter") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpdir) + + initmap := make(map[*types.Package]InitData) + imp := GetImporter([]string{tmpdir}, initmap) + + artmpdir, err := os.MkdirTemp("", "TestObjImporter") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(artmpdir) + + arinitmap := make(map[*types.Package]InitData) + arimp := GetImporter([]string{artmpdir}, arinitmap) + + for _, test := range importerTests { + if major < test.gccgoVersion { + // Support for type aliases was added in GCC 7. + t.Logf("skipping %q: not supported before gccgo version %d", test.pkgpath, test.gccgoVersion) + continue + } + + gofile := filepath.Join("testdata", test.pkgpath+".go") + if _, err := os.Stat(gofile); os.IsNotExist(err) { + continue + } + ofile := filepath.Join(tmpdir, test.pkgpath+".o") + afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a") + + cmd := exec.Command(gpath, "-fgo-pkgpath="+test.pkgpath, "-c", "-o", ofile, gofile) + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("%s", out) + t.Fatalf("gccgo %s failed: %s", gofile, err) + } + + runImporterTest(t, imp, initmap, &test) + + cmd = exec.Command("ar", "cr", afile, ofile) + out, err = cmd.CombinedOutput() + if err != nil { + t.Logf("%s", out) + t.Fatalf("ar cr %s %s failed: %s", afile, ofile, err) + } + + runImporterTest(t, arimp, arinitmap, &test) + + if err = os.Remove(ofile); err != nil { + t.Fatal(err) + } + if err = os.Remove(afile); err != nil { + t.Fatal(err) + } + } +} diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go new file mode 100644 index 0000000..1b1d07d --- /dev/null +++ b/src/go/internal/gccgoimporter/parser.go @@ -0,0 +1,1284 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gccgoimporter + +import ( + "bytes" + "errors" + "fmt" + "go/constant" + "go/token" + "go/types" + "io" + "strconv" + "strings" + "text/scanner" + "unicode/utf8" +) + +type parser struct { + scanner *scanner.Scanner + version string // format version + tok rune // current token + lit string // literal string; only valid for Ident, Int, String tokens + pkgpath string // package path of imported package + pkgname string // name of imported package + pkg *types.Package // reference to imported package + imports map[string]*types.Package // package path -> package object + typeList []types.Type // type number -> type + typeData []string // unparsed type data (v3 and later) + fixups []fixupRecord // fixups to apply at end of parsing + initdata InitData // package init priority data + aliases map[int]string // maps saved type number to alias name +} + +// When reading export data it's possible to encounter a defined type +// N1 with an underlying defined type N2 while we are still reading in +// that defined type N2; see issues #29006 and #29198 for instances +// of this. Example: +// +// type N1 N2 +// type N2 struct { +// ... +// p *N1 +// } +// +// To handle such cases, the parser generates a fixup record (below) and +// delays setting of N1's underlying type until parsing is complete, at +// which point fixups are applied. + +type fixupRecord struct { + toUpdate *types.Named // type to modify when fixup is processed + target types.Type // type that was incomplete when fixup was created +} + +func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) { + p.scanner = new(scanner.Scanner) + p.initScanner(filename, src) + p.imports = imports + p.aliases = make(map[int]string) + p.typeList = make([]types.Type, 1 /* type numbers start at 1 */, 16) +} + +func (p *parser) initScanner(filename string, src io.Reader) { + p.scanner.Init(src) + p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } + p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings + p.scanner.Whitespace = 1<<'\t' | 1<<' ' + p.scanner.Filename = filename // for good error messages + p.next() +} + +type importError struct { + pos scanner.Position + err error +} + +func (e importError) Error() string { + return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) +} + +func (p *parser) error(err interface{}) { + if s, ok := err.(string); ok { + err = errors.New(s) + } + // panic with a runtime.Error if err is not an error + panic(importError{p.scanner.Pos(), err.(error)}) +} + +func (p *parser) errorf(format string, args ...interface{}) { + p.error(fmt.Errorf(format, args...)) +} + +func (p *parser) expect(tok rune) string { + lit := p.lit + if p.tok != tok { + p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) + } + p.next() + return lit +} + +func (p *parser) expectEOL() { + if p.version == "v1" || p.version == "v2" { + p.expect(';') + } + p.expect('\n') +} + +func (p *parser) expectKeyword(keyword string) { + lit := p.expect(scanner.Ident) + if lit != keyword { + p.errorf("expected keyword %s, got %q", keyword, lit) + } +} + +func (p *parser) parseString() string { + str, err := strconv.Unquote(p.expect(scanner.String)) + if err != nil { + p.error(err) + } + return str +} + +// unquotedString = { unquotedStringChar } . +// unquotedStringChar = <neither a whitespace nor a ';' char> . +func (p *parser) parseUnquotedString() string { + if p.tok == scanner.EOF { + p.error("unexpected EOF") + } + var buf bytes.Buffer + buf.WriteString(p.scanner.TokenText()) + // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon, + // we need to let it be consumed by p.next(). + for ch := p.scanner.Peek(); ch != '\n' && ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() { + buf.WriteRune(ch) + p.scanner.Next() + } + p.next() + return buf.String() +} + +func (p *parser) next() { + p.tok = p.scanner.Scan() + switch p.tok { + case scanner.Ident, scanner.Int, scanner.Float, scanner.String, '·': + p.lit = p.scanner.TokenText() + default: + p.lit = "" + } +} + +func (p *parser) parseQualifiedName() (path, name string) { + return p.parseQualifiedNameStr(p.parseString()) +} + +func (p *parser) parseUnquotedQualifiedName() (path, name string) { + return p.parseQualifiedNameStr(p.parseUnquotedString()) +} + +// qualifiedName = [ ["."] unquotedString "." ] unquotedString . +// +// The above production uses greedy matching. +func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name string) { + parts := strings.Split(unquotedName, ".") + if parts[0] == "" { + parts = parts[1:] + } + + switch len(parts) { + case 0: + p.errorf("malformed qualified name: %q", unquotedName) + case 1: + // unqualified name + pkgpath = p.pkgpath + name = parts[0] + default: + // qualified name, which may contain periods + pkgpath = strings.Join(parts[0:len(parts)-1], ".") + name = parts[len(parts)-1] + } + + return +} + +// getPkg returns the package for a given path. If the package is +// not found but we have a package name, create the package and +// add it to the p.imports map. +// +func (p *parser) getPkg(pkgpath, name string) *types.Package { + // package unsafe is not in the imports map - handle explicitly + if pkgpath == "unsafe" { + return types.Unsafe + } + pkg := p.imports[pkgpath] + if pkg == nil && name != "" { + pkg = types.NewPackage(pkgpath, name) + p.imports[pkgpath] = pkg + } + return pkg +} + +// parseExportedName is like parseQualifiedName, but +// the package path is resolved to an imported *types.Package. +// +// ExportedName = string [string] . +func (p *parser) parseExportedName() (pkg *types.Package, name string) { + path, name := p.parseQualifiedName() + var pkgname string + if p.tok == scanner.String { + pkgname = p.parseString() + } + pkg = p.getPkg(path, pkgname) + if pkg == nil { + p.errorf("package %s (path = %q) not found", name, path) + } + return +} + +// Name = QualifiedName | "?" . +func (p *parser) parseName() string { + if p.tok == '?' { + // Anonymous. + p.next() + return "" + } + // The package path is redundant for us. Don't try to parse it. + _, name := p.parseUnquotedQualifiedName() + return name +} + +func deref(typ types.Type) types.Type { + if p, _ := typ.(*types.Pointer); p != nil { + typ = p.Elem() + } + return typ +} + +// Field = Name Type [string] . +func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) { + name := p.parseName() + typ, n := p.parseTypeExtended(pkg) + anon := false + if name == "" { + anon = true + // Alias? + if aname, ok := p.aliases[n]; ok { + name = aname + } else { + switch typ := deref(typ).(type) { + case *types.Basic: + name = typ.Name() + case *types.Named: + name = typ.Obj().Name() + default: + p.error("embedded field expected") + } + } + } + field = types.NewField(token.NoPos, pkg, name, typ, anon) + if p.tok == scanner.String { + tag = p.parseString() + } + return +} + +// Param = Name ["..."] Type . +func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) { + name := p.parseName() + // Ignore names invented for inlinable functions. + if strings.HasPrefix(name, "p.") || strings.HasPrefix(name, "r.") || strings.HasPrefix(name, "$ret") { + name = "" + } + if p.tok == '<' && p.scanner.Peek() == 'e' { + // EscInfo = "<esc:" int ">" . (optional and ignored) + p.next() + p.expectKeyword("esc") + p.expect(':') + p.expect(scanner.Int) + p.expect('>') + } + if p.tok == '.' { + p.next() + p.expect('.') + p.expect('.') + isVariadic = true + } + typ := p.parseType(pkg) + if isVariadic { + typ = types.NewSlice(typ) + } + param = types.NewParam(token.NoPos, pkg, name, typ) + return +} + +// Var = Name Type . +func (p *parser) parseVar(pkg *types.Package) *types.Var { + name := p.parseName() + v := types.NewVar(token.NoPos, pkg, name, p.parseType(pkg)) + if name[0] == '.' || name[0] == '<' { + // This is an unexported variable, + // or a variable defined in a different package. + // We only want to record exported variables. + return nil + } + return v +} + +// Conversion = "convert" "(" Type "," ConstValue ")" . +func (p *parser) parseConversion(pkg *types.Package) (val constant.Value, typ types.Type) { + p.expectKeyword("convert") + p.expect('(') + typ = p.parseType(pkg) + p.expect(',') + val, _ = p.parseConstValue(pkg) + p.expect(')') + return +} + +// ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) | Conversion . +// FloatOrComplex = float ["i" | ("+"|"-") float "i"] . +func (p *parser) parseConstValue(pkg *types.Package) (val constant.Value, typ types.Type) { + // v3 changed to $false, $true, $convert, to avoid confusion + // with variable names in inline function bodies. + if p.tok == '$' { + p.next() + if p.tok != scanner.Ident { + p.errorf("expected identifier after '$', got %s (%q)", scanner.TokenString(p.tok), p.lit) + } + } + + switch p.tok { + case scanner.String: + str := p.parseString() + val = constant.MakeString(str) + typ = types.Typ[types.UntypedString] + return + + case scanner.Ident: + b := false + switch p.lit { + case "false": + case "true": + b = true + + case "convert": + return p.parseConversion(pkg) + + default: + p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) + } + + p.next() + val = constant.MakeBool(b) + typ = types.Typ[types.UntypedBool] + return + } + + sign := "" + if p.tok == '-' { + p.next() + sign = "-" + } + + switch p.tok { + case scanner.Int: + val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0) + if val == nil { + p.error("could not parse integer literal") + } + + p.next() + if p.tok == '\'' { + p.next() + typ = types.Typ[types.UntypedRune] + } else { + typ = types.Typ[types.UntypedInt] + } + + case scanner.Float: + re := sign + p.lit + p.next() + + var im string + switch p.tok { + case '+': + p.next() + im = p.expect(scanner.Float) + + case '-': + p.next() + im = "-" + p.expect(scanner.Float) + + case scanner.Ident: + // re is in fact the imaginary component. Expect "i" below. + im = re + re = "0" + + default: + val = constant.MakeFromLiteral(re, token.FLOAT, 0) + if val == nil { + p.error("could not parse float literal") + } + typ = types.Typ[types.UntypedFloat] + return + } + + p.expectKeyword("i") + reval := constant.MakeFromLiteral(re, token.FLOAT, 0) + if reval == nil { + p.error("could not parse real component of complex literal") + } + imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0) + if imval == nil { + p.error("could not parse imag component of complex literal") + } + val = constant.BinaryOp(reval, token.ADD, imval) + typ = types.Typ[types.UntypedComplex] + + default: + p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit) + } + + return +} + +// Const = Name [Type] "=" ConstValue . +func (p *parser) parseConst(pkg *types.Package) *types.Const { + name := p.parseName() + var typ types.Type + if p.tok == '<' { + typ = p.parseType(pkg) + } + p.expect('=') + val, vtyp := p.parseConstValue(pkg) + if typ == nil { + typ = vtyp + } + return types.NewConst(token.NoPos, pkg, name, typ, val) +} + +// reserved is a singleton type used to fill type map slots that have +// been reserved (i.e., for which a type number has been parsed) but +// which don't have their actual type yet. When the type map is updated, +// the actual type must replace a reserved entry (or we have an internal +// error). Used for self-verification only - not required for correctness. +var reserved = new(struct{ types.Type }) + +// reserve reserves the type map entry n for future use. +func (p *parser) reserve(n int) { + // Notes: + // - for pre-V3 export data, the type numbers we see are + // guaranteed to be in increasing order, so we append a + // reserved entry onto the list. + // - for V3+ export data, type numbers can appear in + // any order, however the 'types' section tells us the + // total number of types, hence typeList is pre-allocated. + if len(p.typeData) == 0 { + if n != len(p.typeList) { + p.errorf("invalid type number %d (out of sync)", n) + } + p.typeList = append(p.typeList, reserved) + } else { + if p.typeList[n] != nil { + p.errorf("previously visited type number %d", n) + } + p.typeList[n] = reserved + } +} + +// update sets the type map entries for the entries in nlist to t. +// An entry in nlist can be a type number in p.typeList, +// used to resolve named types, or it can be a *types.Pointer, +// used to resolve pointers to named types in case they are referenced +// by embedded fields. +func (p *parser) update(t types.Type, nlist []interface{}) { + if t == reserved { + p.errorf("internal error: update(%v) invoked on reserved", nlist) + } + if t == nil { + p.errorf("internal error: update(%v) invoked on nil", nlist) + } + for _, n := range nlist { + switch n := n.(type) { + case int: + if p.typeList[n] == t { + continue + } + if p.typeList[n] != reserved { + p.errorf("internal error: update(%v): %d not reserved", nlist, n) + } + p.typeList[n] = t + case *types.Pointer: + if *n != (types.Pointer{}) { + elem := n.Elem() + if elem == t { + continue + } + p.errorf("internal error: update: pointer already set to %v, expected %v", elem, t) + } + *n = *types.NewPointer(t) + default: + p.errorf("internal error: %T on nlist", n) + } + } +} + +// NamedType = TypeName [ "=" ] Type { Method } . +// TypeName = ExportedName . +// Method = "func" "(" Param ")" Name ParamList ResultList [InlineBody] ";" . +func (p *parser) parseNamedType(nlist []interface{}) types.Type { + pkg, name := p.parseExportedName() + scope := pkg.Scope() + obj := scope.Lookup(name) + if obj != nil && obj.Type() == nil { + p.errorf("%v has nil type", obj) + } + + if p.tok == scanner.Ident && p.lit == "notinheap" { + p.next() + // The go/types package has no way of recording that + // this type is marked notinheap. Presumably no user + // of this package actually cares. + } + + // type alias + if p.tok == '=' { + p.next() + p.aliases[nlist[len(nlist)-1].(int)] = name + if obj != nil { + // use the previously imported (canonical) type + t := obj.Type() + p.update(t, nlist) + p.parseType(pkg) // discard + return t + } + t := p.parseType(pkg, nlist...) + obj = types.NewTypeName(token.NoPos, pkg, name, t) + scope.Insert(obj) + return t + } + + // defined type + if obj == nil { + // A named type may be referred to before the underlying type + // is known - set it up. + tname := types.NewTypeName(token.NoPos, pkg, name, nil) + types.NewNamed(tname, nil, nil) + scope.Insert(tname) + obj = tname + } + + // use the previously imported (canonical), or newly created type + t := obj.Type() + p.update(t, nlist) + + nt, ok := t.(*types.Named) + if !ok { + // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type. + pt := p.parseType(pkg) + if pt != t { + p.error("unexpected underlying type for non-named TypeName") + } + return t + } + + underlying := p.parseType(pkg) + if nt.Underlying() == nil { + if underlying.Underlying() == nil { + fix := fixupRecord{toUpdate: nt, target: underlying} + p.fixups = append(p.fixups, fix) + } else { + nt.SetUnderlying(underlying.Underlying()) + } + } + + if p.tok == '\n' { + p.next() + // collect associated methods + for p.tok == scanner.Ident { + p.expectKeyword("func") + if p.tok == '/' { + // Skip a /*nointerface*/ or /*asm ID */ comment. + p.expect('/') + p.expect('*') + if p.expect(scanner.Ident) == "asm" { + p.parseUnquotedString() + } + p.expect('*') + p.expect('/') + } + p.expect('(') + receiver, _ := p.parseParam(pkg) + p.expect(')') + name := p.parseName() + params, isVariadic := p.parseParamList(pkg) + results := p.parseResultList(pkg) + p.skipInlineBody() + p.expectEOL() + + sig := types.NewSignature(receiver, params, results, isVariadic) + nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) + } + } + + return nt +} + +func (p *parser) parseInt64() int64 { + lit := p.expect(scanner.Int) + n, err := strconv.ParseInt(lit, 10, 64) + if err != nil { + p.error(err) + } + return n +} + +func (p *parser) parseInt() int { + lit := p.expect(scanner.Int) + n, err := strconv.ParseInt(lit, 10, 0 /* int */) + if err != nil { + p.error(err) + } + return int(n) +} + +// ArrayOrSliceType = "[" [ int ] "]" Type . +func (p *parser) parseArrayOrSliceType(pkg *types.Package, nlist []interface{}) types.Type { + p.expect('[') + if p.tok == ']' { + p.next() + + t := new(types.Slice) + p.update(t, nlist) + + *t = *types.NewSlice(p.parseType(pkg)) + return t + } + + t := new(types.Array) + p.update(t, nlist) + + len := p.parseInt64() + p.expect(']') + + *t = *types.NewArray(p.parseType(pkg), len) + return t +} + +// MapType = "map" "[" Type "]" Type . +func (p *parser) parseMapType(pkg *types.Package, nlist []interface{}) types.Type { + p.expectKeyword("map") + + t := new(types.Map) + p.update(t, nlist) + + p.expect('[') + key := p.parseType(pkg) + p.expect(']') + elem := p.parseType(pkg) + + *t = *types.NewMap(key, elem) + return t +} + +// ChanType = "chan" ["<-" | "-<"] Type . +func (p *parser) parseChanType(pkg *types.Package, nlist []interface{}) types.Type { + p.expectKeyword("chan") + + t := new(types.Chan) + p.update(t, nlist) + + dir := types.SendRecv + switch p.tok { + case '-': + p.next() + p.expect('<') + dir = types.SendOnly + + case '<': + // don't consume '<' if it belongs to Type + if p.scanner.Peek() == '-' { + p.next() + p.expect('-') + dir = types.RecvOnly + } + } + + *t = *types.NewChan(dir, p.parseType(pkg)) + return t +} + +// StructType = "struct" "{" { Field } "}" . +func (p *parser) parseStructType(pkg *types.Package, nlist []interface{}) types.Type { + p.expectKeyword("struct") + + t := new(types.Struct) + p.update(t, nlist) + + var fields []*types.Var + var tags []string + + p.expect('{') + for p.tok != '}' && p.tok != scanner.EOF { + field, tag := p.parseField(pkg) + p.expect(';') + fields = append(fields, field) + tags = append(tags, tag) + } + p.expect('}') + + *t = *types.NewStruct(fields, tags) + return t +} + +// ParamList = "(" [ { Parameter "," } Parameter ] ")" . +func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) { + var list []*types.Var + isVariadic := false + + p.expect('(') + for p.tok != ')' && p.tok != scanner.EOF { + if len(list) > 0 { + p.expect(',') + } + par, variadic := p.parseParam(pkg) + list = append(list, par) + if variadic { + if isVariadic { + p.error("... not on final argument") + } + isVariadic = true + } + } + p.expect(')') + + return types.NewTuple(list...), isVariadic +} + +// ResultList = Type | ParamList . +func (p *parser) parseResultList(pkg *types.Package) *types.Tuple { + switch p.tok { + case '<': + p.next() + if p.tok == scanner.Ident && p.lit == "inl" { + return nil + } + taa, _ := p.parseTypeAfterAngle(pkg) + return types.NewTuple(types.NewParam(token.NoPos, pkg, "", taa)) + + case '(': + params, _ := p.parseParamList(pkg) + return params + + default: + return nil + } +} + +// FunctionType = ParamList ResultList . +func (p *parser) parseFunctionType(pkg *types.Package, nlist []interface{}) *types.Signature { + t := new(types.Signature) + p.update(t, nlist) + + params, isVariadic := p.parseParamList(pkg) + results := p.parseResultList(pkg) + + *t = *types.NewSignature(nil, params, results, isVariadic) + return t +} + +// Func = Name FunctionType [InlineBody] . +func (p *parser) parseFunc(pkg *types.Package) *types.Func { + if p.tok == '/' { + // Skip an /*asm ID */ comment. + p.expect('/') + p.expect('*') + if p.expect(scanner.Ident) == "asm" { + p.parseUnquotedString() + } + p.expect('*') + p.expect('/') + } + + name := p.parseName() + f := types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg, nil)) + p.skipInlineBody() + + if name[0] == '.' || name[0] == '<' || strings.ContainsRune(name, '$') { + // This is an unexported function, + // or a function defined in a different package, + // or a type$equal or type$hash function. + // We only want to record exported functions. + return nil + } + + return f +} + +// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" . +func (p *parser) parseInterfaceType(pkg *types.Package, nlist []interface{}) types.Type { + p.expectKeyword("interface") + + t := new(types.Interface) + p.update(t, nlist) + + var methods []*types.Func + var embeddeds []types.Type + + p.expect('{') + for p.tok != '}' && p.tok != scanner.EOF { + if p.tok == '?' { + p.next() + embeddeds = append(embeddeds, p.parseType(pkg)) + } else { + method := p.parseFunc(pkg) + if method != nil { + methods = append(methods, method) + } + } + p.expect(';') + } + p.expect('}') + + *t = *types.NewInterfaceType(methods, embeddeds) + return t +} + +// PointerType = "*" ("any" | Type) . +func (p *parser) parsePointerType(pkg *types.Package, nlist []interface{}) types.Type { + p.expect('*') + if p.tok == scanner.Ident { + p.expectKeyword("any") + t := types.Typ[types.UnsafePointer] + p.update(t, nlist) + return t + } + + t := new(types.Pointer) + p.update(t, nlist) + + *t = *types.NewPointer(p.parseType(pkg, t)) + + return t +} + +// TypeSpec = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType . +func (p *parser) parseTypeSpec(pkg *types.Package, nlist []interface{}) types.Type { + switch p.tok { + case scanner.String: + return p.parseNamedType(nlist) + + case scanner.Ident: + switch p.lit { + case "map": + return p.parseMapType(pkg, nlist) + + case "chan": + return p.parseChanType(pkg, nlist) + + case "struct": + return p.parseStructType(pkg, nlist) + + case "interface": + return p.parseInterfaceType(pkg, nlist) + } + + case '*': + return p.parsePointerType(pkg, nlist) + + case '[': + return p.parseArrayOrSliceType(pkg, nlist) + + case '(': + return p.parseFunctionType(pkg, nlist) + } + + p.errorf("expected type name or literal, got %s", scanner.TokenString(p.tok)) + return nil +} + +const ( + // From gofrontend/go/export.h + // Note that these values are negative in the gofrontend and have been made positive + // in the gccgoimporter. + gccgoBuiltinINT8 = 1 + gccgoBuiltinINT16 = 2 + gccgoBuiltinINT32 = 3 + gccgoBuiltinINT64 = 4 + gccgoBuiltinUINT8 = 5 + gccgoBuiltinUINT16 = 6 + gccgoBuiltinUINT32 = 7 + gccgoBuiltinUINT64 = 8 + gccgoBuiltinFLOAT32 = 9 + gccgoBuiltinFLOAT64 = 10 + gccgoBuiltinINT = 11 + gccgoBuiltinUINT = 12 + gccgoBuiltinUINTPTR = 13 + gccgoBuiltinBOOL = 15 + gccgoBuiltinSTRING = 16 + gccgoBuiltinCOMPLEX64 = 17 + gccgoBuiltinCOMPLEX128 = 18 + gccgoBuiltinERROR = 19 + gccgoBuiltinBYTE = 20 + gccgoBuiltinRUNE = 21 +) + +func lookupBuiltinType(typ int) types.Type { + return [...]types.Type{ + gccgoBuiltinINT8: types.Typ[types.Int8], + gccgoBuiltinINT16: types.Typ[types.Int16], + gccgoBuiltinINT32: types.Typ[types.Int32], + gccgoBuiltinINT64: types.Typ[types.Int64], + gccgoBuiltinUINT8: types.Typ[types.Uint8], + gccgoBuiltinUINT16: types.Typ[types.Uint16], + gccgoBuiltinUINT32: types.Typ[types.Uint32], + gccgoBuiltinUINT64: types.Typ[types.Uint64], + gccgoBuiltinFLOAT32: types.Typ[types.Float32], + gccgoBuiltinFLOAT64: types.Typ[types.Float64], + gccgoBuiltinINT: types.Typ[types.Int], + gccgoBuiltinUINT: types.Typ[types.Uint], + gccgoBuiltinUINTPTR: types.Typ[types.Uintptr], + gccgoBuiltinBOOL: types.Typ[types.Bool], + gccgoBuiltinSTRING: types.Typ[types.String], + gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64], + gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128], + gccgoBuiltinERROR: types.Universe.Lookup("error").Type(), + gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(), + gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(), + }[typ] +} + +// Type = "<" "type" ( "-" int | int [ TypeSpec ] ) ">" . +// +// parseType updates the type map to t for all type numbers n. +// +func (p *parser) parseType(pkg *types.Package, n ...interface{}) types.Type { + p.expect('<') + t, _ := p.parseTypeAfterAngle(pkg, n...) + return t +} + +// (*parser).Type after reading the "<". +func (p *parser) parseTypeAfterAngle(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) { + p.expectKeyword("type") + + n1 = 0 + switch p.tok { + case scanner.Int: + n1 = p.parseInt() + if p.tok == '>' { + if len(p.typeData) > 0 && p.typeList[n1] == nil { + p.parseSavedType(pkg, n1, n) + } + t = p.typeList[n1] + if len(p.typeData) == 0 && t == reserved { + p.errorf("invalid type cycle, type %d not yet defined (nlist=%v)", n1, n) + } + p.update(t, n) + } else { + p.reserve(n1) + t = p.parseTypeSpec(pkg, append(n, n1)) + } + + case '-': + p.next() + n1 := p.parseInt() + t = lookupBuiltinType(n1) + p.update(t, n) + + default: + p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit) + return nil, 0 + } + + if t == nil || t == reserved { + p.errorf("internal error: bad return from parseType(%v)", n) + } + + p.expect('>') + return +} + +// parseTypeExtended is identical to parseType, but if the type in +// question is a saved type, returns the index as well as the type +// pointer (index returned is zero if we parsed a builtin). +func (p *parser) parseTypeExtended(pkg *types.Package, n ...interface{}) (t types.Type, n1 int) { + p.expect('<') + t, n1 = p.parseTypeAfterAngle(pkg, n...) + return +} + +// InlineBody = "<inl:NN>" .{NN} +// Reports whether a body was skipped. +func (p *parser) skipInlineBody() { + // We may or may not have seen the '<' already, depending on + // whether the function had a result type or not. + if p.tok == '<' { + p.next() + p.expectKeyword("inl") + } else if p.tok != scanner.Ident || p.lit != "inl" { + return + } else { + p.next() + } + + p.expect(':') + want := p.parseInt() + p.expect('>') + + defer func(w uint64) { + p.scanner.Whitespace = w + }(p.scanner.Whitespace) + p.scanner.Whitespace = 0 + + got := 0 + for got < want { + r := p.scanner.Next() + if r == scanner.EOF { + p.error("unexpected EOF") + } + got += utf8.RuneLen(r) + } +} + +// Types = "types" maxp1 exportedp1 (offset length)* . +func (p *parser) parseTypes(pkg *types.Package) { + maxp1 := p.parseInt() + exportedp1 := p.parseInt() + p.typeList = make([]types.Type, maxp1, maxp1) + + type typeOffset struct { + offset int + length int + } + var typeOffsets []typeOffset + + total := 0 + for i := 1; i < maxp1; i++ { + len := p.parseInt() + typeOffsets = append(typeOffsets, typeOffset{total, len}) + total += len + } + + defer func(w uint64) { + p.scanner.Whitespace = w + }(p.scanner.Whitespace) + p.scanner.Whitespace = 0 + + // We should now have p.tok pointing to the final newline. + // The next runes from the scanner should be the type data. + + var sb strings.Builder + for sb.Len() < total { + r := p.scanner.Next() + if r == scanner.EOF { + p.error("unexpected EOF") + } + sb.WriteRune(r) + } + allTypeData := sb.String() + + p.typeData = []string{""} // type 0, unused + for _, to := range typeOffsets { + p.typeData = append(p.typeData, allTypeData[to.offset:to.offset+to.length]) + } + + for i := 1; i < int(exportedp1); i++ { + p.parseSavedType(pkg, i, nil) + } +} + +// parseSavedType parses one saved type definition. +func (p *parser) parseSavedType(pkg *types.Package, i int, nlist []interface{}) { + defer func(s *scanner.Scanner, tok rune, lit string) { + p.scanner = s + p.tok = tok + p.lit = lit + }(p.scanner, p.tok, p.lit) + + p.scanner = new(scanner.Scanner) + p.initScanner(p.scanner.Filename, strings.NewReader(p.typeData[i])) + p.expectKeyword("type") + id := p.parseInt() + if id != i { + p.errorf("type ID mismatch: got %d, want %d", id, i) + } + if p.typeList[i] == reserved { + p.errorf("internal error: %d already reserved in parseSavedType", i) + } + if p.typeList[i] == nil { + p.reserve(i) + p.parseTypeSpec(pkg, append(nlist, i)) + } + if p.typeList[i] == nil || p.typeList[i] == reserved { + p.errorf("internal error: parseSavedType(%d,%v) reserved/nil", i, nlist) + } +} + +// PackageInit = unquotedString unquotedString int . +func (p *parser) parsePackageInit() PackageInit { + name := p.parseUnquotedString() + initfunc := p.parseUnquotedString() + priority := -1 + if p.version == "v1" { + priority = p.parseInt() + } + return PackageInit{Name: name, InitFunc: initfunc, Priority: priority} +} + +// Create the package if we have parsed both the package path and package name. +func (p *parser) maybeCreatePackage() { + if p.pkgname != "" && p.pkgpath != "" { + p.pkg = p.getPkg(p.pkgpath, p.pkgname) + } +} + +// InitDataDirective = ( "v1" | "v2" | "v3" ) ";" | +// "priority" int ";" | +// "init" { PackageInit } ";" | +// "checksum" unquotedString ";" . +func (p *parser) parseInitDataDirective() { + if p.tok != scanner.Ident { + // unexpected token kind; panic + p.expect(scanner.Ident) + } + + switch p.lit { + case "v1", "v2", "v3": + p.version = p.lit + p.next() + p.expect(';') + p.expect('\n') + + case "priority": + p.next() + p.initdata.Priority = p.parseInt() + p.expectEOL() + + case "init": + p.next() + for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF { + p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit()) + } + p.expectEOL() + + case "init_graph": + p.next() + // The graph data is thrown away for now. + for p.tok != '\n' && p.tok != ';' && p.tok != scanner.EOF { + p.parseInt64() + p.parseInt64() + } + p.expectEOL() + + case "checksum": + // Don't let the scanner try to parse the checksum as a number. + defer func(mode uint) { + p.scanner.Mode = mode + }(p.scanner.Mode) + p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats + p.next() + p.parseUnquotedString() + p.expectEOL() + + default: + p.errorf("unexpected identifier: %q", p.lit) + } +} + +// Directive = InitDataDirective | +// "package" unquotedString [ unquotedString ] [ unquotedString ] ";" | +// "pkgpath" unquotedString ";" | +// "prefix" unquotedString ";" | +// "import" unquotedString unquotedString string ";" | +// "indirectimport" unquotedString unquotedstring ";" | +// "func" Func ";" | +// "type" Type ";" | +// "var" Var ";" | +// "const" Const ";" . +func (p *parser) parseDirective() { + if p.tok != scanner.Ident { + // unexpected token kind; panic + p.expect(scanner.Ident) + } + + switch p.lit { + case "v1", "v2", "v3", "priority", "init", "init_graph", "checksum": + p.parseInitDataDirective() + + case "package": + p.next() + p.pkgname = p.parseUnquotedString() + p.maybeCreatePackage() + if p.version != "v1" && p.tok != '\n' && p.tok != ';' { + p.parseUnquotedString() + p.parseUnquotedString() + } + p.expectEOL() + + case "pkgpath": + p.next() + p.pkgpath = p.parseUnquotedString() + p.maybeCreatePackage() + p.expectEOL() + + case "prefix": + p.next() + p.pkgpath = p.parseUnquotedString() + p.expectEOL() + + case "import": + p.next() + pkgname := p.parseUnquotedString() + pkgpath := p.parseUnquotedString() + p.getPkg(pkgpath, pkgname) + p.parseString() + p.expectEOL() + + case "indirectimport": + p.next() + pkgname := p.parseUnquotedString() + pkgpath := p.parseUnquotedString() + p.getPkg(pkgpath, pkgname) + p.expectEOL() + + case "types": + p.next() + p.parseTypes(p.pkg) + p.expectEOL() + + case "func": + p.next() + fun := p.parseFunc(p.pkg) + if fun != nil { + p.pkg.Scope().Insert(fun) + } + p.expectEOL() + + case "type": + p.next() + p.parseType(p.pkg) + p.expectEOL() + + case "var": + p.next() + v := p.parseVar(p.pkg) + if v != nil { + p.pkg.Scope().Insert(v) + } + p.expectEOL() + + case "const": + p.next() + c := p.parseConst(p.pkg) + p.pkg.Scope().Insert(c) + p.expectEOL() + + default: + p.errorf("unexpected identifier: %q", p.lit) + } +} + +// Package = { Directive } . +func (p *parser) parsePackage() *types.Package { + for p.tok != scanner.EOF { + p.parseDirective() + } + for _, f := range p.fixups { + if f.target.Underlying() == nil { + p.errorf("internal error: fixup can't be applied, loop required") + } + f.toUpdate.SetUnderlying(f.target.Underlying()) + } + p.fixups = nil + for _, typ := range p.typeList { + if it, ok := typ.(*types.Interface); ok { + it.Complete() + } + } + p.pkg.MarkComplete() + return p.pkg +} diff --git a/src/go/internal/gccgoimporter/parser_test.go b/src/go/internal/gccgoimporter/parser_test.go new file mode 100644 index 0000000..00128b4 --- /dev/null +++ b/src/go/internal/gccgoimporter/parser_test.go @@ -0,0 +1,78 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gccgoimporter + +import ( + "bytes" + "go/types" + "strings" + "testing" + "text/scanner" +) + +var typeParserTests = []struct { + id, typ, want, underlying, methods string +}{ + {id: "foo", typ: "<type -1>", want: "int8"}, + {id: "foo", typ: "<type 1 *<type -19>>", want: "*error"}, + {id: "foo", typ: "<type 1 *any>", want: "unsafe.Pointer"}, + {id: "foo", typ: "<type 1 \"Bar\" <type 2 *<type 1>>>", want: "foo.Bar", underlying: "*foo.Bar"}, + {id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1>\nfunc (? <type 1>) M ();\n>", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"}, + {id: "foo", typ: "<type 1 \".bar.foo\" \"bar\" <type -1>>", want: "bar.foo", underlying: "int8"}, + {id: "foo", typ: "<type 1 []<type -1>>", want: "[]int8"}, + {id: "foo", typ: "<type 1 [42]<type -1>>", want: "[42]int8"}, + {id: "foo", typ: "<type 1 map [<type -1>] <type -2>>", want: "map[int8]int16"}, + {id: "foo", typ: "<type 1 chan <type -1>>", want: "chan int8"}, + {id: "foo", typ: "<type 1 chan <- <type -1>>", want: "<-chan int8"}, + {id: "foo", typ: "<type 1 chan -< <type -1>>", want: "chan<- int8"}, + {id: "foo", typ: "<type 1 struct { I8 <type -1>; I16 <type -2> \"i16\"; }>", want: "struct{I8 int8; I16 int16 \"i16\"}"}, + {id: "foo", typ: "<type 1 interface { Foo (a <type -1>, b <type -2>) <type -1>; Bar (? <type -2>, ? ...<type -1>) (? <type -2>, ? <type -1>); Baz (); }>", want: "interface{Bar(int16, ...int8) (int16, int8); Baz(); Foo(a int8, b int16) int8}"}, + {id: "foo", typ: "<type 1 (? <type -1>) <type -2>>", want: "func(int8) int16"}, +} + +func TestTypeParser(t *testing.T) { + for _, test := range typeParserTests { + var p parser + p.init("test.gox", strings.NewReader(test.typ), make(map[string]*types.Package)) + p.version = "v2" + p.pkgname = test.id + p.pkgpath = test.id + p.maybeCreatePackage() + typ := p.parseType(p.pkg) + + if p.tok != scanner.EOF { + t.Errorf("expected full parse, stopped at %q", p.lit) + } + + // interfaces must be explicitly completed + if ityp, _ := typ.(*types.Interface); ityp != nil { + ityp.Complete() + } + + got := typ.String() + if got != test.want { + t.Errorf("got type %q, expected %q", got, test.want) + } + + if test.underlying != "" { + underlying := typ.Underlying().String() + if underlying != test.underlying { + t.Errorf("got underlying type %q, expected %q", underlying, test.underlying) + } + } + + if test.methods != "" { + nt := typ.(*types.Named) + var buf bytes.Buffer + for i := 0; i != nt.NumMethods(); i++ { + buf.WriteString(nt.Method(i).String()) + } + methods := buf.String() + if methods != test.methods { + t.Errorf("got methods %q, expected %q", methods, test.methods) + } + } + } +} diff --git a/src/go/internal/gccgoimporter/testdata/aliases.go b/src/go/internal/gccgoimporter/testdata/aliases.go new file mode 100644 index 0000000..cfb59b3 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/aliases.go @@ -0,0 +1,65 @@ +package aliases + +type ( + T0 [10]int + T1 []byte + T2 struct { + x int + } + T3 interface { + m() T2 + } + T4 func(int, T0) chan T2 +) + +// basic aliases +type ( + Ai = int + A0 = T0 + A1 = T1 + A2 = T2 + A3 = T3 + A4 = T4 + + A10 = [10]int + A11 = []byte + A12 = struct { + x int + } + A13 = interface { + m() A2 + } + A14 = func(int, A0) chan A2 +) + +// alias receiver types +func (T0) m1() {} +func (A0) m2() {} + +// alias receiver types (long type declaration chains) +type ( + V0 = V1 + V1 = (V2) + V2 = (V3) + V3 = T0 +) + +func (V1) n() {} + +// cycles +type C0 struct { + f1 C1 + f2 C2 +} + +type ( + C1 *C0 + C2 = C1 +) + +type ( + C5 struct { + f *C6 + } + C6 = C5 +) diff --git a/src/go/internal/gccgoimporter/testdata/aliases.gox b/src/go/internal/gccgoimporter/testdata/aliases.gox new file mode 100644 index 0000000..2428c06 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/aliases.gox @@ -0,0 +1,33 @@ +v2; +package aliases; +prefix go; +package aliases go.aliases go.aliases; +type <type 1 "A0" = <type 2 "T0" <type 3 [10 ] <type -11>> + func (? <esc:0x1> <type 2>) .go.aliases.m1 (); + func (? <esc:0x1> <type 1>) .go.aliases.m2 (); + func (? <esc:0x1> <type 4 "V1" = <type 5 "V2" = <type 6 "V3" = <type 2>>>>) .go.aliases.n (); +>>; +type <type 7 "A1" = <type 8 "T1" <type 9 [] <type -20>>>>; +type <type 10 "A10" = <type 11 [10 ] <type -11>>>; +type <type 12 "A11" = <type 13 [] <type -20>>>; +type <type 14 "A12" = <type 15 struct { .go.aliases.x <type -11>; }>>; +type <type 16 "A13" = <type 17 interface { .go.aliases.m () <type 18 "A2" = <type 19 "T2" <type 20 struct { .go.aliases.x <type -11>; }>>>; }>>; +type <type 21 "A14" = <type 22 (? <type -11>, ? <type 1>) <type 23 chan <type 18>>>>; +type <type 18>; +type <type 24 "A3" = <type 25 "T3" <type 26 interface { .go.aliases.m () <type 19>; }>>>; +type <type 27 "A4" = <type 28 "T4" <type 29 (? <type -11>, ? <type 2>) <type 30 chan <type 19>>>>>; +type <type 31 "Ai" = <type -11>>; +type <type 32 "C0" <type 33 struct { .go.aliases.f1 <type 34 "C1" <type 35 *<type 32>>>; .go.aliases.f2 <type 36 "C2" = <type 34>>; }>>; +type <type 34>; +type <type 36>; +type <type 37 "C5" <type 38 struct { .go.aliases.f <type 39 *<type 40 "C6" = <type 37>>>; }>>; +type <type 40>; +type <type 2>; +type <type 8>; +type <type 19>; +type <type 25>; +type <type 28>; +type <type 41 "V0" = <type 4>>; +type <type 4>; +type <type 5>; +type <type 6>; diff --git a/src/go/internal/gccgoimporter/testdata/complexnums.go b/src/go/internal/gccgoimporter/testdata/complexnums.go new file mode 100644 index 0000000..a51b6b0 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/complexnums.go @@ -0,0 +1,6 @@ +package complexnums + +const NN = -1 - 1i +const NP = -1 + 1i +const PN = 1 - 1i +const PP = 1 + 1i diff --git a/src/go/internal/gccgoimporter/testdata/complexnums.gox b/src/go/internal/gccgoimporter/testdata/complexnums.gox new file mode 100644 index 0000000..b66524f --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/complexnums.gox @@ -0,0 +1,8 @@ +v1; +package complexnums; +pkgpath complexnums; +priority 1; +const NN = -0.1E1-0.1E1i ; +const NP = -0.1E1+0.1E1i ; +const PN = 0.1E1-0.1E1i ; +const PP = 0.1E1+0.1E1i ; diff --git a/src/go/internal/gccgoimporter/testdata/conversions.go b/src/go/internal/gccgoimporter/testdata/conversions.go new file mode 100644 index 0000000..653927a --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/conversions.go @@ -0,0 +1,5 @@ +package conversions + +type Units string + +const Bits = Units("bits") diff --git a/src/go/internal/gccgoimporter/testdata/conversions.gox b/src/go/internal/gccgoimporter/testdata/conversions.gox new file mode 100644 index 0000000..7de6cda --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/conversions.gox @@ -0,0 +1,6 @@ +v2; +package conversions; +prefix go; +package conversions go.conversions go.conversions; +const Bits <type 1 "Units" <type -16>> = convert(<type 1>, "bits"); +type <type 1>; diff --git a/src/go/internal/gccgoimporter/testdata/escapeinfo.go b/src/go/internal/gccgoimporter/testdata/escapeinfo.go new file mode 100644 index 0000000..103ad95 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/escapeinfo.go @@ -0,0 +1,13 @@ +// Test case for escape info in export data. To compile and extract .gox file: +// gccgo -fgo-optimize-allocs -c escapeinfo.go +// objcopy -j .go_export escapeinfo.o escapeinfo.gox + +package escapeinfo + +type T struct{ data []byte } + +func NewT(data []byte) *T { + return &T{data} +} + +func (*T) Read(p []byte) {} diff --git a/src/go/internal/gccgoimporter/testdata/escapeinfo.gox b/src/go/internal/gccgoimporter/testdata/escapeinfo.gox Binary files differnew file mode 100644 index 0000000..1db8156 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/escapeinfo.gox 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 Binary files differnew file mode 100644 index 0000000..80c2dbc --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/time.gox diff --git a/src/go/internal/gccgoimporter/testdata/unicode.gox b/src/go/internal/gccgoimporter/testdata/unicode.gox Binary files differnew file mode 100644 index 0000000..e70e539 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/unicode.gox diff --git a/src/go/internal/gccgoimporter/testdata/v1reflect.gox b/src/go/internal/gccgoimporter/testdata/v1reflect.gox Binary files differnew file mode 100644 index 0000000..ea46841 --- /dev/null +++ b/src/go/internal/gccgoimporter/testdata/v1reflect.gox |