diff --git a/deps/fbjni/BUCK b/deps/fbjni/BUCK
index 58b06e3ea..2fa37b922 100644
--- a/deps/fbjni/BUCK
+++ b/deps/fbjni/BUCK
@@ -59,7 +59,7 @@ profilo_oss_xplat_cxx_library(
profilo_oss_android_library(
name = "fbjni-java",
srcs = glob([
- "**/*.java",
+ "java/**/*.java",
]),
visibility = [
"PUBLIC",
diff --git a/deps/fbjni/test/AndroidManifest.xml b/deps/fbjni/test/AndroidManifest.xml
new file mode 100644
index 000000000..79c56d40a
--- /dev/null
+++ b/deps/fbjni/test/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/deps/fbjni/test/BaseFBJniTests.java b/deps/fbjni/test/BaseFBJniTests.java
new file mode 100644
index 000000000..590dc590b
--- /dev/null
+++ b/deps/fbjni/test/BaseFBJniTests.java
@@ -0,0 +1,19 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import com.facebook.soloader.SoLoader;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+
+public class BaseFBJniTests {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @BeforeClass
+ public static void setup() {
+ // Explicitly load fbjni to ensure that its JNI_OnLoad is run.
+ SoLoader.loadLibrary("fbjni");
+ SoLoader.loadLibrary("fbjni-tests");
+ }
+}
diff --git a/deps/fbjni/test/ByteBufferTests.java b/deps/fbjni/test/ByteBufferTests.java
new file mode 100644
index 000000000..73cf6c9e3
--- /dev/null
+++ b/deps/fbjni/test/ByteBufferTests.java
@@ -0,0 +1,66 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import org.junit.Test;
+
+public class ByteBufferTests extends BaseFBJniTests {
+ @Test
+ public void testDirectByteBuffer() {
+ assertThat(nativeTestDirectByteBuffer()).isTrue();
+ }
+
+ public static native boolean nativeTestDirectByteBuffer();
+
+ @Test
+ public void testEmptyDirectByteBuffer() {
+ assertThat(nativeTestEmptyDirectByteBuffer()).isTrue();
+ }
+
+ public static native boolean nativeTestEmptyDirectByteBuffer();
+
+ @Test
+ public void testRewindBuffer() {
+ assertThat(nativeTestRewindBuffer()).isTrue();
+ }
+
+ public native boolean nativeTestRewindBuffer();
+
+ @Test
+ public void testAllocateDirect() {
+ ByteBuffer buffer = nativeAllocateDirect(5);
+ assertThat(buffer.isDirect()).isTrue();
+ assertThat(buffer.capacity()).isEqualTo(5);
+ }
+
+ public native ByteBuffer nativeAllocateDirect(int size);
+
+ // called from native
+ public static void writeBytes(ByteBuffer dest, byte a, byte b, byte c, byte d) {
+ dest.put(a).put(b).put(c).put(d);
+ }
+
+ @Test
+ public void testFloatBuffer() {
+ final int BUFFER_COUNT = 5;
+ final int FLOAT_SIZE = 4;
+ FloatBuffer buffer =
+ ByteBuffer.allocateDirect(BUFFER_COUNT * FLOAT_SIZE)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ buffer.put(1f);
+ buffer.put(2f);
+ buffer.put(2.5f);
+ buffer.put(2.75f);
+ buffer.put(3f);
+ assertThat(nativeTestFloatBuffer(buffer)).isTrue();
+ }
+
+ public native boolean nativeTestFloatBuffer(Buffer buffer);
+}
diff --git a/deps/fbjni/test/FBJniTests.java b/deps/fbjni/test/FBJniTests.java
new file mode 100644
index 000000000..dc5e459ff
--- /dev/null
+++ b/deps/fbjni/test/FBJniTests.java
@@ -0,0 +1,877 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+
+import com.facebook.jni.annotations.DoNotStrip;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class FBJniTests extends BaseFBJniTests {
+ class CustomException extends Throwable {
+ int mGetMessageCalls = 0;
+
+ @Override
+ public String getMessage() {
+ return "getMessages: " + (++mGetMessageCalls);
+ }
+ }
+
+ public interface Callbacks {
+ void voidFoo();
+
+ boolean booleanFoo();
+
+ byte byteFoo();
+
+ char charFoo();
+
+ short shortFoo();
+
+ int intFoo();
+
+ long longFoo();
+
+ float floatFoo();
+
+ double doubleFoo();
+
+ Object objectFoo();
+
+ String stringFoo();
+ }
+
+ public static class TestThing {
+ int foo;
+ }
+
+ @Mock private static Callbacks mCallbacksMock;
+
+ private int mIntFieldTest;
+ private String mStringFieldTest;
+ private TestThing mReferenceFieldTest;
+ private static int sIntFieldTest;
+ private static String sStringFieldTest;
+ private static TestThing sReferenceFieldTest;
+
+ @DoNotStrip // Resolved from fbjni_tests::TestFieldAccess
+ int bar(double d) {
+ return 42;
+ }
+
+ // Test case for nonvirtual function
+ public boolean nonVirtualMethod(boolean s) {
+ return s;
+ }
+
+ private static void verifyAllCallbacksCalled(Callbacks mock) {
+ verify(mock).voidFoo();
+ verify(mock).booleanFoo();
+ verify(mock).byteFoo();
+ verify(mock).charFoo();
+ verify(mock).shortFoo();
+ verify(mock).intFoo();
+ verify(mock).longFoo();
+ verify(mock).floatFoo();
+ verify(mock).doubleFoo();
+ verify(mock).objectFoo();
+ verify(mock).stringFoo();
+ }
+
+ // Instead of mocking, lets call non-static functions and verify them.
+ public static void voidFooStatic() {
+ mCallbacksMock.voidFoo();
+ }
+
+ public static boolean booleanFooStatic() {
+ return mCallbacksMock.booleanFoo();
+ }
+
+ public static byte byteFooStatic() {
+ return mCallbacksMock.byteFoo();
+ }
+
+ public static char charFooStatic(char c, int s) {
+ return mCallbacksMock.charFoo();
+ }
+
+ public static short shortFooStatic(short s, short t) {
+ return mCallbacksMock.shortFoo();
+ }
+
+ public static int intFooStatic(int s) {
+ return mCallbacksMock.intFoo();
+ }
+
+ public static long longFooStatic() {
+ return mCallbacksMock.longFoo();
+ }
+
+ public static float floatFooStatic() {
+ return mCallbacksMock.floatFoo();
+ }
+
+ public static double doubleFooStatic() {
+ return mCallbacksMock.doubleFoo();
+ }
+
+ public static Object objectFooStatic() {
+ return mCallbacksMock.objectFoo();
+ }
+
+ public static String stringFooStatic() {
+ return mCallbacksMock.stringFoo();
+ }
+
+ @Test
+ public void resolveClass() throws ClassNotFoundException {
+ assertThat(nativeTestClassResolution("java/lang/Object")).isTrue();
+ }
+
+ // Some versions of Android throw ClassNotFoundException while others throw NoClassDefFoundError.
+ // Flatten that to always be ClassNotFoundException.
+ private static void wrapClassLoadingErrors(Callable> code) throws Exception {
+ try {
+ code.call();
+ } catch (NoClassDefFoundError ex) {
+ throw new ClassNotFoundException("chained NoClassDefFoundError", ex);
+ }
+ }
+
+ @Test(expected = ClassNotFoundException.class)
+ public void failingToResolveClass() throws Exception {
+ wrapClassLoadingErrors(
+ new Callable() {
+ @Override
+ public Boolean call() throws Exception {
+ return nativeTestClassResolution("ThisClassDoesNotExist");
+ }
+ });
+ }
+
+ private native boolean nativeTestClassResolution(String className) throws ClassNotFoundException;
+
+ @Test
+ public void lazyClassResolution() throws ClassNotFoundException {
+ assertThat(nativeTestLazyClassResolution("java/lang/Object")).isTrue();
+ }
+
+ @Test(expected = ClassNotFoundException.class)
+ public void failedLazyClassResolution() throws Exception {
+ wrapClassLoadingErrors(
+ new Callable() {
+ @Override
+ public Boolean call() throws Exception {
+ return nativeTestLazyClassResolution("ThisClassDoesNotExist");
+ }
+ });
+ }
+
+ private native boolean nativeTestLazyClassResolution(String className)
+ throws ClassNotFoundException;
+
+ @Test
+ public void instanceCreation() {
+ assertThat(nativeCreateInstanceOf("java/lang/String"))
+ .isInstanceOf(String.class)
+ .isEqualTo("java/lang/String");
+ }
+
+ private native Object nativeCreateInstanceOf(String className);
+
+ @Test
+ public void typeDescriptors() {
+ assertThat(nativeTestTypeDescriptors()).isTrue();
+ }
+
+ private native boolean nativeTestTypeDescriptors();
+
+ @Test
+ public void resolveVirtualMethod() throws ClassNotFoundException, NoSuchMethodException {
+ assertThat(nativeTestVirtualMethodResolution_I("java/lang/Object", "hashCode")).isTrue();
+ }
+
+ @Test
+ public void resolveVirtualMethodWithArray() throws ClassNotFoundException, NoSuchMethodException {
+ assertThat(nativeTestVirtualMethodResolution_arrB("java/lang/String", "getBytes")).isTrue();
+ }
+
+ @Test
+ public void resolveVirtualMethodWithObjectArray()
+ throws ClassNotFoundException, NoSuchMethodException {
+ assertThat(nativeTestVirtualMethodResolution_S_arrS("java/lang/String", "split")).isTrue();
+ }
+
+ @Test
+ public void resolveVirtualMethodWithObjectArrayArray()
+ throws ClassNotFoundException, NoSuchMethodException {
+ assertThat(
+ nativeTestVirtualMethodResolution_arrarrS(
+ "com/facebook/jni/FBJniTests", "returnMultidimensionalObjectArray"))
+ .isTrue();
+ }
+
+ public static String[][] returnMultidimensionalObjectArray() {
+ return null;
+ }
+
+ @Test
+ public void resolveVirtualMethodWithPrimitiveArrayArray()
+ throws ClassNotFoundException, NoSuchMethodException {
+ assertThat(
+ nativeTestVirtualMethodResolution_arrarrI(
+ "com/facebook/jni/FBJniTests", "returnMultidimensionalPrimitiveArray"))
+ .isTrue();
+ }
+
+ public static int[][] returnMultidimensionalPrimitiveArray() {
+ return null;
+ }
+
+ @Test(expected = NoSuchMethodError.class)
+ public void failingToResolveVirtualMethod() throws ClassNotFoundException, NoSuchMethodError {
+ nativeTestVirtualMethodResolution_I("java/lang/Object", "ThisMethodDoesNotExist");
+ }
+
+ private native boolean nativeTestVirtualMethodResolution_I(String className, String methodName)
+ throws ClassNotFoundException, NoSuchMethodError;
+
+ private native boolean nativeTestVirtualMethodResolution_arrB(String className, String methodName)
+ throws ClassNotFoundException, NoSuchMethodError;
+
+ private native boolean nativeTestVirtualMethodResolution_S_arrS(
+ String className, String methodName) throws ClassNotFoundException, NoSuchMethodError;
+
+ private native boolean nativeTestVirtualMethodResolution_arrarrS(
+ String className, String methodName) throws ClassNotFoundException, NoSuchMethodError;
+
+ private native boolean nativeTestVirtualMethodResolution_arrarrI(
+ String className, String methodName) throws ClassNotFoundException, NoSuchMethodError;
+
+ @Test
+ public void lazyMethodResolution() throws ClassNotFoundException, NoSuchMethodError {
+ assertThat(nativeTestLazyVirtualMethodResolution_I("java/lang/Object", "hashCode")).isTrue();
+ }
+
+ @Test(expected = NoSuchMethodError.class)
+ public void failedLazyMethodResolution() throws ClassNotFoundException, NoSuchMethodError {
+ nativeTestLazyVirtualMethodResolution_I("java/lang/Object", "ThisMethodDoesNotExist");
+ }
+
+ private native boolean nativeTestLazyVirtualMethodResolution_I(
+ String className, String methodName);
+
+ @Test
+ public void callbacksUsingJMethod() {
+ nativeTestJMethodCallbacks(mCallbacksMock);
+ verifyAllCallbacksCalled(mCallbacksMock);
+ }
+
+ private native void nativeTestJMethodCallbacks(Callbacks callbacks);
+
+ @Test
+ public void callbacksUsingJStaticMethod() {
+ nativeTestJStaticMethodCallbacks();
+ verifyAllCallbacksCalled(mCallbacksMock);
+ }
+
+ private native void nativeTestJStaticMethodCallbacks();
+
+ @Test
+ public void isAssignableFrom() {
+ assertThat(nativeTestIsAssignableFrom(String.class, String.class)).isTrue();
+ assertThat(nativeTestIsAssignableFrom(String.class, Object.class)).isFalse();
+ assertThat(nativeTestIsAssignableFrom(Object.class, String.class)).isTrue();
+ assertThat(nativeTestIsAssignableFrom(ArrayList.class, Iterable.class)).isFalse();
+ assertThat(nativeTestIsAssignableFrom(Iterable.class, ArrayList.class)).isTrue();
+ }
+
+ private native boolean nativeTestIsAssignableFrom(Class cls1, Class cls2);
+
+ @Test
+ public void isInstanceOf() {
+ assertThat(nativeTestIsInstanceOf("", String.class)).isTrue();
+ assertThat(nativeTestIsInstanceOf("", Object.class)).isTrue();
+ assertThat(nativeTestIsInstanceOf(new Object(), String.class)).isFalse();
+ assertThat(nativeTestIsInstanceOf(new ArrayList(), Iterable.class)).isTrue();
+ assertThat(nativeTestIsInstanceOf(null, Iterable.class)).isTrue();
+ }
+
+ private native boolean nativeTestIsInstanceOf(Object object, Class cls);
+
+ @Test
+ public void isSameObject() {
+ Object anObject = new Object();
+ Object anotherObject = new Object();
+ assertThat(nativeTestIsSameObject(anObject, anObject)).isTrue();
+ assertThat(nativeTestIsSameObject(anObject, anotherObject)).isFalse();
+ assertThat(nativeTestIsSameObject(null, anObject)).isFalse();
+ assertThat(nativeTestIsSameObject(anObject, null)).isFalse();
+ assertThat(nativeTestIsSameObject(null, null)).isTrue();
+ }
+
+ private native boolean nativeTestIsSameObject(Object a, Object b);
+
+ @Test
+ public void testGetSuperClass() {
+ Class testClass = String.class;
+ Class superClass = Object.class;
+ Class notSuperClass = Integer.class;
+
+ assertThat(nativeTestGetSuperclass(testClass, superClass)).isTrue();
+ assertThat(nativeTestGetSuperclass(testClass, notSuperClass)).isFalse();
+ }
+
+ private native boolean nativeTestGetSuperclass(Class testClass, Class superOfTest);
+
+ @Test
+ public void testWeakRefs() {
+ assertThat(nativeTestWeakRefs()).isTrue();
+ }
+
+ private native boolean nativeTestWeakRefs();
+
+ @Test
+ public void testAliasRefs() {
+ assertThat(nativeTestAlias()).isTrue();
+ }
+
+ private native boolean nativeTestAlias();
+
+ @Test
+ public void testAliasRefConversions() {
+ assertThat(nativeTestAliasRefConversions()).isTrue();
+ }
+
+ private native boolean nativeTestAliasRefConversions();
+
+ @Test
+ public void testNullJString() {
+ assertThat(nativeTestNullJString()).isTrue();
+ }
+
+ private native boolean nativeTestNullJString();
+
+ @Test
+ public void testSwap() {
+ assertThat(nativeTestSwap(new Object())).isTrue();
+ }
+
+ private native boolean nativeTestSwap(Object other);
+
+ @Test
+ public void testEqualOperator() {
+ assertThat(nativeTestEqualOperator(new Object())).isTrue();
+ }
+
+ private native boolean nativeTestEqualOperator(Object other);
+
+ @Test
+ public void testRelaseAlias() {
+ assertThat(nativeTestReleaseAlias()).isTrue();
+ }
+
+ private native boolean nativeTestReleaseAlias();
+
+ @Test
+ public void testLockingWeakReferences() {
+ assertThat(nativeTestLockingWeakReferences()).isTrue();
+ }
+
+ private native boolean nativeTestLockingWeakReferences();
+
+ @Test
+ public void testCreatingReferences() {
+ assertThat(nativeTestCreatingReferences()).isTrue();
+ }
+
+ private native boolean nativeTestCreatingReferences();
+
+ @Test
+ public void testAssignmentAndCopyConstructors() {
+ assertThat(nativeTestAssignmentAndCopyConstructors()).isTrue();
+ }
+
+ private native boolean nativeTestAssignmentAndCopyConstructors();
+
+ @Test
+ public void testAssignmentAndCopyCrossTypes() {
+ assertThat(nativeTestAssignmentAndCopyCrossTypes()).isTrue();
+ }
+
+ private native boolean nativeTestAssignmentAndCopyCrossTypes();
+
+ @Test
+ public void testNullReferences() {
+ assertThat(nativeTestNullReferences()).isTrue();
+ }
+
+ private native boolean nativeTestNullReferences();
+
+ @Test
+ public void testAutoAliasRefReturningVoid() {
+ nativeTestAutoAliasRefReturningVoid();
+ }
+
+ private native void nativeTestAutoAliasRefReturningVoid();
+
+ @Test
+ public void testFieldAccess() {
+ mIntFieldTest = 17;
+ assertThat(nativeTestFieldAccess("mIntFieldTest", mIntFieldTest, 42)).isTrue();
+ assertThat(mIntFieldTest).isEqualTo(42);
+ }
+
+ private native boolean nativeTestFieldAccess(String name, int oldVal, int newVal);
+
+ @Test
+ public void testStringFieldAccess() {
+ mStringFieldTest = "initial";
+ assertThat(nativeTestStringFieldAccess("mStringFieldTest", mStringFieldTest, "final")).isTrue();
+ assertThat(mStringFieldTest).isEqualTo("final");
+ }
+
+ private native boolean nativeTestStringFieldAccess(String name, String oldVal, String newVal);
+
+ @Test
+ public void testReferenceFieldAccess() {
+ for (boolean useWrapper : new boolean[] {false, true}) {
+ mReferenceFieldTest = new TestThing();
+ TestThing newthing = new TestThing();
+
+ assertThat(
+ nativeTestReferenceFieldAccess(
+ "mReferenceFieldTest", mReferenceFieldTest, newthing, useWrapper))
+ .isTrue();
+ assertThat(mReferenceFieldTest).isEqualTo(newthing);
+ }
+ }
+
+ private native boolean nativeTestReferenceFieldAccess(
+ String name, Object oldVal, Object newVal, boolean useWrapper);
+
+ @Test
+ public void testStaticFieldAccess() {
+ sIntFieldTest = 17;
+ assertThat(nativeTestStaticFieldAccess("sIntFieldTest", sIntFieldTest, 42)).isTrue();
+ assertThat(sIntFieldTest).isEqualTo(42);
+ }
+
+ private native boolean nativeTestStaticFieldAccess(String name, int oldVal, int newVal);
+
+ @Test
+ public void testStaticStringFieldAccess() {
+ sStringFieldTest = "initial";
+ assertThat(nativeTestStaticStringFieldAccess("sStringFieldTest", sStringFieldTest, "final"))
+ .isTrue();
+ assertThat(sStringFieldTest).isEqualTo("final");
+ }
+
+ private native boolean nativeTestStaticStringFieldAccess(String name, String oVal, String nVal);
+
+ @Test
+ public void testStaticReferenceFieldAccess() {
+ for (boolean useWrapper : new boolean[] {false, true}) {
+ sReferenceFieldTest = new TestThing();
+ TestThing newthing = new TestThing();
+
+ assertThat(
+ nativeTestStaticReferenceFieldAccess(
+ "sReferenceFieldTest", sReferenceFieldTest, newthing, useWrapper))
+ .isTrue();
+ assertThat(sReferenceFieldTest).isEqualTo(newthing);
+ }
+ }
+
+ private native boolean nativeTestStaticReferenceFieldAccess(
+ String name, Object oldVal, Object newVal, boolean useWrapper);
+
+ @Test
+ public void testNonVirtualMethod() {
+ assertThat(nativeTestNonVirtualMethod(true)).isTrue();
+ }
+
+ private native boolean nativeTestNonVirtualMethod(boolean s);
+
+ @Test
+ public void testArrayCreation() {
+ String[] expectedStrings = {"one", "two", "three"};
+ String[] joinedStrings =
+ nativeTestArrayCreation(expectedStrings[0], expectedStrings[1], expectedStrings[2]);
+ assertThat(joinedStrings).isEqualTo(expectedStrings);
+ }
+
+ private native String[] nativeTestArrayCreation(String s0, String s1, String s2);
+
+ @Test
+ public void testMultidimensionalObjectArray() {
+ String[] strings = {"one", "two", "three"};
+ String[][] expectedStrings = {{"one", "two"}, {"three"}};
+ String[][] joinedStrings =
+ nativeTestMultidimensionalObjectArray(strings[0], strings[1], strings[2]);
+ assertThat(joinedStrings).isEqualTo(expectedStrings);
+ }
+
+ private native String[][] nativeTestMultidimensionalObjectArray(String s0, String s1, String s2);
+
+ @Test
+ public void testMultidimensionalPrimitiveArray() {
+ int[] nums = {1, 2, 3};
+ int[][] expectedNums = {{1, 2}, {3}};
+ int[][] gotNums = nativeTestMultidimensionalPrimitiveArray(nums[0], nums[1], nums[2]);
+ assertThat(gotNums).isEqualTo(expectedNums);
+ }
+
+ private native int[][] nativeTestMultidimensionalPrimitiveArray(int i0, int i1, int i2);
+
+ private String[] mCapturedStringArray = null;
+
+ @DoNotStrip
+ String captureStringArray(String[] input) {
+ mCapturedStringArray = input;
+ return "Stub";
+ }
+
+ @Test
+ public void testBuildStringArray() throws Exception {
+ String[] input = {"Four", "score", "and", "seven", "beers", "ago"};
+ nativeTestBuildStringArray(input);
+ assertThat(mCapturedStringArray).isEqualTo(input);
+ }
+
+ private native String nativeTestBuildStringArray(String... input);
+
+ public Object methodResolutionWithCxxTypes(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ return null;
+ }
+
+ public void methodResolutionWithCxxTypesVoid(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ }
+
+ public int methodResolutionWithCxxTypesInt(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ return 0;
+ }
+
+ public static Object methodResolutionWithCxxTypesStatic(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ return null;
+ }
+
+ public static void methodResolutionWithCxxTypesVoidStatic(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ }
+
+ public static int methodResolutionWithCxxTypesIntStatic(String t, long val) {
+ if (!"test".equals(t) || val != 3) throw new RuntimeException();
+ return 0;
+ }
+
+ @Test
+ public void testMethodResolutionWithCxxTypes() {
+ testMethodResolutionWithCxxTypesNative("methodResolutionWithCxxTypes", "test", 3);
+ }
+
+ private native void testMethodResolutionWithCxxTypesNative(
+ String callbackName, String str, long val);
+
+ @Test(expected = CustomException.class)
+ public void testHandleJavaCustomException() {
+ testHandleJavaCustomExceptionNative();
+ }
+
+ private native void testHandleJavaCustomExceptionNative();
+
+ @Test
+ public void testHandleNullExceptionMessage() {
+ testHandleNullExceptionMessageNative();
+ }
+
+ private native void testHandleNullExceptionMessageNative();
+
+ @Test
+ public void testHandleNestedException() {
+ try {
+ nativeTestHandleNestedException();
+ } catch (Throwable e) {
+ assertThat(e).isInstanceOf(ArrayIndexOutOfBoundsException.class);
+ e = e.getCause();
+ assertThat(e).isInstanceOf(RuntimeException.class);
+ e = e.getCause();
+ assertThat(e).isInstanceOf(CustomException.class).hasNoCause();
+ }
+ }
+
+ private native void nativeTestHandleNestedException();
+
+ @Test(expected = CppException.class)
+ public void testHandleNoRttiException() {
+ nativeTestHandleNoRttiException();
+ }
+
+ private native void nativeTestHandleNoRttiException();
+
+ @Test
+ public void testCopyConstructor() {
+ assertThat(nativeTestCopyConstructor())
+ .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
+ }
+
+ private native String nativeTestCopyConstructor();
+
+ @Test
+ public void testMoveConstructorWithEmptyWhat() {
+ assertThat(nativeTestMoveConstructorWithEmptyWhat())
+ .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
+ }
+
+ private native String nativeTestMoveConstructorWithEmptyWhat();
+
+ @Test
+ public void testMoveConstructorWithPopulatedWhat() {
+ assertThat(nativeTestMoveConstructorWithPopulatedWhat())
+ .isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
+ }
+
+ private native String nativeTestMoveConstructorWithPopulatedWhat();
+
+ @DoNotStrip // Used in native code.
+ protected void customExceptionThrower() throws CustomException {
+ throw new CustomException();
+ }
+
+ @DoNotStrip // Used in native code.
+ protected void nullMessageThrower() throws NullPointerException {
+ // just like Preconditions.checkNotNull() does
+ throw new NullPointerException();
+ }
+
+ @Test
+ public void testHandleCppRuntimeError() {
+ String message = "Sample runtime error.";
+ thrown.expect(RuntimeException.class);
+ thrown.expectMessage(message);
+ nativeTestHandleCppRuntimeError(message);
+ }
+
+ private native void nativeTestHandleCppRuntimeError(String message);
+
+ @Test(expected = IOException.class)
+ public void testHandleCppIOBaseFailure() {
+ nativeTestHandleCppIOBaseFailure();
+ }
+
+ private native void nativeTestHandleCppIOBaseFailure();
+
+ @Test(expected = CppSystemErrorException.class)
+ public void testHandleCppSystemError() {
+ nativeTestHandleCppSystemError();
+ }
+
+ private native void nativeTestHandleCppSystemError();
+
+ @Test(expected = RuntimeException.class)
+ public void testInterDsoExceptionHandlingA() {
+ nativeTestInterDsoExceptionHandlingA();
+ }
+
+ private native void nativeTestInterDsoExceptionHandlingA();
+
+ @Test
+ public void testInterDsoExceptionHandlingB() {
+ assertThat(nativeTestInterDsoExceptionHandlingB()).isTrue();
+ }
+
+ private native boolean nativeTestInterDsoExceptionHandlingB();
+
+ @Test(expected = UnknownCppException.class)
+ public void testHandleCppCharPointerThrow() {
+ nativeTestHandleCppCharPointerThrow();
+ }
+
+ private native void nativeTestHandleCppCharPointerThrow();
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testThrowJavaExceptionByName() {
+ nativeTestThrowJavaExceptionByName();
+ }
+
+ private native void nativeTestThrowJavaExceptionByName();
+
+ @Test(expected = UnknownCppException.class)
+ public void testHandleCppIntThrow() {
+ nativeTestHandleCppIntThrow();
+ }
+
+ private native void nativeTestHandleCppIntThrow();
+
+ @Test
+ public void testJThread() {
+ assertThat(nativeTestJThread()).isEqualTo(1);
+ }
+
+ private native int nativeTestJThread();
+
+ @Test
+ public void testThreadScopeGuard() {
+ assertThat(nativeTestThreadScopeGuard(17)).isEqualTo(42);
+ }
+
+ private native int nativeTestThreadScopeGuard(double input);
+
+ @Test
+ public void testNestedThreadScopeGuard() {
+ assertThat(nativeTestNestedThreadScopeGuard(17)).isEqualTo(42);
+ }
+
+ private native int nativeTestNestedThreadScopeGuard(double input);
+
+ @Test
+ public void testClassLoadInWorker() {
+ assertThat(nativeTestClassLoadInWorker()).isEqualTo(1);
+ }
+
+ private native int nativeTestClassLoadInWorker();
+
+ @Test
+ public void testClassLoadWorkerFastPath() {
+ assertThat(nativeTestClassLoadWorkerFastPath()).isEqualTo(3);
+ }
+
+ private native int nativeTestClassLoadWorkerFastPath();
+
+ @Test
+ public void testToString() {
+ assertThat(nativeTestToString()).isTrue();
+ }
+
+ private native boolean nativeTestToString();
+
+ // Casting alias_ref
+
+ @Test
+ public void testCorrectStaticCastAliasRef() {
+ // Static cast can't fail at run time. If the object isn't actually
+ // of that type, we just get undefined behaviour, which we can't
+ // check for. So we only do a positive test.
+ assertThat(nativeStaticCastAliasRefToString("hello")).isTrue();
+ }
+
+ @Test
+ public void testNullStaticCastAliasRef() {
+ assertThat(nativeStaticCastAliasRefToString(null)).isTrue();
+ }
+
+ private native boolean nativeStaticCastAliasRefToString(Object a);
+
+ @Test
+ public void testDynamicCastAliasRefToSame() {
+ assertThat(nativeDynamicCastAliasRefToThrowable(new Throwable())).isTrue();
+ }
+
+ public void testDynamicCastAliasRefToBase() {
+ assertThat(nativeDynamicCastAliasRefToThrowable(new Exception())).isTrue();
+ }
+
+ @Test(expected = ClassCastException.class)
+ public void testDynamicCastAliasRefToDerived() {
+ nativeDynamicCastAliasRefToThrowable(new Object());
+ }
+
+ @Test(expected = ClassCastException.class)
+ public void testDynamicCastAliasRefToUnrelated() {
+ nativeDynamicCastAliasRefToThrowable(new Integer(23));
+ }
+
+ @Test
+ public void testNullDynamicCastAliasRef() {
+ assertThat(nativeDynamicCastAliasRefToThrowable(null)).isTrue();
+ }
+
+ private native boolean nativeDynamicCastAliasRefToThrowable(Object a);
+
+ // Casting local_ref
+
+ @Test
+ public void testCorrectStaticCastLocalRef() {
+ // Static cast can't fail at run time. If the object isn't actually
+ // of that type, we just get undefined behaviour, which we can't
+ // check for. So we only do a positive test.
+ assertThat(nativeStaticCastLocalRefToString("hello")).isTrue();
+ }
+
+ @Test
+ public void testNullStaticCastLocalRef() {
+ assertThat(nativeStaticCastLocalRefToString(null)).isTrue();
+ }
+
+ private native boolean nativeStaticCastLocalRefToString(Object a);
+
+ @Test
+ public void testCorrectDynamicCastLocalRef() {
+ assertThat(nativeDynamicCastLocalRefToString("hello")).isTrue();
+ }
+
+ @Test(expected = ClassCastException.class)
+ public void testIncorrectDynamicCastLocalRef() {
+ nativeDynamicCastLocalRefToString(new Integer(23));
+ }
+
+ @Test
+ public void testNullDynamicCastLocalRef() {
+ assertThat(nativeDynamicCastLocalRefToString(null)).isTrue();
+ }
+
+ private native boolean nativeDynamicCastLocalRefToString(Object a);
+
+ // Casting global_ref
+
+ @Test
+ public void testCorrectStaticCastGlobalRef() {
+ // Static cast can't fail at run time. If the object isn't actually
+ // of that type, we just get undefined behaviour, which we can't
+ // check for. So we only do a positive test.
+ assertThat(nativeStaticCastGlobalRefToString("hello")).isTrue();
+ }
+
+ @Test
+ public void testNullStaticCastGlobalRef() {
+ assertThat(nativeStaticCastGlobalRefToString(null)).isTrue();
+ }
+
+ private native boolean nativeStaticCastGlobalRefToString(Object a);
+
+ @Test
+ public void testCorrectDynamicCastGlobalRef() {
+ assertThat(nativeDynamicCastGlobalRefToString("hello")).isTrue();
+ }
+
+ @Test(expected = ClassCastException.class)
+ public void testIncorrectDynamicCastGlobalRef() {
+ nativeDynamicCastGlobalRefToString(new Integer(23));
+ }
+
+ @Test
+ public void testNullDynamicCastGlobalRef() {
+ assertThat(nativeDynamicCastGlobalRefToString(null)).isTrue();
+ }
+
+ private native boolean nativeDynamicCastGlobalRefToString(Object a);
+
+ @Test
+ public void testCriticalNativeMethodBindsAndCanBeInvoked() {
+ assertThat(nativeCriticalNativeMethodBindsAndCanBeInvoked(12, 3.45f)).isTrue();
+ }
+
+ private static native boolean nativeCriticalNativeMethodBindsAndCanBeInvoked(int a, float b);
+}
diff --git a/deps/fbjni/test/HybridTests.java b/deps/fbjni/test/HybridTests.java
new file mode 100644
index 000000000..a72dcce05
--- /dev/null
+++ b/deps/fbjni/test/HybridTests.java
@@ -0,0 +1,295 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import com.facebook.jni.annotations.DoNotStrip;
+import org.junit.Test;
+
+public class HybridTests extends BaseFBJniTests {
+ static class TestHybridClass {
+ // Hybrid classes must include a member which manages the C++ object. It
+ // will be initialized from C++. It must be declared exactly with this
+ // type and name, so JNI can find it, and initialized once in the ctor.
+ // The annotation is necessary to keep proguard from renaming it, or else JNI
+ // won't be able to find it.
+ @DoNotStrip private final HybridData mHybridData;
+
+ // This is the method which creates the C++ instance and initializes
+ // mHybridData. Conventionally, it should be named initHybrid, and invoked
+ // from the constructor. This must be called only once. If the C++
+ // instance is referenced before this is called, a NullPointerException
+ // will be thrown.
+ private native HybridData initHybrid(int i, String s, boolean b);
+
+ // You can have more than one, which may be useful if the ctor is
+ // overloaded. This will call the default C++ ctor.
+ private native HybridData initHybrid();
+
+ // Implements factory-style initialization. You shouldn't usually
+ // need both styles in one class. Here we do it for testing and
+ // demo purposes.
+ private native HybridData initHybrid(String s, int i, boolean b);
+
+ // Java ctor must invoke initHybrid(). This just passes arguments through,
+ // but the ctor can do whatever work it wants, as long as it calls
+ // initHybrid() before any native methods.
+ public TestHybridClass(int i, String s, boolean b) {
+ mHybridData = initHybrid(i, s, b);
+ }
+
+ // This behaves the same as the ctor above, I just wanted a different
+ // signature to demonstrate factory-style initialization.
+ public TestHybridClass(String s, int i, boolean b) {
+ mHybridData = initHybrid(s, i, b);
+ }
+
+ // This is the simplest case. Even if everything is default, initHybrid()
+ // must still be called.
+ public TestHybridClass() {
+ mHybridData = initHybrid();
+ }
+
+ // Java ctor used by C++ newObjectCxxArgs. Note this is private.
+ private TestHybridClass(HybridData hd) {
+ mHybridData = hd;
+ }
+
+ public void doneUsingIt() {
+ mHybridData.resetNative();
+ }
+
+ // Some C++ methods.
+ public native void setBoth(int i, String s);
+
+ public native int getInt();
+
+ public native String getString();
+
+ public native String getCharString();
+
+ public native boolean copy1(TestHybridClass other);
+
+ public native boolean copy2(TestHybridClass other);
+
+ public native void oops();
+
+ public native void setGlobal(String s);
+
+ public native String getGlobal1();
+
+ public native String getGlobal2();
+
+ public static native TestHybridClass makeWithTwo();
+
+ public static native TestHybridClass makeWithThree();
+
+ public static native void autoconvertMany();
+ }
+
+ @Test
+ public void testHybridClass() {
+ TestHybridClass thc1 = new TestHybridClass();
+ assertThat(thc1.getInt()).isEqualTo(0);
+ assertThat(thc1.getString()).isEqualTo("");
+
+ thc1.setBoth(1, "one");
+ assertThat(thc1.getInt()).isEqualTo(1);
+ assertThat(thc1.getString()).isEqualTo("one");
+
+ TestHybridClass thc2 = TestHybridClass.makeWithTwo();
+ assertThat(thc2.getInt()).isEqualTo(2);
+ assertThat(thc2.getString()).isEqualTo("two");
+
+ thc2.doneUsingIt();
+
+ thrown.expect(NullPointerException.class);
+ thc2.getInt();
+ }
+
+ @Test
+ public void testHybridAutoconversion() {
+ TestHybridClass thc3 = TestHybridClass.makeWithThree();
+ assertThat(thc3.copy1(new TestHybridClass(3, "three", false))).isTrue();
+ assertThat(thc3.getInt()).isEqualTo(3);
+ assertThat(thc3.getString()).isEqualTo("three");
+
+ TestHybridClass thc4 = new TestHybridClass();
+ thc4.copy1(new TestHybridClass("four", 4, false));
+ assertThat(thc4.getInt()).isEqualTo(4);
+ assertThat(thc4.getString()).isEqualTo("four");
+ assertThat(thc4.getCharString()).isEqualTo("four");
+
+ TestHybridClass thc5 = new TestHybridClass();
+ assertThat(thc5.copy2(new TestHybridClass(5, "five", false))).isTrue();
+ assertThat(thc5.getInt()).isEqualTo(5);
+ assertThat(thc5.getString()).isEqualTo("five");
+ }
+
+ @Test
+ public void testReturnGlobalRef() {
+ TestHybridClass thc = new TestHybridClass();
+ thc.setGlobal("global_ref");
+ assertThat(thc.getGlobal1()).isEqualTo("global_ref");
+ assertThat(thc.getGlobal2()).isEqualTo("global_ref");
+ }
+
+ @Test
+ public void testLocalLeak() {
+ TestHybridClass.autoconvertMany();
+ }
+
+ @Test
+ public void testExceptionMapping() {
+ TestHybridClass thc1 = new TestHybridClass();
+ thrown.expect(ArrayStoreException.class);
+ thc1.oops();
+ }
+
+ abstract static class AbstractTestHybrid {
+ @DoNotStrip private final HybridData mHybridData;
+
+ private int mAbstractNum;
+
+ protected AbstractTestHybrid(HybridData hybridData, int an) {
+ mHybridData = hybridData;
+ mAbstractNum = an;
+ }
+
+ public int abstractNum() {
+ return mAbstractNum;
+ }
+
+ public native int nativeNum();
+
+ public abstract int concreteNum();
+
+ public abstract int sum();
+ }
+
+ static class ConcreteTestHybrid extends AbstractTestHybrid {
+ public ConcreteTestHybrid(int an, int nn, int cn) {
+ super(initHybrid(nn, cn), an);
+ }
+
+ private static native HybridData initHybrid(int nn, int cn);
+
+ // overrides can be native
+ @Override
+ public native int concreteNum();
+
+ // overrides can be java
+ @Override
+ public int sum() {
+ return nativeNum() + abstractNum() + concreteNum();
+ }
+ }
+
+ @Test
+ public void testHybridInheritance() {
+ AbstractTestHybrid ath = new ConcreteTestHybrid(1, 2, 3);
+ assertThat(ath.abstractNum()).isEqualTo(1);
+ assertThat(ath.nativeNum()).isEqualTo(2);
+ assertThat(ath.concreteNum()).isEqualTo(3);
+ assertThat(ath.sum()).isEqualTo(6);
+ }
+
+ public static native boolean cxxTestInheritance(AbstractTestHybrid ath);
+
+ public static native AbstractTestHybrid makeAbstractHybrid();
+
+ @Test
+ public void testHybridCxx() {
+ AbstractTestHybrid ath = new ConcreteTestHybrid(4, 5, 6);
+ assertThat(cxxTestInheritance(ath)).isTrue();
+
+ AbstractTestHybrid ath2 = makeAbstractHybrid();
+ assertThat(ath2 instanceof ConcreteTestHybrid).isTrue();
+ assertThat(ath2.abstractNum()).isEqualTo(7);
+ assertThat(ath2.nativeNum()).isEqualTo(8);
+ assertThat(ath2.concreteNum()).isEqualTo(9);
+ assertThat(ath2.sum()).isEqualTo(24);
+ }
+
+ static class Base {}
+
+ static class Derived extends Base {
+ @DoNotStrip private final HybridData mHybridData;
+
+ private Derived(HybridData hybridData) {
+ mHybridData = hybridData;
+ }
+ }
+
+ public static native boolean cxxTestDerivedJavaClass();
+
+ @Test
+ public void testDerivedJavaClassCxx() {
+ assertThat(cxxTestDerivedJavaClass()).isTrue();
+ }
+
+ static class TestHybridClassBase extends HybridClassBase {
+ protected native void initHybrid();
+
+ private native void initHybrid(int i);
+
+ protected TestHybridClassBase() {
+ // No initHybrid() here!
+ // Otherwise factory construction will set native pointer twice and process will crash.
+ }
+
+ public TestHybridClassBase(int i) {
+ initHybrid(i);
+ }
+
+ // Some C++ methods.
+ public native void setInt(int i);
+
+ public native int getInt();
+
+ public static native TestHybridClassBase makeWithThree();
+ }
+
+ static class TestHybridClassBaseDefaultCtor extends TestHybridClassBase {
+ public TestHybridClassBaseDefaultCtor() {
+ initHybrid();
+ }
+ }
+
+ @Test
+ public void testHybridBaseDefaultCtor() {
+ TestHybridClassBaseDefaultCtor base = new TestHybridClassBaseDefaultCtor();
+ assertThat(base.getInt()).isZero();
+
+ base.setInt(58);
+ assertThat(base.getInt()).isEqualTo(58);
+ }
+
+ @Test
+ public void testHybridBaseConstructorArgs() {
+ TestHybridClassBase base = new TestHybridClassBase(42);
+ assertThat(base.getInt()).isEqualTo(42);
+ }
+
+ @Test
+ public void testHybridBaseFactoryConstruction() {
+ TestHybridClassBase base = TestHybridClassBase.makeWithThree();
+ assertThat(base.getInt()).isEqualTo(3);
+ }
+
+ static class Destroyable {
+ @DoNotStrip private final HybridData mHybridData;
+
+ private Destroyable(HybridData hybridData) {
+ mHybridData = hybridData;
+ }
+ }
+
+ public static native boolean cxxTestHybridDestruction();
+
+ @Test
+ public void testHybridDestuction() {
+ assertThat(cxxTestHybridDestruction()).isTrue();
+ }
+}
diff --git a/deps/fbjni/test/IteratorTests.java b/deps/fbjni/test/IteratorTests.java
new file mode 100644
index 000000000..fa1fcf4b2
--- /dev/null
+++ b/deps/fbjni/test/IteratorTests.java
@@ -0,0 +1,72 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.junit.Test;
+
+public class IteratorTests extends BaseFBJniTests {
+ @Test
+ public void testListIterator() {
+ List list = new ArrayList();
+ list.add("red");
+ list.add("green");
+ list.add("blue");
+
+ assertThat(nativeTestListIterator(list)).isTrue();
+ }
+
+ private static native boolean nativeTestListIterator(List list);
+
+ @Test
+ public void testMapIterator() {
+ Map map = new HashMap();
+ map.put("one", 1);
+ map.put("two", 2);
+ map.put("four", 4);
+
+ assertThat(nativeTestMapIterator(map)).isTrue();
+ }
+
+ private static native boolean nativeTestMapIterator(Map map);
+
+ @Test(expected = ClassCastException.class)
+ public void testMapIterateWrongType() {
+ Map map = new HashMap();
+ map.put("one", 1);
+ map.put("two", 2);
+ map.put("pi", 3.14);
+
+ assertThat(nativeTestIterateWrongType(map)).isTrue();
+ }
+
+ private static native boolean nativeTestIterateWrongType(Map map);
+
+ @Test
+ public void testMapIterateNullKey() {
+ Map map = new HashMap();
+ map.put("one", 1);
+ map.put(null, -99);
+ map.put("four", 4);
+
+ assertThat(nativeTestIterateNullKey(map)).isTrue();
+ }
+
+ private static native boolean nativeTestIterateNullKey(Map map);
+
+ @Test
+ public void testLargeMapIteration() {
+ Map map = new HashMap();
+ for (int i = 0; i < 3000; i++) {
+ map.put("" + i, "value");
+ }
+ assertThat(nativeTestLargeMapIteration(map)).isTrue();
+ }
+
+ private static native boolean nativeTestLargeMapIteration(Map map);
+}
diff --git a/deps/fbjni/test/PrimitiveArrayTests.java b/deps/fbjni/test/PrimitiveArrayTests.java
new file mode 100644
index 000000000..a94ffdbe8
--- /dev/null
+++ b/deps/fbjni/test/PrimitiveArrayTests.java
@@ -0,0 +1,593 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.fest.assertions.api.Assertions.offset;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class PrimitiveArrayTests extends BaseFBJniTests {
+
+ private static final int MAGIC = 42;
+
+ @Test
+ public void testMakeArrays() {
+ assertThat(nativeTestMakeBooleanArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeByteArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeCharArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeShortArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeIntArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeLongArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeFloatArray(MAGIC).length).isEqualTo(MAGIC);
+ assertThat(nativeTestMakeDoubleArray(MAGIC).length).isEqualTo(MAGIC);
+ }
+
+ private static native boolean[] nativeTestMakeBooleanArray(int size);
+
+ private static native byte[] nativeTestMakeByteArray(int size);
+
+ private static native char[] nativeTestMakeCharArray(int size);
+
+ private static native short[] nativeTestMakeShortArray(int size);
+
+ private static native int[] nativeTestMakeIntArray(int size);
+
+ private static native long[] nativeTestMakeLongArray(int size);
+
+ private static native float[] nativeTestMakeFloatArray(int size);
+
+ private static native double[] nativeTestMakeDoubleArray(int size);
+
+ @Test
+ public void testGetSetBooleanArray() {
+ boolean[] array = {false, true};
+
+ assertThat(nativeTestGetSetBooleanArray(array)).isTrue();
+ assertThat(array).isEqualTo(new boolean[] {true, false});
+ }
+
+ private static native boolean nativeTestGetSetBooleanArray(boolean[] array);
+
+ @Test
+ public void testPinBooleanArray() {
+ boolean[] array = {false, true};
+
+ assertThat(nativeTestPinBooleanArray(array)).isTrue();
+ assertThat(array).isEqualTo(new boolean[] {true, false});
+ }
+
+ private static native boolean nativeTestPinBooleanArray(boolean[] array);
+
+ @Test
+ public void testGetSetByteArray() {
+ byte[] array = new byte[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (byte) i;
+ }
+
+ assertThat(nativeTestGetSetByteArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((byte) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestGetSetByteArray(byte[] array);
+
+ @Test
+ public void testGetSetCharArray() {
+ char[] array = new char[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (char) i;
+ }
+
+ assertThat(nativeTestGetSetCharArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((char) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestGetSetCharArray(char[] array);
+
+ @Test
+ public void testGetSetShortArray() {
+ short[] array = new short[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (short) i;
+ }
+
+ assertThat(nativeTestGetSetShortArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((short) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestGetSetShortArray(short[] array);
+
+ @Test
+ public void testGetSetIntArray() {
+ int[] intArray = new int[MAGIC];
+ for (int i = 0; i < intArray.length; ++i) {
+ intArray[i] = i;
+ }
+
+ assertThat(nativeTestGetSetIntArray(intArray)).isTrue();
+
+ for (int i = 0; i < intArray.length; ++i) {
+ assertThat(intArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestGetSetIntArray(int[] array);
+
+ @Test
+ public void testGetSetLongArray() {
+ long[] longArray = new long[MAGIC];
+ for (int i = 0; i < longArray.length; ++i) {
+ longArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestGetSetLongArray(longArray)).isTrue();
+
+ for (int i = 0; i < longArray.length; ++i) {
+ assertThat(longArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestGetSetLongArray(long[] array);
+
+ @Test
+ public void testGetSetFloatArray() {
+ float[] floatArray = new float[MAGIC];
+ for (int i = 0; i < floatArray.length; ++i) {
+ floatArray[i] = i;
+ }
+
+ assertThat(nativeTestGetSetFloatArray(floatArray)).isTrue();
+
+ for (int i = 0; i < floatArray.length; ++i) {
+ assertThat(floatArray[i]).isEqualTo(2 * i, offset(1e-3f));
+ }
+ }
+
+ private static native boolean nativeTestGetSetFloatArray(float[] array);
+
+ @Test
+ public void testGetSetDoubleArray() {
+ double[] doubleArray = new double[MAGIC];
+ for (int i = 0; i < doubleArray.length; ++i) {
+ doubleArray[i] = i;
+ }
+
+ assertThat(nativeTestGetSetDoubleArray(doubleArray)).isTrue();
+
+ for (int i = 0; i < doubleArray.length; ++i) {
+ assertThat(doubleArray[i]).isEqualTo(2 * i, offset(1e-3));
+ }
+ }
+
+ private static native boolean nativeTestGetSetDoubleArray(double[] array);
+
+ @Test
+ public void testPinByteArray() {
+ byte[] array = new byte[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (byte) i;
+ }
+
+ assertThat(nativeTestPinByteArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((byte) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinByteArray(byte[] array);
+
+ @Test
+ public void testPinCharArray() {
+ char[] array = new char[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (char) i;
+ }
+
+ assertThat(nativeTestPinCharArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((char) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinCharArray(char[] array);
+
+ @Test
+ public void testPinShortArray() {
+ short[] array = new short[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (short) i;
+ }
+
+ assertThat(nativeTestPinShortArray(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((short) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinShortArray(short[] array);
+
+ @Test
+ public void testPinIntArray() {
+ int[] intArray = new int[MAGIC];
+ for (int i = 0; i < intArray.length; ++i) {
+ intArray[i] = i;
+ }
+
+ assertThat(nativeTestPinIntArray(intArray)).isTrue();
+
+ for (int i = 0; i < intArray.length; ++i) {
+ assertThat(intArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinIntArray(int[] array);
+
+ @Test
+ public void testPinLongArray() {
+ long[] longArray = new long[MAGIC];
+ for (int i = 0; i < longArray.length; ++i) {
+ longArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinLongArray(longArray)).isTrue();
+
+ for (int i = 0; i < longArray.length; ++i) {
+ assertThat(longArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinLongArray(long[] array);
+
+ @Test
+ public void testPinFloatArray() {
+ float[] floatArray = new float[MAGIC];
+ for (int i = 0; i < floatArray.length; ++i) {
+ floatArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinFloatArray(floatArray)).isTrue();
+
+ for (int i = 0; i < floatArray.length; ++i) {
+ assertThat(floatArray[i]).isEqualTo(2 * i, offset(1e-3f));
+ }
+ }
+
+ private static native boolean nativeTestPinFloatArray(float[] array);
+
+ @Test
+ public void testPinDoubleArray() {
+ double[] doubleArray = new double[MAGIC];
+ for (int i = 0; i < doubleArray.length; ++i) {
+ doubleArray[i] = (double) i;
+ }
+
+ assertThat(nativeTestPinDoubleArray(doubleArray)).isTrue();
+
+ for (int i = 0; i < doubleArray.length; ++i) {
+ assertThat(doubleArray[i]).isEqualTo(2 * i, offset(1e-3));
+ }
+ }
+
+ private static native boolean nativeTestPinDoubleArray(double[] array);
+
+ @Test
+ public void testPinByteArrayRegion() {
+ byte[] array = new byte[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (byte) i;
+ }
+
+ assertThat(nativeTestPinByteArrayRegion(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((byte) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinByteArrayRegion(byte[] array);
+
+ @Test
+ public void testPinCharArrayRegion() {
+ char[] array = new char[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (char) i;
+ }
+
+ assertThat(nativeTestPinCharArrayRegion(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((char) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinCharArrayRegion(char[] array);
+
+ @Test
+ public void testPinShortArrayRegion() {
+ short[] array = new short[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (short) i;
+ }
+
+ assertThat(nativeTestPinShortArrayRegion(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((short) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinShortArrayRegion(short[] array);
+
+ @Test
+ public void testPinIntArrayRegion() {
+ int[] intArray = new int[MAGIC];
+ for (int i = 0; i < intArray.length; ++i) {
+ intArray[i] = i;
+ }
+
+ assertThat(nativeTestPinIntArrayRegion(intArray)).isTrue();
+
+ for (int i = 0; i < intArray.length; ++i) {
+ assertThat(intArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinIntArrayRegion(int[] array);
+
+ @Test
+ public void testPinLongArrayRegion() {
+ long[] longArray = new long[MAGIC];
+ for (int i = 0; i < longArray.length; ++i) {
+ longArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinLongArrayRegion(longArray)).isTrue();
+
+ for (int i = 0; i < longArray.length; ++i) {
+ assertThat(longArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinLongArrayRegion(long[] array);
+
+ @Test
+ public void testPinFloatArrayRegion() {
+ float[] floatArray = new float[MAGIC];
+ for (int i = 0; i < floatArray.length; ++i) {
+ floatArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinFloatArrayRegion(floatArray)).isTrue();
+
+ for (int i = 0; i < floatArray.length; ++i) {
+ assertThat(floatArray[i]).isEqualTo(2 * i, offset(1e-3f));
+ }
+ }
+
+ private static native boolean nativeTestPinFloatArrayRegion(float[] array);
+
+ @Test
+ public void testPinDoubleArrayRegion() {
+ double[] doubleArray = new double[MAGIC];
+ for (int i = 0; i < doubleArray.length; ++i) {
+ doubleArray[i] = (double) i;
+ }
+
+ assertThat(nativeTestPinDoubleArrayRegion(doubleArray)).isTrue();
+
+ for (int i = 0; i < doubleArray.length; ++i) {
+ assertThat(doubleArray[i]).isEqualTo(2 * i, offset(1e-3));
+ }
+ }
+
+ private static native boolean nativeTestPinDoubleArrayRegion(double[] array);
+
+ @Test
+ public void testPinByteArrayCritical() {
+ byte[] array = new byte[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (byte) i;
+ }
+
+ assertThat(nativeTestPinByteArrayCritical(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((byte) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinByteArrayCritical(byte[] array);
+
+ @Test
+ public void testPinCharArrayCritical() {
+ char[] array = new char[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (char) i;
+ }
+
+ assertThat(nativeTestPinCharArrayCritical(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((char) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinCharArrayCritical(char[] array);
+
+ @Test
+ public void testPinShortArrayCritical() {
+ short[] array = new short[MAGIC];
+ for (int i = 0; i < array.length; ++i) {
+ array[i] = (short) i;
+ }
+
+ assertThat(nativeTestPinShortArrayCritical(array)).isTrue();
+
+ for (int i = 0; i < array.length; ++i) {
+ assertThat(array[i]).isEqualTo((short) (2 * i));
+ }
+ }
+
+ private static native boolean nativeTestPinShortArrayCritical(short[] array);
+
+ @Test
+ public void testPinIntArrayCritical() {
+ int[] intArray = new int[MAGIC];
+ for (int i = 0; i < intArray.length; ++i) {
+ intArray[i] = i;
+ }
+
+ assertThat(nativeTestPinIntArrayCritical(intArray)).isTrue();
+
+ for (int i = 0; i < intArray.length; ++i) {
+ assertThat(intArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinIntArrayCritical(int[] array);
+
+ @Test
+ public void testPinLongArrayCritical() {
+ long[] longArray = new long[MAGIC];
+ for (int i = 0; i < longArray.length; ++i) {
+ longArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinLongArrayCritical(longArray)).isTrue();
+
+ for (int i = 0; i < longArray.length; ++i) {
+ assertThat(longArray[i]).isEqualTo(2 * i);
+ }
+ }
+
+ private static native boolean nativeTestPinLongArrayCritical(long[] array);
+
+ @Test
+ public void testPinFloatArrayCritical() {
+ float[] floatArray = new float[MAGIC];
+ for (int i = 0; i < floatArray.length; ++i) {
+ floatArray[i] = (long) i;
+ }
+
+ assertThat(nativeTestPinFloatArrayCritical(floatArray)).isTrue();
+
+ for (int i = 0; i < floatArray.length; ++i) {
+ assertThat(floatArray[i]).isEqualTo(2 * i, offset(1e-3f));
+ }
+ }
+
+ private static native boolean nativeTestPinFloatArrayCritical(float[] array);
+
+ @Test
+ public void testPinDoubleArrayCritical() {
+ double[] doubleArray = new double[MAGIC];
+ for (int i = 0; i < doubleArray.length; ++i) {
+ doubleArray[i] = (double) i;
+ }
+
+ assertThat(nativeTestPinDoubleArrayCritical(doubleArray)).isTrue();
+
+ for (int i = 0; i < doubleArray.length; ++i) {
+ assertThat(doubleArray[i]).isEqualTo(2 * i, offset(1e-3));
+ }
+ }
+
+ private static native boolean nativeTestPinDoubleArrayCritical(double[] array);
+
+ @Test
+ public void testIndexOutOfBoundsInRegions() {
+ assertThat(nativeTestIndexOutOfBoundsInRegions()).isTrue();
+ }
+
+ private static native boolean nativeTestIndexOutOfBoundsInRegions();
+
+ @Test
+ public void testBooleanArrayIndexing() {
+ boolean[] array = {true, true, false, true, false};
+ for (int ii = 0; ii < 5; ii++) {
+ assertThat(nativeTestBooleanArrayIndexing(array, ii)).isEqualTo(array[ii]);
+ }
+ }
+
+ private native boolean nativeTestBooleanArrayIndexing(boolean[] array, int idx);
+
+ @Test
+ public void testIntegerArrayIndexing() {
+ int[] array = {0, 1, 2, 3, 4};
+ for (int ii = 0; ii < 5; ii++) {
+ assertThat(nativeTestIntegerArrayIndexing(array, ii)).isEqualTo(array[ii]);
+ }
+ }
+
+ private native int nativeTestIntegerArrayIndexing(int[] array, int idx);
+
+ @Test
+ public void testIntegerArraySize() {
+ int[] array = {0, 1, 2, 3, 4};
+ assertThat(nativeTestIntegerArraySize(array)).isEqualTo(array.length);
+ }
+
+ private native int nativeTestIntegerArraySize(int[] array);
+
+ @Test
+ public void testIntegerArrayIncrement() {
+ int[] array = {0, 1, 2, 3, 4};
+ array = nativeTestIntegerArrayIncrement(array);
+ for (int ii = 0; ii < 5; ii++) {
+ assertThat(array[ii]).isEqualTo(ii + 1);
+ }
+ }
+
+ private native int[] nativeTestIntegerArrayIncrement(int[] array);
+
+ @Test
+ public void testIntegerArrayMoveAssignment() {
+ int[] array = {0, 1, 2, 3, 4};
+ nativeTestIntegerArrayMoveAssignment(array);
+ assertThat(array[0]).isEqualTo(0);
+ }
+
+ private native void nativeTestIntegerArrayMoveAssignment(int[] array);
+
+ // On ART, a large array will be placed in the large heap. Arrays here are
+ // non-movable and so the vm pins them in-place. A small array will be a
+ // movable object and thus the pinned array will be a copy.
+ // On Dalvik, all pinned arrays are in-place.
+
+ @Test
+ @Ignore("Flakey Test. See t8845133")
+ public void testCopiedPinnedArray() {
+ int[] array = new int[100];
+ assumeTrue(nativeIsPinnedArrayACopy(array));
+ assertThat(nativeTestCopiedPinnedArray(array)).isTrue();
+ }
+
+ @Test
+ public void testNonCopiedPinnedArray() {
+ int[] array = new int[1000000];
+ assumeFalse(nativeIsPinnedArrayACopy(array));
+ assertThat(nativeTestNonCopiedPinnedArray(array)).isTrue();
+ }
+
+ private native boolean nativeIsPinnedArrayACopy(int[] array);
+
+ private native boolean nativeTestCopiedPinnedArray(int[] array);
+
+ private native boolean nativeTestNonCopiedPinnedArray(int[] array);
+}
diff --git a/deps/fbjni/test/ReadableByteChannelTests.java b/deps/fbjni/test/ReadableByteChannelTests.java
new file mode 100644
index 000000000..e50eb4cca
--- /dev/null
+++ b/deps/fbjni/test/ReadableByteChannelTests.java
@@ -0,0 +1,84 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+package com.facebook.jni;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class ReadableByteChannelTests extends BaseFBJniTests {
+ private static final byte[] data = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ };
+
+ @Test
+ public void testSmallRead() {
+ byte[] testData = Arrays.copyOf(data, 8);
+ ReadableByteChannel channel = new TestChannel(testData);
+ assertThat(nativeTestSmallRead(channel, testData)).isTrue();
+ }
+
+ public static native boolean nativeTestSmallRead(ReadableByteChannel channel, byte[] data);
+
+ @Test
+ public void testReadToBufferCapacity() {
+ ReadableByteChannel channel = new TestChannel(data);
+ assertThat(nativeTestReadToBufferCapacity(channel, data)).isTrue();
+ }
+
+ public static native boolean nativeTestReadToBufferCapacity(
+ ReadableByteChannel channel, byte[] data);
+
+ @Test
+ public void testConsumeChannel() {
+ ReadableByteChannel channel = new TestChannel(data);
+ assertThat(nativeTestConsumeChannel(channel, data)).isTrue();
+ }
+
+ public static native boolean nativeTestConsumeChannel(ReadableByteChannel channel, byte[] data);
+
+ @Test
+ public void testConsumeChannelIteratively() {
+ ReadableByteChannel channel = new TestChannel(data);
+ assertThat(nativeTestConsumeChannelIteratively(channel, data)).isTrue();
+ }
+
+ public static native boolean nativeTestConsumeChannelIteratively(
+ ReadableByteChannel channel, byte[] data);
+
+ private static class TestChannel implements ReadableByteChannel {
+ private final byte[] data;
+ private int offset = 0;
+
+ TestChannel(byte[] data) {
+ this.data = data;
+ }
+
+ @Override
+ public int read(ByteBuffer buffer) {
+ if (offset >= data.length) {
+ return -1;
+ }
+
+ int n = Math.min(buffer.remaining(), data.length - offset);
+ int start = offset;
+ offset += n;
+ buffer.put(Arrays.copyOfRange(data, start, offset));
+ return n;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+ @Override
+ public void close() {}
+ }
+}
diff --git a/deps/fbjni/test/jni/byte_buffer_tests.cpp b/deps/fbjni/test/jni/byte_buffer_tests.cpp
new file mode 100644
index 000000000..2c27be313
--- /dev/null
+++ b/deps/fbjni/test/jni/byte_buffer_tests.cpp
@@ -0,0 +1,100 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include
+
+#include
+#include
+
+#include "expect.h"
+
+using namespace facebook::jni;
+
+namespace {
+
+size_t ByteBufferCapacity(alias_ref buffer) {
+ static auto meth = JByteBuffer::javaClassStatic()->getMethod("capacity");
+ return meth(buffer);
+}
+
+jboolean testDirectByteBuffer(JNIEnv*, jclass) {
+ std::vector vec{5, 4, 3, 2, 1, 0};
+ auto nbb = JByteBuffer::wrapBytes(vec.data(), vec.size());
+
+ EXPECT(ByteBufferCapacity(nbb) == vec.size());
+ EXPECT(nbb->isDirect());
+ EXPECT(nbb->getDirectSize() == vec.size());
+
+ auto bytes = nbb->getDirectBytes();
+
+ for (size_t i = 0; i < vec.size(); i++) {
+ EXPECT(bytes[i] == vec[i]);
+ }
+ return JNI_TRUE;
+}
+
+jboolean testEmptyDirectByteBuffer(JNIEnv*, jclass) {
+ uint8_t data;
+ auto nbb = JByteBuffer::wrapBytes(&data, 0);
+
+ EXPECT(ByteBufferCapacity(nbb) == 0);
+ EXPECT(nbb->isDirect());
+ EXPECT(nbb->getDirectSize() == 0);
+
+ return JNI_TRUE;
+}
+
+jboolean testRewindBuffer(alias_ref self) {
+ std::vector vec{0, 0, 0, 0, 0, 0, 0, 0};
+ auto nbb = JByteBuffer::wrapBytes(vec.data(), vec.size());
+
+ auto cls = self->getClass();
+ auto writeBytes =
+ cls->getStaticMethod("writeBytes");
+
+ writeBytes(cls, *nbb, 0, 1, 2, 3);
+ nbb->rewind();
+ writeBytes(cls, *nbb, 4, 5, 6, 7);
+
+ EXPECT(vec[0] == 4);
+ EXPECT(vec[1] == 5);
+ EXPECT(vec[2] == 6);
+ EXPECT(vec[3] == 7);
+ EXPECT(vec[4] == 0);
+ EXPECT(vec[5] == 0);
+ EXPECT(vec[6] == 0);
+ EXPECT(vec[7] == 0);
+
+ return JNI_TRUE;
+}
+
+local_ref nativeAllocateDirect(alias_ref self, int size) {
+ return JByteBuffer::allocateDirect(size);
+}
+
+jboolean testFloatBuffer(alias_ref self, alias_ref buffer) {
+ EXPECT(buffer->isDirect());
+ EXPECT(buffer->getDirectCapacity() == 5);
+ float* raw = (float*)buffer->getDirectAddress();
+ EXPECT(raw);
+
+ EXPECT(raw[0] == 1);
+ EXPECT(raw[1] == 2);
+ EXPECT(raw[2] == 2.5);
+ EXPECT(raw[3] == 2.75);
+ EXPECT(raw[4] == 3);
+
+ return JNI_TRUE;
+}
+
+
+}
+
+void RegisterByteBufferTests() {
+ registerNatives("com/facebook/jni/ByteBufferTests", {
+ makeNativeMethod("nativeTestDirectByteBuffer", testDirectByteBuffer),
+ makeNativeMethod("nativeTestEmptyDirectByteBuffer", testEmptyDirectByteBuffer),
+ makeNativeMethod("nativeTestRewindBuffer", testRewindBuffer),
+ makeNativeMethod("nativeAllocateDirect", nativeAllocateDirect),
+ makeNativeMethod("nativeTestFloatBuffer", testFloatBuffer),
+ });
+}
diff --git a/deps/fbjni/test/jni/expect.h b/deps/fbjni/test/jni/expect.h
new file mode 100644
index 000000000..da966e29f
--- /dev/null
+++ b/deps/fbjni/test/jni/expect.h
@@ -0,0 +1,14 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#pragma once
+
+#include
+
+#define EXPECT(X) \
+ do { \
+ if (!(X)) { \
+ FBJNI_LOGE("[%s:%d] Expectation failed: %s", __FILE__, __LINE__, #X); \
+ return JNI_FALSE; \
+ } \
+ } while (false)
+
diff --git a/deps/fbjni/test/jni/fbjni_onload.cpp b/deps/fbjni/test/jni/fbjni_onload.cpp
new file mode 100644
index 000000000..849ba8d12
--- /dev/null
+++ b/deps/fbjni/test/jni/fbjni_onload.cpp
@@ -0,0 +1,25 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include
+
+#include
+
+using namespace facebook::jni;
+
+void RegisterFbjniTests();
+void RegisterTestHybridClass();
+void RegisterPrimitiveArrayTests();
+void RegisterIteratorTests();
+void RegisterByteBufferTests();
+void RegisterReadableByteChannelTests();
+
+jint JNI_OnLoad(JavaVM* vm, void*) {
+ return facebook::jni::initialize(vm, [] {
+ RegisterFbjniTests();
+ RegisterTestHybridClass();
+ RegisterPrimitiveArrayTests();
+ RegisterIteratorTests();
+ RegisterByteBufferTests();
+ RegisterReadableByteChannelTests();
+ });
+}
diff --git a/deps/fbjni/test/jni/fbjni_tests.cpp b/deps/fbjni/test/jni/fbjni_tests.cpp
new file mode 100644
index 000000000..f3ed115eb
--- /dev/null
+++ b/deps/fbjni/test/jni/fbjni_tests.cpp
@@ -0,0 +1,1518 @@
+// Copyright 2004-present Facebook. All Rights Reserved.
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "expect.h"
+#include "no_rtti.h"
+
+#include
+
+#define EXPECT_SAME(A, B, C) EXPECT((A) == (B) && (B) == (C) && (C) == (A))
+
+// A lot of the functions here are just confirming that compilation works.
+#pragma GCC diagnostic ignored "-Wunused-function"
+
+using namespace facebook::jni;
+
+namespace {
+
+struct Callbacks : public facebook::jni::JavaClass {
+ constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/FBJniTests$Callbacks;";
+};
+
+struct TestThing : public JavaClass {
+ constexpr static auto kJavaDescriptor = "Lcom/facebook/jni/FBJniTests$TestThing;";
+};
+
+// Yes, sloppy and does not handle conversions correctly but does it's job here
+static std::string ToString(JNIEnv* env, jstring java_string) {
+ auto chars = env->GetStringUTFChars(java_string, nullptr);
+ if (chars == nullptr) {
+ throw std::runtime_error{"Couldn't get UTF chars"};
+ }
+
+ std::string string{chars};
+ env->ReleaseStringUTFChars(java_string, chars);
+
+ return string;
+}
+
+jboolean TestClassResolution(JNIEnv* env, jobject self, jstring class_name) {
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ return resolved_class.get() != nullptr? JNI_TRUE : JNI_FALSE;
+}
+
+jboolean TestLazyClassResolution(JNIEnv* env, jobject self, jstring class_name) {
+ auto resolved_class = alias_ref{};
+ resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ return resolved_class.get() != nullptr? JNI_TRUE : JNI_FALSE;
+}
+
+jobject TestCreateInstanceOf(JNIEnv* env, jobject self, jstring class_name) {
+ auto clazz = findClassLocal(ToString(env, class_name).c_str());
+ auto constructor = clazz->getConstructor();
+ return clazz->newObject(constructor, class_name).release();
+}
+
+jboolean TestTypeDescriptors(JNIEnv* env, jobject self) {
+ EXPECT(jtype_traits::descriptor() == "Z");
+ EXPECT(jtype_traits::descriptor() == "B");
+ EXPECT(jtype_traits::descriptor() == "C");
+ EXPECT(jtype_traits::descriptor() == "D");
+ EXPECT(jtype_traits::descriptor() == "F");
+ EXPECT(jtype_traits::descriptor() == "I");
+ EXPECT(jtype_traits::descriptor() == "J");
+ EXPECT(jtype_traits::descriptor() == "S");
+
+ EXPECT(jtype_traits::descriptor() == "Ljava/lang/String;");
+ EXPECT(jtype_traits::descriptor() == "Ljava/lang/Object;");
+
+ EXPECT(jtype_traits::descriptor() == "[I");
+ EXPECT(jtype_traits>::descriptor() == "[Ljava/lang/String;");
+ EXPECT(jtype_traits>>::descriptor()
+ == "[[Ljava/lang/String;");
+ EXPECT(jtype_traits>::descriptor() == "[[I");
+
+ // base_name() is meaningless for primitive types.
+ EXPECT(jtype_traits::base_name() == "java/lang/String");
+ EXPECT(jtype_traits::base_name() == "java/lang/Object");
+
+ EXPECT(jtype_traits::base_name() == "[I");
+ EXPECT(jtype_traits>::base_name() == "[Ljava/lang/String;");
+ EXPECT(jtype_traits>>::base_name() == "[[Ljava/lang/String;");
+ EXPECT(jtype_traits>::base_name() == "[[I");
+
+ return JNI_TRUE;
+}
+
+jboolean TestVirtualMethodResolution_I(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method =
+ resolved_class->getMethod(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+jboolean TestVirtualMethodResolution_arrB(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method =
+ resolved_class->getMethod(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+jboolean TestVirtualMethodResolution_S_arrS(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method =
+ resolved_class->getMethod(jstring)>(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+jboolean TestVirtualMethodResolution_arrarrS(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method =
+ resolved_class->getStaticMethod>()>(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+jboolean TestVirtualMethodResolution_arrarrI(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method =
+ resolved_class->getStaticMethod()>(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+jboolean TestLazyVirtualMethodResolution_I(
+ JNIEnv* env,
+ jobject self,
+ jstring class_name,
+ jstring method_name)
+{
+ auto resolved_class = findClassLocal(ToString(env, class_name).c_str());
+ auto resolved_method = JMethod{};
+ resolved_method = resolved_class->getMethod(ToString(env, method_name).c_str());
+ return static_cast(resolved_method);
+}
+
+void TestJMethodCallbacks(JNIEnv* env, jobject self, Callbacks::javaobject callbacks) {
+ static const auto callbacks_class = Callbacks::javaClassStatic();
+
+ static const auto void_foo = callbacks_class->getMethod("voidFoo");
+ void_foo(callbacks);
+
+ static const auto boolean_foo = callbacks_class->getMethod("booleanFoo");
+ boolean_foo(callbacks);
+
+ static const auto byte_foo = callbacks_class->getMethod("byteFoo");
+ byte_foo(callbacks);
+
+ static const auto char_foo = callbacks_class->getMethod("charFoo");
+ char_foo(callbacks);
+
+ static const auto short_foo = callbacks_class->getMethod("shortFoo");
+ short_foo(callbacks);
+
+ static const auto int_foo = callbacks_class->getMethod("intFoo");
+ int_foo(callbacks);
+
+ static const auto long_foo = callbacks_class->getMethod("longFoo");
+ long_foo(callbacks);
+
+ static const auto float_foo = callbacks_class->getMethod("floatFoo");
+ float_foo(callbacks);
+
+ static const auto double_foo = callbacks_class->getMethod("doubleFoo");
+ double_foo(callbacks);
+
+ static const auto object_foo = callbacks_class->getMethod("objectFoo");
+ object_foo(callbacks);
+
+ static const auto string_foo = callbacks_class->getMethod("stringFoo");
+ string_foo(callbacks);
+}
+
+// Try to test the static functions
+void TestJStaticMethodCallbacks(JNIEnv* env, jobject self) {
+ // static auto callbacks_class = findClassStatic(callbacks_class_name);
+ auto cls = findClassLocal("com/facebook/jni/FBJniTests");
+ jclass jcls = env->FindClass("com/facebook/jni/FBJniTests");
+
+ static const auto void_foo_static = cls->getStaticMethod("voidFooStatic");
+ void_foo_static(jcls);
+
+ static const auto boolean_foo_static = cls->getStaticMethod("booleanFooStatic");
+ boolean_foo_static(jcls);
+
+ static const auto byte_foo_static = cls->getStaticMethod("byteFooStatic");
+ byte_foo_static(jcls);
+
+ static const auto char_foo_static = cls->getStaticMethod("charFooStatic");
+ char_foo_static(jcls, 'c', 5);
+
+ static const auto short_foo_static = cls->getStaticMethod("shortFooStatic");
+ short_foo_static(jcls, 17, 42);
+
+ static const auto int_foo_static = cls->getStaticMethod("intFooStatic");
+ int_foo_static(jcls, 5);
+
+ static const auto long_foo_static = cls->getStaticMethod("longFooStatic");
+ long_foo_static(jcls);
+
+ static const auto float_foo_static = cls->getStaticMethod("floatFooStatic");
+ float_foo_static(jcls);
+
+ static const auto double_foo_static = cls->getStaticMethod("doubleFooStatic");
+ double_foo_static(jcls);
+
+ static const auto object_foo_static = cls->getStaticMethod("objectFooStatic");
+ object_foo_static(jcls);
+
+ static const auto string_foo_static = cls->getStaticMethod("stringFooStatic");
+ string_foo_static(jcls);
+}
+
+jboolean TestIsAssignableFrom(JNIEnv* env, jobject self, jclass cls1, jclass cls2) {
+ return adopt_local(cls1)->isAssignableFrom(cls2);
+}
+
+jboolean TestIsInstanceOf(JNIEnv* env, jobject self, jobject test_object, jclass cls) {
+ auto clsref = adopt_local(test_object);
+ return clsref->isInstanceOf(cls);
+}
+
+jboolean TestIsSameObject(JNIEnv* env, jobject self, jobject a, jobject b) {
+ return isSameObject(a, b);
+}
+
+jboolean TestGetSuperclass(JNIEnv* env, jobject self, jclass test_class, jclass super_class) {
+ return isSameObject(adopt_local(test_class)->getSuperclass().get(), super_class);
+}
+
+jboolean StaticCastAliasRefToString(JNIEnv *, jobject , jobject string_as_object) {
+ alias_ref string_as_object_alias_ref { string_as_object };
+ alias_ref string_alias_ref = static_ref_cast(string_as_object_alias_ref);
+ return isSameObject(string_alias_ref.get(), string_as_object_alias_ref.get());
+}
+
+jboolean DynamicCastAliasRefToThrowable(JNIEnv *, jobject , jobject might_actually_be_throwable) {
+ alias_ref might_actually_be_throwable_alias_ref { might_actually_be_throwable };
+ // If the next line fails, it will throw an exception.
+ alias_ref throwable_alias_ref = dynamic_ref_cast(might_actually_be_throwable_alias_ref);
+ return isSameObject(throwable_alias_ref.get(), might_actually_be_throwable_alias_ref.get());
+}
+
+jboolean StaticCastLocalRefToString(JNIEnv *, jobject , jobject string_as_object) {
+ local_ref string_as_object_local_ref = adopt_local(string_as_object);
+ local_ref string_local_ref = static_ref_cast(string_as_object_local_ref);
+ return isSameObject(string_local_ref.get(), string_as_object_local_ref.get());
+}
+
+jboolean DynamicCastLocalRefToString(JNIEnv *, jobject , jobject might_actually_be_string) {
+ local_ref might_actually_be_string_local_ref = adopt_local(might_actually_be_string);
+ // If the next line fails, it will throw an exception.
+ local_ref string_local_ref = dynamic_ref_cast(might_actually_be_string_local_ref);
+ return isSameObject(string_local_ref.get(), might_actually_be_string_local_ref.get());
+}
+
+jboolean StaticCastGlobalRefToString(JNIEnv *, jobject , jobject string_as_object) {
+ global_ref string_as_object_global_ref = make_global(string_as_object);
+ global_ref string_global_ref = static_ref_cast(string_as_object_global_ref);
+ return isSameObject(string_global_ref.get(), string_as_object_global_ref.get());
+}
+
+jboolean DynamicCastGlobalRefToString(JNIEnv *, jobject , jobject might_actually_be_string) {
+ global_ref might_actually_be_string_global_ref = make_global(might_actually_be_string);
+ // If the next line fails, it will throw an exception.
+ global_ref string_global_ref = dynamic_ref_cast(might_actually_be_string_global_ref);
+ return isSameObject(string_global_ref.get(), might_actually_be_string_global_ref.get());
+}
+
+template
+static void Use(Args&&... args) {}
+
+jboolean TestWeakRefs(JNIEnv*, jobject self) {
+ using facebook::jni::internal::g_reference_stats;
+
+ g_reference_stats.reset();
+ {
+ // Wrapping existing local that should be deleted (locals = 1)
+ auto local = adopt_local(self);
+ // Make new local (locals = 2)
+ auto local2 = make_local(local);
+ // Make weak (weaks = 1)
+ auto weak = make_weak(local);
+ // Make global (globals = 1)
+ auto global = weak.lockGlobal();
+ // No new refs
+ auto banana = std::move(weak);
+ // No new refs
+ auto& binini = banana;
+ // Create a global of the local (keeping the local) (globals = 2)
+ auto dubglob = make_global(local);
+ // Create a weak (weaks = 2)
+ auto dupweak = make_weak(local);
+ // No new refs
+ swap(local, local2);
+
+ Use(binini);
+ }
+
+ FBJNI_LOGE("locals: %d", g_reference_stats.locals_deleted.load());
+ FBJNI_LOGE("globals: %d", g_reference_stats.globals_deleted.load());
+ FBJNI_LOGE("weaks: %d", g_reference_stats.weaks_deleted.load());
+
+ return (g_reference_stats.locals_deleted == 2 &&
+ g_reference_stats.globals_deleted == 2 &&
+ g_reference_stats.weaks_deleted == 2)? JNI_TRUE : JNI_FALSE;
+}
+
+jboolean TestAlias(JNIEnv* env, jobject self) {
+ auto ref = alias_ref{self};
+ return ref->isInstanceOf(findClassLocal("java/lang/Object"));
+}
+
+jboolean testAliasRefConversions(JNIEnv*, jobject self) {
+ auto aLocalString = make_jstring("foo");
+ alias_ref aString = aLocalString;
+ alias_ref anObject = aLocalString;
+ anObject = (jstring) nullptr;
+ anObject = aString;
+ // aString = anObject; // Shouldn't compile
+
+ return isSameObject(aString, anObject)? JNI_TRUE : JNI_FALSE;
+}
+
+void TestAutoAliasRefReturningVoid(facebook::jni::alias_ref self) {
+ // If this compiles, it succeeds.
+}
+
+jboolean testNullJString(JNIEnv*, jobject) {
+ auto aNullJString = make_jstring(nullptr);
+ EXPECT(aNullJString.get() == (jstring) nullptr);
+ return JNI_TRUE;
+}
+
+jboolean testSwap(JNIEnv*, jobject self, jobject other) {
+ auto selfAlias = wrap_alias(self);
+ auto otherAlias = wrap_alias(other);
+
+ swap(selfAlias, otherAlias);
+ EXPECT(self == otherAlias);
+ EXPECT(other == selfAlias);
+ EXPECT(self != selfAlias);
+ EXPECT(other != otherAlias);
+
+ auto selfLocal = make_local(self);
+ auto otherLocal = make_local(other);
+ swap(selfLocal, otherLocal);
+ EXPECT(self == otherLocal);
+ EXPECT(other == selfLocal);
+ EXPECT(self != selfLocal);
+ EXPECT(other != otherLocal);
+
+ auto selfGlobal = make_global(self);
+ auto otherGlobal = make_global(other);
+ swap(selfGlobal, otherGlobal);
+ EXPECT(self == otherGlobal);
+ EXPECT(other == selfGlobal);
+ EXPECT(self != selfGlobal);
+ EXPECT(other != otherGlobal);
+
+ auto selfWeak = make_weak(self);
+ auto otherWeak = make_weak(other);
+ swap(selfWeak, otherWeak);
+ auto selfLockedWeak = selfWeak.lockLocal();
+ auto otherLockedWeak = otherWeak.lockLocal();
+ EXPECT(self == otherLockedWeak);
+ EXPECT(other == selfLockedWeak);
+ EXPECT(self != selfLockedWeak);
+ EXPECT(other != otherLockedWeak);
+
+ return JNI_TRUE;
+}
+
+jboolean testEqualOperator(JNIEnv*, jobject self, jobject other) {
+ auto selfAlias = wrap_alias(self);
+ auto otherAlias = wrap_alias(other);
+ auto selfLocal = adopt_local(self);
+ auto otherLocal = adopt_local(other);
+ auto selfGlobal = make_global(self);
+ auto otherGlobal = make_global(other);
+ auto selfWeak = make_weak(self);
+ auto otherWeak = make_weak(other);
+ auto selfLockedWeak = selfWeak.lockLocal();
+ auto otherLockedWeak = otherWeak.lockLocal();
+
+ EXPECT(self == selfAlias);
+ EXPECT(selfAlias == selfLocal);
+ EXPECT(selfLocal == selfGlobal);
+ EXPECT(selfGlobal == selfLockedWeak);
+ EXPECT(self != other);
+ EXPECT(self != otherAlias);
+ EXPECT(self != otherLocal);
+ EXPECT(self != otherGlobal);
+ EXPECT(self != otherLockedWeak);
+ EXPECT(selfAlias != nullptr);
+ EXPECT(!(selfAlias == nullptr));
+ EXPECT(nullptr != selfLocal);
+ EXPECT(!(nullptr == selfGlobal));
+
+ return JNI_TRUE;
+}
+
+jboolean testReleaseAlias(JNIEnv*, jobject self) {
+ auto local = adopt_local(self);
+ auto alias = local.releaseAlias();
+
+ EXPECT(typeid(alias) == typeid(alias_ref));
+ EXPECT(isSameObject(self, alias.get()));
+
+ return JNI_TRUE;
+}
+
+jboolean testLockingWeakReferences(JNIEnv*, jobject self) {
+ auto weak = make_weak(self);
+ auto local = weak.lockLocal();
+ auto global = weak.lockGlobal();
+
+ EXPECT(typeid(local) == typeid(local_ref));
+ EXPECT(typeid(global) == typeid(global_ref));
+ EXPECT(self == local);
+ EXPECT(self == global);
+
+ return JNI_TRUE;
+}
+
+jboolean TestFieldAccess(alias_ref self, const std::string& field_name,
+ jint oldval, jint newval) {
+ auto cls = self->getClass();
+ auto fld = cls->getField(field_name.c_str());
+ auto method = cls->getMethod("bar");
+
+ if (method(self.get(), 17) != 42) {
+ return JNI_FALSE;
+ }
+
+ if (method(self, 17) != 42) {
+ return JNI_FALSE;
+ }
+
+ if (self->getFieldValue(fld) != oldval) {
+ return JNI_FALSE;
+ }
+
+ self->setFieldValue(fld, newval);
+
+ return JNI_TRUE;
+}
+
+jboolean TestStringFieldAccess(
+ JNIEnv* env,
+ jobject self,
+ jstring field_name,
+ jstring oldval,
+ jstring newval) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto fld = cls->getField(ToString(env, field_name).c_str());
+ auto oldvalStr = adopt_local(oldval)->toStdString();
+
+ auto curvalRef = me->getFieldValue(fld);
+ if (curvalRef->toStdString() != oldvalStr) {
+ return JNI_FALSE;
+ }
+
+ const alias_ref cme = me;
+ if (cme->getFieldValue(fld)->toStdString() != oldvalStr) {
+ return JNI_FALSE;
+ }
+
+ me->setFieldValue(fld, newval);
+
+ return JNI_TRUE;
+}
+
+jboolean TestReferenceFieldAccess(
+ alias_ref self,
+ std::string const& field_name,
+ jobject oldval,
+ jobject newval,
+ jboolean useWrapper) {
+ auto cls = self->getClass();
+ auto rawfld = cls->getField(field_name.c_str(), TestThing::kJavaDescriptor);
+
+ if (self->getFieldValue(rawfld) != oldval) {
+ return JNI_FALSE;
+ }
+
+ alias_ref const cself = self;
+ if (cself->getFieldValue(rawfld) != oldval) {
+ return JNI_FALSE;
+ }
+
+ if (useWrapper) {
+ auto newvalRef = adopt_local(static_cast(newval));
+ auto fld = cls->getField(field_name.c_str());
+ self->setFieldValue(fld, newvalRef);
+ } else {
+ self->setFieldValue(rawfld, newval);
+ }
+
+ return JNI_TRUE;
+}
+
+jboolean TestStaticFieldAccess(
+ JNIEnv* env,
+ jobject self,
+ jstring field_name,
+ jint oldval,
+ jint newval) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto fld = cls->getStaticField(ToString(env, field_name).c_str());
+
+ if (cls->getStaticFieldValue(fld) != oldval) {
+ return JNI_FALSE;
+ }
+ cls->setStaticFieldValue(fld, newval);
+ return JNI_TRUE;
+}
+
+jboolean TestStaticStringFieldAccess(
+ JNIEnv* env,
+ jobject self,
+ jstring field_name,
+ jstring oldval,
+ jstring newval) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto fld = cls->getStaticField(ToString(env, field_name).c_str());
+
+ auto curvalRef = cls->getStaticFieldValue(fld);
+ if (curvalRef->toStdString() != adopt_local(oldval)->toStdString()) {
+ return JNI_FALSE;
+ }
+ cls->setStaticFieldValue(fld, newval);
+ return JNI_TRUE;
+}
+
+jboolean TestStaticReferenceFieldAccess(
+ alias_ref self,
+ std::string const& field_name,
+ jobject oldval,
+ jobject newval,
+ jboolean useWrapper) {
+ auto cls = self->getClass();
+ auto rawfld = cls->getStaticField(field_name.c_str(), TestThing::kJavaDescriptor);
+
+ auto curvalRef = cls->getStaticFieldValue(rawfld);
+ if (curvalRef != oldval) {
+ return JNI_FALSE;
+ }
+
+ if (useWrapper) {
+ auto newvalRef = adopt_local(static_cast(newval));
+ auto fld = cls->getStaticField(field_name.c_str());
+ cls->setStaticFieldValue(fld, newvalRef);
+ } else {
+ cls->setStaticFieldValue(rawfld, newval);
+ }
+
+ return JNI_TRUE;
+}
+
+jboolean TestNonVirtualMethod(JNIEnv* env, jobject self, jboolean s) {
+ auto me = adopt_local(self);
+ if (!me) {
+ return JNI_FALSE;
+ }
+
+ auto cls = me->getClass();
+ if (!cls) {
+ return JNI_FALSE;
+ }
+ auto method = cls->getNonvirtualMethod("nonVirtualMethod");
+
+ jclass jcls = env->FindClass("com/facebook/jni/FBJniTests");
+
+ return method(self, jcls, s);
+}
+
+jtypeArray
+TestArrayCreation(JNIEnv* env, jobject self, jstring s0, jstring s1, jstring s2) {
+ auto array = JArrayClass::newArray(3);
+ array->setElement(0, s0);
+ array->setElement(1, s1);
+ array->setElement(2, s2);
+ return static_cast>(array.release());
+}
+
+jtypeArray>
+TestMultidimensionalObjectArray(JNIEnv* env, jobject self, jstring s0, jstring s1, jstring s2) {
+ auto array = JArrayClass>::newArray(2);
+ auto row = JArrayClass::newArray(2);
+ row->setElement(0, s0);
+ row->setElement(1, s1);
+ (*array)[0] = row;
+ row = JArrayClass::newArray(1);
+ row->setElement(0, s2);
+ (*array)[1] = row;
+ return array.release();
+}
+
+jtypeArray
+TestMultidimensionalPrimitiveArray(JNIEnv* env, jobject self, jint i0, jint i1, jint i2) {
+ auto array = JArrayClass::newArray(2);
+ auto row = JArrayInt::newArray(2);
+ row->setRegion(0, 1, &i0);
+ row->setRegion(1, 1, &i1);
+ (*array)[0] = row;
+ row = JArrayInt::newArray(1);
+ row->setRegion(0, 1, &i2);
+ (*array)[1] = row;
+ return array.release();
+}
+
+jstring TestBuildStringArray(JNIEnv* env, jobject self, jtypeArray input) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod)>("captureStringArray");
+
+ auto niceInput = adopt_local_array(input);
+ auto length = niceInput->size();
+ auto inputCopy = JArrayClass::newArray(length);
+ for (size_t idx = 0; idx < length; idx++) {
+ switch (idx % 3) {
+ case 0: {
+ // Verify that assignment from a T works.
+ jstring value = (jstring)env->GetObjectArrayElement(input, idx);
+ (*inputCopy)[idx] = value; // Assignment from actual type.
+ env->DeleteLocalRef(value);
+ break;
+ }
+ case 1: {
+ // Verify that direct assignment from an ElementProxy works.
+ (*inputCopy)[idx] = (*niceInput)[idx];
+ break;
+ }
+ case 2:
+ default: {
+ // Verify that assignment from a smart reference works.
+ auto smartRef = adopt_local((*niceInput)[idx]);
+ (*inputCopy)[idx] = smartRef;
+ break;
+ }
+ }
+ }
+
+ return method(self, inputCopy.get()).release();
+}
+
+template
+void tryResolveMethodWithCxxTypes(std::string sig, alias_ref me, std::string methodName, Args... args) {
+ auto cls = me->getClass();
+ auto method = cls->getMethod(methodName.c_str());
+ if (!method) throw std::runtime_error("method lookup failed with signature=" + sig);
+ try {
+ method(me, args...);
+ } catch (std::exception&) {
+ throw std::runtime_error("calling method failed with signature=" + sig);
+ }
+
+ auto nonVirtualMethod = cls->getNonvirtualMethod(methodName.c_str());
+ if (!nonVirtualMethod) throw std::runtime_error("method lookup failed with signature=" + sig);
+ try {
+ nonVirtualMethod(me, cls.get(), args...);
+ } catch (std::exception&) {
+ throw std::runtime_error("calling method failed with signature=" + sig);
+ }
+
+ auto staticMethod = cls->getStaticMethod((methodName + "Static").c_str());
+ if (!staticMethod) throw std::runtime_error("static method lookup failed with signature=" + sig);
+ try {
+ staticMethod(cls, args...);
+ } catch (std::exception&) {
+ throw std::runtime_error("calling static method failed with signature=" + sig);
+ }
+}
+
+// Simple utility to give us a good error message.
+#define runTest(sig, ...) \
+ tryResolveMethodWithCxxTypes(#sig, self, method, __VA_ARGS__);
+
+void TestMethodResolutionWithCxxTypes(alias_ref self, alias_ref jmethod, alias_ref str, jlong v) {
+ auto method = jmethod->toStdString();
+ runTest(jobject(jstring, jlong), str.get(), v);
+ runTest(local_ref(jstring, jlong), str.get(), v);
+
+ runTest(jobject(local_ref, jlong), make_local(str), v);
+ runTest(jobject(alias_ref, jlong), str, v);
+
+ runTest(jobject(alias_ref, int64_t), str, (int64_t)v);
+ runTest(jobject(alias_ref, long long), str, (long long)v);
+
+ runTest(jobject(const char*, int64_t), str->toStdString().c_str(), (int64_t)v);
+
+ method = jmethod->toStdString() + "Void";
+ runTest(void(jstring, int64_t), str.get(), v);
+
+ method = jmethod->toStdString() + "Int";
+ runTest(jint(jstring, int64_t), str.get(), v);
+}
+
+#undef runTest
+
+void TestHandleJavaCustomException(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("customExceptionThrower");
+
+ method(self);
+}
+
+void TestHandleNullExceptionMessage(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("nullMessageThrower");
+
+ try {
+ method(self);
+ } catch (const std::exception& ex) {
+ ex.what();
+ }
+}
+
+void TestHandleNestedException(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("customExceptionThrower");
+
+ try {
+ try {
+ method(self);
+ } catch (...) {
+ std::throw_with_nested(std::runtime_error("middle"));
+ }
+ } catch (...) {
+ std::throw_with_nested(std::out_of_range("outer"));
+ }
+}
+
+void TestHandleNoRttiException(JNIEnv* env, jobject self) {
+ nortti::throwException();
+}
+
+jstring TestCopyConstructor(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("customExceptionThrower");
+
+ try {
+ method(self);
+ return env->NewStringUTF("method did not throw");
+ } catch (JniException ex) { // No & -- we're intentionally invoking the copy constructor.
+ return env->NewStringUTF(ex.what());
+ }
+}
+
+jstring TestMoveConstructorWithEmptyWhat(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("customExceptionThrower");
+
+ try {
+ method(self);
+ return env->NewStringUTF("method did not throw");
+ } catch (JniException& ex) {
+ auto replacement = JniException(std::move(ex));
+ return env->NewStringUTF(replacement.what());
+ }
+}
+
+jstring TestMoveConstructorWithPopulatedWhat(JNIEnv* env, jobject self) {
+ auto me = adopt_local(self);
+ auto cls = me->getClass();
+ auto method = cls->getMethod("customExceptionThrower");
+
+ try {
+ method(self);
+ return env->NewStringUTF("method did not throw");
+ } catch (JniException& ex) {
+ ex.what();
+ auto replacement = JniException(std::move(ex));
+ return env->NewStringUTF(replacement.what());
+ }
+}
+
+void TestHandleCppRuntimeError(JNIEnv* env, jobject self, jstring message) {
+ throw std::runtime_error(ToString(env, message));
+}
+
+void TestHandleCppIOBaseFailure(JNIEnv* env, jobject self) {
+ throw std::ios_base::failure("A C++ IO base failure.");
+}
+
+void TestHandleCppSystemError(JNIEnv* env, jobject self) {
+ // Throw a sample std::system_error
+ throw std::system_error(EFAULT, std::system_category());
+}
+
+void TestInterDsoExceptionHandlingA(JNIEnv* env, jobject self) {
+ inter_dso_exception_test_2a();
+}
+
+jboolean TestInterDsoExceptionHandlingB(JNIEnv* env, jobject self) {
+ return inter_dso_exception_test_2b();
+}
+
+void TestHandleCppIntThrow(JNIEnv* env, jobject self) {
+ throw 42;
+}
+
+void TestHandleCppCharPointerThrow(JNIEnv* env, jobject self) {
+ throw "Some random message";
+}
+
+void TestThrowJavaExceptionByName(JNIEnv* env, jobject self) {
+ throwNewJavaException("java/lang/IllegalArgumentException", "bad news: %s", "it didn't work");
+}
+
+jint TestJThread(JNIEnv* env, jobject self) {
+ jint i = -1;
+ auto thread = JThread::create([&] {
+ i = 0;
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ i = 1;
+ });
+ thread->start();
+ thread->join();
+ return i;
+}
+
+// Global for simpleWorker tests. Relies on thread sync points (constructor, join) for "locking".
+jint gWorkerValue;
+
+void simpleWorker(jobject grefSelf, jdouble input) {
+ auto attachGuard = ThreadScope(); // This tests the move constructor.
+ auto self = adopt_global(grefSelf);
+ // Claw up from the object to avoid classloader issues.
+ auto barMethod = self->getClass()->getMethod("bar");
+ gWorkerValue = barMethod(self.get(), input);
+}
+
+void nestedSimpleWorker(jobject grefSelf, jdouble input) {
+ ThreadScope attachGuard; // More efficient version of guard; no move constructor required.
+ simpleWorker(grefSelf, input);
+}
+
+jint TestThreadScopeGuard(JNIEnv* env, jobject self, jdouble input) {
+ // Turn self into a global reference before passing it to a working thread.
+ auto grefSelf = make_global(adopt_local(self));
+ auto childThread = std::thread(simpleWorker, grefSelf.release(), input);
+ childThread.join();
+ return gWorkerValue;
+}
+
+jint TestNestedThreadScopeGuard(JNIEnv* env, jobject self, jdouble input) {
+ // Turn self into a global reference before passing it to a working thread.
+ auto grefSelf = make_global(adopt_local(self));
+ auto childThread = std::thread(nestedSimpleWorker, grefSelf.release(), input);
+ childThread.join();
+ return gWorkerValue;
+}
+
+void classLoadWorker() {
+ gWorkerValue = 0;
+ try {
+ // This should fail because we aren't attached.
+ Callbacks::javaClassLocal();
+ gWorkerValue = -1;
+ return;
+ } catch (std::exception& e) {
+ // ignored
+ }
+ try {
+ ThreadScope::WithClassLoader([&] {
+ // This should now succeed.
+ Callbacks::javaClassLocal();
+ gWorkerValue = 1;
+ });
+ } catch (std::exception& e) {
+ gWorkerValue = -2;
+ // Catch this and log it so that we get a test failure instead of a crash.
+ FBJNI_LOGE("%s", e.what());
+ }
+}
+
+jint TestClassLoadInWorker(JNIEnv* env, jobject self) {
+ std::thread t(classLoadWorker);
+ t.join();
+ return gWorkerValue;
+}
+
+jint TestClassLoadWorkerFastPath(JNIEnv* env, jobject self) {
+ jint i = 0;
+ ThreadScope::WithClassLoader([&] {
+ // Execute on the fast path
+ Callbacks::javaClassLocal();
+ i += 1;
+ });
+
+ std::thread t([&] {
+ ThreadScope::WithClassLoader([&] {
+ // Execute on the slow path
+ Callbacks::javaClassLocal();
+ i += 1;
+ });
+ });
+ t.join();
+
+ std::thread t2([&] {
+ ThreadScope scope;
+ ThreadScope::WithClassLoader([&] {
+ // Execute on the slow path even though thread is already attached.
+ Callbacks::javaClassLocal();
+ i += 1;
+ });
+ });
+ t2.join();
+
+ return i;
+}
+
+void testNewObject(JNIEnv*, jobject self) {
+ // This is a compilation only test, verifies that all the types work out.
+ auto cls = findClassLocal("java/lang/String");
+ auto ctr = cls->getConstructor();
+ local_ref obj = cls->newObject(ctr);
+ auto str = obj->toStdString();
+ Use(str);
+}
+
+template
+static jboolean copyAndVerify(T& orig) {
+ T copy{orig};
+ EXPECT(orig == copy);
+
+ return JNI_TRUE;
+}
+
+template
+static jboolean assignAndVerify(T& orig) {
+ T copy{};
+ copy = orig;
+ EXPECT(orig == copy);
+
+ return JNI_TRUE;
+}
+
+jboolean testNullReferences(JNIEnv*, jobject) {
+ jobject nullobject = nullptr;
+
+ auto local = local_ref{};
+ EXPECT(!local);
+
+ auto localWrap = adopt_local(nullobject);
+ EXPECT(!localWrap);
+
+ auto localMake = make_local(local);
+ EXPECT(!localMake);
+ EXPECT_SAME(local, localWrap, localMake);
+
+ auto global = global_ref{};
+ EXPECT(!global);
+
+ auto globalWrap = adopt_global(nullobject);
+ EXPECT(!globalWrap);
+
+ auto globalMake = make_global(global);
+ EXPECT(!globalMake);
+ EXPECT_SAME(global, globalWrap, globalMake);
+
+ weak_ref weak_global = weak_ref{};
+ EXPECT(!weak_global.lockLocal());
+
+ weak_ref weak_globalWrap = adopt_weak_global(nullobject);
+ EXPECT(!weak_globalWrap.lockLocal());
+ EXPECT(weak_global.lockLocal() == weak_globalWrap.lockGlobal());
+ EXPECT(!make_local(adopt_local(nullobject)));
+ EXPECT(!make_global(nullobject));
+
+ return JNI_TRUE;
+}
+
+jboolean testCreatingReferences(JNIEnv*, jobject self) {
+ auto a = wrap_alias(self);
+ auto l = adopt_local(self);
+ auto g = make_global(l);
+ auto w = make_weak(l);
+
+ EXPECT(a == l && a == g && a == w.lockLocal());
+
+ auto lp = make_local(self);
+ auto la = make_local(a);
+ auto ll = make_local(l);
+ auto lg = make_local(g);
+
+ EXPECT(a == lp && a == la && a == ll && a == lg);
+
+ auto gp = make_global(self);
+ auto ga = make_global(a);
+ auto gl = make_global(l);
+ auto gg = make_global(g);
+
+ EXPECT(a == gp && a == ga && a == gl && a == gg);
+
+ return JNI_TRUE;
+}
+
+jboolean testAssignmentAndCopyConstructors(JNIEnv*, jobject self) {
+ using facebook::jni::internal::g_reference_stats;
+
+ g_reference_stats.reset();
+ {
+ // Wrapping existing local that should be deleted (locals = 1)
+ auto local = adopt_local(self);
+ // Copy constructor (locals = 2)
+ EXPECT(copyAndVerify(local));
+
+ // Assignment (locals = 3)
+ EXPECT(assignAndVerify(local));
+
+ // Creating a new global (globals = 1)
+ auto global = make_global(local);
+ // Copy constructor (globals = 2)
+ EXPECT(copyAndVerify(global));
+
+ // Assignment (globals = 3)
+ EXPECT(assignAndVerify(global));
+
+ // Creating a new weak (weaks = 1)
+ auto weak = make_weak(local);
+ // Copy constructor (weaks = 2, globals = 5)
+ weak_ref weakCopy{weak};
+ EXPECT(weak.lockGlobal() == weakCopy.lockGlobal());
+
+ // Assignment (weaks = 3, globals = 7)
+ weakCopy = weak;
+ EXPECT(weak.lockGlobal() == weakCopy.lockGlobal());
+
+ auto alias = alias_ref{local};
+ alias_ref aliasCopy{alias};
+ EXPECT(alias == aliasCopy);
+
+ aliasCopy = alias;
+ EXPECT(alias == aliasCopy);
+
+ alias = self;
+ alias = global;
+ // alias = weak; // Should not compile
+ }
+
+ FBJNI_LOGE("locals: %d", g_reference_stats.locals_deleted.load());
+ FBJNI_LOGE("globals: %d", g_reference_stats.globals_deleted.load());
+ FBJNI_LOGE("weaks: %d", g_reference_stats.weaks_deleted.load());
+
+ EXPECT(g_reference_stats.locals_deleted == 3 &&
+ g_reference_stats.globals_deleted == 7 &&
+ g_reference_stats.weaks_deleted == 3);
+
+ return JNI_TRUE;
+}
+
+template class RefType, typename T>
+static jboolean copyAndVerifyCross(RefType& orig) {
+ RefType> reprCopy{orig};
+ RefType> jniCopy{orig};
+ EXPECT(orig == reprCopy);
+ EXPECT(orig == jniCopy);
+ return JNI_TRUE;
+}
+
+template class RefType, typename T>
+static jboolean assignAndVerifyCross(RefType& orig) {
+ RefType> reprCopy{};
+ reprCopy = orig;
+ RefType> jniCopy{};
+ jniCopy = orig;
+ EXPECT(orig == reprCopy);
+ EXPECT(orig == jniCopy);
+ return JNI_TRUE;
+}
+
+template class RefType, typename T>
+static jboolean verifyMakeCross(RefType& orig) {
+ RefType> copy{orig};
+ {
+ local_ref local = make_local(copy);
+ global_ref global = make_global(copy);
+ weak_ref weak = make_weak(copy);
+ }
+
+ {
+ local_ref> local = make_local(copy);
+ global_ref> global = make_global(copy);
+ weak_ref> weak = make_weak(copy);
+ }
+
+ {
+ local_ref local = make_local(orig);
+ global_ref global = make_global(orig);
+ weak_ref weak = make_weak(orig);
+ }
+
+ {
+ local_ref> local = make_local(orig);
+ global_ref> global = make_global(orig);
+ weak_ref> weak = make_weak(orig);
+ }
+
+ return JNI_TRUE;
+}
+
+
+jboolean testAssignmentAndCopyCrossTypes(JNIEnv*, jobject self) {
+ using facebook::jni::internal::g_reference_stats;
+
+ size_t locals = 0, globals = 0, weaks = 0;
+ g_reference_stats.reset();
+ {
+ auto local = adopt_local(self);
+ locals += 1;
+
+ EXPECT(copyAndVerifyCross(local));
+ locals += 2;
+
+ EXPECT(assignAndVerifyCross(local));
+ locals += 2;
+
+ EXPECT(verifyMakeCross(local));
+ locals += 1;
+ locals += 6;
+ globals += 6;
+ weaks += 6;
+
+ auto global = make_global(local);
+ globals += 1;
+
+ EXPECT(copyAndVerifyCross(global));
+ globals += 2;
+
+ EXPECT(assignAndVerifyCross(global));
+ globals += 2;
+
+ auto weak = make_weak(local);
+ weaks += 1;
+
+ weak_ref weakCopy{weak};
+ weaks += 1;
+
+ EXPECT(weak.lockGlobal() == weakCopy.lockGlobal());
+ globals += 3; // One extra required as the two globals are different types.
+
+ weakCopy = weak;
+ weaks += 1;
+ EXPECT(weak.lockGlobal() == weakCopy.lockGlobal());
+ globals += 3; // One extra required as the two globals are different types.
+
+ auto alias = alias_ref{local};
+ alias_ref{local};
+
+ alias_ref aliasCopy{alias};
+ EXPECT(alias == aliasCopy);
+
+ aliasCopy = alias;
+ alias = aliasCopy;
+ EXPECT(alias == aliasCopy);
+
+ alias = self;
+ alias = global;
+ // alias = weak; // Should not compile
+ }
+
+ FBJNI_LOGE("locals: %d, expected: %d", g_reference_stats.locals_deleted.load(), locals);
+ FBJNI_LOGE("globals: %d, expected: %d", g_reference_stats.globals_deleted.load(), globals);
+ FBJNI_LOGE("weaks: %d, expected: %d", g_reference_stats.weaks_deleted.load(), weaks);
+
+ EXPECT(g_reference_stats.locals_deleted == locals &&
+ g_reference_stats.globals_deleted == globals &&
+ g_reference_stats.weaks_deleted == weaks);
+
+ return JNI_TRUE;
+
+}
+
+jboolean testToString(JNIEnv* env, jobject self) {
+ auto dateClass = findClassLocal("java/util/Date");
+ auto dateConstructor = dateClass->getConstructor();
+ auto date = dateClass->newObject(dateConstructor);
+
+ auto objectClass = findClassLocal("java/lang/Object");
+ auto objectConstructor = objectClass->getConstructor();
+ auto object = objectClass->newObject(objectConstructor);
+
+ // First call the date implementation of toString
+ auto dateString = date->toString();
+ // And ensure that we don't use Date's toString method when calling
+ // toString on the object. If this doesn't crash we are fine.
+ auto objectString = object->toString();
+
+ return JNI_TRUE;
+}
+
+jboolean testCriticalNativeMethodBindsAndCanBeInvoked(jint a, jfloat b) {
+ return JNI_TRUE;
+}
+
+// These implicit nullptr tests aren't called, the test is that it
+// compiles.
+alias_ref returnNullAliasRef() {
+ return nullptr;
+}
+
+local_ref returnNullLocalRef() {
+ return nullptr;
+}
+
+global_ref returnNullGlobalRef() {
+ return nullptr;
+}
+
+void takesAliasRef(alias_ref) {}
+void takesLocalRef(local_ref) {}
+void takesGlobalRef(global_ref) {}
+
+void callWithNullRefs() {
+ takesAliasRef(nullptr);
+ takesLocalRef(nullptr);
+ takesGlobalRef(nullptr);
+}
+
+struct SomeJavaFoo;
+struct OtherJavaFoo;
+
+struct SomeJavaFoo : JavaClass {
+ // Ensure that smart references can be used in declarations using forward-declared types.
+ alias_ref call(alias_ref);
+ static alias_ref funcWithForwardDeclaredRefs(local_ref foo);
+};
+
+
+using sjf = SomeJavaFoo::javaobject;
+
+static_assert(IsNonWeakReference>(), "");
+static_assert(IsNonWeakReference>(), "");
+static_assert(IsNonWeakReference>(), "");
+static_assert(IsNonWeakReference(), "");
+static_assert(IsNonWeakReference(), "");
+static_assert(IsNonWeakReference(), "");
+static_assert(IsNonWeakReference(), "");
+
+static_assert(!IsNonWeakReference>(), "");
+static_assert(!IsNonWeakReference>(), "");
+static_assert(!IsNonWeakReference(), "");
+static_assert(!IsNonWeakReference(), "");
+static_assert(!IsNonWeakReference(), "");
+static_assert(!IsNonWeakReference(), "");
+
+
+static_assert(IsAnyReference>(), "");
+static_assert(IsAnyReference>(), "");
+static_assert(IsAnyReference>(), "");
+static_assert(IsAnyReference(), "");
+static_assert(IsAnyReference(), "");
+static_assert(IsAnyReference(), "");
+static_assert(IsAnyReference(), "");
+static_assert(IsAnyReference>(), "");
+static_assert(IsAnyReference>(), "");
+
+static_assert(!IsAnyReference(), "");
+static_assert(!IsAnyReference