Commit f6ae5f96 authored by Robert Griesemer's avatar Robert Griesemer

go/internal/gccgoimporter: unmodified copy of x/tools/go/gccgoimporter

This change will brake the build. The immediately following change
contains the necessary adjustments to make it work again. We're
doing this in two steps to expose the manual changes applied.

Change-Id: I225947da23e190b12e12cbd0c5e6e91628de7f53
Reviewed-on: https://go-review.googlesource.com/11151Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent bbf79575
// 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"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/tools/go/types"
)
// 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.
func (inst *GccgoInstallation) InitFromDriver(gccgoPath string) (err error) {
cmd := exec.Command(gccgoPath, "-###", "-S", "-x", "go", "-")
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:])
}
}
}
}
stdout, err := exec.Command(gccgoPath, "-dumpversion").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) types.Importer {
return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap)
}
// 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 (
"runtime"
"testing"
"golang.org/x/tools/go/types"
)
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",
"encoding/hex",
"encoding/json",
"encoding/pem",
"encoding/xml",
"errors",
"exp/proxy",
"exp/terminal",
"expvar",
"flag",
"fmt",
"go/ast",
"go/build",
"go/doc",
"go/format",
"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",
"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",
"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",
"old/regexp",
"old/template",
"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, which it most likely will be if we
// were compiled with gccgo.
if runtime.Compiler != "gccgo" {
t.Skip("This test needs gccgo")
return
}
var inst GccgoInstallation
err := inst.InitFromDriver("gccgo")
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)
if err != nil {
t.Error(err)
}
}
for _, pkg := range importablePackages {
_, err = imp(make(map[string]*types.Package), pkg)
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 []uint8) (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: "Ints", want: "func Ints(a []int)"},
{pkgpath: "unsafe", name: "Pointer", want: "type Pointer unsafe.Pointer"},
} {
runImporterTest(t, imp, nil, &test)
}
}
// 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 "golang.org/x/tools/go/gccgoimporter"
import (
"bytes"
"debug/elf"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"golang.org/x/tools/go/importer"
"golang.org/x/tools/go/types"
)
// 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"
goimporterMagic = "\n$$ "
archiveMagic = "!<ar"
)
// 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 elfreader io.ReaderAt
switch string(magic[:]) {
case gccgov1Magic, goimporterMagic:
// Raw export data.
reader = f
return
case archiveMagic:
// TODO(pcc): Read the archive directly instead of using "ar".
f.Close()
closer = nil
cmd := exec.Command("ar", "p", fpath)
var out []byte
out, err = cmd.Output()
if err != nil {
return
}
elfreader = bytes.NewReader(out)
default:
elfreader = f
}
ef, err := elf.NewFile(elfreader)
if err != nil {
return
}
sec := ef.Section(".go_export")
if sec == nil {
err = fmt.Errorf("%s: .go_export section not found", fpath)
return
}
reader = sec.Open()
return
}
func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) types.Importer {
return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
if pkgpath == "unsafe" {
return types.Unsafe, nil
}
fpath, err := findExportFile(searchpaths, pkgpath)
if err != nil {
return
}
reader, closer, err := openExportFile(fpath)
if err != nil {
return
}
if closer != nil {
defer closer.Close()
}
var magic [4]byte
_, err = reader.Read(magic[:])
if err != nil {
return
}
_, err = reader.Seek(0, 0)
if err != nil {
return
}
switch string(magic[:]) {
case gccgov1Magic:
var p parser
p.init(fpath, reader, imports)
pkg = p.parsePackage()
if initmap != nil {
initmap[pkg] = p.initdata
}
case goimporterMagic:
var data []byte
data, err = ioutil.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", string(magic[:]))
}
return
}
}
// 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 (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"testing"
"golang.org/x/tools/go/types"
)
type importerTest struct {
pkgpath, name, want, wantval string
wantinits []string
}
func runImporterTest(t *testing.T, imp types.Importer, initmap map[*types.Package]InitData, test *importerTest) {
pkg, err := imp(make(map[string]*types.Package), test.pkgpath)
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(pkg, obj)
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] {
if initdata.Priority != pkginit.Priority {
t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority)
}
found = true
break
}
}
if !found {
t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0])
}
// Each init function in the list other than the first one is a
// dependency of the function immediately before it. Check that
// the init functions appear in descending priority order.
priority := initdata.Priority
for _, wantdepinit := range test.wantinits[1:] {
found = false
for _, pkginit := range initdata.Inits {
if pkginit.InitFunc == wantdepinit {
if priority <= pkginit.Priority {
t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority)
}
found = true
priority = pkginit.Priority
break
}
}
if !found {
t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit)
}
}
}
}
var importerTests = [...]importerTest{
{pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
{pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1/1 + -1/1i)"},
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1/1 + 1/1i)"},
{pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1/1 + -1/1i)"},
{pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1/1 + 1/1i)"},
// TODO: enable this entry once bug has been tracked down
//{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
}
func TestGoxImporter(t *testing.T) {
initmap := make(map[*types.Package]InitData)
imp := GetImporter([]string{"testdata"}, initmap)
for _, test := range importerTests {
runImporterTest(t, imp, initmap, &test)
}
}
func TestObjImporter(t *testing.T) {
// This test relies on gccgo being around, which it most likely will be if we
// were compiled with gccgo.
if runtime.Compiler != "gccgo" {
t.Skip("This test needs gccgo")
return
}
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
initmap := make(map[*types.Package]InitData)
imp := GetImporter([]string{tmpdir}, initmap)
artmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
arinitmap := make(map[*types.Package]InitData)
arimp := GetImporter([]string{artmpdir}, arinitmap)
for _, test := range importerTests {
gofile := filepath.Join("testdata", test.pkgpath+".go")
ofile := filepath.Join(tmpdir, test.pkgpath+".o")
afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a")
cmd := exec.Command("gccgo", "-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)
}
}
if err = os.Remove(tmpdir); err != nil {
t.Fatal(err)
}
}
This diff is collapsed.
// 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"
"strings"
"testing"
"text/scanner"
"golang.org/x/tools/go/types"
)
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> func (? <type 1>) M (); >", 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.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)
}
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)
}
}
}
}
package complexnums
const NN = -1 - 1i
const NP = -1 + 1i
const PN = 1 - 1i
const PP = 1 + 1i
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 ;
package imports
import "fmt"
var Hello = fmt.Sprintf("Hello, world")
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>;
package pointer
type Int8Ptr *int8
v1;
package pointer;
pkgpath pointer;
type <type 1 "Int8Ptr" <type 2 *<type -1>>>;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment