From e60cd87aa02f8dd231ced408e9bfec5d41c172fc Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Tue, 14 May 2024 03:14:27 -0700 Subject: [PATCH] Migrate NativeArray classes to Kotlin Summary: # Changelog: [Internal] - This converts the vertical of NativeArray/ReadableNativeArray/WritableNativeArray classes to Kotlin. Differential Revision: D57327835 --- .../ReactAndroid/api/ReactAndroid.api | 12 +- .../facebook/react/bridge/NativeArray.java | 28 --- .../com/facebook/react/bridge/NativeArray.kt | 25 +++ .../facebook/react/bridge/ReadableArray.kt | 2 +- .../react/bridge/ReadableNativeArray.java | 174 ------------------ .../react/bridge/ReadableNativeArray.kt | 93 ++++++++++ .../react/bridge/WritableNativeArray.java | 68 ------- .../react/bridge/WritableNativeArray.kt | 56 ++++++ 8 files changed, 185 insertions(+), 273 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.kt delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.kt delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 575cb907e14185..b7adde5eeca2cb 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -951,10 +951,14 @@ public class com/facebook/react/bridge/ModuleSpec { } public abstract class com/facebook/react/bridge/NativeArray : com/facebook/react/bridge/NativeArrayInterface { + protected static final field Companion Lcom/facebook/react/bridge/NativeArray$Companion; protected fun (Lcom/facebook/jni/HybridData;)V public fun toString ()Ljava/lang/String; } +protected final class com/facebook/react/bridge/NativeArray$Companion { +} + public abstract interface class com/facebook/react/bridge/NativeArrayInterface { public abstract fun toString ()Ljava/lang/String; } @@ -1378,6 +1382,7 @@ public abstract interface class com/facebook/react/bridge/ReadableMapKeySetItera } public class com/facebook/react/bridge/ReadableNativeArray : com/facebook/react/bridge/NativeArray, com/facebook/react/bridge/ReadableArray { + public static final field Companion Lcom/facebook/react/bridge/ReadableNativeArray$Companion; protected fun (Lcom/facebook/jni/HybridData;)V public fun equals (Ljava/lang/Object;)Z public synthetic fun getArray (I)Lcom/facebook/react/bridge/ReadableArray; @@ -1386,7 +1391,6 @@ 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 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; @@ -1398,6 +1402,10 @@ public class com/facebook/react/bridge/ReadableNativeArray : com/facebook/react/ public fun toArrayList ()Ljava/util/ArrayList; } +public final class com/facebook/react/bridge/ReadableNativeArray$Companion { + public final fun getJniPassCounter ()I +} + public class com/facebook/react/bridge/ReadableNativeMap : com/facebook/react/bridge/NativeMap, com/facebook/react/bridge/ReadableMap { protected fun (Lcom/facebook/jni/HybridData;)V public fun equals (Ljava/lang/Object;)Z @@ -1523,7 +1531,7 @@ public abstract interface class com/facebook/react/bridge/WritableMap : com/face public abstract fun putString (Ljava/lang/String;Ljava/lang/String;)V } -public class com/facebook/react/bridge/WritableNativeArray : com/facebook/react/bridge/ReadableNativeArray, com/facebook/react/bridge/WritableArray { +public final class com/facebook/react/bridge/WritableNativeArray : com/facebook/react/bridge/ReadableNativeArray, com/facebook/react/bridge/WritableArray { public fun ()V public fun pushArray (Lcom/facebook/react/bridge/ReadableArray;)V public fun pushBoolean (Z)V 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 deleted file mode 100644 index c46dbcc6dad564..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.java +++ /dev/null @@ -1,28 +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 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 new file mode 100644 index 00000000000000..d947a2cf5dd659 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/NativeArray.kt @@ -0,0 +1,25 @@ +/* + * 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 + + protected 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 de59f930ad5829..8d024a81b8e052 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 deleted file mode 100644 index 2f74df2334cac6..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.java +++ /dev/null @@ -1,174 +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 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 new file mode 100644 index 00000000000000..1dfef0107efc57 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeArray.kt @@ -0,0 +1,93 @@ +/* + * 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 +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) { + // WriteOnce but not in the constructor fields + jniPassCounter++ + Assertions.assertNotNull(importArray()) + } + + private external fun importArray(): Array + + private val localTypeArray: Array by + lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + // WriteOnce but not in the constructor fields + jniPassCounter++ + val tempArray = Assertions.assertNotNull(importTypeArray()) + Arrays.copyOf(tempArray, tempArray.size, Array::class.java) + } + + 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 = (localArray[index] as Double).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 + } + + public companion object { + init { + ReactBridge.staticInit() + } + + public var jniPassCounter: Int = 0 + private set + } +} 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 deleted file mode 100644 index 06ad5650e11c45..00000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.java +++ /dev/null @@ -1,68 +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 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 new file mode 100644 index 00000000000000..1ca58658381f74 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/WritableNativeArray.kt @@ -0,0 +1,56 @@ +/* + * 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 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 map 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() + } + + private external fun initHybrid(): HybridData? + } +}