Expose methods of persistent yoga for Java
Summary: This diff extends the JNI version of yoga in order to allow Java instances of the YogaConfig class to receive a callback when a Yoga node is cloned. Reviewed By: priteshrnandgaonkar Differential Revision: D6918605 fbshipit-source-id: e424c78680c04e21154ebe21405671c4e90f6529
This commit is contained in:
committed by
Facebook Github Bot
parent
3ec41b656f
commit
b1222bf83e
@@ -23,6 +23,7 @@ public class YogaConfig {
|
|||||||
|
|
||||||
long mNativePointer;
|
long mNativePointer;
|
||||||
private YogaLogger mLogger;
|
private YogaLogger mLogger;
|
||||||
|
private YogaNodeClonedFunction mNodeClonedFunction;
|
||||||
|
|
||||||
private native long jni_YGConfigNew();
|
private native long jni_YGConfigNew();
|
||||||
public YogaConfig() {
|
public YogaConfig() {
|
||||||
@@ -80,4 +81,17 @@ public class YogaConfig {
|
|||||||
public YogaLogger getLogger() {
|
public YogaLogger getLogger() {
|
||||||
return mLogger;
|
return mLogger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private native void jni_YGConfigSetHasNodeClonedFunc(long nativePointer, boolean hasClonedFunc);
|
||||||
|
|
||||||
|
public void setOnNodeCloned(YogaNodeClonedFunction nodeClonedFunction) {
|
||||||
|
mNodeClonedFunction = nodeClonedFunction;
|
||||||
|
jni_YGConfigSetHasNodeClonedFunc(mNativePointer, nodeClonedFunction != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public final void onNodeCloned(
|
||||||
|
YogaNode oldNode, YogaNode newNode, YogaNode parent, int childIndex) {
|
||||||
|
mNodeClonedFunction.onNodeCloned(oldNode, newNode, parent, childIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
java/com/facebook/yoga/YogaNodeClonedFunction.java
Normal file
19
java/com/facebook/yoga/YogaNodeClonedFunction.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.yoga;
|
||||||
|
|
||||||
|
import com.facebook.proguard.annotations.DoNotStrip;
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
public interface YogaNodeClonedFunction {
|
||||||
|
|
||||||
|
@DoNotStrip
|
||||||
|
void onNodeCloned(YogaNode oldNode, YogaNode newNode, YogaNode parent, int childIndex);
|
||||||
|
}
|
@@ -19,6 +19,22 @@ struct JYogaNode : public JavaClass<JYogaNode> {
|
|||||||
static constexpr auto kJavaDescriptor = "Lcom/facebook/yoga/YogaNode;";
|
static constexpr auto kJavaDescriptor = "Lcom/facebook/yoga/YogaNode;";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct JYogaConfig : public JavaClass<JYogaConfig> {
|
||||||
|
static constexpr auto kJavaDescriptor = "Lcom/facebook/yoga/YogaConfig;";
|
||||||
|
};
|
||||||
|
|
||||||
|
struct YGConfigContext {
|
||||||
|
global_ref<jobject>* logger;
|
||||||
|
global_ref<jobject>* config;
|
||||||
|
YGConfigContext() : logger(nullptr), config(nullptr) {}
|
||||||
|
~YGConfigContext() {
|
||||||
|
delete config;
|
||||||
|
config = nullptr;
|
||||||
|
delete logger;
|
||||||
|
logger = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static inline weak_ref<JYogaNode> *YGNodeJobject(YGNodeRef node) {
|
static inline weak_ref<JYogaNode> *YGNodeJobject(YGNodeRef node) {
|
||||||
return reinterpret_cast<weak_ref<JYogaNode>*>(node->getContext());
|
return reinterpret_cast<weak_ref<JYogaNode>*>(node->getContext());
|
||||||
}
|
}
|
||||||
@@ -118,11 +134,39 @@ static float YGJNIBaselineFunc(YGNodeRef node, float width, float height) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static YGSize YGJNIMeasureFunc(YGNodeRef node,
|
static void YGJNIOnNodeClonedFunc(
|
||||||
float width,
|
YGNodeRef oldNode,
|
||||||
YGMeasureMode widthMode,
|
YGNodeRef newNode,
|
||||||
float height,
|
YGNodeRef parent,
|
||||||
YGMeasureMode heightMode) {
|
int childIndex) {
|
||||||
|
auto config = oldNode->getConfig();
|
||||||
|
if (!config) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static auto onNodeClonedFunc = findClassStatic("com/facebook/yoga/YogaConfig")
|
||||||
|
->getMethod<void(
|
||||||
|
local_ref<JYogaNode>,
|
||||||
|
local_ref<JYogaNode>,
|
||||||
|
local_ref<JYogaNode>,
|
||||||
|
jint)>("onNodeCloned");
|
||||||
|
|
||||||
|
auto context = reinterpret_cast<YGConfigContext*>(YGConfigGetContext(config));
|
||||||
|
auto javaConfig = context->config;
|
||||||
|
|
||||||
|
onNodeClonedFunc(
|
||||||
|
javaConfig->get(),
|
||||||
|
YGNodeJobject(oldNode)->lockLocal(),
|
||||||
|
YGNodeJobject(newNode)->lockLocal(),
|
||||||
|
YGNodeJobject(parent)->lockLocal(),
|
||||||
|
childIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static YGSize YGJNIMeasureFunc(
|
||||||
|
YGNodeRef node,
|
||||||
|
float width,
|
||||||
|
YGMeasureMode widthMode,
|
||||||
|
float height,
|
||||||
|
YGMeasureMode heightMode) {
|
||||||
if (auto obj = YGNodeJobject(node)->lockLocal()) {
|
if (auto obj = YGNodeJobject(node)->lockLocal()) {
|
||||||
static auto measureFunc = findClassStatic("com/facebook/yoga/YogaNode")
|
static auto measureFunc = findClassStatic("com/facebook/yoga/YogaNode")
|
||||||
->getMethod<jlong(jfloat, jint, jfloat, jint)>("measure");
|
->getMethod<jlong(jfloat, jint, jfloat, jint)>("measure");
|
||||||
@@ -396,6 +440,10 @@ jlong jni_YGConfigNew(alias_ref<jobject>) {
|
|||||||
|
|
||||||
void jni_YGConfigFree(alias_ref<jobject>, jlong nativePointer) {
|
void jni_YGConfigFree(alias_ref<jobject>, jlong nativePointer) {
|
||||||
const YGConfigRef config = _jlong2YGConfigRef(nativePointer);
|
const YGConfigRef config = _jlong2YGConfigRef(nativePointer);
|
||||||
|
auto context = reinterpret_cast<YGConfigContext*>(YGConfigGetContext(config));
|
||||||
|
if (context) {
|
||||||
|
delete context;
|
||||||
|
}
|
||||||
YGConfigFree(config);
|
YGConfigFree(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,19 +478,48 @@ void jni_YGConfigSetUseLegacyStretchBehaviour(alias_ref<jobject>,
|
|||||||
YGConfigSetUseLegacyStretchBehaviour(config, useLegacyStretchBehaviour);
|
YGConfigSetUseLegacyStretchBehaviour(config, useLegacyStretchBehaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jni_YGConfigSetLogger(alias_ref<jobject>, jlong nativePointer, alias_ref<jobject> logger) {
|
void jni_YGConfigSetHasNodeClonedFunc(
|
||||||
|
alias_ref<jobject> thiz,
|
||||||
|
jlong nativePointer,
|
||||||
|
jboolean hasNodeClonedFunc) {
|
||||||
const YGConfigRef config = _jlong2YGConfigRef(nativePointer);
|
const YGConfigRef config = _jlong2YGConfigRef(nativePointer);
|
||||||
|
auto context = reinterpret_cast<YGConfigContext*>(YGConfigGetContext(config));
|
||||||
|
if (context && context->config) {
|
||||||
|
delete context->config;
|
||||||
|
context->config = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto context = YGConfigGetContext(config);
|
if (hasNodeClonedFunc) {
|
||||||
if (context) {
|
if (!context) {
|
||||||
delete reinterpret_cast<global_ref<jobject> *>(context);
|
context = new YGConfigContext();
|
||||||
|
YGConfigSetContext(config, context);
|
||||||
|
}
|
||||||
|
context->config = new global_ref<jobject>(make_global(thiz));
|
||||||
|
YGConfigSetNodeClonedFunc(config, YGJNIOnNodeClonedFunc);
|
||||||
|
} else {
|
||||||
|
YGConfigSetNodeClonedFunc(config, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jni_YGConfigSetLogger(
|
||||||
|
alias_ref<jobject>,
|
||||||
|
jlong nativePointer,
|
||||||
|
alias_ref<jobject> logger) {
|
||||||
|
const YGConfigRef config = _jlong2YGConfigRef(nativePointer);
|
||||||
|
auto context = reinterpret_cast<YGConfigContext*>(YGConfigGetContext(config));
|
||||||
|
if (context && context->logger) {
|
||||||
|
delete context->logger;
|
||||||
|
context->logger = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (logger) {
|
if (logger) {
|
||||||
YGConfigSetContext(config, new global_ref<jobject>(make_global(logger)));
|
if (!context) {
|
||||||
|
context = new YGConfigContext();
|
||||||
|
YGConfigSetContext(config, context);
|
||||||
|
}
|
||||||
|
context->logger = new global_ref<jobject>(make_global(logger));
|
||||||
YGConfigSetLogger(config, YGJNILogFunc);
|
YGConfigSetLogger(config, YGJNILogFunc);
|
||||||
} else {
|
} else {
|
||||||
YGConfigSetContext(config, NULL);
|
|
||||||
YGConfigSetLogger(config, NULL);
|
YGConfigSetLogger(config, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,6 +622,7 @@ jint JNI_OnLoad(JavaVM *vm, void *) {
|
|||||||
YGMakeNativeMethod(jni_YGConfigSetPointScaleFactor),
|
YGMakeNativeMethod(jni_YGConfigSetPointScaleFactor),
|
||||||
YGMakeNativeMethod(jni_YGConfigSetUseLegacyStretchBehaviour),
|
YGMakeNativeMethod(jni_YGConfigSetUseLegacyStretchBehaviour),
|
||||||
YGMakeNativeMethod(jni_YGConfigSetLogger),
|
YGMakeNativeMethod(jni_YGConfigSetLogger),
|
||||||
|
YGMakeNativeMethod(jni_YGConfigSetHasNodeClonedFunc),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -9,10 +9,11 @@
|
|||||||
|
|
||||||
package com.facebook.yoga;
|
package com.facebook.yoga;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class YogaNodeTest {
|
public class YogaNodeTest {
|
||||||
|
|
||||||
@@ -218,4 +219,32 @@ public class YogaNodeTest {
|
|||||||
assertTrue(YogaConstants.isUndefined(node.getBorder(edge)));
|
assertTrue(YogaConstants.isUndefined(node.getBorder(edge)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNodeClonedLeak() throws Exception {
|
||||||
|
YogaConfig config = new YogaConfig();
|
||||||
|
config.setOnNodeCloned(
|
||||||
|
new YogaNodeClonedFunction() {
|
||||||
|
@Override
|
||||||
|
public void onNodeCloned(
|
||||||
|
YogaNode oldNode, YogaNode newNode, YogaNode parent, int childIndex) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
config.setOnNodeCloned(null);
|
||||||
|
java.lang.ref.WeakReference<Object> ref = new java.lang.ref.WeakReference<Object>(config);
|
||||||
|
// noinspection UnusedAssignment
|
||||||
|
config = null;
|
||||||
|
// try and free for the next 5 seconds, usually it works after the
|
||||||
|
// first GC attempt.
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
System.gc();
|
||||||
|
if (ref.get() == null) {
|
||||||
|
// free successfully
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread.sleep(100);
|
||||||
|
}
|
||||||
|
fail("YogaConfig leaked");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user