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