Add exception handling in vanilla jni

Summary:
Exception handling in vanilla jni

## Changelog:
[Internal] [Added] Added exception handling for vanilla jni implementation in yoga

Reviewed By: amir-shalem

Differential Revision: D18036134

fbshipit-source-id: 965eaa2fddbc00b9ac0120b79678608e280d03db
This commit is contained in:
Sidharth Guglani
2019-10-22 10:45:20 -07:00
committed by Facebook Github Bot
parent 42bba10894
commit 688bd4ef72
3 changed files with 195 additions and 22 deletions

View File

@@ -352,6 +352,7 @@ static void jni_YGNodeCalculateLayoutJNI(
jlongArray nativePointers,
jobjectArray javaNodes) {
try {
void* layoutContext = nullptr;
auto map = PtrJNodeMapVanilla{};
if (nativePointers) {
@@ -371,6 +372,9 @@ static void jni_YGNodeCalculateLayoutJNI(
YGNodeStyleGetDirection(_jlong2YGNodeRef(nativePointer)),
layoutContext);
YGTransferLayoutOutputsRecursive(env, obj, root, layoutContext);
} catch (jthrowable throwable) {
env->Throw(throwable);
}
}
static void jni_YGNodeMarkDirtyJNI(

View File

@@ -62,12 +62,16 @@ void logErrorMessageAndDie(const char* message) {
}
void assertNoPendingJniException(JNIEnv* env) {
// This method cannot call any other method of the library, since other
// methods of the library use it to check for exceptions too
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
logErrorMessageAndDie("Aborting due to pending Java exception in JNI");
if (env->ExceptionCheck() == JNI_FALSE) {
return;
}
auto throwable = env->ExceptionOccurred();
if (!throwable) {
logErrorMessageAndDie("Unable to get pending JNI exception.");
}
env->ExceptionClear();
throw throwable;
}
} // namespace vanillajni

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.yoga;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class YogaExceptionTest {
@Parameterized.Parameters(name = "{0}")
public static Iterable<TestParametrization.NodeFactory> nodeFactories() {
return TestParametrization.nodeFactories();
}
@Parameterized.Parameter public TestParametrization.NodeFactory mNodeFactory;
@Test(expected = RuntimeException.class)
public void testBaselineThrows() {
final YogaNode root = createNode();
root.setFlexDirection(YogaFlexDirection.ROW);
root.setAlignItems(YogaAlign.BASELINE);
final YogaNode child1 = createNode();
root.addChildAt(child1, 0);
final YogaNode child2 = createNode();
child2.setBaselineFunction(new YogaBaselineFunction() {
public float baseline(YogaNode node, float width, float height) {
throw new RuntimeException();
}
});
root.addChildAt(child2, 1);
root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
@Test
public void testBaselineThrowsAndStops() {
final YogaNode root = createNode();
root.setFlexDirection(YogaFlexDirection.ROW);
root.setAlignItems(YogaAlign.BASELINE);
final YogaNode child1 = createNode();
root.addChildAt(child1, 0);
final YogaNode child2 = createNode();
final AtomicReference<Exception> expected = new AtomicReference();
child2.setBaselineFunction(new YogaBaselineFunction() {
public float baseline(YogaNode node, float width, float height) {
RuntimeException e = new RuntimeException();
expected.set(e);
throw e;
}
});
root.addChildAt(child2, 1);
final YogaNode child3 = createNode();
final AtomicBoolean child3Called = new AtomicBoolean();
child3.setBaselineFunction(new YogaBaselineFunction() {
public float baseline(YogaNode node, float width, float height) {
child3Called.set(true);
return 1.0f;
}
});
root.addChildAt(child3, 2);
try {
root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
fail();
} catch (RuntimeException e) {
assertEquals(expected.get(), e);
}
assertFalse(child3Called.get());
}
@Test(expected = RuntimeException.class)
public void testMeasureThrows() {
final YogaNode node = createNode();
node.setMeasureFunction(new YogaMeasureFunction() {
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
throw new RuntimeException();
}
});
node.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
}
@Test
public void testMeasureThrowsAndStops() {
final YogaNode root = createNode();
root.setFlexDirection(YogaFlexDirection.ROW);
root.setAlignItems(YogaAlign.BASELINE);
final YogaNode child1 = createNode();
root.addChildAt(child1, 0);
final YogaNode child2 = createNode();
final AtomicReference<Exception> expected = new AtomicReference();
child2.setMeasureFunction(new YogaMeasureFunction() {
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
RuntimeException e = new RuntimeException();
expected.set(e);
throw e;
}
});
root.addChildAt(child2, 1);
final YogaNode child3 = createNode();
final AtomicBoolean child3Called = new AtomicBoolean();
child3.setMeasureFunction(new YogaMeasureFunction() {
public long measure(
YogaNode node,
float width,
YogaMeasureMode widthMode,
float height,
YogaMeasureMode heightMode) {
child3Called.set(true);
return 1;
}
});
root.addChildAt(child3, 2);
try {
root.calculateLayout(YogaConstants.UNDEFINED, YogaConstants.UNDEFINED);
fail();
} catch (RuntimeException e) {
assertEquals(expected.get(), e);
}
assertFalse(child3Called.get());
}
private YogaNode createNode() {
return mNodeFactory.create();
}
private YogaNode createNode(YogaConfig config) {
return mNodeFactory.create(config);
}
}