Adding upstream version 0.0~git20250520.a1d9079+dfsg.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
590ac7ff5f
commit
20149b7f3a
456 changed files with 70406 additions and 0 deletions
157
bind/java/ClassesTest.java
Normal file
157
bind/java/ClassesTest.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
// 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.
|
||||
|
||||
package go;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
import javapkg.Javapkg;
|
||||
import javapkg.I;
|
||||
import javapkg.GoObject;
|
||||
import javapkg.GoRunnable;
|
||||
import javapkg.GoSubset;
|
||||
import javapkg.GoInputStream;
|
||||
import javapkg.GoArrayList;
|
||||
|
||||
public class ClassesTest extends InstrumentationTestCase {
|
||||
public void testConst() {
|
||||
assertEquals("const Float", Float.MIN_VALUE, Javapkg.floatMin());
|
||||
assertEquals("const String", java.util.jar.JarFile.MANIFEST_NAME, Javapkg.manifestName());
|
||||
assertEquals("const Int", 7, Integer.SIZE, Javapkg.integerBytes());
|
||||
}
|
||||
|
||||
public void testFunction() {
|
||||
Javapkg.systemCurrentTimeMillis();
|
||||
}
|
||||
|
||||
public void testMethod() {
|
||||
try {
|
||||
assertEquals("Integer.decode", 0xff, Javapkg.integerDecode("0xff"));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Exception exc = null;
|
||||
try {
|
||||
Javapkg.integerDecode("obviously wrong");
|
||||
} catch (Exception e) {
|
||||
exc = e;
|
||||
}
|
||||
assertNotNull("IntegerDecode Exception", exc);
|
||||
}
|
||||
|
||||
public void testOverloadedMethod() {
|
||||
try {
|
||||
assertEquals("Integer.parseInt", 0xc4, Javapkg.integerParseInt("c4", 16));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Exception exc = null;
|
||||
try {
|
||||
Javapkg.integerParseInt("wrong", 16);
|
||||
} catch (Exception e) {
|
||||
exc = e;
|
||||
}
|
||||
assertNotNull("integerParseInt Exception", exc);
|
||||
assertEquals("Integer.valueOf", 42, Javapkg.integerValueOf(42));
|
||||
}
|
||||
|
||||
public void testException() {
|
||||
Exception exc = null;
|
||||
try {
|
||||
Javapkg.provokeRuntimeException();
|
||||
} catch (Exception e) {
|
||||
exc = e;
|
||||
}
|
||||
assertNotNull("RuntimeException", exc);
|
||||
}
|
||||
|
||||
public void testField() {
|
||||
GoRunnable r = new GoRunnable();
|
||||
String f = r.getField();
|
||||
}
|
||||
|
||||
public void testGoObject() {
|
||||
Runnable r = new GoRunnable();
|
||||
r.run();
|
||||
assertEquals("GoRunnable.toString", r.toString(), Javapkg.ToStringPrefix);
|
||||
Runnable r2 = ((GoRunnable)r).getThis();
|
||||
assertTrue("GoObject.this", r == r2);
|
||||
Object o = new GoObject();
|
||||
assertEquals("GoObject hashCode", 42, o.hashCode());
|
||||
Object o2 = Javapkg.constructGoObject();
|
||||
assertEquals("GoObject hashCode", 42, o2.hashCode());
|
||||
assertTrue("GoObject.toString", o.toString().startsWith(Javapkg.ToStringPrefix));
|
||||
Javapkg.runRunnable(r);
|
||||
final boolean[] ran = new boolean[1];
|
||||
Runnable r3 = new Runnable(){
|
||||
@Override public void run() {
|
||||
ran[0] = true;
|
||||
}
|
||||
};
|
||||
Javapkg.runRunnable(r3);
|
||||
assertTrue("RunRunnable", ran[0]);
|
||||
assertTrue("RunnableRoundtrip Java", r3 == Javapkg.runnableRoundtrip(r3));
|
||||
assertTrue("RunnableRoundtrip Go", r == Javapkg.runnableRoundtrip(r));
|
||||
Runnable r5 = Javapkg.constructGoRunnable();
|
||||
r5.run();
|
||||
}
|
||||
|
||||
public void testTypedException() {
|
||||
InputStream is = new GoInputStream();
|
||||
Exception exc = null;
|
||||
try {
|
||||
is.read();
|
||||
} catch (IOException e) {
|
||||
exc = e;
|
||||
}
|
||||
assertNotNull("IOException", exc);
|
||||
assertEquals("IOException message", Javapkg.IOExceptionMessage, exc.getMessage());
|
||||
}
|
||||
|
||||
public void testInnerClass() {
|
||||
Character.Subset s = new Character.Subset(""){};
|
||||
Character.Subset s2 = new GoSubset("");
|
||||
Javapkg.callSubset(s);
|
||||
Javapkg.callSubset(s2);
|
||||
}
|
||||
|
||||
public void testNew() {
|
||||
Object o = Javapkg.newJavaObject();
|
||||
assertTrue("new Object()", o != null);
|
||||
Integer i = Javapkg.newJavaInteger();
|
||||
assertEquals("new Integer(42)", 42, i.intValue());
|
||||
}
|
||||
|
||||
private static class InterfaceRunnable implements I, Runnable {
|
||||
@Override public void run() {
|
||||
}
|
||||
}
|
||||
|
||||
public void testCast() {
|
||||
Runnable r1 = new GoRunnable();
|
||||
Runnable r1c = Javapkg.castRunnable((Object)r1);
|
||||
assertTrue("Casting Go object", r1c != null);
|
||||
Runnable r2 = new Runnable() {
|
||||
@Override public void run() {
|
||||
}
|
||||
};
|
||||
Runnable r2c = Javapkg.castRunnable((Object)r2);
|
||||
assertTrue("Casting Java object", r2c != null);
|
||||
Runnable r3c = Javapkg.castInterface(new InterfaceRunnable());
|
||||
assertTrue("Casting Go interface implementation", r3c != null);
|
||||
Runnable r4c = Javapkg.castRunnable(new Object());
|
||||
assertTrue("Invalid cast", r4c == null);
|
||||
}
|
||||
|
||||
public void testUnwrap() {
|
||||
GoArrayList l = new GoArrayList();
|
||||
Javapkg.unwrapGoArrayList(l);
|
||||
}
|
||||
}
|
15
bind/java/CustomPkgTest.java
Normal file
15
bind/java/CustomPkgTest.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
// 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.
|
||||
|
||||
package go;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
import org.golang.custompkg.testpkg.Testpkg;
|
||||
|
||||
public class CustomPkgTest extends InstrumentationTestCase {
|
||||
public void testHi() {
|
||||
Testpkg.hi();
|
||||
}
|
||||
}
|
400
bind/java/Seq.java
Normal file
400
bind/java/Seq.java
Normal file
|
@ -0,0 +1,400 @@
|
|||
// 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.content.Context;
|
||||
|
||||
import java.lang.ref.PhantomReference;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import go.Universe;
|
||||
|
||||
// Seq is a sequence of machine-dependent encoded values.
|
||||
// Used by automatically generated language bindings to talk to Go.
|
||||
public class Seq {
|
||||
private static Logger log = Logger.getLogger("GoSeq");
|
||||
|
||||
// also known to bind/seq/ref.go and bind/objc/seq_darwin.m
|
||||
private static final int NULL_REFNUM = 41;
|
||||
|
||||
// use single Ref for null Object
|
||||
public static final Ref nullRef = new Ref(NULL_REFNUM, null);
|
||||
|
||||
// The singleton GoRefQueue
|
||||
private static final GoRefQueue goRefQueue = new GoRefQueue();
|
||||
|
||||
static {
|
||||
System.loadLibrary("gojni");
|
||||
init();
|
||||
Universe.touch();
|
||||
}
|
||||
|
||||
// setContext sets the context in the go-library to be used in RunOnJvm.
|
||||
public static void setContext(Context context) {
|
||||
setContext((java.lang.Object)context);
|
||||
}
|
||||
|
||||
private static native void init();
|
||||
|
||||
// Empty method to run class initializer
|
||||
public static void touch() {}
|
||||
|
||||
private Seq() {
|
||||
}
|
||||
|
||||
// ctx is an android.context.Context.
|
||||
static native void setContext(java.lang.Object ctx);
|
||||
|
||||
public static void incRefnum(int refnum) {
|
||||
tracker.incRefnum(refnum);
|
||||
}
|
||||
|
||||
// incRef increments the reference count of Java objects.
|
||||
// For proxies for Go objects, it calls into the Proxy method
|
||||
// incRefnum() to make sure the Go reference count is positive
|
||||
// even if the Proxy is garbage collected and its Ref is finalized.
|
||||
public static int incRef(Object o) {
|
||||
return tracker.inc(o);
|
||||
}
|
||||
|
||||
public static int incGoObjectRef(GoObject o) {
|
||||
return o.incRefnum();
|
||||
}
|
||||
|
||||
// trackGoRef tracks a Go reference and decrements its refcount
|
||||
// when the given GoObject wrapper is garbage collected.
|
||||
//
|
||||
// TODO(crawshaw): We could cut down allocations for frequently
|
||||
// sent Go objects by maintaining a map to weak references. This
|
||||
// however, would require allocating two objects per reference
|
||||
// instead of one. It also introduces weak references, the bane
|
||||
// of any Java debugging session.
|
||||
//
|
||||
// When we have real code, examine the tradeoffs.
|
||||
public static void trackGoRef(int refnum, GoObject obj) {
|
||||
if (refnum > 0) {
|
||||
throw new RuntimeException("trackGoRef called with Java refnum " + refnum);
|
||||
}
|
||||
goRefQueue.track(refnum, obj);
|
||||
}
|
||||
|
||||
public static Ref getRef(int refnum) {
|
||||
return tracker.get(refnum);
|
||||
}
|
||||
|
||||
// Increment the Go reference count before sending over a refnum.
|
||||
// The ref parameter is only used to make sure the referenced
|
||||
// object is not garbage collected before Go increments the
|
||||
// count. It's the equivalent of Go's runtime.KeepAlive.
|
||||
public static native void incGoRef(int refnum, GoObject ref);
|
||||
|
||||
// Informs the Go ref tracker that Java is done with this refnum.
|
||||
static native void destroyRef(int refnum);
|
||||
|
||||
// decRef is called from seq.FinalizeRef
|
||||
static void decRef(int refnum) {
|
||||
tracker.dec(refnum);
|
||||
}
|
||||
|
||||
// A GoObject is a Java class implemented in Go. When a GoObject
|
||||
// is passed to Go, it is wrapped in a Go proxy, to make it behave
|
||||
// the same as passing a regular Java class.
|
||||
public interface GoObject {
|
||||
// Increment refcount and return the refnum of the proxy.
|
||||
//
|
||||
// The Go reference count need to be bumped while the
|
||||
// refnum is passed to Go, to avoid finalizing and
|
||||
// invalidating it before being translated on the Go side.
|
||||
int incRefnum();
|
||||
}
|
||||
// A Proxy is a Java object that proxies a Go object. Proxies, unlike
|
||||
// GoObjects, are unwrapped to their Go counterpart when deserialized
|
||||
// in Go.
|
||||
public interface Proxy extends GoObject {}
|
||||
|
||||
// A Ref represents an instance of a Java object passed back and forth
|
||||
// across the language boundary.
|
||||
public static final class Ref {
|
||||
public final int refnum;
|
||||
|
||||
private int refcnt; // Track how many times sent to Go.
|
||||
|
||||
public final Object obj; // The referenced Java obj.
|
||||
|
||||
Ref(int refnum, Object o) {
|
||||
if (refnum < 0) {
|
||||
throw new RuntimeException("Ref instantiated with a Go refnum " + refnum);
|
||||
}
|
||||
this.refnum = refnum;
|
||||
this.refcnt = 0;
|
||||
this.obj = o;
|
||||
}
|
||||
|
||||
void inc() {
|
||||
// Count how many times this ref's Java object is passed to Go.
|
||||
if (refcnt == Integer.MAX_VALUE) {
|
||||
throw new RuntimeException("refnum " + refnum + " overflow");
|
||||
}
|
||||
refcnt++;
|
||||
}
|
||||
}
|
||||
|
||||
static final RefTracker tracker = new RefTracker();
|
||||
|
||||
static final class RefTracker {
|
||||
private static final int REF_OFFSET = 42;
|
||||
|
||||
// Next Java object reference number.
|
||||
//
|
||||
// Reference numbers are positive for Java objects,
|
||||
// and start, arbitrarily at a different offset to Go
|
||||
// to make debugging by reading Seq hex a little easier.
|
||||
private int next = REF_OFFSET; // next Java object ref
|
||||
|
||||
// Java objects that have been passed to Go. refnum -> Ref
|
||||
// The Ref obj field is non-null.
|
||||
// This map pins Java objects so they don't get GCed while the
|
||||
// only reference to them is held by Go code.
|
||||
private final RefMap javaObjs = new RefMap();
|
||||
|
||||
// Java objects to refnum
|
||||
private final IdentityHashMap<Object, Integer> javaRefs = new IdentityHashMap<>();
|
||||
|
||||
// inc increments the reference count of a Java object when it
|
||||
// is sent to Go. inc returns the refnum for the object.
|
||||
synchronized int inc(Object o) {
|
||||
if (o == null) {
|
||||
return NULL_REFNUM;
|
||||
}
|
||||
if (o instanceof Proxy) {
|
||||
return ((Proxy)o).incRefnum();
|
||||
}
|
||||
Integer refnumObj = javaRefs.get(o);
|
||||
if (refnumObj == null) {
|
||||
if (next == Integer.MAX_VALUE) {
|
||||
throw new RuntimeException("createRef overflow for " + o);
|
||||
}
|
||||
refnumObj = next++;
|
||||
javaRefs.put(o, refnumObj);
|
||||
}
|
||||
int refnum = refnumObj;
|
||||
Ref ref = javaObjs.get(refnum);
|
||||
if (ref == null) {
|
||||
ref = new Ref(refnum, o);
|
||||
javaObjs.put(refnum, ref);
|
||||
}
|
||||
ref.inc();
|
||||
return refnum;
|
||||
}
|
||||
|
||||
synchronized void incRefnum(int refnum) {
|
||||
Ref ref = javaObjs.get(refnum);
|
||||
if (ref == null) {
|
||||
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
|
||||
}
|
||||
ref.inc();
|
||||
}
|
||||
|
||||
// dec decrements the reference count of a Java object when
|
||||
// Go signals a corresponding proxy object is finalized.
|
||||
// If the count reaches zero, the Java object is removed
|
||||
// from the javaObjs map.
|
||||
synchronized void dec(int refnum) {
|
||||
if (refnum <= 0) {
|
||||
// We don't keep track of the Go object.
|
||||
// This must not happen.
|
||||
log.severe("dec request for Go object "+ refnum);
|
||||
return;
|
||||
}
|
||||
if (refnum == Seq.nullRef.refnum) {
|
||||
return;
|
||||
}
|
||||
// Java objects are removed on request of Go.
|
||||
Ref obj = javaObjs.get(refnum);
|
||||
if (obj == null) {
|
||||
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
|
||||
}
|
||||
obj.refcnt--;
|
||||
if (obj.refcnt <= 0) {
|
||||
javaObjs.remove(refnum);
|
||||
javaRefs.remove(obj.obj);
|
||||
}
|
||||
}
|
||||
|
||||
// get returns an existing Ref to a Java object.
|
||||
synchronized Ref get(int refnum) {
|
||||
if (refnum < 0) {
|
||||
throw new RuntimeException("ref called with Go refnum " + refnum);
|
||||
}
|
||||
if (refnum == NULL_REFNUM) {
|
||||
return nullRef;
|
||||
}
|
||||
Ref ref = javaObjs.get(refnum);
|
||||
if (ref == null) {
|
||||
throw new RuntimeException("unknown java Ref: "+refnum);
|
||||
}
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
// GoRefQueue is a queue of GoRefs that are no longer live. An internal thread
|
||||
// processes the queue and decrement the reference count on the Go side.
|
||||
static class GoRefQueue extends ReferenceQueue<GoObject> {
|
||||
// The set of tracked GoRefs. If we don't hold on to the GoRef instances, the Java GC
|
||||
// will not add them to the queue when their referents are reclaimed.
|
||||
private final Collection<GoRef> refs = Collections.synchronizedCollection(new HashSet<GoRef>());
|
||||
|
||||
void track(int refnum, GoObject obj) {
|
||||
refs.add(new GoRef(refnum, obj, this));
|
||||
}
|
||||
|
||||
GoRefQueue() {
|
||||
Thread daemon = new Thread(new Runnable() {
|
||||
@Override public void run() {
|
||||
while (true) {
|
||||
try {
|
||||
GoRef ref = (GoRef)remove();
|
||||
refs.remove(ref);
|
||||
destroyRef(ref.refnum);
|
||||
ref.clear();
|
||||
} catch (InterruptedException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
daemon.setDaemon(true);
|
||||
daemon.setName("GoRefQueue Finalizer Thread");
|
||||
daemon.start();
|
||||
}
|
||||
}
|
||||
|
||||
// A GoRef is a PhantomReference to a Java proxy for a Go object.
|
||||
// GoRefs are enqueued to the singleton GoRefQueue when no longer live,
|
||||
// so the corresponding reference count can be decremented.
|
||||
static class GoRef extends PhantomReference<GoObject> {
|
||||
final int refnum;
|
||||
|
||||
GoRef(int refnum, GoObject obj, GoRefQueue q) {
|
||||
super(obj, q);
|
||||
if (refnum > 0) {
|
||||
throw new RuntimeException("GoRef instantiated with a Java refnum " + refnum);
|
||||
}
|
||||
this.refnum = refnum;
|
||||
}
|
||||
}
|
||||
|
||||
// RefMap is a mapping of integers to Ref objects.
|
||||
//
|
||||
// The integers can be sparse. In Go this would be a map[int]*Ref.
|
||||
static final class RefMap {
|
||||
private int next = 0;
|
||||
private int live = 0;
|
||||
private int[] keys = new int[16];
|
||||
private Ref[] objs = new Ref[16];
|
||||
|
||||
RefMap() {}
|
||||
|
||||
Ref get(int key) {
|
||||
int i = Arrays.binarySearch(keys, 0, next, key);
|
||||
if (i >= 0) {
|
||||
return objs[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void remove(int key) {
|
||||
int i = Arrays.binarySearch(keys, 0, next, key);
|
||||
if (i >= 0) {
|
||||
if (objs[i] != null) {
|
||||
objs[i] = null;
|
||||
live--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void put(int key, Ref obj) {
|
||||
if (obj == null) {
|
||||
throw new RuntimeException("put a null ref (with key "+key+")");
|
||||
}
|
||||
int i = Arrays.binarySearch(keys, 0, next, key);
|
||||
if (i >= 0) {
|
||||
if (objs[i] == null) {
|
||||
objs[i] = obj;
|
||||
live++;
|
||||
}
|
||||
if (objs[i] != obj) {
|
||||
throw new RuntimeException("replacing an existing ref (with key "+key+")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (next >= keys.length) {
|
||||
grow();
|
||||
i = Arrays.binarySearch(keys, 0, next, key);
|
||||
}
|
||||
i = ~i;
|
||||
if (i < next) {
|
||||
// Insert, shift everything afterwards down.
|
||||
System.arraycopy(keys, i, keys, i+1, next-i);
|
||||
System.arraycopy(objs, i, objs, i+1, next-i);
|
||||
}
|
||||
keys[i] = key;
|
||||
objs[i] = obj;
|
||||
live++;
|
||||
next++;
|
||||
}
|
||||
|
||||
private void grow() {
|
||||
// Compact and (if necessary) grow backing store.
|
||||
int[] newKeys;
|
||||
Ref[] newObjs;
|
||||
int len = 2*roundPow2(live);
|
||||
if (len > keys.length) {
|
||||
newKeys = new int[keys.length*2];
|
||||
newObjs = new Ref[objs.length*2];
|
||||
} else {
|
||||
newKeys = keys;
|
||||
newObjs = objs;
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
if (objs[i] != null) {
|
||||
newKeys[j] = keys[i];
|
||||
newObjs[j] = objs[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
for (int i = j; i < newKeys.length; i++) {
|
||||
newKeys[i] = 0;
|
||||
newObjs[i] = null;
|
||||
}
|
||||
|
||||
keys = newKeys;
|
||||
objs = newObjs;
|
||||
next = j;
|
||||
|
||||
if (live != next) {
|
||||
throw new RuntimeException("bad state: live="+live+", next="+next);
|
||||
}
|
||||
}
|
||||
|
||||
private static int roundPow2(int x) {
|
||||
int p = 1;
|
||||
while (p < x) {
|
||||
p *= 2;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
162
bind/java/SeqBench.java
Normal file
162
bind/java/SeqBench.java
Normal file
|
@ -0,0 +1,162 @@
|
|||
// 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.
|
||||
|
||||
package go;
|
||||
|
||||
import android.test.InstrumentationTestCase;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import benchmark.*;
|
||||
|
||||
public class SeqBench extends InstrumentationTestCase {
|
||||
|
||||
public static class AnI implements I {
|
||||
@Override public void f() {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Benchmarks implements benchmark.Benchmarks {
|
||||
private static Map<String, Runnable> benchmarks;
|
||||
private static ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
static {
|
||||
benchmarks = new HashMap<String, Runnable>();
|
||||
benchmarks.put("Empty", new Runnable() {
|
||||
@Override public void run() {
|
||||
}
|
||||
});
|
||||
benchmarks.put("Noargs", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.noargs();
|
||||
}
|
||||
});
|
||||
benchmarks.put("Onearg", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.onearg(0);
|
||||
}
|
||||
});
|
||||
benchmarks.put("Manyargs", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.manyargs(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
});
|
||||
benchmarks.put("Oneret", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.oneret();
|
||||
}
|
||||
});
|
||||
final I javaRef = new AnI();
|
||||
benchmarks.put("Refforeign", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.ref(javaRef);
|
||||
}
|
||||
});
|
||||
final I goRef = Benchmark.newI();
|
||||
benchmarks.put("Refgo", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.ref(goRef);
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringShort", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.string(Benchmark.ShortString);
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringLong", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.string(Benchmark.LongString);
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringShortUnicode", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.string(Benchmark.ShortStringUnicode);
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringLongUnicode", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.string(Benchmark.LongStringUnicode);
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringRetShort", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.stringRetShort();
|
||||
}
|
||||
});
|
||||
benchmarks.put("StringRetLong", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.stringRetLong();
|
||||
}
|
||||
});
|
||||
final byte[] shortSlice = Benchmark.getShortSlice();
|
||||
benchmarks.put("SliceShort", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.slice(shortSlice);
|
||||
}
|
||||
});
|
||||
final byte[] longSlice = Benchmark.getLongSlice();
|
||||
benchmarks.put("SliceLong", new Runnable() {
|
||||
@Override public void run() {
|
||||
Benchmark.slice(longSlice);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void runDirect(String name, final long n) {
|
||||
final Runnable r = benchmarks.get(name);
|
||||
try {
|
||||
executor.submit(new Runnable() {
|
||||
@Override public void run() {
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}).get();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void run(String name, long n) {
|
||||
final Runnable r = benchmarks.get(name);
|
||||
for (int i = 0; i < n; i++) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public I newI() {
|
||||
return new AnI();
|
||||
}
|
||||
@Override public void ref(I i) {
|
||||
}
|
||||
@Override public void noargs() {
|
||||
}
|
||||
@Override public void onearg(long i) {
|
||||
}
|
||||
@Override public long oneret() {
|
||||
return 0;
|
||||
}
|
||||
@Override public void manyargs(long p0, long p1, long p2, long p3, long p4, long p5, long p6, long p7, long gp8, long p9) {
|
||||
}
|
||||
@Override public void string(String s) {
|
||||
}
|
||||
@Override public void slice(byte[] s) {
|
||||
}
|
||||
@Override public String stringRetShort() {
|
||||
return Benchmark.ShortString;
|
||||
}
|
||||
@Override public String stringRetLong() {
|
||||
return Benchmark.LongString;
|
||||
}
|
||||
}
|
||||
|
||||
public void testBenchmark() {
|
||||
Benchmark.runBenchmarks(new Benchmarks());
|
||||
}
|
||||
}
|
602
bind/java/SeqTest.java
Normal file
602
bind/java/SeqTest.java
Normal file
|
@ -0,0 +1,602 @@
|
|||
// 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 testpkg.*;
|
||||
import secondpkg.Secondpkg;
|
||||
|
||||
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());
|
||||
|
||||
S s0 = Testpkg.getStructVar();
|
||||
assertEquals("var StructVar", "a struct var", s0.string());
|
||||
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() {
|
||||
String[] tests = new String[]{
|
||||
"abcxyz09{}",
|
||||
"Hello, 世界",
|
||||
"\uffff\uD800\uDC00\uD800\uDC01\uD808\uDF45\uDBFF\uDFFF",
|
||||
// From Go std lib tests in unicode/utf16/utf16_test.go
|
||||
"\u0001\u0002\u0003\u0004",
|
||||
"\uffff\ud800\udc00\ud800\udc01\ud808\udf45\udbff\udfff",
|
||||
"\ud800a",
|
||||
"\udfff"
|
||||
};
|
||||
String[] wants = new String[]{
|
||||
"abcxyz09{}",
|
||||
"Hello, 世界",
|
||||
"\uffff\uD800\uDC00\uD800\uDC01\uD808\uDF45\uDBFF\uDFFF",
|
||||
"\u0001\u0002\u0003\u0004",
|
||||
"\uffff\ud800\udc00\ud800\udc01\ud808\udf45\udbff\udfff",
|
||||
"\ufffda",
|
||||
"\ufffd"
|
||||
};
|
||||
for (int i = 0; i < tests.length; i++) {
|
||||
String got = Testpkg.strDup(tests[i]);
|
||||
String want = wants[i];
|
||||
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() {
|
||||
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 implements I {
|
||||
public void e() throws Exception {
|
||||
throw new Exception("my exception from E");
|
||||
}
|
||||
|
||||
boolean calledF;
|
||||
public void f() {
|
||||
calledF = true;
|
||||
}
|
||||
|
||||
public I i() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public S s() {
|
||||
return Testpkg.new_();
|
||||
}
|
||||
|
||||
public String stoString(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 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";
|
||||
I i = Testpkg.callI(obj);
|
||||
assertEquals("Want AnI.I to return itself", i.string(), obj.string());
|
||||
|
||||
runGC();
|
||||
|
||||
i = Testpkg.callI(obj);
|
||||
assertEquals("Want AnI.I to return itself", i.string(), obj.string());
|
||||
}
|
||||
|
||||
public void testInterfaceMethodReturnsStructPointer() {
|
||||
final AnI obj = new AnI();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
S s = Testpkg.callS(obj);
|
||||
runGC();
|
||||
}
|
||||
}
|
||||
|
||||
public void testInterfaceMethodTakesStructPointer() {
|
||||
final AnI obj = new AnI();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
boolean finalizedAnI;
|
||||
|
||||
private class AnI_Traced extends AnI {
|
||||
@Override
|
||||
public void finalize() throws Throwable {
|
||||
finalizedAnI = true;
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
public void testJavaRefKeep() {
|
||||
finalizedAnI = false;
|
||||
AnI obj = new AnI_Traced();
|
||||
Testpkg.callF(obj);
|
||||
assertTrue("want F to be called", obj.calledF);
|
||||
Testpkg.callF(obj);
|
||||
obj = null;
|
||||
int attempts = 0;
|
||||
while (true) {
|
||||
runGC();
|
||||
if (finalizedAnI)
|
||||
break;
|
||||
attempts++;
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (attempts >= 10)
|
||||
fail("want obj not to be kept by Go; tried " + attempts + " garbage collections.");
|
||||
}
|
||||
|
||||
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 implements I {
|
||||
public void f() { countI++; }
|
||||
|
||||
public void e() throws Exception {}
|
||||
public I i() { return null; }
|
||||
public S s() { return null; }
|
||||
public String stoString(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() {
|
||||
Node a = Testpkg.newNode("A");
|
||||
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() {
|
||||
Interface intf = Testpkg.newConcrete();
|
||||
}
|
||||
|
||||
public void testErrorField() {
|
||||
Node n = Testpkg.newNode("ErrTest");
|
||||
Exception want = new Exception("an error message");
|
||||
n.setErr(want);
|
||||
Exception got = n.getErr();
|
||||
assertTrue("want back the error we set", want == got);
|
||||
String msg = Testpkg.errorMessage(want);
|
||||
assertEquals("the error message must match", want.getMessage(), msg);
|
||||
}
|
||||
|
||||
public void testErrorDup() {
|
||||
Exception err = Testpkg.getGlobalErr();
|
||||
assertTrue("the Go error instance must preserve its identity", Testpkg.isGlobalErr(err));
|
||||
assertEquals("the Go error message must be preserved", "global err", err.getMessage());
|
||||
}
|
||||
|
||||
//test if we have JNI local reference table overflow error
|
||||
public void testLocalReferenceOverflow() {
|
||||
Testpkg.callWithCallback(new GoCallback() {
|
||||
|
||||
@Override
|
||||
public void varUpdate() {
|
||||
//do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testNullReferences() {
|
||||
assertTrue(Testpkg.callWithNull(null, new NullTest() {
|
||||
public NullTest null_() {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
assertEquals("Go nil interface is null", null, Testpkg.newNullInterface());
|
||||
assertEquals("Go nil struct pointer is null", null, Testpkg.newNullStruct());
|
||||
|
||||
Issue20330 nullArger = new Issue20330();
|
||||
assertTrue(nullArger.callWithNull(null));
|
||||
}
|
||||
|
||||
public void testPassByteArray() {
|
||||
Testpkg.passByteArray(new B() {
|
||||
@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 Receiver() {
|
||||
@Override public void hello(String msg) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void testImportedPkg() {
|
||||
Testpkg.callImportedI(new secondpkg.I() {
|
||||
@Override public long f(long i) {
|
||||
return i;
|
||||
}
|
||||
});
|
||||
assertEquals("imported string should match", Secondpkg.HelloString, Secondpkg.hello());
|
||||
secondpkg.I i = Testpkg.newImportedI();
|
||||
secondpkg.S s = Testpkg.newImportedS();
|
||||
i = Testpkg.getImportedVarI();
|
||||
s = Testpkg.getImportedVarS();
|
||||
assertEquals("numbers should match", 8, i.f(8));
|
||||
assertEquals("numbers should match", 8, s.f(8));
|
||||
Testpkg.setImportedVarI(i);
|
||||
Testpkg.setImportedVarS(s);
|
||||
ImportedFields fields = Testpkg.newImportedFields();
|
||||
i = fields.getI();
|
||||
s = fields.getS();
|
||||
fields.setI(i);
|
||||
fields.setS(s);
|
||||
Testpkg.withImportedI(i);
|
||||
Testpkg.withImportedS(s);
|
||||
|
||||
secondpkg.IF f = new AnI();
|
||||
f = Testpkg.new_();
|
||||
secondpkg.Ser ser = Testpkg.newSer();
|
||||
}
|
||||
|
||||
public void testRoundtripEquality() {
|
||||
I want = new AnI();
|
||||
assertTrue("java object passed through Go should not be wrapped", want == Testpkg.iDup(want));
|
||||
InterfaceDupper idup = new InterfaceDupper(){
|
||||
@Override public Interface iDup(Interface i) {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
assertTrue("Go interface passed through Java should not be wrapped", Testpkg.callIDupper(idup));
|
||||
ConcreteDupper cdup = new ConcreteDupper(){
|
||||
@Override public Concrete cDup(Concrete c) {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
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());
|
||||
|
||||
s4 = new S4(123.456);
|
||||
assertEquals("Overloaded constructor argument", 123, s4.getI());
|
||||
|
||||
s4 = new S4(false);
|
||||
assertEquals("Exceptional constructor", 0, s4.getI());
|
||||
|
||||
try {
|
||||
s4 = new S4(true);
|
||||
fail("Constructor error wasn't caught");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testEmptyError() {
|
||||
try {
|
||||
Testpkg.emptyError();
|
||||
fail("Empty error wasn't caught");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
EmptyErrorer empty = new EmptyErrorer() {
|
||||
@Override public void emptyError() throws Exception {
|
||||
throw new Exception("");
|
||||
}
|
||||
};
|
||||
try {
|
||||
Testpkg.callEmptyError(empty);
|
||||
fail("Empty exception wasn't caught");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void testInitCaller() {
|
||||
Testpkg.init();
|
||||
|
||||
InitCaller initer = Testpkg.newInitCaller();
|
||||
initer.init();
|
||||
}
|
||||
|
||||
public void testSIGPIPE() {
|
||||
Testpkg.testSIGPIPE();
|
||||
}
|
||||
|
||||
public void testTags() {
|
||||
assertEquals("Constant from a tagged file", 42, Testpkg.TaggedConst);
|
||||
}
|
||||
|
||||
public void testClassNameWithPackageName() {
|
||||
testpkg.Testpkg_ o = new secondpkg.Secondpkg_();
|
||||
secondpkg.Secondpkg_ o2 = Secondpkg.newSecondpkg();
|
||||
o2.m();
|
||||
o2.setV("hi");
|
||||
assertEquals(o2.getV(), "hi");
|
||||
Testpkg.clashingParameterFromOtherPackage(o2);
|
||||
}
|
||||
}
|
16
bind/java/context_android.c
Normal file
16
bind/java/context_android.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
// 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.
|
||||
|
||||
#include <jni.h>
|
||||
#include "seq_android.h"
|
||||
#include "_cgo_export.h"
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_go_Seq_setContext(JNIEnv* env, jclass clazz, jobject ctx) {
|
||||
JavaVM* vm;
|
||||
if ((*env)->GetJavaVM(env, &vm) != 0) {
|
||||
LOG_FATAL("failed to get JavaVM");
|
||||
}
|
||||
setContext(vm, (*env)->NewGlobalRef(env, ctx));
|
||||
}
|
21
bind/java/context_android.go
Normal file
21
bind/java/context_android.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
package java
|
||||
|
||||
// #cgo LDFLAGS: -llog
|
||||
//
|
||||
//#include <jni.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/mobile/internal/mobileinit"
|
||||
)
|
||||
|
||||
//export setContext
|
||||
func setContext(vm *C.JavaVM, ctx C.jobject) {
|
||||
mobileinit.SetCurrentContext(unsafe.Pointer(vm), uintptr(ctx))
|
||||
}
|
8
bind/java/doc.go
Normal file
8
bind/java/doc.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
// Copyright 2015 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 java implements the Java language bindings.
|
||||
//
|
||||
// See the design document (http://golang.org/s/gobind).
|
||||
package java
|
401
bind/java/seq_android.c.support
Normal file
401
bind/java/seq_android.c.support
Normal file
|
@ -0,0 +1,401 @@
|
|||
// 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.
|
||||
|
||||
// C support functions for bindings. This file is copied into the
|
||||
// generated gomobile_bind package and compiled along with the
|
||||
// generated binding files.
|
||||
|
||||
#include <android/log.h>
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include "seq.h"
|
||||
#include "_cgo_export.h"
|
||||
|
||||
#define NULL_REFNUM 41
|
||||
|
||||
// initClasses are only exported from Go if reverse bindings are used.
|
||||
// If they're not, weakly define a no-op function.
|
||||
__attribute__((weak)) void initClasses(void) { }
|
||||
|
||||
static JavaVM *jvm;
|
||||
// jnienvs holds the per-thread JNIEnv* for Go threads where we called AttachCurrentThread.
|
||||
// A pthread key destructor is supplied to call DetachCurrentThread on exit. This trick is
|
||||
// documented in http://developer.android.com/training/articles/perf-jni.html under "Threads".
|
||||
static pthread_key_t jnienvs;
|
||||
|
||||
static jclass seq_class;
|
||||
static jmethodID seq_getRef;
|
||||
static jmethodID seq_decRef;
|
||||
static jmethodID seq_incRef;
|
||||
static jmethodID seq_incGoObjectRef;
|
||||
static jmethodID seq_incRefnum;
|
||||
|
||||
static jfieldID ref_objField;
|
||||
|
||||
static jclass throwable_class;
|
||||
|
||||
// env_destructor is registered as a thread data key destructor to
|
||||
// clean up a Go thread that is attached to the JVM.
|
||||
static void env_destructor(void *env) {
|
||||
if ((*jvm)->DetachCurrentThread(jvm) != JNI_OK) {
|
||||
LOG_INFO("failed to detach current thread");
|
||||
}
|
||||
}
|
||||
|
||||
static JNIEnv *go_seq_get_thread_env(void) {
|
||||
JNIEnv *env;
|
||||
jint ret = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
LOG_FATAL("failed to get thread env");
|
||||
}
|
||||
if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) != JNI_OK) {
|
||||
LOG_FATAL("failed to attach current thread");
|
||||
}
|
||||
pthread_setspecific(jnienvs, env);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
void go_seq_maybe_throw_exception(JNIEnv *env, jobject exc) {
|
||||
if (exc != NULL) {
|
||||
(*env)->Throw(env, exc);
|
||||
}
|
||||
}
|
||||
|
||||
jobject go_seq_get_exception(JNIEnv *env) {
|
||||
jthrowable exc = (*env)->ExceptionOccurred(env);
|
||||
if (!exc) {
|
||||
return NULL;
|
||||
}
|
||||
(*env)->ExceptionClear(env);
|
||||
return exc;
|
||||
}
|
||||
|
||||
jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy) {
|
||||
if (s.ptr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
jbyteArray res = (*env)->NewByteArray(env, s.len);
|
||||
if (res == NULL) {
|
||||
LOG_FATAL("NewByteArray failed");
|
||||
}
|
||||
(*env)->SetByteArrayRegion(env, res, 0, s.len, s.ptr);
|
||||
if (copy) {
|
||||
free(s.ptr);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define surr1 0xd800
|
||||
#define surr2 0xdc00
|
||||
#define surr3 0xe000
|
||||
|
||||
// Unicode replacement character
|
||||
#define replacementChar 0xFFFD
|
||||
|
||||
#define rune1Max ((1<<7) - 1)
|
||||
#define rune2Max ((1<<11) - 1)
|
||||
#define rune3Max ((1<<16) - 1)
|
||||
// Maximum valid Unicode code point.
|
||||
#define MaxRune 0x0010FFFF
|
||||
|
||||
#define surrogateMin 0xD800
|
||||
#define surrogateMax 0xDFFF
|
||||
// 0011 1111
|
||||
#define maskx 0x3F
|
||||
// 1000 0000
|
||||
#define tx 0x80
|
||||
// 1100 0000
|
||||
#define t2 0xC0
|
||||
// 1110 0000
|
||||
#define t3 0xE0
|
||||
// 1111 0000
|
||||
#define t4 0xF0
|
||||
|
||||
// encode_rune writes into p (which must be large enough) the UTF-8 encoding
|
||||
// of the rune. It returns the number of bytes written.
|
||||
static int encode_rune(uint8_t *p, uint32_t r) {
|
||||
if (r <= rune1Max) {
|
||||
p[0] = (uint8_t)r;
|
||||
return 1;
|
||||
} else if (r <= rune2Max) {
|
||||
p[0] = t2 | (uint8_t)(r>>6);
|
||||
p[1] = tx | (((uint8_t)(r))&maskx);
|
||||
return 2;
|
||||
} else {
|
||||
if (r > MaxRune || (surrogateMin <= r && r <= surrogateMax)) {
|
||||
r = replacementChar;
|
||||
}
|
||||
if (r <= rune3Max) {
|
||||
p[0] = t3 | (uint8_t)(r>>12);
|
||||
p[1] = tx | (((uint8_t)(r>>6))&maskx);
|
||||
p[2] = tx | (((uint8_t)(r))&maskx);
|
||||
return 3;
|
||||
} else {
|
||||
p[0] = t4 | (uint8_t)(r>>18);
|
||||
p[1] = tx | (((uint8_t)(r>>12))&maskx);
|
||||
p[2] = tx | (((uint8_t)(r>>6))&maskx);
|
||||
p[3] = tx | (((uint8_t)(r))&maskx);
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// utf16_decode decodes an array of UTF16 characters to a UTF-8 encoded
|
||||
// nstring copy. The support functions and utf16_decode itself are heavily
|
||||
// based on the unicode/utf8 and unicode/utf16 Go packages.
|
||||
static nstring utf16_decode(jchar *chars, jsize len) {
|
||||
jsize worstCaseLen = 4*len;
|
||||
uint8_t *buf = malloc(worstCaseLen);
|
||||
if (buf == NULL) {
|
||||
LOG_FATAL("utf16Decode: malloc failed");
|
||||
}
|
||||
jsize nsrc = 0;
|
||||
jsize ndst = 0;
|
||||
while (nsrc < len) {
|
||||
uint32_t r = chars[nsrc];
|
||||
nsrc++;
|
||||
if (surr1 <= r && r < surr2 && nsrc < len) {
|
||||
uint32_t r2 = chars[nsrc];
|
||||
if (surr2 <= r2 && r2 < surr3) {
|
||||
nsrc++;
|
||||
r = (((r-surr1)<<10) | (r2 - surr2)) + 0x10000;
|
||||
}
|
||||
}
|
||||
if (ndst + 4 > worstCaseLen) {
|
||||
LOG_FATAL("utf16Decode: buffer overflow");
|
||||
}
|
||||
ndst += encode_rune(buf + ndst, r);
|
||||
}
|
||||
struct nstring res = {.chars = buf, .len = ndst};
|
||||
return res;
|
||||
}
|
||||
|
||||
nstring go_seq_from_java_string(JNIEnv *env, jstring str) {
|
||||
struct nstring res = {NULL, 0};
|
||||
if (str == NULL) {
|
||||
return res;
|
||||
}
|
||||
jsize nchars = (*env)->GetStringLength(env, str);
|
||||
if (nchars == 0) {
|
||||
return res;
|
||||
}
|
||||
jchar *chars = (jchar *)(*env)->GetStringChars(env, str, NULL);
|
||||
if (chars == NULL) {
|
||||
LOG_FATAL("GetStringChars failed");
|
||||
}
|
||||
nstring nstr = utf16_decode(chars, nchars);
|
||||
(*env)->ReleaseStringChars(env, str, chars);
|
||||
return nstr;
|
||||
}
|
||||
|
||||
nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray arr, int copy) {
|
||||
struct nbyteslice res = {NULL, 0};
|
||||
if (arr == NULL) {
|
||||
return res;
|
||||
}
|
||||
|
||||
jsize len = (*env)->GetArrayLength(env, arr);
|
||||
if (len == 0) {
|
||||
return res;
|
||||
}
|
||||
jbyte *ptr = (*env)->GetByteArrayElements(env, arr, NULL);
|
||||
if (ptr == NULL) {
|
||||
LOG_FATAL("GetByteArrayElements failed");
|
||||
}
|
||||
if (copy) {
|
||||
void *ptr_copy = (void *)malloc(len);
|
||||
if (ptr_copy == NULL) {
|
||||
LOG_FATAL("malloc failed");
|
||||
}
|
||||
memcpy(ptr_copy, ptr, len);
|
||||
(*env)->ReleaseByteArrayElements(env, arr, ptr, JNI_ABORT);
|
||||
ptr = (jbyte *)ptr_copy;
|
||||
}
|
||||
res.ptr = ptr;
|
||||
res.len = len;
|
||||
return res;
|
||||
}
|
||||
|
||||
int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o) {
|
||||
if (o == NULL) {
|
||||
return NULL_REFNUM;
|
||||
}
|
||||
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incGoObjectRef, o);
|
||||
}
|
||||
|
||||
int32_t go_seq_to_refnum(JNIEnv *env, jobject o) {
|
||||
if (o == NULL) {
|
||||
return NULL_REFNUM;
|
||||
}
|
||||
return (int32_t)(*env)->CallStaticIntMethod(env, seq_class, seq_incRef, o);
|
||||
}
|
||||
|
||||
int32_t go_seq_unwrap(jint refnum) {
|
||||
JNIEnv *env = go_seq_push_local_frame(0);
|
||||
jobject jobj = go_seq_from_refnum(env, refnum, NULL, NULL);
|
||||
int32_t goref = go_seq_to_refnum_go(env, jobj);
|
||||
go_seq_pop_local_frame(env);
|
||||
return goref;
|
||||
}
|
||||
|
||||
jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons) {
|
||||
if (refnum == NULL_REFNUM) {
|
||||
return NULL;
|
||||
}
|
||||
if (refnum < 0) { // Go object
|
||||
// return new <Proxy>(refnum)
|
||||
return (*env)->NewObject(env, proxy_class, proxy_cons, refnum);
|
||||
}
|
||||
// Seq.Ref ref = Seq.getRef(refnum)
|
||||
jobject ref = (*env)->CallStaticObjectMethod(env, seq_class, seq_getRef, (jint)refnum);
|
||||
if (ref == NULL) {
|
||||
LOG_FATAL("Unknown reference: %d", refnum);
|
||||
}
|
||||
// Go incremented the reference count just before passing the refnum. Decrement it here.
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)refnum);
|
||||
// return ref.obj
|
||||
return (*env)->GetObjectField(env, ref, ref_objField);
|
||||
}
|
||||
|
||||
// go_seq_to_java_string converts a nstring to a jstring.
|
||||
jstring go_seq_to_java_string(JNIEnv *env, nstring str) {
|
||||
jstring s = (*env)->NewString(env, str.chars, str.len/2);
|
||||
if (str.chars != NULL) {
|
||||
free(str.chars);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// go_seq_push_local_frame retrieves or creates the JNIEnv* for the current thread
|
||||
// and pushes a JNI reference frame. Must be matched with call to go_seq_pop_local_frame.
|
||||
JNIEnv *go_seq_push_local_frame(jint nargs) {
|
||||
JNIEnv *env = go_seq_get_thread_env();
|
||||
// Given the number of function arguments, compute a conservative bound for the minimal frame size.
|
||||
// Assume two slots for each per parameter (Seq.Ref and Seq.Object) and add extra
|
||||
// extra space for the receiver, the return value, and exception (if any).
|
||||
jint frameSize = 2*nargs + 10;
|
||||
if ((*env)->PushLocalFrame(env, frameSize) < 0) {
|
||||
LOG_FATAL("PushLocalFrame failed");
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
// Pop the current local frame, freeing all JNI local references in it
|
||||
void go_seq_pop_local_frame(JNIEnv *env) {
|
||||
(*env)->PopLocalFrame(env, NULL);
|
||||
}
|
||||
|
||||
void go_seq_inc_ref(int32_t ref) {
|
||||
JNIEnv *env = go_seq_get_thread_env();
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_incRefnum, (jint)ref);
|
||||
}
|
||||
|
||||
void go_seq_dec_ref(int32_t ref) {
|
||||
JNIEnv *env = go_seq_get_thread_env();
|
||||
(*env)->CallStaticVoidMethod(env, seq_class, seq_decRef, (jint)ref);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_go_Seq_init(JNIEnv *env, jclass clazz) {
|
||||
if ((*env)->GetJavaVM(env, &jvm) != 0) {
|
||||
LOG_FATAL("failed to get JVM");
|
||||
}
|
||||
if (pthread_key_create(&jnienvs, env_destructor) != 0) {
|
||||
LOG_FATAL("failed to initialize jnienvs thread local storage");
|
||||
}
|
||||
|
||||
seq_class = (*env)->NewGlobalRef(env, clazz);
|
||||
seq_getRef = (*env)->GetStaticMethodID(env, seq_class, "getRef", "(I)Lgo/Seq$Ref;");
|
||||
if (seq_getRef == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.getRef");
|
||||
}
|
||||
seq_decRef = (*env)->GetStaticMethodID(env, seq_class, "decRef", "(I)V");
|
||||
if (seq_decRef == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.decRef");
|
||||
}
|
||||
seq_incRefnum = (*env)->GetStaticMethodID(env, seq_class, "incRefnum", "(I)V");
|
||||
if (seq_incRefnum == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.incRefnum");
|
||||
}
|
||||
seq_incRef = (*env)->GetStaticMethodID(env, seq_class, "incRef", "(Ljava/lang/Object;)I");
|
||||
if (seq_incRef == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.incRef");
|
||||
}
|
||||
seq_incGoObjectRef = (*env)->GetStaticMethodID(env, seq_class, "incGoObjectRef", "(Lgo/Seq$GoObject;)I");
|
||||
if (seq_incGoObjectRef == NULL) {
|
||||
LOG_FATAL("failed to find method Seq.incGoObjectRef");
|
||||
}
|
||||
jclass ref_class = (*env)->FindClass(env, "go/Seq$Ref");
|
||||
if (ref_class == NULL) {
|
||||
LOG_FATAL("failed to find the Seq.Ref class");
|
||||
}
|
||||
ref_objField = (*env)->GetFieldID(env, ref_class, "obj", "Ljava/lang/Object;");
|
||||
if (ref_objField == NULL) {
|
||||
LOG_FATAL("failed to find the Seq.Ref.obj field");
|
||||
}
|
||||
initClasses();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_go_Seq_destroyRef(JNIEnv *env, jclass clazz, jint refnum) {
|
||||
DestroyRef(refnum);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_go_Seq_incGoRef(JNIEnv *env, jclass clazz, jint refnum, jobject ref) {
|
||||
IncGoRef(refnum);
|
||||
}
|
||||
|
||||
jclass go_seq_find_class(const char *name) {
|
||||
JNIEnv *env = go_seq_push_local_frame(0);
|
||||
jclass clazz = (*env)->FindClass(env, name);
|
||||
if (clazz == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
} else {
|
||||
clazz = (*env)->NewGlobalRef(env, clazz);
|
||||
}
|
||||
go_seq_pop_local_frame(env);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig) {
|
||||
JNIEnv *env = go_seq_push_local_frame(0);
|
||||
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
|
||||
if (m == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
go_seq_pop_local_frame(env);
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig) {
|
||||
JNIEnv *env = go_seq_push_local_frame(0);
|
||||
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
|
||||
if (m == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
go_seq_pop_local_frame(env);
|
||||
return m;
|
||||
}
|
||||
|
||||
void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr) {
|
||||
if (ptr != NULL) {
|
||||
(*env)->ReleaseByteArrayElements(env, arr, ptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int go_seq_isinstanceof(jint refnum, jclass clazz) {
|
||||
JNIEnv *env = go_seq_push_local_frame(0);
|
||||
jobject obj = go_seq_from_refnum(env, refnum, NULL, NULL);
|
||||
jboolean isinst = (*env)->IsInstanceOf(env, obj, clazz);
|
||||
go_seq_pop_local_frame(env);
|
||||
return isinst;
|
||||
}
|
98
bind/java/seq_android.go.support
Normal file
98
bind/java/seq_android.go.support
Normal file
|
@ -0,0 +1,98 @@
|
|||
// 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.
|
||||
|
||||
package main
|
||||
|
||||
// Go support functions for bindings. This file is copied into the
|
||||
// generated main package and compiled along with the generated binding
|
||||
// files.
|
||||
|
||||
//#cgo CFLAGS: -Werror
|
||||
//#cgo LDFLAGS: -llog
|
||||
//#include <jni.h>
|
||||
//#include <stdint.h>
|
||||
//#include <stdlib.h>
|
||||
//#include "seq_android.h"
|
||||
import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/mobile/bind/seq"
|
||||
)
|
||||
|
||||
// DestroyRef is called by Java to inform Go it is done with a reference.
|
||||
//export DestroyRef
|
||||
func DestroyRef(refnum C.int32_t) {
|
||||
seq.Delete(int32(refnum))
|
||||
}
|
||||
|
||||
// encodeString returns a copy of a Go string as a UTF16 encoded nstring.
|
||||
// The returned data is freed in go_seq_to_java_string.
|
||||
//
|
||||
// encodeString uses UTF16 as the intermediate format. Note that UTF8 is an obvious
|
||||
// alternative, but JNI only supports a C-safe variant of UTF8 (modified UTF8).
|
||||
func encodeString(s string) C.nstring {
|
||||
n := C.int(len(s))
|
||||
if n == 0 {
|
||||
return C.nstring{}
|
||||
}
|
||||
// Allocate enough for the worst case estimate, every character is a surrogate pair
|
||||
worstCaseLen := 4 * len(s)
|
||||
utf16buf := C.malloc(C.size_t(worstCaseLen))
|
||||
if utf16buf == nil {
|
||||
panic("encodeString: malloc failed")
|
||||
}
|
||||
chars := (*[1<<30 - 1]uint16)(unsafe.Pointer(utf16buf))[:worstCaseLen/2 : worstCaseLen/2]
|
||||
nchars := seq.UTF16Encode(s, chars)
|
||||
return C.nstring{chars: unsafe.Pointer(utf16buf), len: C.jsize(nchars*2)}
|
||||
}
|
||||
|
||||
// decodeString decodes a UTF8 encoded nstring to a Go string. The data
|
||||
// in str is freed after use.
|
||||
func decodeString(str C.nstring) string {
|
||||
if str.chars == nil {
|
||||
return ""
|
||||
}
|
||||
chars := (*[1<<31 - 1]byte)(str.chars)[:str.len]
|
||||
s := string(chars)
|
||||
C.free(str.chars)
|
||||
return s
|
||||
}
|
||||
|
||||
// fromSlice converts a slice to a jbyteArray cast as a nbyteslice. If cpy
|
||||
// is set, the returned slice is a copy to be free by go_seq_to_java_bytearray.
|
||||
func fromSlice(s []byte, cpy bool) C.nbyteslice {
|
||||
if s == nil || len(s) == 0 {
|
||||
return C.nbyteslice{}
|
||||
}
|
||||
var ptr *C.jbyte
|
||||
n := C.jsize(len(s))
|
||||
if cpy {
|
||||
ptr = (*C.jbyte)(C.malloc(C.size_t(n)))
|
||||
if ptr == nil {
|
||||
panic("fromSlice: malloc failed")
|
||||
}
|
||||
copy((*[1<<31 - 1]byte)(unsafe.Pointer(ptr))[:n], s)
|
||||
} else {
|
||||
ptr = (*C.jbyte)(unsafe.Pointer(&s[0]))
|
||||
}
|
||||
return C.nbyteslice{ptr: unsafe.Pointer(ptr), len: n}
|
||||
}
|
||||
|
||||
// toSlice takes a nbyteslice (jbyteArray) and returns a byte slice
|
||||
// with the data. If cpy is set, the slice contains a copy of the data and is
|
||||
// freed.
|
||||
func toSlice(s C.nbyteslice, cpy bool) []byte {
|
||||
if s.ptr == nil || s.len == 0 {
|
||||
return nil
|
||||
}
|
||||
var b []byte
|
||||
if cpy {
|
||||
b = C.GoBytes(s.ptr, C.int(s.len))
|
||||
C.free(s.ptr)
|
||||
} else {
|
||||
b = (*[1<<31 - 1]byte)(unsafe.Pointer(s.ptr))[:s.len:s.len]
|
||||
}
|
||||
return b
|
||||
}
|
67
bind/java/seq_android.h
Normal file
67
bind/java/seq_android.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
// 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.
|
||||
|
||||
#ifndef __GO_SEQ_ANDROID_HDR__
|
||||
#define __GO_SEQ_ANDROID_HDR__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <android/log.h>
|
||||
// For abort()
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
|
||||
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "go/Seq", __VA_ARGS__)
|
||||
#define LOG_FATAL(...) \
|
||||
{ \
|
||||
__android_log_print(ANDROID_LOG_FATAL, "go/Seq", __VA_ARGS__); \
|
||||
abort(); \
|
||||
}
|
||||
|
||||
// Platform specific types
|
||||
typedef struct nstring {
|
||||
// UTF16 or UTF8 Encoded string. When converting from Java string to Go
|
||||
// string, UTF16. When converting from Go to Java, UTF8.
|
||||
void *chars;
|
||||
// length in bytes, regardless of encoding
|
||||
jsize len;
|
||||
} nstring;
|
||||
typedef struct nbyteslice {
|
||||
void *ptr;
|
||||
jsize len;
|
||||
} nbyteslice;
|
||||
typedef jlong nint;
|
||||
|
||||
extern void go_seq_dec_ref(int32_t ref);
|
||||
extern void go_seq_inc_ref(int32_t ref);
|
||||
// go_seq_unwrap takes a reference number to a Java wrapper and returns
|
||||
// a reference number to its wrapped Go object.
|
||||
extern int32_t go_seq_unwrap(jint refnum);
|
||||
extern int32_t go_seq_to_refnum(JNIEnv *env, jobject o);
|
||||
extern int32_t go_seq_to_refnum_go(JNIEnv *env, jobject o);
|
||||
extern jobject go_seq_from_refnum(JNIEnv *env, int32_t refnum, jclass proxy_class, jmethodID proxy_cons);
|
||||
|
||||
extern void go_seq_maybe_throw_exception(JNIEnv *env, jobject msg);
|
||||
// go_seq_get_exception returns any pending exception and clears the exception status.
|
||||
extern jobject go_seq_get_exception(JNIEnv *env);
|
||||
|
||||
extern jbyteArray go_seq_to_java_bytearray(JNIEnv *env, nbyteslice s, int copy);
|
||||
extern nbyteslice go_seq_from_java_bytearray(JNIEnv *env, jbyteArray s, int copy);
|
||||
extern void go_seq_release_byte_array(JNIEnv *env, jbyteArray arr, jbyte* ptr);
|
||||
|
||||
extern jstring go_seq_to_java_string(JNIEnv *env, nstring str);
|
||||
extern nstring go_seq_from_java_string(JNIEnv *env, jstring s);
|
||||
|
||||
// push_local_frame retrieves or creates the JNIEnv* for the current thread
|
||||
// and pushes a JNI reference frame. Must be matched with call to pop_local_frame.
|
||||
extern JNIEnv *go_seq_push_local_frame(jint cap);
|
||||
// Pop the current local frame, releasing all JNI local references in it
|
||||
extern void go_seq_pop_local_frame(JNIEnv *env);
|
||||
|
||||
// Return a global reference to the given class. Return NULL and clear exception if not found.
|
||||
extern jclass go_seq_find_class(const char *name);
|
||||
extern jmethodID go_seq_get_static_method_id(jclass clazz, const char *name, const char *sig);
|
||||
extern jmethodID go_seq_get_method_id(jclass clazz, const char *name, const char *sig);
|
||||
extern int go_seq_isinstanceof(jint refnum, jclass clazz);
|
||||
|
||||
#endif // __GO_SEQ_ANDROID_HDR__
|
256
bind/java/seq_test.go
Normal file
256
bind/java/seq_test.go
Normal file
|
@ -0,0 +1,256 @@
|
|||
// Copyright 2015 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 java
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/mobile/internal/importers/java"
|
||||
"golang.org/x/mobile/internal/sdkpath"
|
||||
)
|
||||
|
||||
var gomobileBin string
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(testMain(m))
|
||||
}
|
||||
|
||||
func testMain(m *testing.M) int {
|
||||
// Build gomobile and gobind and put them into PATH.
|
||||
binDir, err := os.MkdirTemp("", "bind-java-test-")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(binDir)
|
||||
exe := ""
|
||||
if runtime.GOOS == "windows" {
|
||||
exe = ".exe"
|
||||
}
|
||||
if runtime.GOOS != "android" {
|
||||
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||
gomobileBin = filepath.Join(binDir, "gomobile"+exe)
|
||||
gobindBin := filepath.Join(binDir, "gobind"+exe)
|
||||
if out, err := exec.Command(gocmd, "build", "-o", gomobileBin, "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil {
|
||||
log.Fatalf("gomobile build failed: %v: %s", err, out)
|
||||
}
|
||||
if out, err := exec.Command(gocmd, "build", "-o", gobindBin, "golang.org/x/mobile/cmd/gobind").CombinedOutput(); err != nil {
|
||||
log.Fatalf("gobind build failed: %v: %s", err, out)
|
||||
}
|
||||
path := binDir
|
||||
if oldPath := os.Getenv("PATH"); oldPath != "" {
|
||||
path += string(filepath.ListSeparator) + oldPath
|
||||
}
|
||||
os.Setenv("PATH", path)
|
||||
}
|
||||
return m.Run()
|
||||
}
|
||||
|
||||
func TestClasses(t *testing.T) {
|
||||
if !java.IsAvailable() {
|
||||
t.Skipf("java importer is not available")
|
||||
}
|
||||
runTest(t, []string{
|
||||
"golang.org/x/mobile/bind/testdata/testpkg/javapkg",
|
||||
}, "", "ClassesTest")
|
||||
}
|
||||
|
||||
func TestCustomPkg(t *testing.T) {
|
||||
runTest(t, []string{
|
||||
"golang.org/x/mobile/bind/testdata/testpkg",
|
||||
}, "org.golang.custompkg", "CustomPkgTest")
|
||||
}
|
||||
|
||||
func TestJavaSeqTest(t *testing.T) {
|
||||
runTest(t, []string{
|
||||
"golang.org/x/mobile/bind/testdata/testpkg",
|
||||
"golang.org/x/mobile/bind/testdata/testpkg/secondpkg",
|
||||
"golang.org/x/mobile/bind/testdata/testpkg/simplepkg",
|
||||
}, "", "SeqTest")
|
||||
}
|
||||
|
||||
// TestJavaSeqBench runs java test SeqBench.java, with the same
|
||||
// environment requirements as TestJavaSeqTest.
|
||||
//
|
||||
// The benchmarks runs on the phone, so the benchmarkpkg implements
|
||||
// rudimentary timing logic and outputs benchcmp compatible runtimes
|
||||
// to logcat. Use
|
||||
//
|
||||
// adb logcat -v raw GoLog:* *:S
|
||||
//
|
||||
// while running the benchmark to see the results.
|
||||
func TestJavaSeqBench(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping benchmark in short mode.")
|
||||
}
|
||||
runTest(t, []string{"golang.org/x/mobile/bind/testdata/benchmark"}, "", "SeqBench")
|
||||
}
|
||||
|
||||
// runTest runs the Android java test class specified with javaCls. If javaPkg is
|
||||
// set, it is passed with the -javapkg flag to gomobile. The pkgNames lists the Go
|
||||
// packages to bind for the test.
|
||||
// This requires the gradle command to be in PATH and the Android SDK to be
|
||||
// installed.
|
||||
func runTest(t *testing.T, pkgNames []string, javaPkg, javaCls string) {
|
||||
if gomobileBin == "" {
|
||||
t.Skipf("no gomobile on %s", runtime.GOOS)
|
||||
}
|
||||
gradle, err := exec.LookPath("gradle")
|
||||
if err != nil {
|
||||
t.Skip("command gradle not found, skipping")
|
||||
}
|
||||
if _, err := sdkpath.AndroidHome(); err != nil {
|
||||
t.Skip("Android SDK not found, skipping")
|
||||
}
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("failed pwd: %v", err)
|
||||
}
|
||||
tmpdir, err := os.MkdirTemp("", "bind-java-seq-test-")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to prepare temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpdir)
|
||||
t.Logf("tmpdir = %s", tmpdir)
|
||||
|
||||
if err := os.Chdir(tmpdir); err != nil {
|
||||
t.Fatalf("failed chdir: %v", err)
|
||||
}
|
||||
defer os.Chdir(cwd)
|
||||
|
||||
for _, d := range []string{"src/main", "src/androidTest/java/go", "libs", "src/main/res/values"} {
|
||||
err = os.MkdirAll(filepath.Join(tmpdir, d), 0700)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
args := []string{"bind", "-tags", "aaa bbb", "-o", "pkg.aar"}
|
||||
if javaPkg != "" {
|
||||
args = append(args, "-javapkg", javaPkg)
|
||||
}
|
||||
args = append(args, pkgNames...)
|
||||
cmd := exec.Command(gomobileBin, args...)
|
||||
// Reverse binding doesn't work with Go module since imports starting with Java or ObjC are not valid FQDNs.
|
||||
// Disable Go module explicitly until this problem is solved. See golang/go#27234.
|
||||
cmd.Env = append(os.Environ(), "GO111MODULE=off")
|
||||
buf, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("%s", buf)
|
||||
t.Fatalf("failed to run gomobile bind: %v", err)
|
||||
}
|
||||
|
||||
fname := filepath.Join(tmpdir, "libs", "pkg.aar")
|
||||
err = cp(fname, filepath.Join(tmpdir, "pkg.aar"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to copy pkg.aar: %v", err)
|
||||
}
|
||||
|
||||
fname = filepath.Join(tmpdir, "src/androidTest/java/go/"+javaCls+".java")
|
||||
err = cp(fname, filepath.Join(cwd, javaCls+".java"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to copy SeqTest.java: %v", err)
|
||||
}
|
||||
|
||||
fname = filepath.Join(tmpdir, "src/main/AndroidManifest.xml")
|
||||
err = os.WriteFile(fname, []byte(androidmanifest), 0700)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write android manifest file: %v", err)
|
||||
}
|
||||
|
||||
// Add a dummy string resource to avoid errors from the Android build system.
|
||||
fname = filepath.Join(tmpdir, "src/main/res/values/strings.xml")
|
||||
err = os.WriteFile(fname, []byte(stringsxml), 0700)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write strings.xml file: %v", err)
|
||||
}
|
||||
|
||||
fname = filepath.Join(tmpdir, "build.gradle")
|
||||
err = os.WriteFile(fname, []byte(buildgradle), 0700)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write build.gradle file: %v", err)
|
||||
}
|
||||
|
||||
if buf, err := run(gradle + " connectedAndroidTest"); err != nil {
|
||||
t.Logf("%s", buf)
|
||||
t.Errorf("failed to run gradle test: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(cmd string) ([]byte, error) {
|
||||
c := strings.Split(cmd, " ")
|
||||
return exec.Command(c[0], c[1:]...).CombinedOutput()
|
||||
}
|
||||
|
||||
func cp(dst, src string) error {
|
||||
r, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read source: %v", err)
|
||||
}
|
||||
defer r.Close()
|
||||
w, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open destination: %v", err)
|
||||
}
|
||||
_, err = io.Copy(w, r)
|
||||
cerr := w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cerr
|
||||
}
|
||||
|
||||
const androidmanifest = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.BindTest"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
</manifest>`
|
||||
|
||||
const buildgradle = `buildscript {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 'android-19'
|
||||
defaultConfig { minSdkVersion 16 }
|
||||
}
|
||||
|
||||
repositories {
|
||||
flatDir { dirs 'libs' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(name: "pkg", ext: "aar")
|
||||
}
|
||||
`
|
||||
|
||||
const stringsxml = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="dummy">dummy</string>
|
||||
</resources>`
|
Loading…
Add table
Add a link
Reference in a new issue