blob: 3a26dfb8b92994468b03b3e40e904e91c9c290b0 [file] [log] [blame]
// 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 java package takes the result of an AST traversal by the
// importers package and queries the java command for the type
// information for the referenced Java classes and interfaces.
//
// It is the of go/types for Java types and is used by the bind
// package to generate Go wrappers for Java API on Android.
package java
import (
"bufio"
"bytes"
"errors"
"fmt"
"os/exec"
"reflect"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/mobile/internal/importers"
)
// Class is the bind representation of a Java class or
// interface.
// Use Import to convert class references to Class.
type Class struct {
// "java.pkg.Class.Inner"
Name string
// "java.pkg.Class$Inner"
FindName string
// JNI mangled name
JNIName string
// "Inner"
PkgName string
Funcs []*FuncSet
Methods []*FuncSet
// funcMap maps function names.
funcMap map[string]*FuncSet
// FuncMap maps method names.
methodMap map[string]*FuncSet
// All methods, including methods from
// supers.
AllMethods []*FuncSet
Vars []*Var
Supers []string
Final bool
Abstract bool
Interface bool
Throwable bool
// Whether the class has a no-arg constructor
HasNoArgCon bool
}
// FuncSet is the set of overloaded variants of a function.
// If the function is not overloaded, its FuncSet contains
// one entry.
type FuncSet struct {
Name string
GoName string
Funcs []*Func
CommonSig
}
// CommonSig is a signature compatible with every
// overloaded variant of a FuncSet.
type CommonSig struct {
// Variadic is set if the signature covers variants
// with varying number of parameters.
Variadic bool
// HasRet is true if at least one variant returns a
// value.
HasRet bool
Throws bool
Params []*Type
Ret *Type
}
// Func is a Java static function or method or constructor.
type Func struct {
FuncSig
ArgDesc string
// Mangled JNI name
JNIName string
Static bool
Abstract bool
Final bool
Public bool
Constructor bool
Params []*Type
Ret *Type
Decl string
Throws string
}
// FuncSig uniquely identifies a Java Func.
type FuncSig struct {
Name string
// The method descriptor, in JNI format.
Desc string
}
// Var is a Java member variable.
type Var struct {
Name string
Static bool
Final bool
Val string
Type *Type
}
// Type is a Java type.
type Type struct {
Kind TypeKind
Class string
Elem *Type
}
type TypeKind int
type Importer struct {
Bootclasspath string
Classpath string
// JavaPkg is java package name for generated classes.
JavaPkg string
clsMap map[string]*Class
}
// funcRef is a reference to a Java function (static method).
// It is used as a key to filter unused Java functions.
type funcRef struct {
clsName string
goName string
}
type errClsNotFound struct {
name string
}
const (
Int TypeKind = iota
Boolean
Short
Char
Byte
Long
Float
Double
String
Array
Object
)
func (e *errClsNotFound) Error() string {
return "class not found: " + e.name
}
// IsAvailable reports whether the required tools are available for
// Import to work. In particular, IsAvailable checks the existence
// of the javap binary.
func IsAvailable() bool {
_, err := javapPath()
return err == nil
}
func javapPath() (string, error) {
return exec.LookPath("javap")
}
// Import returns Java Class descriptors for a list of references.
//
// The javap command from the Java SDK is used to dump
// class information. Its output looks like this:
//
// Compiled from "System.java"
// public final class java.lang.System {
// public static final java.io.InputStream in;
// descriptor: Ljava/io/InputStream;
// public static final java.io.PrintStream out;
// descriptor: Ljava/io/PrintStream;
// public static final java.io.PrintStream err;
// descriptor: Ljava/io/PrintStream;
// public static void setIn(java.io.InputStream);
// descriptor: (Ljava/io/InputStream;)V
//
// ...
//
// }
func (j *Importer) Import(refs *importers.References) ([]*Class, error) {
if j.clsMap == nil {
j.clsMap = make(map[string]*Class)
}
clsSet := make(map[string]struct{})
var names []string
for _, ref := range refs.Refs {
// The reference could be to some/pkg.Class or some/pkg/Class.Identifier. Include both.
pkg := strings.Replace(ref.Pkg, "/", ".", -1)
for _, cls := range []string{pkg, pkg + "." + ref.Name} {
if _, exists := clsSet[cls]; !exists {
clsSet[cls] = struct{}{}
names = append(names, cls)
}
}
}
// Make sure toString() is included; it is called when wrapping Java exception types to Go
// errors.
refs.Names["ToString"] = struct{}{}
funcRefs := make(map[funcRef]struct{})
for _, ref := range refs.Refs {
pkgName := strings.Replace(ref.Pkg, "/", ".", -1)
funcRefs[funcRef{pkgName, ref.Name}] = struct{}{}
}
classes, err := j.importClasses(names, true)
if err != nil {
return nil, err
}
j.filterReferences(classes, refs, funcRefs)
supers, err := j.importReferencedClasses(classes)
if err != nil {
return nil, err
}
j.filterReferences(supers, refs, funcRefs)
// Embedders refer to every exported Go struct that will have its class
// generated. Allow Go code to reverse bind to those classes by synthesizing
// their class descriptors.
for _, emb := range refs.Embedders {
n := emb.Pkg + "." + emb.Name
if j.JavaPkg != "" {
n = j.JavaPkg + "." + n
}
if _, exists := j.clsMap[n]; exists {
continue
}
clsSet[n] = struct{}{}
cls := &Class{
Name: n,
FindName: n,
JNIName: JNIMangle(n),
PkgName: emb.Name,
HasNoArgCon: true,
}
for _, ref := range emb.Refs {
jpkg := strings.Replace(ref.Pkg, "/", ".", -1)
super := jpkg + "." + ref.Name
if _, exists := j.clsMap[super]; !exists {
return nil, fmt.Errorf("failed to find Java class %s, embedded by %s", super, n)
}
cls.Supers = append(cls.Supers, super)
}
classes = append(classes, cls)
j.clsMap[cls.Name] = cls
}
// Include implicit classes that are used in parameter or return values.
for _, cls := range classes {
for _, fsets := range [][]*FuncSet{cls.Funcs, cls.Methods} {
for _, fs := range fsets {
for _, f := range fs.Funcs {
names := j.implicitFuncTypes(f)
for _, name := range names {
if _, exists := clsSet[name]; exists {
continue
}
clsSet[name] = struct{}{}
classes = append(classes, j.clsMap[name])
}
}
}
}
}
for _, cls := range j.clsMap {
j.fillFuncSigs(cls.Funcs)
j.fillFuncSigs(cls.Methods)
for _, m := range cls.Methods {
j.fillSuperSigs(cls, m)
}
}
for _, cls := range j.clsMap {
j.fillAllMethods(cls)
}
// Include classes that appear as ancestor types for overloaded signatures.
for _, cls := range classes {
for _, funcs := range [][]*FuncSet{cls.Funcs, cls.AllMethods} {
for _, f := range funcs {
for _, p := range f.Params {
if p == nil || p.Kind != Object {
continue
}
if _, exists := clsSet[p.Class]; !exists {
clsSet[p.Class] = struct{}{}
classes = append(classes, j.clsMap[p.Class])
}
}
if t := f.Ret; t != nil && t.Kind == Object {
if _, exists := clsSet[t.Class]; !exists {
clsSet[t.Class] = struct{}{}
classes = append(classes, j.clsMap[t.Class])
}
}
}
}
}
for _, cls := range classes {
j.fillJNINames(cls.Funcs)
j.fillJNINames(cls.AllMethods)
}
j.fillThrowables(classes)
return classes, nil
}
func (j *Importer) fillJNINames(funcs []*FuncSet) {
for _, fs := range funcs {
for _, f := range fs.Funcs {
f.JNIName = JNIMangle(f.Name)
if len(fs.Funcs) > 1 {
f.JNIName += "__" + JNIMangle(f.ArgDesc)
}
}
}
}
// commonType finds the most specific type common to t1 and t2.
// If t1 and t2 are both Java classes, the most specific ancestor
// class is returned.
// Else if the types are equal, their type is returned.
// Finally, nil is returned, indicating no common type.
func commonType(clsMap map[string]*Class, t1, t2 *Type) *Type {
if t1 == nil || t2 == nil {
return nil
}
if reflect.DeepEqual(t1, t2) {
return t1
}
if t1.Kind != Object || t2.Kind != Object {
// The types are fundamentally incompatible
return nil
}
superSet := make(map[string]struct{})
supers := []string{t1.Class}
for len(supers) > 0 {
var newSupers []string
for _, s := range supers {
cls := clsMap[s]
superSet[s] = struct{}{}
newSupers = append(newSupers, cls.Supers...)
}
supers = newSupers
}
supers = []string{t2.Class}
for len(supers) > 0 {
var newSupers []string
for _, s := range supers {
if _, exists := superSet[s]; exists {
return &Type{Kind: Object, Class: s}
}
cls := clsMap[s]
newSupers = append(newSupers, cls.Supers...)
}
supers = newSupers
}
return &Type{Kind: Object, Class: "java.lang.Object"}
}
// combineSigs finds the most specific function signature
// that covers all its overload variants.
// If a function has only one variant, its common signature
// is the signature of that variant.
func combineSigs(clsMap map[string]*Class, sigs ...CommonSig) CommonSig {
var common CommonSig
minp := len(sigs[0].Params)
for i := 1; i < len(sigs); i++ {
sig := sigs[i]
n := len(sig.Params)
common.Variadic = common.Variadic || sig.Variadic || n != minp
if n < minp {
minp = n
}
}
for i, sig := range sigs {
for j, p := range sig.Params {
idx := j
// If the common signature is variadic, combine all parameters in the
// last parameter type of the shortest parameter list.
if idx > minp {
idx = minp
}
if idx < len(common.Params) {
common.Params[idx] = commonType(clsMap, common.Params[idx], p)
} else {
common.Params = append(common.Params, p)
}
}
common.Throws = common.Throws || sig.Throws
common.HasRet = common.HasRet || sig.HasRet
if i > 0 {
common.Ret = commonType(clsMap, common.Ret, sig.Ret)
} else {
common.Ret = sig.Ret
}
}
return common
}
// fillSuperSigs combines methods signatures with super class signatures,
// to preserve the assignability of classes to their super classes.
//
// For example, the class
//
// class A {
// void f();
// }
//
// is by itself represented by the Go interface
//
// type A interface {
// f()
// }
//
// However, if class
//
// class B extends A {
// void f(int);
// }
//
// is also imported, it will be represented as
//
// type B interface {
// f(...int32)
// }
//
// To make Go B assignable to Go A, the signature of A's f must
// be updated to f(...int32) as well.
func (j *Importer) fillSuperSigs(cls *Class, m *FuncSet) {
for _, s := range cls.Supers {
sup := j.clsMap[s]
if sm, exists := sup.methodMap[m.GoName]; exists {
sm.CommonSig = combineSigs(j.clsMap, sm.CommonSig, m.CommonSig)
}
j.fillSuperSigs(sup, m)
}
}
func (v *Var) Constant() bool {
return v.Static && v.Final && v.Val != ""
}
// Mangle a name according to
// http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/design.html#wp16696
//
// TODO: Support unicode characters
func JNIMangle(s string) string {
var m []byte
for i := 0; i < len(s); i++ {
switch c := s[i]; c {
case '.', '/':
m = append(m, '_')
case '$':
m = append(m, "_00024"...)
case '_':
m = append(m, "_1"...)
case ';':
m = append(m, "_2"...)
case '[':
m = append(m, "_3"...)
default:
m = append(m, c)
}
}
return string(m)
}
func (t *Type) Type() string {
switch t.Kind {
case Int:
return "int"
case Boolean:
return "boolean"
case Short:
return "short"
case Char:
return "char"
case Byte:
return "byte"
case Long:
return "long"
case Float:
return "float"
case Double:
return "double"
case String:
return "String"
case Array:
return t.Elem.Type() + "[]"
case Object:
return t.Class
default:
panic("invalid kind")
}
}
func (t *Type) JNIType() string {
switch t.Kind {
case Int:
return "jint"
case Boolean:
return "jboolean"
case Short:
return "jshort"
case Char:
return "jchar"
case Byte:
return "jbyte"
case Long:
return "jlong"
case Float:
return "jfloat"
case Double:
return "jdouble"
case String:
return "jstring"
case Array:
return "jarray"
case Object:
return "jobject"
default:
panic("invalid kind")
}
}
func (t *Type) CType() string {
switch t.Kind {
case Int, Boolean, Short, Char, Byte, Long, Float, Double:
return t.JNIType()
case String:
return "nstring"
case Array:
if t.Elem.Kind != Byte {
panic("unsupported array type")
}
return "nbyteslice"
case Object:
return "jint"
default:
panic("invalid kind")
}
}
func (t *Type) JNICallType() string {
switch t.Kind {
case Int:
return "Int"
case Boolean:
return "Boolean"
case Short:
return "Short"
case Char:
return "Char"
case Byte:
return "Byte"
case Long:
return "Long"
case Float:
return "Float"
case Double:
return "Double"
case String, Object, Array:
return "Object"
default:
panic("invalid kind")
}
}
func (j *Importer) filterReferences(classes []*Class, refs *importers.References, funcRefs map[funcRef]struct{}) {
for _, cls := range classes {
var filtered []*FuncSet
for _, f := range cls.Funcs {
if _, exists := funcRefs[funcRef{cls.Name, f.GoName}]; exists {
filtered = append(filtered, f)
}
}
cls.Funcs = filtered
filtered = nil
for _, m := range cls.Methods {
if _, exists := refs.Names[m.GoName]; exists {
filtered = append(filtered, m)
}
}
cls.Methods = filtered
}
}
// importClasses imports the named classes from the classpaths of the Importer.
func (j *Importer) importClasses(names []string, allowMissingClasses bool) ([]*Class, error) {
if len(names) == 0 {
return nil, nil
}
args := []string{"-J-Duser.language=en", "-s", "-protected", "-constants"}
args = append(args, "-classpath", j.Classpath)
if j.Bootclasspath != "" {
args = append(args, "-bootclasspath", j.Bootclasspath)
}
args = append(args, names...)
javapPath, err := javapPath()
if err != nil {
return nil, err
}
javap := exec.Command(javapPath, args...)
out, err := javap.CombinedOutput()
if err != nil {
if _, ok := err.(*exec.ExitError); !ok {
return nil, fmt.Errorf("javap failed: %v", err)
}
// Not every name is a Java class so an exit error from javap is not
// fatal.
}
s := bufio.NewScanner(bytes.NewBuffer(out))
var classes []*Class
for _, name := range names {
cls, err := j.scanClass(s, name)
if err != nil {
_, notfound := err.(*errClsNotFound)
if notfound && allowMissingClasses {
continue
}
if notfound && name != "android.databinding.DataBindingComponent" {
return nil, err
}
// The Android Databinding library generates android.databinding.DataBindingComponent
// too late in the build process for the gobind plugin to import it. Synthesize a class
// for it instead.
cls = &Class{
Name: name,
FindName: name,
Interface: true,
PkgName: "databinding",
JNIName: JNIMangle(name),
}
}
classes = append(classes, cls)
j.clsMap[name] = cls
}
return classes, nil
}
// importReferencedClasses imports all implicit classes (super types, parameter and
// return types) for the given classes not already imported.
func (j *Importer) importReferencedClasses(classes []*Class) ([]*Class, error) {
var allCls []*Class
// Include methods from extended or implemented classes.
for {
set := make(map[string]struct{})
for _, cls := range classes {
j.unknownImplicitClasses(cls, set)
}
if len(set) == 0 {
break
}
var names []string
for n := range set {
names = append(names, n)
}
newCls, err := j.importClasses(names, false)
if err != nil {
return nil, err
}
allCls = append(allCls, newCls...)
classes = newCls
}
return allCls, nil
}
func (j *Importer) implicitFuncTypes(f *Func) []string {
var unk []string
if rt := f.Ret; rt != nil && rt.Kind == Object {
unk = append(unk, rt.Class)
}
for _, t := range f.Params {
if t.Kind == Object {
unk = append(unk, t.Class)
}
}
return unk
}
func (j *Importer) unknownImplicitClasses(cls *Class, set map[string]struct{}) {
for _, fsets := range [][]*FuncSet{cls.Funcs, cls.Methods} {
for _, fs := range fsets {
for _, f := range fs.Funcs {
names := j.implicitFuncTypes(f)
for _, name := range names {
if _, exists := j.clsMap[name]; !exists {
set[name] = struct{}{}
}
}
}
}
}
for _, n := range cls.Supers {
if s, exists := j.clsMap[n]; exists {
j.unknownImplicitClasses(s, set)
} else {
set[n] = struct{}{}
}
}
}
func (j *Importer) implicitFuncClasses(funcs []*FuncSet, impl []string) []string {
var l []string
for _, fs := range funcs {
for _, f := range fs.Funcs {
if rt := f.Ret; rt != nil && rt.Kind == Object {
l = append(l, rt.Class)
}
for _, t := range f.Params {
if t.Kind == Object {
l = append(l, t.Class)
}
}
}
}
return impl
}
func (j *Importer) scanClass(s *bufio.Scanner, name string) (*Class, error) {
if !s.Scan() {
return nil, fmt.Errorf("%s: missing javap header", name)
}
head := s.Text()
if errPref := "Error: "; strings.HasPrefix(head, errPref) {
msg := head[len(errPref):]
if strings.HasPrefix(msg, "class not found: "+name) {
return nil, &errClsNotFound{name}
}
return nil, errors.New(msg)
}
if !strings.HasPrefix(head, "Compiled from ") {
return nil, fmt.Errorf("%s: unexpected header: %s", name, head)
}
if !s.Scan() {
return nil, fmt.Errorf("%s: missing javap class declaration", name)
}
clsDecl := s.Text()
cls, err := j.scanClassDecl(name, clsDecl)
if err != nil {
return nil, err
}
cls.JNIName = JNIMangle(cls.Name)
clsElems := strings.Split(cls.Name, ".")
cls.PkgName = clsElems[len(clsElems)-1]
var funcs []*Func
for s.Scan() {
decl := strings.TrimSpace(s.Text())
if decl == "}" {
break
} else if decl == "" {
continue
}
if !s.Scan() {
return nil, fmt.Errorf("%s: missing descriptor for member %q", name, decl)
}
desc := strings.TrimSpace(s.Text())
desc = strings.TrimPrefix(desc, "descriptor: ")
var static, final, abstract, public bool
// Trim modifiders from the declaration.
loop:
for {
idx := strings.Index(decl, " ")
if idx == -1 {
break
}
keyword := decl[:idx]
switch keyword {
case "public":
public = true
case "protected", "native":
// ignore
case "static":
static = true
case "final":
final = true
case "abstract":
abstract = true
default:
// Hopefully we reached the declaration now.
break loop
}
decl = decl[idx+1:]
}
// Trim ending ;
decl = decl[:len(decl)-1]
if idx := strings.Index(decl, "("); idx != -1 {
f, err := j.scanMethod(decl, desc, idx)
if err != nil {
return nil, fmt.Errorf("%s: %v", name, err)
}
if f != nil {
f.Static = static
f.Abstract = abstract
f.Public = public || cls.Interface
f.Final = final
f.Constructor = f.Name == cls.FindName
if f.Constructor {
cls.HasNoArgCon = cls.HasNoArgCon || len(f.Params) == 0
f.Public = f.Public && !cls.Abstract
f.Name = "new"
f.Ret = &Type{Class: name, Kind: Object}
}
funcs = append(funcs, f)
}
} else {
// Member is a variable
v, err := j.scanVar(decl, desc)
if err != nil {
return nil, fmt.Errorf("%s: %v", name, err)
}
if v != nil && public {
v.Static = static
v.Final = final
cls.Vars = append(cls.Vars, v)
}
}
}
for _, f := range funcs {
var m map[string]*FuncSet
var l *[]*FuncSet
goName := initialUpper(f.Name)
if f.Static || f.Constructor {
m = cls.funcMap
l = &cls.Funcs
} else {
m = cls.methodMap
l = &cls.Methods
}
fs, exists := m[goName]
if !exists {
fs = &FuncSet{
Name: f.Name,
GoName: goName,
}
m[goName] = fs
*l = append(*l, fs)
}
fs.Funcs = append(fs.Funcs, f)
}
return cls, nil
}
func (j *Importer) scanClassDecl(name string, decl string) (*Class, error) {
isRoot := name == "java.lang.Object"
cls := &Class{
Name: name,
funcMap: make(map[string]*FuncSet),
methodMap: make(map[string]*FuncSet),
HasNoArgCon: isRoot,
}
const (
stMod = iota
stName
stExt
stImpl
)
superClsDecl := isRoot
st := stMod
var w []byte
// if > 0, we're inside a generics declaration
gennest := 0
for i := 0; i < len(decl); i++ {
c := decl[i]
switch c {
default:
if gennest == 0 {
w = append(w, c)
}
case '>':
gennest--
case '<':
gennest++
case '{':
if !superClsDecl && !cls.Interface {
cls.Supers = append(cls.Supers, "java.lang.Object")
}
return cls, nil
case ' ', ',':
if gennest > 0 {
break
}
switch w := string(w); w {
default:
switch st {
case stName:
if strings.Replace(w, "$", ".", -1) != strings.Replace(name, "$", ".", -1) {
return nil, fmt.Errorf("unexpected name %q in class declaration: %q", w, decl)
}
cls.FindName = w
case stExt:
superClsDecl = true
cls.Supers = append(cls.Supers, w)
case stImpl:
if !cls.Interface {
cls.Supers = append(cls.Supers, w)
}
default:
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
case "":
// skip
case "public":
if st != stMod {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
case "abstract":
if st != stMod {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
cls.Abstract = true
case "final":
if st != stMod {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
cls.Final = true
case "interface":
cls.Interface = true
fallthrough
case "class":
if st != stMod {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
st = stName
case "extends":
if st != stName {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
st = stExt
case "implements":
if st != stName && st != stExt {
return nil, fmt.Errorf("unexpected %q in class declaration: %q", w, decl)
}
st = stImpl
}
w = w[:0]
}
}
return nil, fmt.Errorf("missing ending { in class declaration: %q", decl)
}
func (j *Importer) scanVar(decl, desc string) (*Var, error) {
v := new(Var)
const eq = " = "
idx := strings.Index(decl, eq)
if idx != -1 {
val, ok := j.parseJavaValue(decl[idx+len(eq):])
if !ok {
// Skip constants that cannot be represented in Go
return nil, nil
}
v.Val = val
} else {
idx = len(decl)
}
for i := idx - 1; i >= 0; i-- {
if i == 0 || decl[i-1] == ' ' {
v.Name = decl[i:idx]
break
}
}
if v.Name == "" {
return nil, fmt.Errorf("unable to parse member name from declaration: %q", decl)
}
typ, _, err := j.parseJavaType(desc)
if err != nil {
return nil, fmt.Errorf("invalid type signature for %s: %q", v.Name, desc)
}
v.Type = typ
return v, nil
}
func (j *Importer) scanMethod(decl, desc string, parenIdx int) (*Func, error) {
// Member is a method
f := new(Func)
f.Desc = desc
for i := parenIdx - 1; i >= 0; i-- {
if i == 0 || decl[i-1] == ' ' {
f.Name = decl[i:parenIdx]
break
}
}
if f.Name == "" {
return nil, fmt.Errorf("unable to parse method name from declaration: %q", decl)
}
if desc[0] != '(' {
return nil, fmt.Errorf("invalid descriptor for method %s: %q", f.Name, desc)
}
const throws = " throws "
if idx := strings.Index(decl, throws); idx != -1 {
f.Throws = decl[idx+len(throws):]
}
i := 1
for desc[i] != ')' {
typ, n, err := j.parseJavaType(desc[i:])
if err != nil {
return nil, fmt.Errorf("invalid descriptor for method %s: %v", f.Name, err)
}
i += n
f.Params = append(f.Params, typ)
}
f.ArgDesc = desc[1:i]
i++ // skip ending )
if desc[i] != 'V' {
typ, _, err := j.parseJavaType(desc[i:])
if err != nil {
return nil, fmt.Errorf("invalid descriptor for method %s: %v", f.Name, err)
}
f.Ret = typ
}
return f, nil
}
func (j *Importer) fillThrowables(classes []*Class) {
thrCls, ok := j.clsMap["java.lang.Throwable"]
if !ok {
// If Throwable isn't in the class map
// no imported class inherits from Throwable
return
}
for _, cls := range classes {
j.fillThrowableFor(cls, thrCls)
}
}
func (j *Importer) fillThrowableFor(cls, thrCls *Class) {
if cls.Interface || cls.Throwable {
return
}
cls.Throwable = cls == thrCls
for _, name := range cls.Supers {
sup := j.clsMap[name]
j.fillThrowableFor(sup, thrCls)
cls.Throwable = cls.Throwable || sup.Throwable
}
}
func commonSig(f *Func) CommonSig {
return CommonSig{
Params: f.Params,
Ret: f.Ret,
HasRet: f.Ret != nil,
Throws: f.Throws != "",
}
}
func (j *Importer) fillFuncSigs(funcs []*FuncSet) {
for _, fs := range funcs {
var sigs []CommonSig
for _, f := range fs.Funcs {
sigs = append(sigs, commonSig(f))
}
fs.CommonSig = combineSigs(j.clsMap, sigs...)
}
}
func (j *Importer) fillAllMethods(cls *Class) {
if len(cls.AllMethods) > 0 {
return
}
for _, supName := range cls.Supers {
super := j.clsMap[supName]
j.fillAllMethods(super)
}
var fsets []*FuncSet
fsets = append(fsets, cls.Methods...)
for _, supName := range cls.Supers {
super := j.clsMap[supName]
fsets = append(fsets, super.AllMethods...)
}
sigs := make(map[FuncSig]struct{})
methods := make(map[string]*FuncSet)
for _, fs := range fsets {
clsFs, exists := methods[fs.Name]
if !exists {
clsFs = &FuncSet{
Name: fs.Name,
GoName: fs.GoName,
CommonSig: fs.CommonSig,
}
cls.AllMethods = append(cls.AllMethods, clsFs)
methods[fs.Name] = clsFs
} else {
// Combine the (overloaded) signature with the other variants.
clsFs.CommonSig = combineSigs(j.clsMap, clsFs.CommonSig, fs.CommonSig)
}
for _, f := range fs.Funcs {
if _, exists := sigs[f.FuncSig]; exists {
continue
}
sigs[f.FuncSig] = struct{}{}
clsFs.Funcs = append(clsFs.Funcs, f)
}
}
}
func (j *Importer) parseJavaValue(v string) (string, bool) {
v = strings.TrimRight(v, "ldf")
switch v {
case "", "NaN", "Infinity", "-Infinity":
return "", false
default:
if v[0] == '\'' {
// Skip character constants, since they can contain invalid code points
// that are unacceptable to Go.
return "", false
}
return v, true
}
}
func (j *Importer) parseJavaType(desc string) (*Type, int, error) {
t := new(Type)
var n int
if desc == "" {
return t, n, errors.New("empty type signature")
}
n++
switch desc[0] {
case 'Z':
t.Kind = Boolean
case 'B':
t.Kind = Byte
case 'C':
t.Kind = Char
case 'S':
t.Kind = Short
case 'I':
t.Kind = Int
case 'J':
t.Kind = Long
case 'F':
t.Kind = Float
case 'D':
t.Kind = Double
case 'L':
var clsName string
for i := n; i < len(desc); i++ {
if desc[i] == ';' {
clsName = strings.Replace(desc[n:i], "/", ".", -1)
clsName = strings.Replace(clsName, "$", ".", -1)
n += i - n + 1
break
}
}
if clsName == "" {
return t, n, errors.New("missing ; in class type signature")
}
if clsName == "java.lang.String" {
t.Kind = String
} else {
t.Kind = Object
t.Class = clsName
}
case '[':
et, n2, err := j.parseJavaType(desc[n:])
if err != nil {
return t, n, err
}
n += n2
t.Kind = Array
t.Elem = et
default:
return t, n, fmt.Errorf("invalid type signature: %s", desc)
}
return t, n, nil
}
func initialUpper(s string) string {
if s == "" {
return ""
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToUpper(r)) + s[n:]
}