Add dynamic type for javascript arguments passed over bridge with unkown type

Reviewed By: astreet

Differential Revision: D4380882

fbshipit-source-id: f1b9fb9cf727d003dcc2264626e75fc300a47dee
This commit is contained in:
Emil Sjolander 2017-01-08 04:28:14 -08:00 committed by Facebook Github Bot
parent 3b5f04b002
commit e3c8d80b3c
14 changed files with 217 additions and 3 deletions

View File

@ -16,6 +16,7 @@ import java.util.Set;
import com.facebook.react.bridge.BaseJavaModule;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.InvalidIteratorException;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NoSuchKeyException;
@ -45,6 +46,7 @@ public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTe
private interface TestJSToJavaParametersModule extends JavaScriptModule {
void returnBasicTypes();
void returnDynamicTypes();
void returnArrayWithBasicTypes();
void returnNestedArray();
@ -113,6 +115,17 @@ public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTe
assertNull(args[3]);
}
public void testDynamicType() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnDynamicTypes();
waitForBridgeAndUIIdle();
List<Dynamic> dynamicCalls = mRecordingTestModule.getDynamicCalls();
assertEquals(2, dynamicCalls.size());
assertEquals("foo", dynamicCalls.get(0).asString());
assertEquals(3.14, dynamicCalls.get(1).asDouble());
}
public void testArrayWithBasicTypes() {
mCatalystInstance.getJSModule(TestJSToJavaParametersModule.class).returnArrayWithBasicTypes();
waitForBridgeAndUIIdle();
@ -673,6 +686,7 @@ public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTe
private final List<Object[]> mBasicTypesCalls = new ArrayList<Object[]>();
private final List<ReadableArray> mArrayCalls = new ArrayList<ReadableArray>();
private final List<ReadableMap> mMapCalls = new ArrayList<ReadableMap>();
private final List<Dynamic> mDynamicCalls = new ArrayList<Dynamic>();
@Override
public String getName() {
@ -694,6 +708,11 @@ public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTe
mMapCalls.add(map);
}
@ReactMethod
public void receiveDynamic(Dynamic dynamic) {
mDynamicCalls.add(dynamic);
}
public List<Object[]> getBasicTypesCalls() {
return mBasicTypesCalls;
}
@ -705,5 +724,9 @@ public class CatalystNativeJSToJavaParametersTestCase extends ReactIntegrationTe
public List<ReadableMap> getMapCalls() {
return mMapCalls;
}
public List<Dynamic> getDynamicCalls() {
return mDynamicCalls;
}
}
}

View File

@ -18,6 +18,10 @@ var TestJSToJavaParametersModule = {
returnBasicTypes: function() {
Recording.receiveBasicTypes('foo', 3.14, true, null);
},
returnDynamicTypes: function() {
Recording.receiveDynamic('foo');
Recording.receiveDynamic(3.14);
},
returnArrayWithBasicTypes: function() {
Recording.receiveArray(['foo', 3.14, -111, true, null]);
},

View File

@ -116,6 +116,15 @@ public abstract class BaseJavaModule implements NativeModule {
}
};
static final private ArgumentExtractor<Dynamic> ARGUMENT_EXTRACTOR_DYNAMIC =
new ArgumentExtractor<Dynamic>() {
@Override
public Dynamic extractArgument(
CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
return new DynamicFromArray(jsArguments, atIndex);
}
};
static final private ArgumentExtractor<ReadableMap> ARGUMENT_EXTRACTOR_MAP =
new ArgumentExtractor<ReadableMap>() {
@Override
@ -259,6 +268,8 @@ public abstract class BaseJavaModule implements NativeModule {
argumentExtractors[i] = ARGUMENT_EXTRACTOR_MAP;
} else if (argumentClass == ReadableArray.class) {
argumentExtractors[i] = ARGUMENT_EXTRACTOR_ARRAY;
} else if (argumentClass == Dynamic.class) {
argumentExtractors[i] = ARGUMENT_EXTRACTOR_DYNAMIC;
} else {
throw new RuntimeException(
"Got unknown argument class: " + argumentClass.getSimpleName());
@ -484,6 +495,8 @@ public abstract class BaseJavaModule implements NativeModule {
return 'M';
} else if (paramClass == ReadableArray.class) {
return 'A';
} else if (paramClass == Dynamic.class) {
return 'Y';
} else {
throw new RuntimeException(
"Got unknown param class: " + paramClass.getSimpleName());

View File

@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-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.react.bridge;
/**
* Type representing a piece of data with unkown runtime type. Useful for allowing javascript to
* pass one of multiple types down to the native layer.
*/
public interface Dynamic {
boolean asBoolean();
double asDouble();
int asInt();
String asString();
ReadableArray asArray();
ReadableMap asMap();
ReadableType getType();
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2015-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.react.bridge;
/**
* Implementation of Dynamic wrapping a ReadableArray.
*/
public class DynamicFromArray implements Dynamic {
private final ReadableArray mArray;
private final int mIndex;
public DynamicFromArray(ReadableArray array, int index) {
mArray = array;
mIndex = index;
}
@Override
public boolean asBoolean() {
return mArray.getBoolean(mIndex);
}
@Override
public double asDouble() {
return mArray.getDouble(mIndex);
}
@Override
public int asInt() {
return mArray.getInt(mIndex);
}
@Override
public String asString() {
return mArray.getString(mIndex);
}
@Override
public ReadableArray asArray() {
return mArray.getArray(mIndex);
}
@Override
public ReadableMap asMap() {
return mArray.getMap(mIndex);
}
@Override
public ReadableType getType() {
return mArray.getType(mIndex);
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright (c) 2015-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.react.bridge;
/**
* Implementation of Dynamic wrapping a ReadableMap.
*/
public class DynamicFromMap implements Dynamic {
private final ReadableMap mMap;
private final String mName;
public DynamicFromMap(ReadableMap map, String name) {
mMap = map;
mName = name;
}
@Override
public boolean asBoolean() {
return mMap.getBoolean(mName);
}
@Override
public double asDouble() {
return mMap.getDouble(mName);
}
@Override
public int asInt() {
return mMap.getInt(mName);
}
@Override
public String asString() {
return mMap.getString(mName);
}
@Override
public ReadableArray asArray() {
return mMap.getArray(mName);
}
@Override
public ReadableMap asMap() {
return mMap.getMap(mName);
}
@Override
public ReadableType getType() {
return mMap.getType(mName);
}
}

View File

@ -88,6 +88,11 @@ public class JavaOnlyArray implements ReadableArray, WritableArray {
return (JavaOnlyMap) mBackingList.get(index);
}
@Override
public Dynamic getDynamic(int index) {
return new DynamicFromArray(this, index);
}
@Override
public ReadableType getType(int index) {
Object object = mBackingList.get(index);

View File

@ -88,6 +88,11 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
return (JavaOnlyArray) mBackingMap.get(name);
}
@Override
public Dynamic getDynamic(String name) {
return new DynamicFromMap(this, name);
}
@Override
public ReadableType getType(String name) {
Object value = mBackingMap.get(name);
@ -103,6 +108,8 @@ public class JavaOnlyMap implements ReadableMap, WritableMap {
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");

View File

@ -23,5 +23,6 @@ public interface ReadableArray {
String getString(int index);
ReadableArray getArray(int index);
ReadableMap getMap(int index);
Dynamic getDynamic(int index);
ReadableType getType(int index);
}

View File

@ -23,6 +23,7 @@ public interface ReadableMap {
String getString(String name);
ReadableArray getArray(String name);
ReadableMap getMap(String name);
Dynamic getDynamic(String name);
ReadableType getType(String name);
ReadableMapKeySetIterator keySetIterator();
}

View File

@ -11,7 +11,6 @@ package com.facebook.react.bridge;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
import java.util.ArrayList;
@ -48,6 +47,11 @@ public class ReadableNativeArray extends NativeArray implements ReadableArray {
@Override
public native ReadableType getType(int index);
@Override
public Dynamic getDynamic(int index) {
return new DynamicFromArray(this, index);
}
public ArrayList<Object> toArrayList() {
ArrayList<Object> arrayList = new ArrayList<>();

View File

@ -11,11 +11,9 @@ package com.facebook.react.bridge;
import com.facebook.jni.HybridData;
import com.facebook.proguard.annotations.DoNotStrip;
import com.facebook.soloader.SoLoader;
import java.util.HashMap;
/**
* Implementation of a read-only map in native memory. This will generally be constructed and filled
* in native code so you shouldn't construct one yourself.
@ -49,6 +47,11 @@ public class ReadableNativeMap extends NativeMap implements ReadableMap {
@Override
public native ReadableType getType(String name);
@Override
public Dynamic getDynamic(String name) {
return new DynamicFromMap(this, name);
}
@Override
public ReadableMapKeySetIterator keySetIterator() {
return new ReadableNativeMapKeySetIterator(this);

View File

@ -33,6 +33,7 @@ import java.util.Set;
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.uimanager.annotations.ReactPropertyHolder;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.annotations.ReactPropGroup;
@ -68,6 +69,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
private static final TypeName STRING_TYPE = TypeName.get(String.class);
private static final TypeName READABLE_MAP_TYPE = TypeName.get(ReadableMap.class);
private static final TypeName READABLE_ARRAY_TYPE = TypeName.get(ReadableArray.class);
private static final TypeName DYNAMIC_TYPE = TypeName.get(Dynamic.class);
private static final TypeName VIEW_MANAGER_TYPE =
ClassName.get("com.facebook.react.uimanager", "ViewManager");
@ -118,6 +120,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
DEFAULT_TYPES.put(STRING_TYPE, "String");
DEFAULT_TYPES.put(READABLE_ARRAY_TYPE, "Array");
DEFAULT_TYPES.put(READABLE_MAP_TYPE, "Map");
DEFAULT_TYPES.put(DYNAMIC_TYPE, "Dynamic");
BOXED_PRIMITIVES = new HashSet<>();
BOXED_PRIMITIVES.add(TypeName.BOOLEAN.box());
@ -365,6 +368,8 @@ public class ReactPropertyProcessor extends AbstractProcessor {
return builder.add("props.getArray(name)");
} else if (propertyType.equals(READABLE_MAP_TYPE)) {
return builder.add("props.getMap(name)");
} else if (propertyType.equals(DYNAMIC_TYPE)) {
return builder.add("props.getDynamic(name)");
}
if (BOXED_PRIMITIVES.contains(propertyType)) {

View File

@ -15,6 +15,7 @@ import android.view.View;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.Dynamic;
/**
* Wrapper for {@link ReadableMap} which should be used for styles property map. It extends
@ -82,6 +83,11 @@ public class ReactStylesDiffMap {
return mBackingMap.getMap(key);
}
@Nullable
public Dynamic getDynamic(String key) {
return mBackingMap.getDynamic(key);
}
@Override
public String toString() {
return "{ " + getClass().getSimpleName() + ": " + mBackingMap.toString() + " }";