go/gccgoexporter: an API for parsing gccgo export data
This package is provided as a stop-gap until gccgo uses the same export data format as gc. Once that occurs, this package will be deprecated and eventually deleted. The API is similar to (a subset of) gcexportdata. Change-Id: I3398dbb8eab508a24d12036bbadaa60c2c1e77b3 Reviewed-on: https://go-review.googlesource.com/31822 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
20055e012d
commit
5e7fa1cbaa
|
@ -0,0 +1,122 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package gccgoexportdata provides functions for reading export data
|
||||
// files containing type information produced by the gccgo compiler.
|
||||
//
|
||||
// This package is a stop-gap until gccgo uses the same export data
|
||||
// format as gc. Once that occurs, this package will be deprecated and
|
||||
// eventually deleted.
|
||||
package gccgoexportdata
|
||||
|
||||
// TODO(adonovan): add Find, Write, Importer to the API,
|
||||
// for symmetry with gcexportdata.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/internal/gccgoimporter"
|
||||
)
|
||||
|
||||
// CompilerInfo executes the specified gccgo compiler and returns
|
||||
// information about it: its version (e.g. "4.8.0"), its target triple
|
||||
// (e.g. "x86_64-unknown-linux-gnu"), and the list of directories it
|
||||
// searches to find standard packages.
|
||||
func CompilerInfo(gccgo string) (version, triple string, dirs []string, err error) {
|
||||
var inst gccgoimporter.GccgoInstallation
|
||||
err = inst.InitFromDriver(gccgo)
|
||||
if err == nil {
|
||||
version = inst.GccVersion
|
||||
triple = inst.TargetTriple
|
||||
dirs = inst.SearchPaths()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the file is an archive, extract the first section.
|
||||
const archiveMagic = "!<arch>\n"
|
||||
if bytes.HasPrefix(data, []byte(archiveMagic)) {
|
||||
section, err := firstSection(data[len(archiveMagic):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = section
|
||||
}
|
||||
|
||||
// Data contains an ELF file with a .go_export section.
|
||||
// ELF magic number is "\x7fELF".
|
||||
ef, err := elf.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sec := ef.Section(".go_export")
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("no .go_export section")
|
||||
}
|
||||
return sec.Open(), nil
|
||||
}
|
||||
|
||||
// firstSection returns the contents of the first non-empty section of the archive file.
|
||||
func firstSection(a []byte) ([]byte, error) {
|
||||
for len(a) >= 60 {
|
||||
var hdr []byte
|
||||
hdr, a = a[:60], a[60:]
|
||||
|
||||
modeStr := string(string(hdr[40:48]))
|
||||
mode, err := strconv.Atoi(strings.TrimSpace(modeStr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid mode: %q", modeStr)
|
||||
}
|
||||
|
||||
sizeStr := string(hdr[48:58])
|
||||
size, err := strconv.Atoi(strings.TrimSpace(sizeStr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid size: %q", sizeStr)
|
||||
}
|
||||
|
||||
var payload []byte
|
||||
payload, a = a[:size], a[size:]
|
||||
|
||||
if mode == 0 {
|
||||
continue // skip "/"
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
return nil, fmt.Errorf("archive has no non-empty sections")
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
// The package name is specified by path.
|
||||
//
|
||||
// The FileSet parameter is currently unused but exists for symmetry
|
||||
// with gcexportdata.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
return gccgoimporter.Parse(in, imports, path)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package gccgoexportdata_test
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/gccgoexportdata"
|
||||
)
|
||||
|
||||
// Test ensures this package can read gccgo export data from the
|
||||
// .go_export section of an ELF file.
|
||||
func Test(t *testing.T) {
|
||||
f, err := os.Open("testdata/errors.gox")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r, err := gccgoexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imports := make(map[string]*types.Package)
|
||||
pkg, err := gccgoexportdata.Read(r, nil, imports, "errors")
|
||||
if err != nil {
|
||||
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check type of errors.New.
|
||||
got := pkg.Scope().Lookup("New").Type().String()
|
||||
want := "func(text string) error"
|
||||
if got != want {
|
||||
t.Errorf("New.Type = %s, want %s", got, want)
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,28 @@
|
|||
package gccgoimporter
|
||||
|
||||
// This file opens a back door to the parser for golang.org/x/tools/go/gccgoexportdata.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Parse reads and parses gccgo export data from in and constructs a
|
||||
// Package, inserting it into the imports map.
|
||||
func Parse(in io.Reader, imports map[string]*types.Package, path string) (_ *types.Package, err error) {
|
||||
var p parser
|
||||
p.init(path, in, imports)
|
||||
defer func() {
|
||||
switch x := recover().(type) {
|
||||
case nil:
|
||||
// success
|
||||
case importError:
|
||||
err = x
|
||||
default:
|
||||
panic(x) // resume unexpected panic
|
||||
}
|
||||
}()
|
||||
pkg := p.parsePackage()
|
||||
imports[path] = pkg
|
||||
return pkg, err
|
||||
}
|
Loading…
Reference in New Issue