2
0
mirror of synced 2025-02-23 14:58:12 +00:00
Elias Naur e4531be66f internal/importers: introduce package to analyze Java classes
The importers package adds functions to traverse the AST of a Go
file or set of packages and extract references to Java packages and
types.

The java package adds a parser that uses the javap command to extract
type information about Java classes and interfaces.

The resulting type information is needed to generate Java API wrappers
in Go.

This is the first part of the implementation of proposal golang/go#16876.

For golang/go#16876

Change-Id: Ic844472a1101354d61401d9e8c120acdee2519df
Reviewed-on: https://go-review.googlesource.com/28595
Reviewed-by: David Crawshaw <crawshaw@golang.org>
2016-09-16 17:24:51 +00:00

161 lines
4.4 KiB
Go

// 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.
// The importers package uses go/ast to analyze Go packages or Go files
// and collect references to types whose package has a package prefix.
// It is used by the language specific importers to determine the set of
// wrapper types to be generated.
//
// For example, in the Go file
//
// package javaprogram
//
// import "Java/java/lang"
//
// func F() {
// o := lang.Object.New()
// ...
// }
//
// the java importer uses this package to determine that the "java/lang"
// package and the wrapper interface, lang.Object, needs to be generated.
// After calling AnalyzeFile or AnalyzePackages, the References result
// contains the reference to lang.Object and the names set will contain
// "New".
package importers
import (
"errors"
"go/ast"
"go/build"
"go/parser"
"go/token"
"path"
"path/filepath"
"strconv"
"strings"
)
// References is the result of analyzing a Go file or set of Go packages.
//
// For example, the Go file
//
// package pkg
//
// import "Prefix/some/Package"
//
// var A = Package.Identifier
//
// Will result in a single PkgRef with the "some/Package" package and
// the Identifier name. The Names set will contain the single name,
// "Identifier".
type References struct {
// The list of references to identifiers in packages that are
// identified by a package prefix.
Refs []PkgRef
// The list of names used in at least one selector expression.
// Useful as a conservative upper bound on the set of identifiers
// referenced from a set of packages.
Names map[string]struct{}
}
// PkgRef is a reference to an identifier in a package.
type PkgRef struct {
Pkg string
Name string
}
type refsSaver struct {
pkgPrefix string
References
refMap map[PkgRef]struct{}
}
// AnalyzeFile scans the provided file for references to packages with the given
// package prefix. The list of unique (package, identifier) pairs is returned
func AnalyzeFile(file *ast.File, pkgPrefix string) (*References, error) {
visitor := newRefsSaver(pkgPrefix)
fset := token.NewFileSet()
files := map[string]*ast.File{file.Name.Name: file}
// Ignore errors (from unknown packages)
pkg, _ := ast.NewPackage(fset, files, visitor.importer(), nil)
ast.Walk(visitor, pkg)
return &visitor.References, nil
}
// AnalyzePackages scans the provided packages for references to packages with the given
// package prefix. The list of unique (package, identifier) pairs is returned
func AnalyzePackages(pkgs []*build.Package, pkgPrefix string) (*References, error) {
visitor := newRefsSaver(pkgPrefix)
imp := visitor.importer()
fset := token.NewFileSet()
for _, pkg := range pkgs {
fileNames := append(append([]string{}, pkg.GoFiles...), pkg.CgoFiles...)
files := make(map[string]*ast.File)
for _, name := range fileNames {
f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, name), nil, 0)
if err != nil {
return nil, err
}
files[name] = f
}
// Ignore errors (from unknown packages)
astpkg, _ := ast.NewPackage(fset, files, imp, nil)
ast.Walk(visitor, astpkg)
}
return &visitor.References, nil
}
func newRefsSaver(pkgPrefix string) *refsSaver {
s := &refsSaver{
pkgPrefix: pkgPrefix,
refMap: make(map[PkgRef]struct{}),
}
s.Names = make(map[string]struct{})
return s
}
func (v *refsSaver) importer() ast.Importer {
return func(imports map[string]*ast.Object, pkgPath string) (*ast.Object, error) {
if pkg, exists := imports[pkgPath]; exists {
return pkg, nil
}
if !strings.HasPrefix(pkgPath, v.pkgPrefix) {
return nil, errors.New("ignored")
}
pkg := ast.NewObj(ast.Pkg, path.Base(pkgPath))
imports[pkgPath] = pkg
return pkg, nil
}
}
func (v *refsSaver) Visit(n ast.Node) ast.Visitor {
switch n := n.(type) {
case *ast.SelectorExpr:
v.Names[n.Sel.Name] = struct{}{}
if x, ok := n.X.(*ast.Ident); ok && x.Obj != nil {
if imp, ok := x.Obj.Decl.(*ast.ImportSpec); ok {
pkgPath, err := strconv.Unquote(imp.Path.Value)
if err != nil {
return nil
}
if strings.HasPrefix(pkgPath, v.pkgPrefix) {
pkgPath = pkgPath[len(v.pkgPrefix):]
ref := PkgRef{Pkg: pkgPath, Name: n.Sel.Name}
if _, exists := v.refMap[ref]; !exists {
v.refMap[ref] = struct{}{}
v.Refs = append(v.Refs, ref)
}
}
return nil
}
}
case *ast.FuncDecl:
if n.Recv != nil { // Methods
v.Names[n.Name.Name] = struct{}{}
}
}
return v
}