From e603fde886f8440c3320a3e2b38cd536dc4095a2 Mon Sep 17 00:00:00 2001 From: Adam Woods-Mccormick Date: Wed, 6 Nov 2024 12:49:38 -0800 Subject: [PATCH] Revert D65478448: Convert com.facebook.react.bridge.JavaOnlyMap to Kotlin Differential Revision: D65478448 Original commit changeset: 24e6732a80f5 Original Phabricator Diff: D65478448 fbshipit-source-id: fffd2259aa2757b816373bfded96948b3e6dfeba --- .../ReactAndroid/api/ReactAndroid.api | 18 +- .../facebook/react/bridge/JavaOnlyMap.java | 270 ++++++++++++++++++ .../com/facebook/react/bridge/JavaOnlyMap.kt | 180 ------------ .../com/facebook/react/bridge/ReadableMap.kt | 4 +- .../react/bridge/ReadableNativeMap.kt | 6 +- 5 files changed, 280 insertions(+), 198 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index d082bd2dc03..4f305598e64 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -926,14 +926,12 @@ public class com/facebook/react/bridge/JavaOnlyArray : com/facebook/react/bridge public fun toString ()Ljava/lang/String; } -public final class com/facebook/react/bridge/JavaOnlyMap : com/facebook/react/bridge/ReadableMap, com/facebook/react/bridge/WritableMap { - public static final field Companion Lcom/facebook/react/bridge/JavaOnlyMap$Companion; +public class com/facebook/react/bridge/JavaOnlyMap : com/facebook/react/bridge/ReadableMap, com/facebook/react/bridge/WritableMap { public fun ()V - public synthetic fun ([Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun copy ()Lcom/facebook/react/bridge/WritableMap; - public static final fun deepClone (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/bridge/JavaOnlyMap; + public static fun deepClone (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/bridge/JavaOnlyMap; public fun equals (Ljava/lang/Object;)Z - public static final fun from (Ljava/util/Map;)Lcom/facebook/react/bridge/JavaOnlyMap; + public static fun from (Ljava/util/Map;)Lcom/facebook/react/bridge/JavaOnlyMap; public fun getArray (Ljava/lang/String;)Lcom/facebook/react/bridge/ReadableArray; public fun getBoolean (Ljava/lang/String;)Z public fun getDouble (Ljava/lang/String;)D @@ -949,7 +947,7 @@ public final class com/facebook/react/bridge/JavaOnlyMap : com/facebook/react/br public fun isNull (Ljava/lang/String;)Z public fun keySetIterator ()Lcom/facebook/react/bridge/ReadableMapKeySetIterator; public fun merge (Lcom/facebook/react/bridge/ReadableMap;)V - public static final fun of ([Ljava/lang/Object;)Lcom/facebook/react/bridge/JavaOnlyMap; + public static fun of ([Ljava/lang/Object;)Lcom/facebook/react/bridge/JavaOnlyMap; public fun putArray (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;)V public fun putBoolean (Ljava/lang/String;Z)V public fun putDouble (Ljava/lang/String;D)V @@ -958,17 +956,11 @@ public final class com/facebook/react/bridge/JavaOnlyMap : com/facebook/react/br public fun putMap (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableMap;)V public fun putNull (Ljava/lang/String;)V public fun putString (Ljava/lang/String;Ljava/lang/String;)V - public final fun remove (Ljava/lang/String;)V + public fun remove (Ljava/lang/String;)V public fun toHashMap ()Ljava/util/HashMap; public fun toString ()Ljava/lang/String; } -public final class com/facebook/react/bridge/JavaOnlyMap$Companion { - public final fun deepClone (Lcom/facebook/react/bridge/ReadableMap;)Lcom/facebook/react/bridge/JavaOnlyMap; - public final fun from (Ljava/util/Map;)Lcom/facebook/react/bridge/JavaOnlyMap; - public final fun of ([Ljava/lang/Object;)Lcom/facebook/react/bridge/JavaOnlyMap; -} - public class com/facebook/react/bridge/JavaScriptContextHolder { public fun (J)V public fun clear ()V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java new file mode 100644 index 00000000000..afb27217b44 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.java @@ -0,0 +1,270 @@ +/* + * 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 static androidx.core.util.Preconditions.checkNotNull; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Java {@link HashMap} backed implementation of {@link ReadableMap} and {@link WritableMap} + * Instances of this class SHOULD NOT be used for communication between java and JS, use instances + * of {@link WritableNativeMap} created via {@link Arguments#createMap} or just {@link ReadableMap} + * interface if you want your "native" module method to take a map from JS as an argument. + * + *

Main purpose for this class is to be used in java-only unit tests, but could also be used + * outside of tests in the code that operates only in java and needs to communicate with RN modules + * via their JS-exposed API. + */ +public class JavaOnlyMap implements ReadableMap, WritableMap { + + private final Map mBackingMap; + + public static JavaOnlyMap of(Object... keysAndValues) { + return new JavaOnlyMap(keysAndValues); + } + + public static JavaOnlyMap from(Map map) { + return new JavaOnlyMap(map); + } + + public static JavaOnlyMap deepClone(ReadableMap map) { + JavaOnlyMap res = new JavaOnlyMap(); + ReadableMapKeySetIterator iter = map.keySetIterator(); + while (iter.hasNextKey()) { + String propKey = iter.nextKey(); + ReadableType type = map.getType(propKey); + switch (type) { + case Null: + res.putNull(propKey); + break; + case Boolean: + res.putBoolean(propKey, map.getBoolean(propKey)); + break; + case Number: + res.putDouble(propKey, map.getDouble(propKey)); + break; + case String: + res.putString(propKey, map.getString(propKey)); + break; + case Map: + res.putMap(propKey, deepClone(map.getMap(propKey))); + break; + case Array: + res.putArray(propKey, JavaOnlyArray.deepClone(map.getArray(propKey))); + break; + } + } + return res; + } + + /** + * @param keysAndValues keys and values, interleaved + */ + private JavaOnlyMap(Object... keysAndValues) { + if (keysAndValues.length % 2 != 0) { + throw new IllegalArgumentException("You must provide the same number of keys and values"); + } + mBackingMap = new HashMap(); + for (int i = 0; i < keysAndValues.length; i += 2) { + Object val = keysAndValues[i + 1]; + if (val instanceof Number) { + // all values from JS are doubles, so emulate that here for tests. + val = ((Number) val).doubleValue(); + } + mBackingMap.put(keysAndValues[i], val); + } + } + + public JavaOnlyMap() { + mBackingMap = new HashMap(); + } + + @Override + public boolean hasKey(@NonNull String name) { + return mBackingMap.containsKey(name); + } + + @Override + public boolean isNull(@NonNull String name) { + return mBackingMap.get(name) == null; + } + + @Override + public boolean getBoolean(@NonNull String name) { + return (Boolean) mBackingMap.get(name); + } + + @Override + public double getDouble(@NonNull String name) { + return ((Number) mBackingMap.get(name)).doubleValue(); + } + + @Override + public int getInt(@NonNull String name) { + return ((Number) mBackingMap.get(name)).intValue(); + } + + @Override + public long getLong(@NonNull String name) { + return ((Number) checkNotNull(mBackingMap.get(name))).longValue(); + } + + @Override + public String getString(@NonNull String name) { + return (String) mBackingMap.get(name); + } + + @Override + public ReadableMap getMap(@NonNull String name) { + return (ReadableMap) mBackingMap.get(name); + } + + @Override + public ReadableArray getArray(@NonNull String name) { + return (ReadableArray) mBackingMap.get(name); + } + + @Override + public @NonNull Dynamic getDynamic(@NonNull String name) { + return DynamicFromMap.create(this, name); + } + + @Override + public @NonNull ReadableType getType(@NonNull String name) { + Object value = mBackingMap.get(name); + if (value == null) { + return ReadableType.Null; + } else if (value instanceof Number) { + return ReadableType.Number; + } else if (value instanceof String) { + return ReadableType.String; + } else if (value instanceof Boolean) { + return ReadableType.Boolean; + } else if (value instanceof ReadableMap) { + return ReadableType.Map; + } else if (value instanceof ReadableArray) { + return ReadableType.Array; + } else if (value instanceof Dynamic) { + return ((Dynamic) value).getType(); + } else { + throw new IllegalArgumentException( + "Invalid value " + value.toString() + " for key " + name + "contained in JavaOnlyMap"); + } + } + + @Override + public @NonNull Iterator> getEntryIterator() { + return mBackingMap.entrySet().iterator(); + } + + @Override + public @NonNull ReadableMapKeySetIterator keySetIterator() { + return new ReadableMapKeySetIterator() { + Iterator> mIterator = mBackingMap.entrySet().iterator(); + + @Override + public boolean hasNextKey() { + return mIterator.hasNext(); + } + + @Override + public String nextKey() { + return mIterator.next().getKey(); + } + }; + } + + @Override + public void putBoolean(@NonNull String key, boolean value) { + mBackingMap.put(key, value); + } + + @Override + public void putDouble(@NonNull String key, double value) { + mBackingMap.put(key, value); + } + + @Override + public void putInt(@NonNull String key, int value) { + mBackingMap.put(key, new Double(value)); + } + + @Override + public void putLong(@NonNull String key, long value) { + mBackingMap.put(key, value); + } + + @Override + public void putString(@NonNull String key, @Nullable String value) { + mBackingMap.put(key, value); + } + + @Override + public void putNull(@NonNull String key) { + mBackingMap.put(key, null); + } + + @Override + public void putMap(@NonNull String key, @Nullable ReadableMap value) { + mBackingMap.put(key, value); + } + + @Override + public void merge(@NonNull ReadableMap source) { + mBackingMap.putAll(((JavaOnlyMap) source).mBackingMap); + } + + @Override + public WritableMap copy() { + final JavaOnlyMap target = new JavaOnlyMap(); + target.merge(this); + return target; + } + + @Override + public void putArray(@NonNull String key, @Nullable ReadableArray value) { + mBackingMap.put(key, value); + } + + @Override + public @NonNull HashMap toHashMap() { + return new HashMap(mBackingMap); + } + + @Override + public String toString() { + return mBackingMap.toString(); + } + + public void remove(@NonNull String key) { + mBackingMap.remove(key); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JavaOnlyMap that = (JavaOnlyMap) o; + + if (mBackingMap != null ? !mBackingMap.equals(that.mBackingMap) : that.mBackingMap != null) + return false; + + return true; + } + + @Override + public int hashCode() { + return mBackingMap != null ? mBackingMap.hashCode() : 0; + } +} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.kt deleted file mode 100644 index e1a9456ddc0..00000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaOnlyMap.kt +++ /dev/null @@ -1,180 +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 java.util.HashMap -import kotlin.collections.Iterator -import kotlin.collections.Map - -/** - * Java [HashMap] backed implementation of [ReadableMap] and [WritableMap] Instances of this class - * SHOULD NOT be used for communication between java and JS, use instances of [WritableNativeMap] - * created via [Arguments#createMap] or just [ReadableMap] interface if you want your "native" - * module method to take a map from JS as an argument. - * - * Main purpose for this class is to be used in java-only unit tests, but could also be used outside - * of tests in the code that operates only in java and needs to communicate with RN modules via - * their JS-exposed API. - */ -public class JavaOnlyMap() : ReadableMap, WritableMap { - public companion object { - @JvmStatic public fun of(vararg keysAndValues: Any?): JavaOnlyMap = JavaOnlyMap(*keysAndValues) - - @JvmStatic public fun from(map: Map): JavaOnlyMap = JavaOnlyMap(map) - - @JvmStatic - public fun deepClone(map: ReadableMap?): JavaOnlyMap { - val res = JavaOnlyMap() - if (map == null) { - return res - } - val iter = map.keySetIterator() - while (iter.hasNextKey()) { - val propKey = iter.nextKey() - val type = map.getType(propKey) - when (type) { - ReadableType.Null -> res.putNull(propKey) - ReadableType.Boolean -> res.putBoolean(propKey, map.getBoolean(propKey)) - ReadableType.Number -> res.putDouble(propKey, map.getDouble(propKey)) - ReadableType.String -> res.putString(propKey, map.getString(propKey)) - ReadableType.Map -> res.putMap(propKey, deepClone(checkNotNull(map.getMap(propKey)))) - ReadableType.Array -> - res.putArray(propKey, JavaOnlyArray.deepClone(map.getArray(propKey))) - } - } - return res - } - } - - private val backingMap: MutableMap = HashMap() - - /** @param keysAndValues keys and values, interleaved */ - private constructor(vararg keysAndValues: Any?) : this() { - require(keysAndValues.size % 2 == 0) { "You must provide the same number of keys and values" } - for (i in keysAndValues.indices step 2) { - var value = keysAndValues[i + 1] - if (value is Number) { - // all values from JS are doubles, so emulate that here for tests. - value = value.toDouble() - } - backingMap[keysAndValues[i] as String] = value - } - } - - override fun hasKey(name: String): Boolean = backingMap.containsKey(name) - - override fun isNull(name: String): Boolean = backingMap[name] == null - - override fun getBoolean(name: String): Boolean = backingMap[name] as Boolean - - override fun getDouble(name: String): Double = (backingMap[name] as Number).toDouble() - - override fun getInt(name: String): Int = (backingMap[name] as Number).toInt() - - override fun getLong(name: String): Long = (checkNotNull(backingMap[name] as Number).toLong()) - - override fun getString(name: String): String? = backingMap[name] as String? - - override fun getMap(name: String): ReadableMap? = backingMap[name] as ReadableMap? - - override fun getArray(name: String): ReadableArray? = backingMap[name] as ReadableArray? - - override fun getDynamic(name: String): Dynamic = DynamicFromMap.create(this, name) - - override fun getType(name: String): ReadableType { - val value = backingMap[name] - return when { - value == null -> ReadableType.Null - value is Number -> ReadableType.Number - value is String -> ReadableType.String - value is Boolean -> ReadableType.Boolean - value is ReadableMap -> ReadableType.Map - value is ReadableArray -> ReadableType.Array - value is Dynamic -> value.type - else -> { - throw IllegalArgumentException( - "Invalid value $value for key $name contained in JavaOnlyMap") - } - } - } - - override val entryIterator: Iterator> - get() = backingMap.entries.iterator() - - override fun keySetIterator(): ReadableMapKeySetIterator { - return object : ReadableMapKeySetIterator { - private val iterator = backingMap.entries.iterator() - - override fun hasNextKey(): Boolean = iterator.hasNext() - - override fun nextKey(): String = iterator.next().key - } - } - - override fun putBoolean(key: String, value: Boolean) { - backingMap[key] = value - } - - override fun putDouble(key: String, value: Double) { - backingMap[key] = value - } - - override fun putInt(key: String, value: Int) { - backingMap[key] = value.toDouble() - } - - override fun putLong(key: String, value: Long) { - backingMap[key] = value.toDouble() - } - - override fun putString(key: String, value: String?) { - backingMap[key] = value - } - - override fun putNull(key: String) { - backingMap[key] = null - } - - override fun putMap(key: String, value: ReadableMap?) { - backingMap[key] = value - } - - override fun merge(source: ReadableMap) { - backingMap.putAll((source as JavaOnlyMap).backingMap) - } - - override fun copy(): WritableMap { - val target = JavaOnlyMap() - target.merge(this) - return target - } - - override fun putArray(key: String, value: ReadableArray?) { - backingMap[key] = value - } - - public fun remove(key: String) { - backingMap.remove(key) - } - - override fun toHashMap(): HashMap = HashMap(backingMap) - - override fun toString(): String = backingMap.toString() - - override fun equals(other: Any?): Boolean { - return if (this === other) { - true - } else if (other == null || javaClass != other.javaClass) { - false - } else { - backingMap == (other as JavaOnlyMap).backingMap - } - } - - override fun hashCode(): Int = backingMap.hashCode() -} diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.kt index 4920fb6dda4..a8319fcecae 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableMap.kt @@ -16,7 +16,7 @@ import kotlin.collections.Map * Kotlin. */ public interface ReadableMap { - public val entryIterator: Iterator> + public val entryIterator: Iterator> public fun getArray(name: String): ReadableArray? @@ -42,5 +42,5 @@ public interface ReadableMap { public fun keySetIterator(): ReadableMapKeySetIterator - public fun toHashMap(): HashMap + public fun toHashMap(): HashMap } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.kt index 580282b5134..68812fe11f6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.kt @@ -20,10 +20,10 @@ public open class ReadableNativeMap protected constructor() : NativeMap(), Reada private val keys: Array by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { importKeys().also { jniPassCounter++ } } - private val localMap: HashMap by + private val localMap: HashMap by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { val length = keys.size - val res = HashMap(length) + val res = HashMap(length) val values = importValues() jniPassCounter++ for (i in 0 until length) { @@ -157,7 +157,7 @@ public open class ReadableNativeMap protected constructor() : NativeMap(), Reada false } else localMap == other.localMap - override fun toHashMap(): HashMap { + override fun toHashMap(): HashMap { // we can almost just return getLocalMap(), but we need to convert nested arrays and maps to the // correct types first val hashMap = HashMap(localMap)