diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 9f8e13c3b20..116179370c2 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -1489,7 +1489,7 @@ public class com/facebook/react/bridge/ReadableNativeArray : com/facebook/react/ public fun getDouble (I)D public fun getDynamic (I)Lcom/facebook/react/bridge/Dynamic; public fun getInt (I)I - public static final fun getJNIPassCounter ()I + public static fun getJNIPassCounter ()I public fun getLong (I)J public synthetic fun getMap (I)Lcom/facebook/react/bridge/ReadableMap; public fun getMap (I)Lcom/facebook/react/bridge/ReadableNativeMap; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.java index d032e99805e..daa3eaca785 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaMethodWrapper.java @@ -79,9 +79,6 @@ class JavaMethodWrapper implements JavaModuleWrapper.NativeMethod { @Override public ReadableArray extractArgument( JSInstance jsInstance, ReadableArray jsArguments, int atIndex) { - if (jsArguments.isNull(atIndex) || jsArguments.getType(atIndex) != ReadableType.Array) { - return null; - } return jsArguments.getArray(atIndex); } }; @@ -100,9 +97,6 @@ class JavaMethodWrapper implements JavaModuleWrapper.NativeMethod { @Override public ReadableMap extractArgument( JSInstance jsInstance, ReadableArray jsArguments, int atIndex) { - if (jsArguments.isNull(atIndex) || jsArguments.getType(atIndex) != ReadableType.Map) { - return null; - } return jsArguments.getMap(atIndex); } }; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java new file mode 100644 index 00000000000..c46dbcc6dad --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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.react.bridge; + +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; + +/** Base class for an array whose members are stored in native code (C++). */ +@DoNotStrip +public abstract class NativeArray implements NativeArrayInterface { + static { + ReactBridge.staticInit(); + } + + protected NativeArray(HybridData hybridData) { + mHybridData = hybridData; + } + + @Override + public native String toString(); + + @DoNotStrip private HybridData mHybridData; +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.kt deleted file mode 100644 index 5275bf13d8d..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.kt +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and 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.react.bridge - -import com.facebook.jni.HybridData -import com.facebook.proguard.annotations.DoNotStrip - -/** Base class for an array whose members are stored in native code (C++). */ -@DoNotStrip -public abstract class NativeArray -protected constructor(@field:DoNotStrip private val mHybridData: HybridData?) : - NativeArrayInterface { - external override fun toString(): String - - private companion object { - init { - ReactBridge.staticInit() - } - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableArray.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableArray.kt index 8d024a81b8e..de59f930ad5 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableArray.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableArray.kt @@ -36,5 +36,5 @@ public interface ReadableArray { public fun size(): Int - public fun toArrayList(): ArrayList + public fun toArrayList(): ArrayList } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java new file mode 100644 index 00000000000..2f74df2334c --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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.react.bridge; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.facebook.infer.annotation.Assertions; +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Implementation of a NativeArray that allows read-only access to its members. This will generally + * be constructed and filled in native code so you shouldn't construct one yourself. + */ +@DoNotStrip +public class ReadableNativeArray extends NativeArray implements ReadableArray { + static { + ReactBridge.staticInit(); + } + + protected ReadableNativeArray(HybridData hybridData) { + super(hybridData); + } + + // WriteOnce but not in the constructor fields + private @Nullable Object[] mLocalArray; + private @Nullable ReadableType[] mLocalTypeArray; + + private static int jniPassCounter = 0; + + public static int getJNIPassCounter() { + return jniPassCounter; + } + + private Object[] getLocalArray() { + if (mLocalArray != null) { + return mLocalArray; + } + synchronized (this) { + // Make sure no concurrent call already updated + if (mLocalArray == null) { + jniPassCounter++; + mLocalArray = Assertions.assertNotNull(importArray()); + } + } + return mLocalArray; + } + + private native Object[] importArray(); + + private ReadableType[] getLocalTypeArray() { + if (mLocalTypeArray != null) { + return mLocalTypeArray; + } + synchronized (this) { + // Make sure no concurrent call already updated + if (mLocalTypeArray == null) { + jniPassCounter++; + Object[] tempArray = Assertions.assertNotNull(importTypeArray()); + mLocalTypeArray = Arrays.copyOf(tempArray, tempArray.length, ReadableType[].class); + } + } + return mLocalTypeArray; + } + + private native Object[] importTypeArray(); + + @Override + public int size() { + return getLocalArray().length; + } + + @Override + public boolean isNull(int index) { + return getLocalArray()[index] == null; + } + + @Override + public boolean getBoolean(int index) { + return ((Boolean) getLocalArray()[index]).booleanValue(); + } + + @Override + public double getDouble(int index) { + return ((Double) getLocalArray()[index]).doubleValue(); + } + + @Override + public int getInt(int index) { + return ((Double) getLocalArray()[index]).intValue(); + } + + @Override + public long getLong(int index) { + return ((Long) getLocalArray()[index]).longValue(); + } + + @Override + public @NonNull String getString(int index) { + return (String) getLocalArray()[index]; + } + + @Override + public @NonNull ReadableNativeArray getArray(int index) { + return (ReadableNativeArray) getLocalArray()[index]; + } + + @Override + public @NonNull ReadableNativeMap getMap(int index) { + return (ReadableNativeMap) getLocalArray()[index]; + } + + @Override + public @NonNull ReadableType getType(int index) { + return getLocalTypeArray()[index]; + } + + @Override + public @NonNull Dynamic getDynamic(int index) { + return DynamicFromArray.create(this, index); + } + + @Override + public int hashCode() { + return getLocalArray().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof ReadableNativeArray)) { + return false; + } + ReadableNativeArray other = (ReadableNativeArray) obj; + return Arrays.deepEquals(getLocalArray(), other.getLocalArray()); + } + + @Override + public @NonNull ArrayList toArrayList() { + ArrayList arrayList = new ArrayList<>(); + + for (int i = 0; i < this.size(); i++) { + switch (getType(i)) { + case Null: + arrayList.add(null); + break; + case Boolean: + arrayList.add(getBoolean(i)); + break; + case Number: + arrayList.add(getDouble(i)); + break; + case String: + arrayList.add(getString(i)); + break; + case Map: + arrayList.add(getMap(i).toHashMap()); + break; + case Array: + arrayList.add(getArray(i).toArrayList()); + break; + default: + throw new IllegalArgumentException("Could not convert object at index: " + i + "."); + } + } + return arrayList; + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.kt deleted file mode 100644 index 7896e2be36e..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and 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.react.bridge - -import com.facebook.jni.HybridData -import com.facebook.proguard.annotations.DoNotStripAny -import java.util.Arrays - -/** - * Implementation of a NativeArray that allows read-only access to its members. This will generally - * be constructed and filled in native code so you shouldn't construct one yourself. - */ -@DoNotStripAny -public open class ReadableNativeArray protected constructor(hybridData: HybridData?) : - NativeArray(hybridData), ReadableArray { - - private val localArray: Array by - lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - jniPassCounter++ - importArray() - } - - private external fun importArray(): Array - - private val localTypeArray: Array by - lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - jniPassCounter++ - importTypeArray() - } - - private external fun importTypeArray(): Array - - override fun size(): Int = localArray.size - - override fun isNull(index: Int): Boolean = localArray[index] == null - - override fun getBoolean(index: Int): Boolean = localArray[index] as Boolean - - override fun getDouble(index: Int): Double = localArray[index] as Double - - override fun getInt(index: Int): Int = getDouble(index).toInt() - - override fun getLong(index: Int): Long = localArray[index] as Long - - override fun getString(index: Int): String = localArray[index] as String - - override fun getArray(index: Int): ReadableNativeArray = localArray[index] as ReadableNativeArray - - override fun getMap(index: Int): ReadableNativeMap = localArray[index] as ReadableNativeMap - - override fun getType(index: Int): ReadableType = localTypeArray[index] - - override fun getDynamic(index: Int): Dynamic = DynamicFromArray.create(this, index) - - override fun hashCode(): Int = localArray.hashCode() - - override fun equals(other: Any?): Boolean = - if (other !is ReadableNativeArray) false else Arrays.deepEquals(localArray, other.localArray) - - override fun toArrayList(): ArrayList { - val arrayList = ArrayList() - for (i in 0 until size()) { - when (getType(i)) { - ReadableType.Null -> arrayList.add(null) - ReadableType.Boolean -> arrayList.add(getBoolean(i)) - ReadableType.Number -> arrayList.add(getDouble(i)) - ReadableType.String -> arrayList.add(getString(i)) - ReadableType.Map -> arrayList.add(getMap(i).toHashMap()) - ReadableType.Array -> arrayList.add(getArray(i).toArrayList()) - else -> throw IllegalArgumentException("Could not convert object at index: $i.") - } - } - return arrayList - } - - private companion object { - init { - ReactBridge.staticInit() - } - - private var jniPassCounter: Int = 0 - - @JvmStatic public fun getJNIPassCounter(): Int = jniPassCounter - } -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java new file mode 100644 index 00000000000..06ad5650e11 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) Meta Platforms, Inc. and 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.react.bridge; + +import androidx.annotation.Nullable; +import com.facebook.infer.annotation.Assertions; +import com.facebook.jni.HybridData; +import com.facebook.proguard.annotations.DoNotStrip; + +/** + * Implementation of a write-only array stored in native memory. Use {@link Arguments#createArray()} + * if you need to stub out creating this class in a test. TODO(5815532): Check if consumed on read + */ +@DoNotStrip +public class WritableNativeArray extends ReadableNativeArray implements WritableArray { + static { + ReactBridge.staticInit(); + } + + public WritableNativeArray() { + super(initHybrid()); + } + + @Override + public native void pushNull(); + + @Override + public native void pushBoolean(boolean value); + + @Override + public native void pushDouble(double value); + + @Override + public native void pushInt(int value); + + @Override + public native void pushLong(long value); + + @Override + public native void pushString(@Nullable String value); + + // Note: this consumes the map so do not reuse it. + @Override + public void pushArray(@Nullable ReadableArray array) { + Assertions.assertCondition( + array == null || array instanceof ReadableNativeArray, "Illegal type provided"); + pushNativeArray((ReadableNativeArray) array); + } + + // Note: this consumes the map so do not reuse it. + @Override + public void pushMap(@Nullable ReadableMap map) { + Assertions.assertCondition( + map == null || map instanceof ReadableNativeMap, "Illegal type provided"); + pushNativeMap((ReadableNativeMap) map); + } + + private static native HybridData initHybrid(); + + private native void pushNativeArray(ReadableNativeArray array); + + private native void pushNativeMap(ReadableNativeMap map); +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.kt deleted file mode 100644 index 7cc0c6aa83b..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and 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.react.bridge - -import com.facebook.infer.annotation.Assertions -import com.facebook.jni.HybridData -import com.facebook.proguard.annotations.DoNotStripAny - -/** - * Implementation of a write-only array stored in native memory. Use [Arguments.createArray] if you - * need to stub out creating this class in a test. TODO(5815532): Check if consumed on read - */ -@DoNotStripAny -public open class WritableNativeArray : ReadableNativeArray(initHybrid()), WritableArray { - external override fun pushNull() - - external override fun pushBoolean(value: Boolean) - - external override fun pushDouble(value: Double) - - external override fun pushInt(value: Int) - - external override fun pushLong(value: Long) - - external override fun pushString(value: String?) - - // Note: this consumes the array so do not reuse it. - override fun pushArray(array: ReadableArray?) { - Assertions.assertCondition( - array == null || array is ReadableNativeArray, "Illegal type provided") - pushNativeArray(array as ReadableNativeArray?) - } - - // Note: this consumes the map so do not reuse it. - override fun pushMap(map: ReadableMap?) { - Assertions.assertCondition(map == null || map is ReadableNativeMap, "Illegal type provided") - pushNativeMap(map as ReadableNativeMap?) - } - - private external fun pushNativeArray(array: ReadableNativeArray?) - - private external fun pushNativeMap(map: ReadableNativeMap?) - - private companion object { - init { - ReactBridge.staticInit() - } - - @JvmStatic private external fun initHybrid(): HybridData? - } -} diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp index c5341a2d61f..1f4b7c1d495 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JavaModuleWrapper.cpp @@ -22,7 +22,6 @@ #endif #include "CatalystInstanceImpl.h" -#include "NativeMap.h" #include "ReadableNativeArray.h" using facebook::xplat::module::CxxModule; diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.cpp index dc7e8dc154c..9add683e2a4 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.cpp @@ -6,6 +6,7 @@ */ #include "ReadableNativeArray.h" + #include "ReadableNativeMap.h" using namespace facebook::jni; @@ -30,10 +31,9 @@ local_ref> ReadableNativeArray::importArray() { return jarray; } -local_ref> -ReadableNativeArray::importTypeArray() { +local_ref> ReadableNativeArray::importTypeArray() { auto size = static_cast(array_.size()); - auto jarray = JArrayClass::newArray(size); + auto jarray = JArrayClass::newArray(size); for (jint ii = 0; ii < size; ii++) { (*jarray)[ii] = ReadableType::getType(array_.at(ii).type()); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.h b/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.h index 107df4bca09..02d9445192e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/ReadableNativeArray.h @@ -8,7 +8,9 @@ #pragma once #include "NativeArray.h" + #include "NativeCommon.h" +#include "NativeMap.h" namespace facebook::react { @@ -34,7 +36,7 @@ class ReadableNativeArray static void registerNatives(); jni::local_ref> importArray(); - jni::local_ref> importTypeArray(); + jni::local_ref> importTypeArray(); }; } // namespace facebook::react