diff --git a/java/BUCK b/java/BUCK index 4ddabcef..dbd2d335 100644 --- a/java/BUCK +++ b/java/BUCK @@ -29,9 +29,19 @@ yoga_cxx_library( ], ) +yoga_java_library( + name = "stubs", + srcs = glob(["stubs/**/*.java"]), + source = "1.7", + target = "1.7", +) + yoga_java_library( name = "java", srcs = glob(["com/facebook/yoga/*.java"]), + provided_deps = [ + ":stubs", + ], required_for_source_only_abi = True, source = "1.7", target = "1.7", diff --git a/java/build.gradle b/java/build.gradle index 21034e42..2ed33d0c 100644 --- a/java/build.gradle +++ b/java/build.gradle @@ -52,6 +52,7 @@ android { dependencies { compileOnly 'com.google.code.findbugs:jsr305:3.0.1' compileOnly project(':yoga:proguard-annotations') + compileOnly project(':yoga:stubs') implementation 'com.facebook.soloader:soloader:0.5.1' testImplementation 'junit:junit:4.12' } diff --git a/java/com/facebook/yoga/YogaNode.java b/java/com/facebook/yoga/YogaNode.java index 73305370..2924f0c3 100644 --- a/java/com/facebook/yoga/YogaNode.java +++ b/java/com/facebook/yoga/YogaNode.java @@ -23,6 +23,7 @@ public class YogaNode implements Cloneable { public static final int BYTE_BUFFER = 1; public static final int HYBRID = 2; + public static final int UNSAFE = 3; /** Get native instance count. Useful for testing only. */ static native int jni_YGNodeGetInstanceCount(); @@ -50,6 +51,9 @@ public class YogaNode implements Cloneable { case HYBRID: mDelegate = new YogaNodePropertiesHybrid(this); break; + case UNSAFE: + mDelegate = new YogaNodePropertiesUnsafe(this); + break; default: mDelegate = new YogaNodePropertiesJNI(this); } @@ -63,6 +67,9 @@ public class YogaNode implements Cloneable { case HYBRID: mDelegate = new YogaNodePropertiesHybrid(this, config); break; + case UNSAFE: + mDelegate = new YogaNodePropertiesUnsafe(this, config); + break; default: mDelegate = new YogaNodePropertiesJNI(this, config); } diff --git a/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java b/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java index daca67db..f189a5b4 100644 --- a/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java +++ b/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java @@ -31,16 +31,16 @@ public class YogaNodePropertiesByteBuffer implements YogaNodeProperties, Cloneab private static native ByteBuffer jni_getLayoutBuffer(long nativePointer); - private static native long jni_YGNodeNewByteBuffer(YogaNode node); + private static native long jni_YGNodeNewNoProps(YogaNode node); public YogaNodePropertiesByteBuffer(YogaNode node) { - this(jni_YGNodeNewByteBuffer(node)); + this(jni_YGNodeNewNoProps(node)); } - private static native long jni_YGNodeNewByteBufferWithConfig(YogaNode node, long configPointer); + private static native long jni_YGNodeNewNoPropsWithConfig(YogaNode node, long configPointer); public YogaNodePropertiesByteBuffer(YogaNode node, YogaConfig config) { - this(jni_YGNodeNewByteBufferWithConfig(node, config.mNativePointer)); + this(jni_YGNodeNewNoPropsWithConfig(node, config.mNativePointer)); } public YogaNodePropertiesByteBuffer(long nativePointer) { @@ -84,6 +84,7 @@ public class YogaNodePropertiesByteBuffer implements YogaNodeProperties, Cloneab @Override public void reset() { mHasBorderSet = false; + mHasNewLayout = true; jni_YGNodeReset(getNativePointer()); } diff --git a/java/com/facebook/yoga/YogaNodePropertiesUnsafe.java b/java/com/facebook/yoga/YogaNodePropertiesUnsafe.java new file mode 100644 index 00000000..f8af890b --- /dev/null +++ b/java/com/facebook/yoga/YogaNodePropertiesUnsafe.java @@ -0,0 +1,580 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * 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 com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; +import java.lang.reflect.Field; +import sun.misc.Unsafe; + +@DoNotStrip +public class YogaNodePropertiesUnsafe implements YogaNodeProperties { + + private static final int TRUE_BITS = 0x01000001; + private static final int FLOAT_SIZE = 4; + private static final int AUTO = YogaUnit.AUTO.intValue(); + private static final int POINT = YogaUnit.POINT.intValue(); + private static final int PERCENT = YogaUnit.PERCENT.intValue(); + private static final int UNDEFINED = YogaUnit.UNDEFINED.intValue(); + private static final int RTL = YogaDirection.RTL.intValue(); + private static final Unsafe UNSAFE; + + private final long mNativePointer; + private final long mStyleNativePointer; + private final long mLayoutNativePointer; + private boolean mHasBorderSet = false; + private boolean mHasNewLayout = true; + private boolean mIsFreed = false; + + static { + SoLoader.loadLibrary("yoga"); + Field instanceField = null; + try { + instanceField = Unsafe.class.getDeclaredField("theUnsafe"); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + instanceField.setAccessible(true); + try { + UNSAFE = (Unsafe) instanceField.get(null); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static native long jni_YGNodeNewNoProps(YogaNode node); + private static native long jni_YGNodeNewNoPropsWithConfig(YogaNode node, long configPointer); + private static native long jni_YGNodeStylePointer(long nativePointer); + private static native long jni_YGNodeLayoutPointer(long nativePointer); + + public YogaNodePropertiesUnsafe(YogaNode node) { + this(jni_YGNodeNewNoProps(node)); + } + + public YogaNodePropertiesUnsafe(YogaNode node, YogaConfig config) { + this(jni_YGNodeNewNoPropsWithConfig(node, config.mNativePointer)); + } + + public YogaNodePropertiesUnsafe(long nativePointer) { + mNativePointer = nativePointer; + mStyleNativePointer = jni_YGNodeStylePointer(nativePointer); + mLayoutNativePointer = jni_YGNodeLayoutPointer(nativePointer); + } + + private static native long jni_YGNodeCloneNoProps(long nativePointer, YogaNode newNode); + + @Override + public YogaNodeProperties clone(YogaNode node) { + long clonedNativePointer = jni_YGNodeCloneNoProps(getNativePointer(), node); + YogaNodePropertiesUnsafe clone = + new YogaNodePropertiesUnsafe(clonedNativePointer); + clone.mHasBorderSet = mHasBorderSet; + clone.mHasNewLayout = mHasNewLayout; + return clone; + } + + @Override + public long getNativePointer() { + return mNativePointer; + } + + @Override + public void onAfterCalculateLayout(boolean hasNewLayout) { + mHasNewLayout = hasNewLayout; + } + + private static native void jni_YGNodeReset(long nativePointer); + + @Override + public void reset() { + mHasNewLayout = true; + jni_YGNodeReset(getNativePointer()); + } + + @Override + public boolean hasNewLayout() { + return mHasNewLayout; + } + + private static native boolean jni_YGNodeIsDirty(long nativePointer); + + @Override + public boolean isDirty() { + return jni_YGNodeIsDirty(mNativePointer); + } + + @Override + public void markLayoutSeen() { + mHasNewLayout = false; + } + + @Override + public YogaDirection getStyleDirection() { + return YogaDirection.fromInt(getStyleInt(YogaNodeMemoryLayout.styleDirection)); + } + + @Override + public void setDirection(YogaDirection direction) { + putStyleInt(YogaNodeMemoryLayout.styleDirection, direction.intValue()); + } + + @Override + public YogaFlexDirection getFlexDirection() { + return YogaFlexDirection.fromInt(getStyleInt(YogaNodeMemoryLayout.styleFlexDirection)); + } + + @Override + public void setFlexDirection(YogaFlexDirection flexDirection) { + putStyleInt(YogaNodeMemoryLayout.styleFlexDirection, flexDirection.intValue()); + } + + @Override + public YogaJustify getJustifyContent() { + return YogaJustify.fromInt(getStyleInt(YogaNodeMemoryLayout.styleJustifyContent)); + } + + @Override + public void setJustifyContent(YogaJustify justifyContent) { + putStyleInt(YogaNodeMemoryLayout.styleJustifyContent, justifyContent.intValue()); + } + + @Override + public YogaAlign getAlignItems() { + return YogaAlign.fromInt(getStyleInt(YogaNodeMemoryLayout.styleAlignItems)); + } + + @Override + public void setAlignItems(YogaAlign alignItems) { + putStyleInt(YogaNodeMemoryLayout.styleAlignItems, alignItems.intValue()); + } + + @Override + public YogaAlign getAlignSelf() { + return YogaAlign.fromInt(getStyleInt(YogaNodeMemoryLayout.styleAlignSelf)); + } + + @Override + public void setAlignSelf(YogaAlign alignSelf) { + putStyleInt(YogaNodeMemoryLayout.styleAlignSelf, alignSelf.intValue()); + } + + @Override + public YogaAlign getAlignContent() { + return YogaAlign.fromInt(getStyleInt(YogaNodeMemoryLayout.styleAlignContent)); + } + + @Override + public void setAlignContent(YogaAlign alignContent) { + putStyleInt(YogaNodeMemoryLayout.styleAlignContent, alignContent.intValue()); + } + + @Override + public YogaPositionType getPositionType() { + return YogaPositionType.fromInt(getStyleInt(YogaNodeMemoryLayout.stylePositionType)); + } + + @Override + public void setPositionType(YogaPositionType positionType) { + putStyleInt(YogaNodeMemoryLayout.stylePositionType, positionType.intValue()); + } + + @Override + public void setWrap(YogaWrap flexWrap) { + putStyleInt(YogaNodeMemoryLayout.styleFlexWrap, flexWrap.intValue()); + } + + @Override + public YogaOverflow getOverflow() { + return YogaOverflow.fromInt(getStyleInt(YogaNodeMemoryLayout.styleOverflow)); + } + + @Override + public void setOverflow(YogaOverflow overflow) { + putStyleInt(YogaNodeMemoryLayout.styleOverflow, overflow.intValue()); + } + + @Override + public YogaDisplay getDisplay() { + return YogaDisplay.fromInt(getStyleInt(YogaNodeMemoryLayout.styleDisplay)); + } + + @Override + public void setDisplay(YogaDisplay display) { + putStyleInt(YogaNodeMemoryLayout.styleDisplay, display.intValue()); + } + + @Override + public void setFlex(float flex) { + putStyleOptional(YogaNodeMemoryLayout.styleFlex, flex); + } + + @Override + public float getFlexGrow() { + return getStyleFloat(YogaNodeMemoryLayout.styleFlexGrow); + } + + @Override + public void setFlexGrow(float flexGrow) { + putStyleOptional(YogaNodeMemoryLayout.styleFlexGrow, flexGrow); + } + + @Override + public float getFlexShrink() { + return getStyleFloat(YogaNodeMemoryLayout.styleFlexShrink); + } + + @Override + public void setFlexShrink(float flexShrink) { + putStyleOptional(YogaNodeMemoryLayout.styleFlexShrink, flexShrink); + } + + @Override + public YogaValue getFlexBasis() { + return getStyleValue(YogaNodeMemoryLayout.styleFlexBasis); + } + + @Override + public void setFlexBasis(float flexBasis) { + putStylePoints(YogaNodeMemoryLayout.styleFlexBasis, flexBasis); + } + + @Override + public void setFlexBasisPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleFlexBasis, percent); + } + + @Override + public void setFlexBasisAuto() { + putStyleAuto(YogaNodeMemoryLayout.styleFlexBasis); + } + + @Override + public YogaValue getMargin(YogaEdge edge) { + return getStyleValue(YogaNodeMemoryLayout.styleMarginOffset(edge)); + } + + @Override + public void setMargin(YogaEdge edge, float margin) { + putStylePoints(YogaNodeMemoryLayout.styleMarginOffset(edge), margin); + } + + @Override + public void setMarginPercent(YogaEdge edge, float percent) { + putStylePercent(YogaNodeMemoryLayout.styleMarginOffset(edge), percent); + } + + @Override + public void setMarginAuto(YogaEdge edge) { + putStyleAuto(YogaNodeMemoryLayout.styleMarginOffset(edge)); + } + + @Override + public YogaValue getPadding(YogaEdge edge) { + return getStyleValue(YogaNodeMemoryLayout.stylePaddingOffset(edge)); + } + + @Override + public void setPadding(YogaEdge edge, float padding) { + putStylePoints(YogaNodeMemoryLayout.stylePaddingOffset(edge), padding); + } + + @Override + public void setPaddingPercent(YogaEdge edge, float percent) { + putStylePercent(YogaNodeMemoryLayout.stylePaddingOffset(edge), percent); + } + + @Override + public float getBorder(YogaEdge edge) { + return mHasBorderSet + ? getStyleFloat(YogaNodeMemoryLayout.styleBorderOffset(edge)) + : YogaConstants.UNDEFINED; + } + + @Override + public void setBorder(YogaEdge edge, float border) { + mHasBorderSet = true; + putStylePoints(YogaNodeMemoryLayout.styleBorderOffset(edge), border); + } + + @Override + public YogaValue getPosition(YogaEdge edge) { + return getStyleValue(YogaNodeMemoryLayout.stylePositionOffset(edge)); + } + + @Override + public void setPosition(YogaEdge edge, float position) { + putStylePoints(YogaNodeMemoryLayout.stylePositionOffset(edge), position); + } + + @Override + public void setPositionPercent(YogaEdge edge, float percent) { + putStylePercent(YogaNodeMemoryLayout.stylePositionOffset(edge), percent); + } + + @Override + public YogaValue getWidth() { + return getStyleValue(YogaNodeMemoryLayout.styleWidth); + } + + @Override + public void setWidth(float width) { + putStylePoints(YogaNodeMemoryLayout.styleWidth, width); + } + + @Override + public void setWidthPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleWidth, percent); + } + + @Override + public void setWidthAuto() { + putStyleAuto(YogaNodeMemoryLayout.styleWidth); + } + + @Override + public YogaValue getHeight() { + return getStyleValue(YogaNodeMemoryLayout.styleHeight); + } + + @Override + public void setHeight(float height) { + putStylePoints(YogaNodeMemoryLayout.styleHeight, height); + } + + @Override + public void setHeightPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleHeight, percent); + } + + @Override + public void setHeightAuto() { + putStyleAuto(YogaNodeMemoryLayout.styleHeight); + } + + @Override + public YogaValue getMinWidth() { + return getStyleValue(YogaNodeMemoryLayout.styleMinWidth); + } + + @Override + public void setMinWidth(float minWidth) { + putStylePoints(YogaNodeMemoryLayout.styleMinWidth, minWidth); + } + + @Override + public void setMinWidthPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleMinWidth, percent); + } + + @Override + public YogaValue getMinHeight() { + return getStyleValue(YogaNodeMemoryLayout.styleMinHeight); + } + + @Override + public void setMinHeight(float minHeight) { + putStylePoints(YogaNodeMemoryLayout.styleMinHeight, minHeight); + } + + @Override + public void setMinHeightPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleMinHeight, percent); + } + + @Override + public YogaValue getMaxWidth() { + return getStyleValue(YogaNodeMemoryLayout.styleMaxWidth); + } + + @Override + public void setMaxWidth(float maxWidth) { + putStylePoints(YogaNodeMemoryLayout.styleMaxWidth, maxWidth); + } + + @Override + public void setMaxWidthPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleMaxWidth, percent); + } + + @Override + public YogaValue getMaxHeight() { + return getStyleValue(YogaNodeMemoryLayout.styleMaxHeight); + } + + @Override + public void setMaxHeight(float maxHeight) { + putStylePoints(YogaNodeMemoryLayout.styleMaxHeight, maxHeight); + } + + @Override + public void setMaxHeightPercent(float percent) { + putStylePercent(YogaNodeMemoryLayout.styleMaxHeight, percent); + } + + @Override + public float getAspectRatio() { + return getStyleOptional(YogaNodeMemoryLayout.styleAspectRatio); + } + + @Override + public void setAspectRatio(float aspectRatio) { + putStyleOptional(YogaNodeMemoryLayout.styleAspectRatio, aspectRatio); + } + + @Override + public float getLayoutX() { + return getLayoutFloat(YogaNodeMemoryLayout.layoutX); + } + + @Override + public float getLayoutY() { + return getLayoutFloat(YogaNodeMemoryLayout.layoutY); + } + + @Override + public float getLayoutWidth() { + return getLayoutFloat(YogaNodeMemoryLayout.layoutWidth); + } + + @Override + public float getLayoutHeight() { + return getLayoutFloat(YogaNodeMemoryLayout.layoutHeight); + } + + @Override + public boolean getDoesLegacyStretchFlagAffectsLayout() { + return getBool(mLayoutNativePointer + YogaNodeMemoryLayout.layoutDoesLegacyStretchFlagAffectsLayout); + } + + @Override + public float getLayoutMargin(YogaEdge edge) { + return getLayoutFloat(YogaNodeMemoryLayout.layoutMarginOffset(layoutEdge(edge))); + } + + @Override + public float getLayoutPadding(YogaEdge edge) { + return getLayoutFloat(YogaNodeMemoryLayout.layoutPaddingOffset(layoutEdge(edge))); + } + + @Override + public float getLayoutBorder(YogaEdge edge) { + return getLayoutFloat(YogaNodeMemoryLayout.layoutBorderOffset(layoutEdge(edge))); + } + + @Override + public YogaDirection getLayoutDirection() { + return YogaDirection.fromInt(getLayoutDirectionInt()); + } + + private static native void jni_YGNodeFree(long nativePointer); + + @Override + public void freeNatives() { + if (!mIsFreed) { + mIsFreed = true; + jni_YGNodeFree(mNativePointer); + } + } + + private int getLayoutDirectionInt() { + return UNSAFE.getInt(null, mLayoutNativePointer + YogaNodeMemoryLayout.layoutDirection); + } + + private YogaEdge layoutEdge(YogaEdge edge) { + int layoutDirection = getLayoutDirectionInt(); + switch (edge) { + case LEFT: + return layoutDirection == RTL ? YogaEdge.END : YogaEdge.START; + case RIGHT: + return layoutDirection == RTL ? YogaEdge.START : YogaEdge.END; + case TOP: + case BOTTOM: + case START: + case END: + return edge; + default: + throw new IllegalArgumentException("Cannot get layout properties of multi-edge shorthands"); + } + } + + private int getStyleInt(int offset) { + return UNSAFE.getInt(null, mStyleNativePointer + offset); + } + + private void putStyleInt(int offset, int value) { + UNSAFE.putInt(null, mStyleNativePointer + offset, value); + } + + private float getStyleFloat(int offset) { + return getFloat(mStyleNativePointer + offset); + } + + private void putStyleFloat(int offset, float value) { + putFloat(mStyleNativePointer + offset, value); + } + + private void putStylePoints(int offset, float value) { + putStyleValue(offset, value, POINT); + } + + private void putStylePercent(int offset, float value) { + putStyleValue(offset, value, PERCENT); + } + + private void putStyleAuto(int offset) { + putStyleValue(offset, 0, AUTO); + } + + private void putStyleValue(int offset, float value, int unit) { + if (YogaConstants.isUndefined(value)) { + value = YogaConstants.UNDEFINED; + unit = UNDEFINED; + } + putStyleFloat(offset, value); + putStyleInt(offset + FLOAT_SIZE, unit); + } + + private YogaValue getStyleValue(int offset) { + float value = getStyleFloat(offset); + int unit = getStyleInt(offset + FLOAT_SIZE); + return new YogaValue(value, YogaUnit.fromInt(unit)); + } + + private void putStyleOptional(int offset, float value) { + int isUndefinedBits = YogaConstants.isUndefined(value) ? TRUE_BITS : 0; + putStyleFloat(offset, value); + putStyleInt(offset + FLOAT_SIZE, isUndefinedBits); + } + + private float getStyleOptional(int offset) { + boolean isUndefined = getBool(mStyleNativePointer + offset + FLOAT_SIZE); + return isUndefined + ? YogaConstants.UNDEFINED + : getStyleFloat(offset); + } + + private float getLayoutFloat(int offset) { + return getFloat(mLayoutNativePointer + offset); + } + + private static float getFloat(long offset) { + int intBits = UNSAFE.getInt(null, offset); + return Float.intBitsToFloat(intBits); + } + + private static void putFloat(long offset, float value) { + int intBits = Float.floatToRawIntBits(value); + UNSAFE.putInt(null, offset, intBits); + } + + private static boolean getBool(long offset) { + // assumes little endian + return (UNSAFE.getInt(null, offset) & 0xFF) != 0; + } + +} diff --git a/java/jni/YGJNI.cpp b/java/jni/YGJNI.cpp index 7f4dc041..482c14ae 100644 --- a/java/jni/YGJNI.cpp +++ b/java/jni/YGJNI.cpp @@ -344,13 +344,11 @@ jlong jni_YGNodeNewWithConfig( return reinterpret_cast(node); } -jlong jni_YGNodeNewByteBuffer( - alias_ref, - alias_ref javaNode) { +jlong jni_YGNodeNewNoProps(alias_ref, alias_ref javaNode) { return jni_YGNodeNew(nullptr, javaNode); } -jlong jni_YGNodeNewByteBufferWithConfig( +jlong jni_YGNodeNewNoPropsWithConfig( alias_ref, alias_ref javaNode, jlong configPointer) { @@ -747,6 +745,14 @@ local_ref jni_getLayoutBuffer( reinterpret_cast(layout), sizeof(YGLayout)); } +jlong jni_YGNodeStylePointer(alias_ref, jlong nativePointer) { + return reinterpret_cast(&_jlong2YGNodeRef(nativePointer)->getStyle()); +} + +jlong jni_YGNodeLayoutPointer(alias_ref, jlong nativePointer) { + return reinterpret_cast(&_jlong2YGNodeRef(nativePointer)->getLayout()); +} + #define YGMakeNativeMethod(name) makeNativeMethod(#name, name) jint JNI_OnLoad(JavaVM* vm, void*) { @@ -860,8 +866,8 @@ jint JNI_OnLoad(JavaVM* vm, void*) { { YGMakeNativeMethod(jni_YGNodeCloneNoProps), YGMakeNativeMethod(jni_YGNodeFree), - YGMakeNativeMethod(jni_YGNodeNewByteBuffer), - YGMakeNativeMethod(jni_YGNodeNewByteBufferWithConfig), + YGMakeNativeMethod(jni_YGNodeNewNoProps), + YGMakeNativeMethod(jni_YGNodeNewNoPropsWithConfig), YGMakeNativeMethod(jni_YGNodeReset), YGMakeNativeMethod(jni_YGNodeIsDirty), YGMakeNativeMethod(jni_getStyleBuffer), @@ -872,5 +878,17 @@ jint JNI_OnLoad(JavaVM* vm, void*) { { YGMakeNativeMethod(jni_getStyleBuffer), }); + registerNatives( + "com/facebook/yoga/YogaNodePropertiesUnsafe", + { + YGMakeNativeMethod(jni_YGNodeCloneNoProps), + YGMakeNativeMethod(jni_YGNodeFree), + YGMakeNativeMethod(jni_YGNodeNewNoProps), + YGMakeNativeMethod(jni_YGNodeNewNoPropsWithConfig), + YGMakeNativeMethod(jni_YGNodeStylePointer), + YGMakeNativeMethod(jni_YGNodeLayoutPointer), + YGMakeNativeMethod(jni_YGNodeIsDirty), + YGMakeNativeMethod(jni_YGNodeReset), + }); }); } diff --git a/java/stubs/build.gradle b/java/stubs/build.gradle new file mode 100644 index 00000000..f2faa446 --- /dev/null +++ b/java/stubs/build.gradle @@ -0,0 +1,10 @@ +apply plugin: 'java' + +sourceSets { + main { + java { + srcDirs = ['src'] + } + } +} + diff --git a/java/stubs/src/sun/misc/Unsafe.java b/java/stubs/src/sun/misc/Unsafe.java new file mode 100644 index 00000000..01ff08dc --- /dev/null +++ b/java/stubs/src/sun/misc/Unsafe.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +package sun.misc; + +/** + * Stub for sun.misc.Unsafe, which is not exposed by the Android SDK. + * + * This only contains the methods and fields we need for Yoga. + */ +public final class Unsafe { + private static final Unsafe theUnsafe = null; + + public final int getInt(Object object, long offset) { + throw new RuntimeException("Stub!"); + } + + public final void putInt(Object object, long offset, int value) { + throw new RuntimeException("Stub!"); + } +} + diff --git a/java/tests/com/facebook/yoga/TestParametrization.java b/java/tests/com/facebook/yoga/TestParametrization.java index ecb81ca1..23fe67d1 100644 --- a/java/tests/com/facebook/yoga/TestParametrization.java +++ b/java/tests/com/facebook/yoga/TestParametrization.java @@ -59,6 +59,22 @@ public class TestParametrization { public String toString() { return "Hybrid"; } + }, + new NodeFactory() { + @Override + public YogaNode create() { + return new YogaNode(YogaNode.UNSAFE); + } + + @Override + public YogaNode create(YogaConfig config) { + return new YogaNode(YogaNode.UNSAFE, config); + } + + @Override + public String toString() { + return "Unsafe"; + } }); } diff --git a/settings.gradle b/settings.gradle index 93d836ba..f18ef534 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ -include ':yoga', ':yogacore', ':yoga-layout', ':yoga:proguard-annotations', ':libfb' +include ':yoga', ':yogacore', ':yoga-layout', ':yoga:proguard-annotations', ':yoga:stubs', ':libfb' project(':yoga').projectDir = file('java') project(':yoga:proguard-annotations').projectDir = file('java/proguard-annotations') +project(':yoga:stubs').projectDir = file('java/stubs') project(':yoga-layout').projectDir = file('android') project(':libfb').projectDir = file('lib/fb')