// 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:] }