go.tools/cmd/godex: a tool to dump export information
Initial implementation. Lots of missing pieces. Example use: godex math godex math.Sin godex math.Sin math.Cos LGTM=adonovan R=adonovan CC=golang-codereviews https://golang.org/cl/76890044
This commit is contained in:
parent
a1c1cf19ba
commit
4a27ee3a1b
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
// The godex command prints (dumps) exported information of packages
|
||||
// or selected package objects.
|
||||
//
|
||||
// In contrast to godoc, godex extracts this information from compiled
|
||||
// object files. Hence the exported data is truly what a compiler will
|
||||
// see, at the cost of missing commentary.
|
||||
//
|
||||
// Usage: godex [flags] {path|qualifiedIdent}
|
||||
//
|
||||
// Each argument must be a package path, or a qualified identifier.
|
||||
//
|
||||
// The flags are:
|
||||
//
|
||||
// -s=src
|
||||
// only consider packages from src, where src is one of the supported compilers
|
||||
// -v
|
||||
// verbose mode
|
||||
//
|
||||
// The following sources are supported:
|
||||
//
|
||||
// gc
|
||||
// gc-generated object files
|
||||
// gccgo
|
||||
// gccgo-generated object files
|
||||
// gccgo-new
|
||||
// gccgo-generated object files using a condensed format (experimental)
|
||||
// source
|
||||
// (uncompiled) source code (not yet implemented)
|
||||
//
|
||||
// If no -s argument is provided, godex will try to find a matching source.
|
||||
//
|
||||
// TODO(gri) expand this documentation
|
||||
//
|
||||
package main
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements access to gc-generated export data.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/gcimporter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("gc", protect(gcimporter.Import))
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements access to gccgo-generated export data.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"code.google.com/p/go.tools/go/gccgoimporter"
|
||||
"code.google.com/p/go.tools/go/importer"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// importer for default gccgo
|
||||
var inst gccgoimporter.GccgoInstallation
|
||||
inst.InitFromDriver("gccgo")
|
||||
register("gccgo", protect(inst.GetImporter(nil)))
|
||||
|
||||
// importer for gccgo using condensed export format (experimental)
|
||||
register("gccgo-new", protect(gccgoNewImporter))
|
||||
}
|
||||
|
||||
func gccgoNewImporter(packages map[string]*types.Package, path string) (*types.Package, error) {
|
||||
reader, closer, err := openGccgoExportFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
// TODO(gri) importer.ImportData takes a []byte instead of an io.Reader;
|
||||
// hence the need to read some amount of data. At the same time we don't
|
||||
// want to read the entire, potentially very large object file. For now,
|
||||
// read 10K. Fix this!
|
||||
var data = make([]byte, 10<<10)
|
||||
n, err := reader.Read(data)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return importer.ImportData(packages, data[:n])
|
||||
}
|
||||
|
||||
// openGccgoExportFile was copied from gccgoimporter.
|
||||
func openGccgoExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
closer = f
|
||||
|
||||
var magic [4]byte
|
||||
_, err = f.ReadAt(magic[:], 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if string(magic[:]) == "v1;\n" {
|
||||
// Raw export data.
|
||||
reader = f
|
||||
return
|
||||
}
|
||||
|
||||
ef, err := elf.NewFile(f)
|
||||
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
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2014 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 main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var (
|
||||
source = flag.String("s", "", "only consider packages from this source")
|
||||
verbose = flag.Bool("v", false, "verbose mode")
|
||||
)
|
||||
|
||||
var (
|
||||
importFailed = errors.New("import failed")
|
||||
importers = make(map[string]types.Importer)
|
||||
packages = make(map[string]*types.Package)
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func report(msg string) {
|
||||
fmt.Fprintln(os.Stderr, "error: "+msg)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() == 0 {
|
||||
report("no package name, path, or file provided")
|
||||
}
|
||||
|
||||
imp := tryImport
|
||||
if *source != "" {
|
||||
imp = importers[*source]
|
||||
if imp == nil {
|
||||
report("source must be one of: " + importersList())
|
||||
}
|
||||
}
|
||||
|
||||
for _, arg := range flag.Args() {
|
||||
if *verbose {
|
||||
fmt.Fprintf(os.Stderr, "(processing %s)\n", arg)
|
||||
}
|
||||
|
||||
// determine import path, object name
|
||||
var path, name string
|
||||
elems := strings.Split(arg, ".")
|
||||
switch len(elems) {
|
||||
case 2:
|
||||
name = elems[1]
|
||||
fallthrough
|
||||
case 1:
|
||||
path = elems[0]
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "ignoring %q: invalid path or (qualified) identifier\n", arg)
|
||||
continue
|
||||
}
|
||||
|
||||
// import package
|
||||
pkg, err := imp(packages, path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "ignoring %q: %s\n", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// filter objects if needed
|
||||
filter := exportFilter
|
||||
if name != "" {
|
||||
f := filter
|
||||
filter = func(obj types.Object) bool {
|
||||
// TODO(gri) perhaps use regular expression matching here?
|
||||
return f(obj) && obj.Name() == name
|
||||
}
|
||||
}
|
||||
|
||||
// print contents
|
||||
print(os.Stdout, pkg, filter)
|
||||
}
|
||||
}
|
||||
|
||||
// protect protects an importer imp from panics and returns the protected importer.
|
||||
func protect(imp types.Importer) types.Importer {
|
||||
return func(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
pkg = nil
|
||||
err = importFailed
|
||||
}
|
||||
}()
|
||||
return imp(packages, path)
|
||||
}
|
||||
}
|
||||
|
||||
func tryImport(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
|
||||
for source, imp := range importers {
|
||||
if *verbose {
|
||||
fmt.Fprintf(os.Stderr, "(trying as %s)\n", source)
|
||||
}
|
||||
pkg, err = imp(packages, path)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func register(source string, imp types.Importer) {
|
||||
if _, ok := importers[source]; ok {
|
||||
panic(source + " importer already registered")
|
||||
}
|
||||
importers[source] = imp
|
||||
}
|
||||
|
||||
func importersList() string {
|
||||
var s string
|
||||
for n := range importers {
|
||||
if len(s) == 0 {
|
||||
s = n
|
||||
} else {
|
||||
s = s + ", " + n
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func exportFilter(obj types.Object) bool {
|
||||
return obj.Exported()
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2014 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 main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// TODO(gri) handle indentation
|
||||
// TODO(gri) filter unexported fields of struct types?
|
||||
// TODO(gri) use tabwriter for alignment?
|
||||
|
||||
func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) {
|
||||
var p printer
|
||||
p.pkg = pkg
|
||||
p.printPackage(pkg, filter)
|
||||
io.Copy(w, &p.buf)
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
pkg *types.Package
|
||||
buf bytes.Buffer
|
||||
indent int
|
||||
}
|
||||
|
||||
func (p *printer) print(s string) {
|
||||
p.buf.WriteString(s)
|
||||
}
|
||||
|
||||
func (p *printer) printf(format string, args ...interface{}) {
|
||||
fmt.Fprintf(&p.buf, format, args...)
|
||||
}
|
||||
|
||||
func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) {
|
||||
// collect objects by kind
|
||||
var (
|
||||
consts []*types.Const
|
||||
typez []*types.TypeName // types without methods
|
||||
typem []*types.TypeName // types with methods
|
||||
vars []*types.Var
|
||||
funcs []*types.Func
|
||||
)
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
if !filter(obj) {
|
||||
continue
|
||||
}
|
||||
switch obj := obj.(type) {
|
||||
case *types.Const:
|
||||
consts = append(consts, obj)
|
||||
case *types.TypeName:
|
||||
if obj.Type().(*types.Named).NumMethods() > 0 {
|
||||
typem = append(typem, obj)
|
||||
} else {
|
||||
typez = append(typez, obj)
|
||||
}
|
||||
case *types.Var:
|
||||
vars = append(vars, obj)
|
||||
case *types.Func:
|
||||
funcs = append(funcs, obj)
|
||||
}
|
||||
}
|
||||
|
||||
p.printf("package %s\n\n", pkg.Name())
|
||||
|
||||
if len(consts) > 0 {
|
||||
p.print("const (\n")
|
||||
for _, obj := range consts {
|
||||
p.printObj(obj)
|
||||
p.print("\n")
|
||||
}
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
if len(vars) > 0 {
|
||||
p.print("var (\n")
|
||||
for _, obj := range vars {
|
||||
p.printObj(obj)
|
||||
p.print("\n")
|
||||
}
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
if len(typez) > 0 {
|
||||
p.print("type (\n")
|
||||
for _, obj := range typez {
|
||||
p.printf("\t%s ", obj.Name())
|
||||
types.WriteType(&p.buf, p.pkg, obj.Type().Underlying())
|
||||
p.print("\n")
|
||||
}
|
||||
p.print(")\n\n")
|
||||
}
|
||||
|
||||
for _, obj := range typem {
|
||||
p.printf("type %s ", obj.Name())
|
||||
typ := obj.Type().(*types.Named)
|
||||
types.WriteType(&p.buf, p.pkg, typ.Underlying())
|
||||
p.print("\n")
|
||||
for i, n := 0, typ.NumMethods(); i < n; i++ {
|
||||
p.printFunc(typ.Method(i))
|
||||
p.print("\n")
|
||||
}
|
||||
p.print("\n")
|
||||
}
|
||||
|
||||
for _, obj := range funcs {
|
||||
p.printFunc(obj)
|
||||
p.print("\n")
|
||||
}
|
||||
|
||||
p.print("\n")
|
||||
}
|
||||
|
||||
func (p *printer) printObj(obj types.Object) {
|
||||
p.printf("\t %s", obj.Name())
|
||||
// don't write untyped types (for constants)
|
||||
if typ := obj.Type(); typed(typ) {
|
||||
p.print(" ")
|
||||
types.WriteType(&p.buf, p.pkg, typ)
|
||||
}
|
||||
// write constant value
|
||||
if obj, ok := obj.(*types.Const); ok {
|
||||
p.printf(" = %s", obj.Val())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) printFunc(obj *types.Func) {
|
||||
p.print("func ")
|
||||
sig := obj.Type().(*types.Signature)
|
||||
if recv := sig.Recv(); recv != nil {
|
||||
p.print("(")
|
||||
if name := recv.Name(); name != "" {
|
||||
p.print(name)
|
||||
p.print(" ")
|
||||
}
|
||||
types.WriteType(&p.buf, p.pkg, recv.Type())
|
||||
p.print(") ")
|
||||
}
|
||||
p.print(obj.Name())
|
||||
types.WriteSignature(&p.buf, p.pkg, sig)
|
||||
}
|
||||
|
||||
func typed(typ types.Type) bool {
|
||||
if t, ok := typ.Underlying().(*types.Basic); ok {
|
||||
return t.Info()&types.IsUntyped == 0
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements access to export data from source.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register("source", protect(sourceImporter))
|
||||
}
|
||||
|
||||
func sourceImporter(packages map[string]*types.Package, path string) (*types.Package, error) {
|
||||
panic("unimplemented")
|
||||
}
|
Loading…
Reference in New Issue