mobile/bind/java/SeqTest.java

460 lines
13 KiB
Java
Raw Normal View History

// 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 go;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import java.util.Arrays;
import java.util.Random;
import go.testpkg.Testpkg;
public class SeqTest extends InstrumentationTestCase {
public SeqTest() {
}
public void testConst() {
assertEquals("const String", "a string", Testpkg.AString);
assertEquals("const Int", 7, Testpkg.AnInt);
assertEquals("const Bool", true, Testpkg.ABool);
assertEquals("const Float", 0.12345, Testpkg.AFloat, 0.0001);
assertEquals("const MinInt32", -1<<31, Testpkg.MinInt32);
assertEquals("const MaxInt32", (1<<31) - 1, Testpkg.MaxInt32);
assertEquals("const MinInt64", -1L<<63, Testpkg.MinInt64);
assertEquals("const MaxInt64", (1L<<63) - 1, Testpkg.MaxInt64);
assertEquals("const SmallestNonzeroFloat64", 4.940656458412465441765687928682213723651e-324, Testpkg.SmallestNonzeroFloat64, 1e-323);
assertEquals("const MaxFloat64", 1.797693134862315708145274237317043567981e+308, Testpkg.MaxFloat64, 0.0001);
assertEquals("const SmallestNonzeroFloat32", 1.401298464324817070923729583289916131280e-45, Testpkg.SmallestNonzeroFloat32, 1e-44);
assertEquals("const MaxFloat32", 3.40282346638528859811704183484516925440e+38, Testpkg.MaxFloat32, 0.0001);
assertEquals("const Log2E", 1/0.693147180559945309417232121458176568075500134360255254120680009, Testpkg.Log2E, 0.0001);
}
public void testRefMap() {
// Ensure that the RefMap.live count is kept in sync
// even a particular reference number is removed and
// added again
Seq.RefMap m = new Seq.RefMap();
Seq.Ref r = new Seq.Ref(1, null);
m.put(r.refnum, r);
m.remove(r.refnum);
m.put(r.refnum, r);
// Force the RefMap to grow, to activate the sanity
// checking of the live count in RefMap.grow.
for (int i = 2; i < 24; i++) {
m.put(i, new Seq.Ref(i, null));
}
}
public void testVar() {
assertEquals("var StringVar", "a string var", Testpkg.getStringVar());
String newStringVar = "a new string var";
Testpkg.setStringVar(newStringVar);
assertEquals("var StringVar", newStringVar, Testpkg.getStringVar());
assertEquals("var IntVar", 77, Testpkg.getIntVar());
long newIntVar = 777;
Testpkg.setIntVar(newIntVar);
assertEquals("var IntVar", newIntVar, Testpkg.getIntVar());
Testpkg.S s0 = Testpkg.getStructVar();
assertEquals("var StructVar", "a struct var", s0.String());
Testpkg.S s1 = Testpkg.New();
Testpkg.setStructVar(s1);
assertEquals("var StructVar", s1.String(), Testpkg.getStructVar().String());
AnI obj = new AnI();
obj.name = "this is an I";
Testpkg.setInterfaceVar(obj);
assertEquals("var InterfaceVar", obj.String(), Testpkg.getInterfaceVar().String());
}
public void testAssets() {
// Make sure that a valid context is set before reading assets
Seq.setContext(getInstrumentation().getContext());
String want = "Hello, Assets.\n";
String got = Testpkg.ReadAsset();
assertEquals("Asset read", want, got);
}
public void testAdd() {
long res = Testpkg.Add(3, 4);
assertEquals("Unexpected arithmetic failure", 7, res);
}
public void testBool() {
assertTrue(Testpkg.Negate(false));
assertFalse(Testpkg.Negate(true));
}
public void testShortString() {
String want = "a short string";
String got = Testpkg.StrDup(want);
assertEquals("Strings should match", want, got);
want = "";
got = Testpkg.StrDup(want);
assertEquals("Strings should match (empty string)", want, got);
got = Testpkg.StrDup(null);
assertEquals("Strings should match (null string)", want, got);
}
public void testLongString() {
StringBuilder b = new StringBuilder();
for (int i = 0; i < 128*1024; i++) {
b.append("0123456789");
}
String want = b.toString();
String got = Testpkg.StrDup(want);
assertEquals("Strings should match", want, got);
}
public void testUnicode() {
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 17:50:33 +00:00
String[] tests = new String[]{
"abcxyz09{}",
"Hello, 世界",
"\uffff\uD800\uDC00\uD800\uDC01\uD808\uDF45\uDBFF\uDFFF"};
for (String want : tests) {
String got = Testpkg.StrDup(want);
assertEquals("Strings should match", want, got);
}
}
public void testNilErr() throws Exception {
Testpkg.Err(null); // returns nil, no exception
}
public void testErr() {
String msg = "Go errors are dropped into the confusing space of exceptions";
try {
Testpkg.Err(msg);
fail("expected non-nil error to be turned into an exception");
} catch (Exception e) {
assertEquals("messages should match", msg, e.getMessage());
}
}
public void testByteArray() {
for (int i = 0; i < 2048; i++) {
if (i == 0) {
byte[] got = Testpkg.BytesAppend(null, null);
assertEquals("Bytes(null+null) should match", (byte[])null, got);
got = Testpkg.BytesAppend(new byte[0], new byte[0]);
assertEquals("Bytes(empty+empty) should match", (byte[])null, got);
continue;
}
byte[] want = new byte[i];
new Random().nextBytes(want);
byte[] s1 = null;
byte[] s2 = null;
if (i > 0) {
s1 = Arrays.copyOfRange(want, 0, 1);
}
if (i > 1) {
s2 = Arrays.copyOfRange(want, 1, i);
}
byte[] got = Testpkg.BytesAppend(s1, s2);
MoreAsserts.assertEquals("Bytes(len="+i+") should match", want, got);
}
}
// Test for golang.org/issue/9486.
public void testByteArrayAfterString() {
byte[] bytes = new byte[1024];
for (int i=0; i < bytes.length; i++) {
bytes[i] = 8;
}
String stuff = "stuff";
byte[] got = Testpkg.AppendToString(stuff, bytes);
try {
byte[] s = stuff.getBytes("UTF-8");
byte[] want = new byte[s.length + bytes.length];
System.arraycopy(s, 0, want, 0, s.length);
System.arraycopy(bytes, 0, want, s.length, bytes.length);
MoreAsserts.assertEquals("Bytes should match", want, got);
} catch (Exception e) {
fail("Cannot perform the test: " + e.toString());
}
}
public void testGoRefGC() {
Testpkg.S s = Testpkg.New();
runGC();
long collected = Testpkg.NumSCollected();
assertEquals("Only S should be pinned", 0, collected);
s = null;
runGC();
collected = Testpkg.NumSCollected();
assertEquals("S should be collected", 1, collected);
}
private class AnI extends Testpkg.I.Stub {
public void E() throws Exception {
throw new Exception("my exception from E");
}
boolean calledF;
public void F() {
calledF = true;
}
public Testpkg.I I() {
return this;
}
public Testpkg.S S() {
return Testpkg.New();
}
public String StoString(Testpkg.S s) {
return s.String();
}
public long V() {
return 1234;
}
public long VE() throws Exception {
throw new Exception("my exception from VE");
}
public String name;
public String String() {
return name;
}
}
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
// TODO(hyangah): add tests for methods that take parameters.
public void testInterfaceMethodReturnsError() {
final AnI obj = new AnI();
try {
Testpkg.CallE(obj);
fail("Expecting exception but none was thrown.");
} catch (Exception e) {
assertEquals("Error messages should match", "my exception from E", e.getMessage());
}
}
public void testInterfaceMethodVoid() {
final AnI obj = new AnI();
Testpkg.CallF(obj);
assertTrue("Want AnI.F to be called", obj.calledF);
}
public void testInterfaceMethodReturnsInterface() {
AnI obj = new AnI();
obj.name = "testing AnI.I";
Testpkg.I i = Testpkg.CallI(obj);
assertEquals("Want AnI.I to return itself", i.String(), obj.String());
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
runGC();
i = Testpkg.CallI(obj);
assertEquals("Want AnI.I to return itself", i.String(), obj.String());
}
public void testInterfaceMethodReturnsStructPointer() {
final AnI obj = new AnI();
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
for (int i = 0; i < 5; i++) {
Testpkg.S s = Testpkg.CallS(obj);
runGC();
}
}
public void testInterfaceMethodTakesStructPointer() {
final AnI obj = new AnI();
Testpkg.S s = Testpkg.CallS(obj);
String got = obj.StoString(s);
String want = s.String();
assertEquals("Want AnI.StoString(s) to call s's String", want, got);
}
public void testInterfaceMethodReturnsInt() {
final AnI obj = new AnI();
assertEquals("Values must match", 1234, Testpkg.CallV(obj));
}
public void testInterfaceMethodReturnsIntOrError() {
final AnI obj = new AnI();
try {
long v = Testpkg.CallVE(obj);
fail("Expecting exception but none was thrown and got value " + v);
} catch (Exception e) {
assertEquals("Error messages should match", "my exception from VE", e.getMessage());
}
}
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
boolean finalizedAnI;
private class AnI_Traced extends AnI {
@Override
public void finalize() throws Throwable {
finalizedAnI = true;
super.finalize();
}
}
public void testJavaRefGC() {
finalizedAnI = false;
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
AnI obj = new AnI_Traced();
Testpkg.CallF(obj);
assertTrue("want F to be called", obj.calledF);
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
Testpkg.CallF(obj);
obj = null;
runGC();
assertTrue("want obj to be collected", finalizedAnI);
}
public void testJavaRefKeep() {
finalizedAnI = false;
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 10:12:57 +00:00
AnI obj = new AnI_Traced();
Testpkg.CallF(obj);
Testpkg.CallF(obj);
obj = null;
runGC();
assertTrue("want obj not to be kept by Go", finalizedAnI);
finalizedAnI = false;
obj = new AnI_Traced();
Testpkg.Keep(obj);
obj = null;
runGC();
assertFalse("want obj to be kept live by Go", finalizedAnI);
}
private int countI = 0;
private class CountI extends Testpkg.I.Stub {
public void F() { countI++; }
public void E() throws Exception {}
public Testpkg.I I() { return null; }
public Testpkg.S S() { return null; }
public String StoString(Testpkg.S s) { return ""; }
public long V() { return 0; }
public long VE() throws Exception { return 0; }
public String String() { return ""; }
}
public void testGoRefMapGrow() {
CountI obj = new CountI();
Testpkg.Keep(obj);
// Push active references beyond base map size.
for (int i = 0; i < 24; i++) {
CountI o = new CountI();
Testpkg.CallF(o);
if (i%3==0) {
Testpkg.Keep(o);
}
}
runGC();
for (int i = 0; i < 128; i++) {
Testpkg.CallF(new CountI());
}
Testpkg.CallF(obj); // original object needs to work.
assertEquals(countI, 1+24+128);
}
private void runGC() {
System.gc();
System.runFinalization();
Testpkg.GC();
System.gc();
System.runFinalization();
}
public void testUnnamedParams() {
final String msg = "1234567";
assertEquals("want the length of \"1234567\" passed after unnamed params",
7, Testpkg.UnnamedParams(10, 20, msg));
}
public void testPointerToStructAsField() {
Testpkg.Node a = Testpkg.NewNode("A");
Testpkg.Node b = Testpkg.NewNode("B");
a.setNext(b);
String got = a.String();
assertEquals("want Node A points to Node B", "A:B:<end>", got);
}
public void testImplementsInterface() {
Testpkg.Interface intf = Testpkg.NewConcrete();
}
public void testErrorField() {
final String want = "an error message";
Testpkg.Node n = Testpkg.NewNode("ErrTest");
n.setErr(want);
String got = n.getErr();
assertEquals("want back the error message we set", want, got);
}
//test if we have JNI local reference table overflow error
public void testLocalReferenceOverflow() {
Testpkg.CallWithCallback(new Testpkg.GoCallback.Stub() {
@Override
public void VarUpdate() {
//do nothing
}
});
}
public void testNullReferences() {
assertTrue(Testpkg.CallWithNull(null, new Testpkg.NullTest.Stub() {
public Testpkg.NullTest Null() {
return null;
}
}));
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 17:50:33 +00:00
assertEquals("Go nil interface is null", null, Testpkg.NewNullInterface());
assertEquals("Go nil struct pointer is null", null, Testpkg.NewNullStruct());
}
public void testPassByteArray() {
Testpkg.PassByteArray(new Testpkg.B.Stub() {
@Override public void B(byte[] b) {
byte[] want = new byte[]{1, 2, 3, 4};
MoreAsserts.assertEquals("bytes should match", want, b);
}
});
}
public void testReader() {
byte[] b = new byte[8];
try {
long n = Testpkg.ReadIntoByteArray(b);
assertEquals("wrote to the entire byte array", b.length, n);
byte[] want = new byte[b.length];
for (int i = 0; i < want.length; i++)
want[i] = (byte)i;
MoreAsserts.assertEquals("bytes should match", want, b);
} catch (Exception e) {
fail("Failed to write: " + e.toString());
}
}
public void testGoroutineCallback() {
Testpkg.GoroutineCallback(new Testpkg.Receiver.Stub() {
@Override public void Hello(String msg) {
}
});
}
}