| // 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 bind |
| |
| import ( |
| "bytes" |
| "fmt" |
| "go/token" |
| "io" |
| "regexp" |
| "unicode" |
| "unicode/utf8" |
| |
| "golang.org/x/tools/go/types" |
| ) |
| |
| // TODO(crawshaw): disallow basic android java type names in exported symbols. |
| // TODO(crawshaw): generate all relevant "implements" relationships for interfaces. |
| // TODO(crawshaw): consider introducing Java functions for casting to and from interfaces at runtime. |
| |
| type ErrorList []error |
| |
| func (list ErrorList) Error() string { |
| buf := new(bytes.Buffer) |
| for i, err := range list { |
| if i > 0 { |
| buf.WriteRune('\n') |
| } |
| io.WriteString(buf, err.Error()) |
| } |
| return buf.String() |
| } |
| |
| type javaGen struct { |
| *printer |
| nextCode int |
| fset *token.FileSet |
| pkg *types.Package |
| err ErrorList |
| } |
| |
| func (g *javaGen) genStruct(obj *types.TypeName, T *types.Struct) { |
| fields := exportedFields(T) |
| methods := exportedMethodSet(types.NewPointer(obj.Type())) |
| |
| g.Printf("public static final class %s implements go.Seq.Object {\n", obj.Name()) |
| g.Indent() |
| g.Printf("private static final String DESCRIPTOR = \"go.%s.%s\";\n", g.pkg.Name(), obj.Name()) |
| for i, f := range fields { |
| g.Printf("private static final int FIELD_%s_GET = 0x%x0f;\n", f.Name(), i) |
| g.Printf("private static final int FIELD_%s_SET = 0x%x1f;\n", f.Name(), i) |
| } |
| for i, m := range methods { |
| g.Printf("private static final int CALL_%s = 0x%x0c;\n", m.Name(), i) |
| } |
| g.Printf("\n") |
| |
| g.Printf("private go.Seq.Ref ref;\n\n") |
| |
| n := obj.Name() |
| g.Printf("private %s(go.Seq.Ref ref) { this.ref = ref; }\n\n", n) |
| g.Printf(`public go.Seq.Ref ref() { return ref; } |
| |
| public void call(int code, go.Seq in, go.Seq out) { |
| throw new RuntimeException("internal error: cycle: cannot call concrete proxy"); |
| } |
| |
| `) |
| |
| for _, f := range fields { |
| g.Printf("public %s get%s() {\n", g.javaType(f.Type()), f.Name()) |
| g.Indent() |
| g.Printf("Seq in = new Seq();\n") |
| g.Printf("Seq out = new Seq();\n") |
| g.Printf("in.writeRef(ref);\n") |
| g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_GET, in, out);\n", f.Name()) |
| if seqType(f.Type()) == "Ref" { |
| g.Printf("return new %s(out.read%s);\n", g.javaType(f.Type()), seqRead(f.Type())) |
| } else { |
| g.Printf("return out.read%s;\n", seqRead(f.Type())) |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| g.Printf("public void set%s(%s v) {\n", f.Name(), g.javaType(f.Type())) |
| g.Indent() |
| g.Printf("Seq in = new Seq();\n") |
| g.Printf("Seq out = new Seq();\n") |
| g.Printf("in.writeRef(ref);\n") |
| g.Printf("in.write%s;\n", seqWrite(f.Type(), "v")) |
| g.Printf("Seq.send(DESCRIPTOR, FIELD_%s_SET, in, out);\n", f.Name()) |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| for _, m := range methods { |
| g.genFunc(m, true) |
| } |
| |
| g.Printf("@Override public boolean equals(Object o) {\n") |
| g.Indent() |
| g.Printf("if (o == null || !(o instanceof %s)) {\n return false;\n}\n", n) |
| g.Printf("%s that = (%s)o;\n", n, n) |
| for _, f := range fields { |
| nf := f.Name() |
| g.Printf("%s this%s = get%s();\n", g.javaType(f.Type()), nf, nf) |
| g.Printf("%s that%s = that.get%s();\n", g.javaType(f.Type()), nf, nf) |
| if isJavaPrimitive(f.Type()) { |
| g.Printf("if (this%s != that%s) {\n return false;\n}\n", nf, nf) |
| } else { |
| g.Printf("if (this%s == null) {\n", nf) |
| g.Indent() |
| g.Printf("if (that%s != null) {\n return false;\n}\n", nf) |
| g.Outdent() |
| g.Printf("} else if (!this%s.equals(that%s)) {\n return false;\n}\n", nf, nf) |
| } |
| } |
| g.Printf("return true;\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| g.Printf("@Override public int hashCode() {\n") |
| g.Printf(" return java.util.Arrays.hashCode(new Object[] {") |
| for i, f := range fields { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("get%s()", f.Name()) |
| } |
| g.Printf("});\n") |
| g.Printf("}\n\n") |
| |
| // TODO(crawshaw): use String() string if it is defined. |
| g.Printf("@Override public String toString() {\n") |
| g.Indent() |
| g.Printf("StringBuilder b = new StringBuilder();\n") |
| g.Printf(`b.append("%s").append("{");`, obj.Name()) |
| g.Printf("\n") |
| for _, f := range fields { |
| n := f.Name() |
| g.Printf(`b.append("%s:").append(get%s()).append(",");`, n, n) |
| g.Printf("\n") |
| } |
| g.Printf(`return b.append("}").toString();`) |
| g.Printf("\n") |
| g.Outdent() |
| g.Printf("}\n\n") |
| |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *javaGen) genInterfaceStub(o *types.TypeName, m *types.Interface) { |
| g.Printf("public static abstract class Stub implements %s {\n", o.Name()) |
| g.Indent() |
| |
| g.Printf("static final String DESCRIPTOR = \"go.%s.%s\";\n\n", g.pkg.Name(), o.Name()) |
| g.Printf("private final go.Seq.Ref ref;\n") |
| g.Printf("public Stub() {\n ref = go.Seq.createRef(this);\n}\n\n") |
| g.Printf("public go.Seq.Ref ref() { return ref; }\n\n") |
| |
| g.Printf("public void call(int code, go.Seq in, go.Seq out) {\n") |
| g.Indent() |
| g.Printf("switch (code) {\n") |
| |
| for i := 0; i < m.NumMethods(); i++ { |
| f := m.Method(i) |
| g.Printf("case Proxy.CALL_%s: {\n", f.Name()) |
| g.Indent() |
| |
| sig := f.Type().(*types.Signature) |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| p := sig.Params().At(i) |
| jt := g.javaType(p.Type()) |
| g.Printf("%s param_%s;\n", jt, paramName(params, i)) |
| g.genRead("param_"+paramName(params, i), "in", p.Type()) |
| } |
| |
| res := sig.Results() |
| var returnsError bool |
| var numRes = res.Len() |
| if (res.Len() == 1 && isErrorType(res.At(0).Type())) || |
| (res.Len() == 2 && isErrorType(res.At(1).Type())) { |
| numRes -= 1 |
| returnsError = true |
| } |
| |
| if returnsError { |
| g.Printf("try {\n") |
| g.Indent() |
| } |
| |
| if numRes > 0 { |
| g.Printf("%s result = ", g.javaType(res.At(0).Type())) |
| } |
| |
| g.Printf("this.%s(", f.Name()) |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| g.Printf("param_%s", paramName(params, i)) |
| } |
| g.Printf(");\n") |
| |
| if numRes > 0 { |
| g.Printf("out.write%s;\n", seqWrite(res.At(0).Type(), "result")) |
| } |
| if returnsError { |
| g.Printf("out.writeString(null);\n") |
| g.Outdent() |
| g.Printf("} catch (Exception e) {\n") |
| g.Indent() |
| if numRes > 0 { |
| resTyp := res.At(0).Type() |
| g.Printf("%s result = %s;\n", g.javaType(resTyp), g.javaTypeDefault(resTyp)) |
| g.Printf("out.write%s;\n", seqWrite(resTyp, "result")) |
| } |
| g.Printf("out.writeString(e.getMessage());\n") |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| g.Printf("return;\n") |
| g.Outdent() |
| g.Printf("}\n") |
| } |
| |
| g.Printf("default:\n throw new RuntimeException(\"unknown code: \"+ code);\n") |
| g.Printf("}\n") |
| g.Outdent() |
| g.Printf("}\n") |
| |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| const javaProxyPreamble = `static final class Proxy implements %s { |
| static final String DESCRIPTOR = Stub.DESCRIPTOR; |
| |
| private go.Seq.Ref ref; |
| |
| Proxy(go.Seq.Ref ref) { this.ref = ref; } |
| |
| public go.Seq.Ref ref() { return ref; } |
| |
| public void call(int code, go.Seq in, go.Seq out) { |
| throw new RuntimeException("cycle: cannot call proxy"); |
| } |
| |
| ` |
| |
| func (g *javaGen) genInterface(o *types.TypeName) { |
| iface := o.Type().(*types.Named).Underlying().(*types.Interface) |
| |
| g.Printf("public interface %s extends go.Seq.Object {\n", o.Name()) |
| g.Indent() |
| |
| methodSigErr := false |
| for i := 0; i < iface.NumMethods(); i++ { |
| if err := g.funcSignature(iface.Method(i), false); err != nil { |
| methodSigErr = true |
| g.errorf("%v", err) |
| } |
| g.Printf(";\n\n") |
| } |
| if methodSigErr { |
| return // skip stub generation, more of the same errors |
| } |
| |
| g.genInterfaceStub(o, iface) |
| |
| g.Printf(javaProxyPreamble, o.Name()) |
| g.Indent() |
| |
| for i := 0; i < iface.NumMethods(); i++ { |
| g.genFunc(iface.Method(i), true) |
| } |
| for i := 0; i < iface.NumMethods(); i++ { |
| g.Printf("static final int CALL_%s = 0x%x0a;\n", iface.Method(i).Name(), i+1) |
| } |
| |
| g.Outdent() |
| g.Printf("}\n") |
| |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func isErrorType(T types.Type) bool { |
| return T == types.Universe.Lookup("error").Type() |
| } |
| |
| func isJavaPrimitive(T types.Type) bool { |
| b, ok := T.(*types.Basic) |
| if !ok { |
| return false |
| } |
| switch b.Kind() { |
| case types.Bool, types.Uint8, types.Float32, types.Float64, |
| types.Int, types.Int8, types.Int16, types.Int32, types.Int64: |
| return true |
| } |
| return false |
| } |
| |
| // javaType returns a string that can be used as a Java type. |
| func (g *javaGen) javaType(T types.Type) string { |
| switch T := T.(type) { |
| case *types.Basic: |
| switch T.Kind() { |
| case types.Bool: |
| return "boolean" |
| case types.Int: |
| return "long" |
| case types.Int8: |
| return "byte" |
| case types.Int16: |
| return "short" |
| case types.Int32: |
| return "int" |
| case types.Int64: |
| return "long" |
| case types.Uint8: |
| // TODO(crawshaw): Java bytes are signed, so this is |
| // questionable, but vital. |
| return "byte" |
| // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: |
| case types.Float32: |
| return "float" |
| case types.Float64: |
| return "double" |
| case types.String: |
| return "String" |
| default: |
| g.errorf("unsupported return type: %s", T) |
| return "TODO" |
| } |
| case *types.Slice: |
| elem := g.javaType(T.Elem()) |
| return elem + "[]" |
| |
| case *types.Pointer: |
| if _, ok := T.Elem().(*types.Named); ok { |
| return g.javaType(T.Elem()) |
| } |
| panic(fmt.Sprintf("unsupporter pointer to type: %s", T)) |
| case *types.Named: |
| n := T.Obj() |
| if n.Pkg() != g.pkg { |
| panic(fmt.Sprintf("type %s is in package %s, must be defined in package %s", n.Name(), n.Pkg().Name(), g.pkg.Name())) |
| } |
| // TODO(crawshaw): more checking here |
| return n.Name() |
| default: |
| g.errorf("unsupported javaType: %#+v, %s\n", T, T) |
| return "TODO" |
| } |
| } |
| |
| // javaTypeDefault returns a string that represents the default value of the mapped java type. |
| // TODO(hyangah): Combine javaType and javaTypeDefault? |
| func (g *javaGen) javaTypeDefault(T types.Type) string { |
| switch T := T.(type) { |
| case *types.Basic: |
| switch T.Kind() { |
| case types.Bool: |
| return "false" |
| case types.Int, types.Int8, types.Int16, types.Int32, |
| types.Int64, types.Uint8, types.Float32, types.Float64: |
| return "0" |
| case types.String: |
| return "null" |
| default: |
| g.errorf("unsupported return type: %s", T) |
| return "TODO" |
| } |
| case *types.Slice, *types.Pointer, *types.Named: |
| return "null" |
| |
| default: |
| g.errorf("unsupported javaType: %#+v, %s\n", T, T) |
| return "TODO" |
| } |
| } |
| |
| var paramRE = regexp.MustCompile(`^p[0-9]+$`) |
| |
| // paramName replaces incompatible name with a p0-pN name. |
| // Missing names, or existing names of the form p[0-9] are incompatible. |
| // TODO(crawshaw): Replace invalid unicode names. |
| func paramName(params *types.Tuple, pos int) string { |
| name := params.At(pos).Name() |
| if name == "" || name == "_" || paramRE.MatchString(name) { |
| name = fmt.Sprintf("p%d", pos) |
| } |
| return name |
| } |
| |
| func (g *javaGen) funcSignature(o *types.Func, static bool) error { |
| sig := o.Type().(*types.Signature) |
| res := sig.Results() |
| |
| var returnsError bool |
| var ret string |
| switch res.Len() { |
| case 2: |
| if !isErrorType(res.At(1).Type()) { |
| return fmt.Errorf("second result value must be of type error: %s", o) |
| } |
| returnsError = true |
| ret = g.javaType(res.At(0).Type()) |
| case 1: |
| if isErrorType(res.At(0).Type()) { |
| returnsError = true |
| ret = "void" |
| } else { |
| ret = g.javaType(res.At(0).Type()) |
| } |
| case 0: |
| ret = "void" |
| default: |
| return fmt.Errorf("too many result values: %s", o) |
| } |
| |
| g.Printf("public ") |
| if static { |
| g.Printf("static ") |
| } |
| g.Printf("%s %s(", ret, o.Name()) |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| if i > 0 { |
| g.Printf(", ") |
| } |
| v := sig.Params().At(i) |
| name := paramName(params, i) |
| jt := g.javaType(v.Type()) |
| g.Printf("%s %s", jt, name) |
| } |
| g.Printf(")") |
| if returnsError { |
| g.Printf(" throws Exception") |
| } |
| return nil |
| } |
| |
| func (g *javaGen) genFunc(o *types.Func, method bool) { |
| if err := g.funcSignature(o, !method); err != nil { |
| g.errorf("%v", err) |
| return |
| } |
| sig := o.Type().(*types.Signature) |
| res := sig.Results() |
| |
| g.Printf(" {\n") |
| g.Indent() |
| g.Printf("go.Seq _in = new go.Seq();\n") |
| g.Printf("go.Seq _out = new go.Seq();\n") |
| |
| returnsError := false |
| var resultType types.Type |
| if res.Len() > 0 { |
| if !isErrorType(res.At(0).Type()) { |
| resultType = res.At(0).Type() |
| } |
| if res.Len() > 1 || isErrorType(res.At(0).Type()) { |
| returnsError = true |
| } |
| } |
| if resultType != nil { |
| t := g.javaType(resultType) |
| g.Printf("%s _result;\n", t) |
| } |
| |
| if method { |
| g.Printf("_in.writeRef(ref);\n") |
| } |
| params := sig.Params() |
| for i := 0; i < params.Len(); i++ { |
| p := params.At(i) |
| g.Printf("_in.write%s;\n", seqWrite(p.Type(), paramName(params, i))) |
| } |
| g.Printf("Seq.send(DESCRIPTOR, CALL_%s, _in, _out);\n", o.Name()) |
| if resultType != nil { |
| g.genRead("_result", "_out", resultType) |
| } |
| if returnsError { |
| g.Printf(`String _err = _out.readString(); |
| if (_err != null) { |
| throw new Exception(_err); |
| } |
| `) |
| } |
| if resultType != nil { |
| g.Printf("return _result;\n") |
| } |
| g.Outdent() |
| g.Printf("}\n\n") |
| } |
| |
| func (g *javaGen) genRead(resName, seqName string, T types.Type) { |
| switch T := T.(type) { |
| case *types.Pointer: |
| // TODO(crawshaw): test *int |
| // TODO(crawshaw): test **Generator |
| switch T := T.Elem().(type) { |
| case *types.Named: |
| o := T.Obj() |
| if o.Pkg() != g.pkg { |
| g.errorf("type %s not defined in package %s", T, g.pkg) |
| return |
| } |
| g.Printf("%s = new %s(%s.readRef());\n", resName, o.Name(), seqName) |
| default: |
| g.errorf("unsupported type %s", T) |
| } |
| case *types.Named: |
| switch T.Underlying().(type) { |
| case *types.Interface, *types.Pointer: |
| o := T.Obj() |
| if o.Pkg() != g.pkg { |
| g.errorf("type %s not defined in package %s", T, g.pkg) |
| return |
| } |
| g.Printf("%s = new %s.Proxy(%s.readRef());\n", resName, o.Name(), seqName) |
| default: |
| g.errorf("unsupported, direct named type %s", T) |
| } |
| default: |
| g.Printf("%s = %s.read%s();\n", resName, seqName, seqType(T)) |
| } |
| } |
| |
| func (g *javaGen) errorf(format string, args ...interface{}) { |
| g.err = append(g.err, fmt.Errorf(format, args...)) |
| } |
| |
| const javaPreamble = `// Java Package %s is a proxy for talking to a Go program. |
| // gobind -lang=java %s |
| // |
| // File is generated by gobind. Do not edit. |
| package go.%s; |
| |
| import go.Seq; |
| |
| ` |
| |
| func (g *javaGen) gen() error { |
| g.Printf(javaPreamble, g.pkg.Name(), g.pkg.Path(), g.pkg.Name()) |
| |
| firstRune, size := utf8.DecodeRuneInString(g.pkg.Name()) |
| className := string(unicode.ToUpper(firstRune)) + g.pkg.Name()[size:] |
| |
| g.Printf("public abstract class %s {\n", className) |
| g.Indent() |
| g.Printf("private %s() {} // uninstantiable\n\n", className) |
| scope := g.pkg.Scope() |
| names := scope.Names() |
| var funcs []string |
| for _, name := range names { |
| obj := scope.Lookup(name) |
| if !obj.Exported() { |
| continue |
| } |
| |
| switch o := obj.(type) { |
| // TODO(crawshaw): case *types.Const: |
| // TODO(crawshaw): case *types.Var: |
| case *types.Func: |
| g.genFunc(o, false) |
| funcs = append(funcs, o.Name()) |
| case *types.TypeName: |
| named := o.Type().(*types.Named) |
| switch t := named.Underlying().(type) { |
| case *types.Struct: |
| g.genStruct(o, t) |
| case *types.Interface: |
| g.genInterface(o) |
| default: |
| g.errorf("%s: cannot generate binding for %s: %T", g.fset.Position(o.Pos()), o.Name(), t) |
| continue |
| } |
| default: |
| g.errorf("unsupported exported type: ", obj) |
| } |
| } |
| |
| for i, name := range funcs { |
| g.Printf("private static final int CALL_%s = %d;\n", name, i+1) |
| } |
| |
| g.Printf("private static final String DESCRIPTOR = %q;\n", g.pkg.Name()) |
| g.Outdent() |
| g.Printf("}\n") |
| |
| if len(g.err) > 0 { |
| return g.err |
| } |
| return nil |
| } |