Before, the Java generator let Java interfaces inherit java.lang.Object methods. However, interfaces strictly doesn't inherit Object and since the JNI GetMethodID returns NULL for Object methods on interface classes, stop making Object a super class to interfaces. Change-Id: I3757c1ed02c07ccffab74a30132d5197742c6513 Reviewed-on: https://go-review.googlesource.com/30096 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
876 lines
19 KiB
Go
876 lines
19 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 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"
|
|
"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 []*Func
|
|
Methods []*Func
|
|
// All methods, including methods from
|
|
// supers.
|
|
AllMethods []*Func
|
|
Vars []*Var
|
|
Supers []string
|
|
Final bool
|
|
Abstract bool
|
|
Interface bool
|
|
Throwable bool
|
|
// Whether the class has a no-arg constructor
|
|
HasNoArgCon bool
|
|
}
|
|
|
|
// Func is a Java static function or method or constructor.
|
|
type Func struct {
|
|
FuncSig
|
|
ArgDesc string
|
|
GoName 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 {
|
|
bootclspath string
|
|
clspath string
|
|
clsMap map[string]*Class
|
|
}
|
|
|
|
const (
|
|
Int TypeKind = iota
|
|
Boolean
|
|
Short
|
|
Char
|
|
Byte
|
|
Long
|
|
Float
|
|
Double
|
|
String
|
|
Array
|
|
Object
|
|
)
|
|
|
|
var (
|
|
errClsNotFound = errors.New("class not found")
|
|
)
|
|
|
|
// IsAvailable returns 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 Import(bootclasspath, classpath string, refs *importers.References) ([]*Class, error) {
|
|
imp := &importer{
|
|
bootclspath: bootclasspath,
|
|
clspath: classpath,
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
classes, err := imp.importClasses(names, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, cls := range classes {
|
|
imp.fillAllMethods(cls)
|
|
}
|
|
imp.fillThrowables()
|
|
for _, cls := range classes {
|
|
imp.mangleOverloads(cls.AllMethods)
|
|
imp.mangleOverloads(cls.Funcs)
|
|
}
|
|
imp.filterReferences(classes, refs)
|
|
return classes, nil
|
|
}
|
|
|
|
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 (c *Class) HasSuper() bool {
|
|
return !c.Final && !c.Interface
|
|
}
|
|
|
|
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) {
|
|
refFuncs := make(map[[2]string]struct{})
|
|
for _, ref := range refs.Refs {
|
|
pkgName := strings.Replace(ref.Pkg, "/", ".", -1)
|
|
cls := j.clsMap[pkgName]
|
|
if cls == nil {
|
|
continue
|
|
}
|
|
refFuncs[[...]string{pkgName, ref.Name}] = struct{}{}
|
|
}
|
|
for _, cls := range classes {
|
|
var filtered []*Func
|
|
for _, f := range cls.Funcs {
|
|
if _, exists := refFuncs[[...]string{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
|
|
filtered = nil
|
|
for _, m := range cls.AllMethods {
|
|
if _, exists := refs.Names[m.GoName]; exists {
|
|
filtered = append(filtered, m)
|
|
}
|
|
}
|
|
cls.AllMethods = filtered
|
|
}
|
|
}
|
|
|
|
func (j *importer) importClasses(names []string, allowMissingClasses bool) ([]*Class, error) {
|
|
if len(names) == 0 {
|
|
return nil, nil
|
|
}
|
|
args := []string{"-s", "-protected", "-constants"}
|
|
if j.clspath != "" {
|
|
args = append(args, "-classpath", j.clspath)
|
|
}
|
|
if j.bootclspath != "" {
|
|
args = append(args, "-bootclasspath", j.bootclspath)
|
|
}
|
|
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: %s", 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 {
|
|
if allowMissingClasses && err == errClsNotFound {
|
|
continue
|
|
}
|
|
return nil, err
|
|
}
|
|
classes = append(classes, cls)
|
|
j.clsMap[name] = cls
|
|
}
|
|
// Include the methods from classes extended or implemented
|
|
unkCls := classes
|
|
for {
|
|
var unknown []string
|
|
for _, cls := range unkCls {
|
|
unknown = j.unknownSuperClasses(cls, unknown)
|
|
}
|
|
if len(unknown) == 0 {
|
|
break
|
|
}
|
|
newCls, err := j.importClasses(unknown, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, cls := range newCls {
|
|
j.clsMap[cls.Name] = cls
|
|
}
|
|
unkCls = newCls
|
|
}
|
|
return classes, nil
|
|
}
|
|
|
|
func (j *importer) unknownSuperClasses(cls *Class, unk []string) []string {
|
|
loop:
|
|
for _, n := range cls.Supers {
|
|
if s, exists := j.clsMap[n]; exists {
|
|
unk = j.unknownSuperClasses(s, unk)
|
|
} else {
|
|
for _, u := range unk {
|
|
if u == n {
|
|
continue loop
|
|
}
|
|
}
|
|
unk = append(unk, n)
|
|
}
|
|
}
|
|
return unk
|
|
}
|
|
|
|
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
|
|
}
|
|
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
|
|
}
|
|
if len(cls.Supers) == 0 {
|
|
if name == "java.lang.Object" {
|
|
cls.HasNoArgCon = true
|
|
} else if !cls.Interface {
|
|
cls.Supers = append(cls.Supers, "java.lang.Object")
|
|
}
|
|
}
|
|
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 {
|
|
if f.Static || f.Constructor {
|
|
cls.Funcs = append(cls.Funcs, f)
|
|
} else {
|
|
cls.Methods = append(cls.Methods, f)
|
|
}
|
|
}
|
|
return cls, nil
|
|
}
|
|
|
|
func (j *importer) scanClassDecl(name string, decl string) (*Class, error) {
|
|
cls := &Class{
|
|
Name: name,
|
|
}
|
|
const (
|
|
stMod = iota
|
|
stName
|
|
stExt
|
|
stImpl
|
|
)
|
|
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 '{':
|
|
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:
|
|
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() {
|
|
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 j.clsMap {
|
|
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 (j *importer) fillAllMethods(cls *Class) {
|
|
if len(cls.AllMethods) > 0 {
|
|
return
|
|
}
|
|
if len(cls.Supers) == 0 {
|
|
cls.AllMethods = cls.Methods
|
|
return
|
|
}
|
|
for _, supName := range cls.Supers {
|
|
super := j.clsMap[supName]
|
|
j.fillAllMethods(super)
|
|
}
|
|
methods := make(map[FuncSig]struct{})
|
|
for _, supName := range cls.Supers {
|
|
super := j.clsMap[supName]
|
|
for _, f := range super.AllMethods {
|
|
if _, exists := methods[f.FuncSig]; !exists {
|
|
methods[f.FuncSig] = struct{}{}
|
|
// Copy function so each class can have its own
|
|
// JNI name mangling.
|
|
cpf := *f
|
|
cls.AllMethods = append(cls.AllMethods, &cpf)
|
|
}
|
|
}
|
|
}
|
|
for _, f := range cls.Methods {
|
|
if _, exists := methods[f.FuncSig]; !exists {
|
|
cls.AllMethods = append(cls.AllMethods, f)
|
|
}
|
|
}
|
|
}
|
|
|
|
// mangleOverloads assigns unique names to overloaded Java functions by appending
|
|
// the argument count. If multiple methods have the same name and argument count,
|
|
// the method signature is appended in JNI mangled form.
|
|
func (j *importer) mangleOverloads(allFuncs []*Func) {
|
|
overloads := make(map[string][]*Func)
|
|
for _, f := range allFuncs {
|
|
overloads[f.Name] = append(overloads[f.Name], f)
|
|
}
|
|
for _, funcs := range overloads {
|
|
for _, f := range funcs {
|
|
f.GoName = initialUpper(f.Name)
|
|
f.JNIName = JNIMangle(f.Name)
|
|
}
|
|
if len(funcs) == 1 {
|
|
continue
|
|
}
|
|
lengths := make(map[int]int)
|
|
for _, f := range funcs {
|
|
f.JNIName += "__" + JNIMangle(f.ArgDesc)
|
|
lengths[len(f.Params)]++
|
|
}
|
|
for _, f := range funcs {
|
|
n := len(f.Params)
|
|
if lengths[n] > 1 {
|
|
f.GoName += "_" + JNIMangle(f.ArgDesc)
|
|
continue
|
|
}
|
|
if n > 0 {
|
|
f.GoName = fmt.Sprintf("%s%d", f.GoName, n)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (j *importer) parseJavaValue(v string) (string, bool) {
|
|
v = strings.TrimRight(v, "df")
|
|
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)
|
|
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:]
|
|
}
|