From 3f7b83ffd484b70ccd24d66425b488dcf680e6da Mon Sep 17 00:00:00 2001 From: Elias Naur Date: Fri, 23 Sep 2016 12:31:57 +0200 Subject: [PATCH] bind: generate Java constructors for every exported struct A recent CL added Java constructors to generated classes that extends or implements other Java classes and interfaces. Constructors for a struct S are Go functions on the form func NewS...(...) *S If no such constructors exists, a default empty constructor is generated. Expand that to cover every exported Go struct. Fixes golang/go#17086 Change-Id: I910aba13d5884c3f67c946c62a8ac4a3db8e2ea7 Reviewed-on: https://go-review.googlesource.com/29710 Reviewed-by: David Crawshaw --- bind/genjava.go | 79 +++++++++++++------------- bind/java/SeqTest.java | 11 ++++ bind/testdata/ignore.java.c.golden | 6 ++ bind/testdata/ignore.java.golden | 4 ++ bind/testdata/issue10788.java.c.golden | 6 ++ bind/testdata/issue10788.java.golden | 4 ++ bind/testdata/issue12328.java.c.golden | 6 ++ bind/testdata/issue12328.java.golden | 4 ++ bind/testdata/structs.java.c.golden | 12 ++++ bind/testdata/structs.java.golden | 8 +++ bind/testdata/vars.java.c.golden | 6 ++ bind/testdata/vars.java.golden | 4 ++ bind/testpkg/testpkg.go | 11 ++++ 13 files changed, 123 insertions(+), 38 deletions(-) diff --git a/bind/genjava.go b/bind/genjava.go index ee5d65a..dadcf8a 100644 --- a/bind/genjava.go +++ b/bind/genjava.go @@ -26,6 +26,10 @@ type JavaGen struct { jstructs map[*types.TypeName]*javaClassInfo clsMap map[string]*java.Class + // Constructors is a map from Go struct types to a list + // of exported constructor functions for the type, on the form + // func New(...) *Type + constructors map[*types.TypeName][]*types.Func } type javaClassInfo struct { @@ -36,9 +40,6 @@ type javaClassInfo struct { methods map[string]*java.Func // Does the class need a default no-arg constructor genNoargCon bool - // Constructors for the type, on the form - // func New(...) *Type - cons []*types.Func } // Init intializes the embedded Generator and initializes the Java class information @@ -50,6 +51,7 @@ func (g *JavaGen) Init(classes []*java.Class) { g.clsMap[cls.Name] = cls } g.jstructs = make(map[*types.TypeName]*javaClassInfo) + g.constructors = make(map[*types.TypeName][]*types.Func) for _, s := range g.structs { classes := embeddedJavaClasses(s.t) if len(classes) == 0 { @@ -87,8 +89,8 @@ func (g *JavaGen) Init(classes []*java.Class) { if jinf != nil { sig := f.Type().(*types.Signature) jinf.genNoargCon = jinf.genNoargCon && sig.Params().Len() > 0 - jinf.cons = append(jinf.cons, f) } + g.constructors[t] = append(g.constructors[t], f) } } } @@ -178,23 +180,22 @@ func (g *JavaGen) genStruct(s structInfo) { g.Printf("static { %s.touch(); }\n\n", g.className()) g.genProxyImpl(n) - if jinf != nil { - for _, f := range jinf.cons { - if !g.isSigSupported(f.Type()) { - g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", n, f.Name()) - continue - } - g.genConstructor(f, n) + cons := g.constructors[s.obj] + for _, f := range cons { + if !g.isSigSupported(f.Type()) { + g.Printf("// skipped constructor %s.%s with unsupported parameter or return types\n\n", n, f.Name()) + continue } - if jinf.genNoargCon { - // Generate constructor for Go instantiated instances. - g.Printf("%s(Seq.Ref ref) { this.ref = ref; }\n\n", n) + g.genConstructor(f, n, jinf != nil) + } + if jinf == nil || jinf.genNoargCon { + // constructor for Go instantiated instances. + g.Printf("%s(Seq.Ref ref) { this.ref = ref; }\n\n", n) + if len(cons) == 0 { // Generate default no-arg constructor g.Printf("public %s() { this.ref = __New(); }\n\n", n) g.Printf("private static native Seq.Ref __New();\n\n") } - } else { - g.Printf("%s(Seq.Ref ref) { this.ref = ref; }\n\n", n) } for _, f := range fields { @@ -266,22 +267,25 @@ func (g *JavaGen) genStruct(s structInfo) { g.Printf("}\n\n") } -func (g *JavaGen) genConstructor(f *types.Func, n string) { +func (g *JavaGen) genConstructor(f *types.Func, n string, jcls bool) { g.Printf("public %s(", n) g.genFuncArgs(f, nil) g.Printf(") {\n") g.Indent() - g.Printf("super(") sig := f.Type().(*types.Signature) params := sig.Params() - for i := 0; i < params.Len(); i++ { - if i > 0 { - g.Printf(", ") + if jcls { + g.Printf("super(") + for i := 0; i < params.Len(); i++ { + if i > 0 { + g.Printf(", ") + } + g.Printf(paramName(params, i)) } - g.Printf(paramName(params, i)) + g.Printf(");\n") } - g.Printf(");\n") - g.Printf("this.ref = __%s(", f.Name()) + g.Printf("this.ref = ") + g.Printf("__%s(", f.Name()) for i := 0; i < params.Len(); i++ { if i > 0 { g.Printf(", ") @@ -1330,21 +1334,20 @@ func (g *JavaGen) GenC() error { } for _, s := range g.structs { sName := s.obj.Name() + cons := g.constructors[s.obj] jinf := g.jstructs[s.obj] - if jinf != nil { - for _, f := range jinf.cons { - g.genJNIConstructor(f, sName) - } - if jinf.genNoargCon { - g.Printf("JNIEXPORT jobject JNICALL\n") - g.Printf("Java_%s_%s_%s(JNIEnv *env, jclass clazz) {\n", g.jniPkgName(), sName, java.JNIMangle("__New")) - g.Indent() - g.Printf("int32_t refnum = new_%s_%s();\n", g.pkgPrefix, sName) - // Pass no proxy class so that the Seq.Ref is returned instead. - g.Printf("return go_seq_from_refnum(env, refnum, NULL, NULL);\n") - g.Outdent() - g.Printf("}\n\n") - } + for _, f := range cons { + g.genJNIConstructor(f, sName) + } + if len(cons) == 0 && (jinf == nil || jinf.genNoargCon) { + g.Printf("JNIEXPORT jobject JNICALL\n") + g.Printf("Java_%s_%s_%s(JNIEnv *env, jclass clazz) {\n", g.jniPkgName(), sName, java.JNIMangle("__New")) + g.Indent() + g.Printf("int32_t refnum = new_%s_%s();\n", g.pkgPrefix, sName) + // Pass no proxy class so that the Seq.Ref is returned instead. + g.Printf("return go_seq_from_refnum(env, refnum, NULL, NULL);\n") + g.Outdent() + g.Printf("}\n\n") } for _, m := range exportedMethodSet(types.NewPointer(s.obj.Type())) { diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java index 5e6af55..101ccba 100644 --- a/bind/java/SeqTest.java +++ b/bind/java/SeqTest.java @@ -527,6 +527,17 @@ public class SeqTest extends InstrumentationTestCase { assertTrue("Go struct passed through Java should not be wrapped", Testpkg.callCDupper(cdup)); } + public void testConstructor() { + Interface i = new Concrete(); + i.f(); + S2 s = new S2(1, 2); + assertEquals("new S2().sum", 3.0, s.sum()); + assertEquals("new S2().tryTwoStrings", "gostring", s.tryTwoStrings("go", "string")); + new S3(); + S4 s4 = new S4(123); + assertEquals("Constructor argument", 123, s4.getI()); + } + public void testEmptyError() { try { Testpkg.emptyError(); diff --git a/bind/testdata/ignore.java.c.golden b/bind/testdata/ignore.java.c.golden index 1de8dc7..981fbdf 100644 --- a/bind/testdata/ignore.java.c.golden +++ b/bind/testdata/ignore.java.c.golden @@ -39,6 +39,12 @@ Java_go_ignore_Ignore__1init(JNIEnv *env, jclass _unused) { // skipped function Result with unsupported parameter or return types +JNIEXPORT jobject JNICALL +Java_go_ignore_S__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_ignore_S(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + // skipped function S.Argument with unsupported parameter or return types // skipped function S.Result with unsupported parameter or return types diff --git a/bind/testdata/ignore.java.golden b/bind/testdata/ignore.java.golden index 7a90fde..0dd0d83 100644 --- a/bind/testdata/ignore.java.golden +++ b/bind/testdata/ignore.java.golden @@ -19,6 +19,10 @@ public final class S implements Seq.Proxy, I { S(Seq.Ref ref) { this.ref = ref; } + public S() { this.ref = __New(); } + + private static native Seq.Ref __New(); + // skipped field S.F with unsupported type: *types.Interface // skipped method S.Argument with unsupported parameter or return types diff --git a/bind/testdata/issue10788.java.c.golden b/bind/testdata/issue10788.java.c.golden index a685c1f..132b42c 100644 --- a/bind/testdata/issue10788.java.c.golden +++ b/bind/testdata/issue10788.java.c.golden @@ -31,6 +31,12 @@ Java_go_issue10788_Issue10788__1init(JNIEnv *env, jclass _unused) { } +JNIEXPORT jobject JNICALL +Java_go_issue10788_TestStruct__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_issue10788_TestStruct(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + JNIEXPORT void JNICALL Java_go_issue10788_TestStruct_setValue(JNIEnv *env, jobject this, jstring v) { int32_t o = go_seq_to_refnum(env, this); diff --git a/bind/testdata/issue10788.java.golden b/bind/testdata/issue10788.java.golden index 08d15f6..9264d36 100644 --- a/bind/testdata/issue10788.java.golden +++ b/bind/testdata/issue10788.java.golden @@ -19,6 +19,10 @@ public final class TestStruct implements Seq.Proxy { TestStruct(Seq.Ref ref) { this.ref = ref; } + public TestStruct() { this.ref = __New(); } + + private static native Seq.Ref __New(); + public final native String getValue(); public final native void setValue(String v); diff --git a/bind/testdata/issue12328.java.c.golden b/bind/testdata/issue12328.java.c.golden index e871285..09e5223 100644 --- a/bind/testdata/issue12328.java.c.golden +++ b/bind/testdata/issue12328.java.c.golden @@ -20,6 +20,12 @@ Java_go_issue12328_Issue12328__1init(JNIEnv *env, jclass _unused) { proxy_class_issue12328_T_cons = (*env)->GetMethodID(env, clazz, "", "(Lgo/Seq$Ref;)V"); } +JNIEXPORT jobject JNICALL +Java_go_issue12328_T__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_issue12328_T(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + JNIEXPORT void JNICALL Java_go_issue12328_T_setErr(JNIEnv *env, jobject this, jobject v) { int32_t o = go_seq_to_refnum(env, this); diff --git a/bind/testdata/issue12328.java.golden b/bind/testdata/issue12328.java.golden index 3dd20f9..23fc5ef 100644 --- a/bind/testdata/issue12328.java.golden +++ b/bind/testdata/issue12328.java.golden @@ -19,6 +19,10 @@ public final class T implements Seq.Proxy { T(Seq.Ref ref) { this.ref = ref; } + public T() { this.ref = __New(); } + + private static native Seq.Ref __New(); + public final native java.lang.Exception getErr(); public final native void setErr(java.lang.Exception v); diff --git a/bind/testdata/structs.java.c.golden b/bind/testdata/structs.java.c.golden index 7c55fc4..5438570 100644 --- a/bind/testdata/structs.java.c.golden +++ b/bind/testdata/structs.java.c.golden @@ -52,6 +52,12 @@ Java_go_structs_Structs_identityWithError(JNIEnv* env, jclass _clazz, jobject s) return _r0; } +JNIEXPORT jobject JNICALL +Java_go_structs_S__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_structs_S(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + JNIEXPORT jobject JNICALL Java_go_structs_S_identity(JNIEnv* env, jobject __this__) { int32_t o = go_seq_to_refnum(env, __this__); @@ -100,6 +106,12 @@ Java_go_structs_S_getY(JNIEnv *env, jobject this) { return _r0; } +JNIEXPORT jobject JNICALL +Java_go_structs_S2__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_structs_S2(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + JNIEXPORT void JNICALL Java_go_structs_S2_m(JNIEnv* env, jobject __this__) { int32_t o = go_seq_to_refnum(env, __this__); diff --git a/bind/testdata/structs.java.golden b/bind/testdata/structs.java.golden index 306f597..416973f 100644 --- a/bind/testdata/structs.java.golden +++ b/bind/testdata/structs.java.golden @@ -19,6 +19,10 @@ public final class S implements Seq.Proxy { S(Seq.Ref ref) { this.ref = ref; } + public S() { this.ref = __New(); } + + private static native Seq.Ref __New(); + public final native double getX(); public final native void setX(double v); @@ -79,6 +83,10 @@ public final class S2 implements Seq.Proxy, I { S2(Seq.Ref ref) { this.ref = ref; } + public S2() { this.ref = __New(); } + + private static native Seq.Ref __New(); + public native void m(); public native String string(); @Override public boolean equals(Object o) { diff --git a/bind/testdata/vars.java.c.golden b/bind/testdata/vars.java.c.golden index dd690bc..eb491d3 100644 --- a/bind/testdata/vars.java.c.golden +++ b/bind/testdata/vars.java.c.golden @@ -27,6 +27,12 @@ Java_go_vars_Vars__1init(JNIEnv *env, jclass _unused) { } +JNIEXPORT jobject JNICALL +Java_go_vars_S__1_1New(JNIEnv *env, jclass clazz) { + int32_t refnum = new_vars_S(); + return go_seq_from_refnum(env, refnum, NULL, NULL); +} + JNIEXPORT void JNICALL Java_go_vars_Vars_setABool(JNIEnv *env, jclass clazz, jboolean v) { char _v = (char)v; diff --git a/bind/testdata/vars.java.golden b/bind/testdata/vars.java.golden index c66da14..27450df 100644 --- a/bind/testdata/vars.java.golden +++ b/bind/testdata/vars.java.golden @@ -19,6 +19,10 @@ public final class S implements Seq.Proxy, I { S(Seq.Ref ref) { this.ref = ref; } + public S() { this.ref = __New(); } + + private static native Seq.Ref __New(); + @Override public boolean equals(Object o) { if (o == null || !(o instanceof S)) { return false; diff --git a/bind/testpkg/testpkg.go b/bind/testpkg/testpkg.go index 091b640..9e743ef 100644 --- a/bind/testpkg/testpkg.go +++ b/bind/testpkg/testpkg.go @@ -547,3 +547,14 @@ var GlobalErr error = errors.New("global err") func IsGlobalErr(err error) bool { return GlobalErr == err } + +type S3 struct { +} + +type S4 struct { + I int +} + +func NewS4WithInt(i int) *S4 { + return &S4{i} +}