mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
use DynamicFromObject to avoid maps
Summary: Changes our property access pattern to iterate through props once and pass the Object value directly rather than looking the value up in the map with the key. Note some ViewManagers methods (especially yoga related ones on shadow nodes) expect a `Dyanamic`, so this diff also creates Dynamic's only when needed by the hand-written code, and introduces a new `DynamicWithObject` to create them that simply wraps the underlying object (as opposed to `DynamicWithMap` which wraps the map and does a lookup any time the `Dynamic` is accessed. Reviewed By: mdvacca Differential Revision: D14453300 fbshipit-source-id: df98567b6eff1e6b7c611f179eb11e413fb94e5d
This commit is contained in:
committed by
Facebook Github Bot
parent
af38a0cf87
commit
a46fba5dd3
+122
-92
@@ -8,6 +8,7 @@ package com.facebook.react.uimanager;
|
||||
import android.view.View;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.DynamicFromObject;
|
||||
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
@@ -33,7 +34,7 @@ import javax.annotation.Nullable;
|
||||
EMPTY_PROPS_MAP.clear();
|
||||
}
|
||||
|
||||
/*package*/ static abstract class PropSetter {
|
||||
/*package*/ abstract static class PropSetter {
|
||||
|
||||
protected final String mPropName;
|
||||
protected final String mPropType;
|
||||
@@ -50,16 +51,18 @@ import javax.annotation.Nullable;
|
||||
|
||||
private PropSetter(ReactProp prop, String defaultType, Method setter) {
|
||||
mPropName = prop.name();
|
||||
mPropType = ReactProp.USE_DEFAULT_TYPE.equals(prop.customType()) ?
|
||||
defaultType : prop.customType();
|
||||
mPropType =
|
||||
ReactProp.USE_DEFAULT_TYPE.equals(prop.customType()) ? defaultType : prop.customType();
|
||||
mSetter = setter;
|
||||
mIndex = null;
|
||||
}
|
||||
|
||||
private PropSetter(ReactPropGroup prop, String defaultType, Method setter, int index) {
|
||||
mPropName = prop.names()[index];
|
||||
mPropType = ReactPropGroup.USE_DEFAULT_TYPE.equals(prop.customType()) ?
|
||||
defaultType : prop.customType();
|
||||
mPropType =
|
||||
ReactPropGroup.USE_DEFAULT_TYPE.equals(prop.customType())
|
||||
? defaultType
|
||||
: prop.customType();
|
||||
mSetter = setter;
|
||||
mIndex = index;
|
||||
}
|
||||
@@ -72,52 +75,55 @@ import javax.annotation.Nullable;
|
||||
return mPropType;
|
||||
}
|
||||
|
||||
public void updateViewProp(
|
||||
ViewManager viewManager,
|
||||
View viewToUpdate,
|
||||
ReactStylesDiffMap props) {
|
||||
public void updateViewProp(ViewManager viewManager, View viewToUpdate, Object value) {
|
||||
try {
|
||||
if (mIndex == null) {
|
||||
VIEW_MGR_ARGS[0] = viewToUpdate;
|
||||
VIEW_MGR_ARGS[1] = extractProperty(props);
|
||||
VIEW_MGR_ARGS[1] = getValueOrDefault(value);
|
||||
mSetter.invoke(viewManager, VIEW_MGR_ARGS);
|
||||
Arrays.fill(VIEW_MGR_ARGS, null);
|
||||
} else {
|
||||
VIEW_MGR_GROUP_ARGS[0] = viewToUpdate;
|
||||
VIEW_MGR_GROUP_ARGS[1] = mIndex;
|
||||
VIEW_MGR_GROUP_ARGS[2] = extractProperty(props);
|
||||
VIEW_MGR_GROUP_ARGS[2] = getValueOrDefault(value);
|
||||
mSetter.invoke(viewManager, VIEW_MGR_GROUP_ARGS);
|
||||
Arrays.fill(VIEW_MGR_GROUP_ARGS, null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
|
||||
throw new JSApplicationIllegalArgumentException("Error while updating property '" +
|
||||
mPropName + "' of a view managed by: " + viewManager.getName(), t);
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Error while updating property '"
|
||||
+ mPropName
|
||||
+ "' of a view managed by: "
|
||||
+ viewManager.getName(),
|
||||
t);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateShadowNodeProp(
|
||||
ReactShadowNode nodeToUpdate,
|
||||
ReactStylesDiffMap props) {
|
||||
public void updateShadowNodeProp(ReactShadowNode nodeToUpdate, Object value) {
|
||||
try {
|
||||
if (mIndex == null) {
|
||||
SHADOW_ARGS[0] = extractProperty(props);
|
||||
SHADOW_ARGS[0] = getValueOrDefault(value);
|
||||
mSetter.invoke(nodeToUpdate, SHADOW_ARGS);
|
||||
Arrays.fill(SHADOW_ARGS, null);
|
||||
} else {
|
||||
SHADOW_GROUP_ARGS[0] = mIndex;
|
||||
SHADOW_GROUP_ARGS[1] = extractProperty(props);
|
||||
SHADOW_GROUP_ARGS[1] = getValueOrDefault(value);
|
||||
mSetter.invoke(nodeToUpdate, SHADOW_GROUP_ARGS);
|
||||
Arrays.fill(SHADOW_GROUP_ARGS, null);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
FLog.e(ViewManager.class, "Error while updating prop " + mPropName, t);
|
||||
throw new JSApplicationIllegalArgumentException("Error while updating property '" +
|
||||
mPropName + "' in shadow node of type: " + nodeToUpdate.getViewClass(), t);
|
||||
throw new JSApplicationIllegalArgumentException(
|
||||
"Error while updating property '"
|
||||
+ mPropName
|
||||
+ "' in shadow node of type: "
|
||||
+ nodeToUpdate.getViewClass(),
|
||||
t);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract @Nullable Object extractProperty(ReactStylesDiffMap props);
|
||||
protected abstract @Nullable Object getValueOrDefault(Object value);
|
||||
}
|
||||
|
||||
private static class DynamicPropSetter extends PropSetter {
|
||||
@@ -131,8 +137,12 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getDynamic(mPropName);
|
||||
protected Object getValueOrDefault(Object value) {
|
||||
if (value instanceof Dynamic) {
|
||||
return value;
|
||||
} else {
|
||||
return new DynamicFromObject(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +161,9 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getInt(mPropName, mDefaultValue);
|
||||
protected Object getValueOrDefault(Object value) {
|
||||
// All numbers from JS are Doubles which can't be simply cast to Integer
|
||||
return value == null ? mDefaultValue : (Integer) ((Double)value).intValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,8 +182,8 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getDouble(mPropName, mDefaultValue);
|
||||
protected Object getValueOrDefault(Object value) {
|
||||
return value == null ? mDefaultValue : (Double) value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +197,9 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getBoolean(mPropName, mDefaultValue) ? Boolean.TRUE : Boolean.FALSE;
|
||||
protected Object getValueOrDefault(Object value) {
|
||||
boolean val = value == null ? mDefaultValue : (boolean) value;
|
||||
return val ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,8 +218,9 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getFloat(mPropName, mDefaultValue);
|
||||
protected Object getValueOrDefault(Object value) {
|
||||
// All numbers from JS are Doubles which can't be simply cast to Float
|
||||
return value == null ? mDefaultValue : (Float) ((Double)value).floatValue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,8 +231,8 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getArray(mPropName);
|
||||
protected @Nullable Object getValueOrDefault(Object value) {
|
||||
return (ReadableArray) value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,8 +243,8 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getMap(mPropName);
|
||||
protected @Nullable Object getValueOrDefault(Object value) {
|
||||
return (ReadableMap) value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,8 +255,8 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
|
||||
return props.getString(mPropName);
|
||||
protected @Nullable Object getValueOrDefault(Object value) {
|
||||
return (String) value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,9 +267,9 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
|
||||
if (!props.isNull(mPropName)) {
|
||||
return props.getBoolean(mPropName, /* ignored */ false) ? Boolean.TRUE : Boolean.FALSE;
|
||||
protected @Nullable Object getValueOrDefault(Object value) {
|
||||
if (value != null) {
|
||||
return (boolean) value ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -273,9 +286,9 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable Object extractProperty(ReactStylesDiffMap props) {
|
||||
if (!props.isNull(mPropName)) {
|
||||
return props.getInt(mPropName, /* ignored */ 0);
|
||||
protected @Nullable Object getValueOrDefault(Object value) {
|
||||
if (value != null) {
|
||||
return (Integer) value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -317,9 +330,10 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
// This is to include all the setters from parent classes. Once calculated the result will be
|
||||
// stored in CLASS_PROPS_CACHE so that we only scan for @ReactProp annotations once per class.
|
||||
props = new HashMap<>(
|
||||
getNativePropSettersForViewManagerClass(
|
||||
(Class<? extends ViewManager>) cls.getSuperclass()));
|
||||
props =
|
||||
new HashMap<>(
|
||||
getNativePropSettersForViewManagerClass(
|
||||
(Class<? extends ViewManager>) cls.getSuperclass()));
|
||||
extractPropSettersFromViewManagerClassDefinition(cls, props);
|
||||
CLASS_PROPS_CACHE.put(cls, props);
|
||||
return props;
|
||||
@@ -343,18 +357,17 @@ import javax.annotation.Nullable;
|
||||
return props;
|
||||
}
|
||||
// This is to include all the setters from parent classes up to ReactShadowNode class
|
||||
props = new HashMap<>(
|
||||
getNativePropSettersForShadowNodeClass(
|
||||
(Class<? extends ReactShadowNode>) cls.getSuperclass()));
|
||||
props =
|
||||
new HashMap<>(
|
||||
getNativePropSettersForShadowNodeClass(
|
||||
(Class<? extends ReactShadowNode>) cls.getSuperclass()));
|
||||
extractPropSettersFromShadowNodeClassDefinition(cls, props);
|
||||
CLASS_PROPS_CACHE.put(cls, props);
|
||||
return props;
|
||||
}
|
||||
|
||||
private static PropSetter createPropSetter(
|
||||
ReactProp annotation,
|
||||
Method method,
|
||||
Class<?> propTypeClass) {
|
||||
ReactProp annotation, Method method, Class<?> propTypeClass) {
|
||||
if (propTypeClass == Dynamic.class) {
|
||||
return new DynamicPropSetter(annotation, method);
|
||||
} else if (propTypeClass == boolean.class) {
|
||||
@@ -376,8 +389,13 @@ import javax.annotation.Nullable;
|
||||
} else if (propTypeClass == ReadableMap.class) {
|
||||
return new MapPropSetter(annotation, method);
|
||||
} else {
|
||||
throw new RuntimeException("Unrecognized type: " + propTypeClass + " for method: " +
|
||||
method.getDeclaringClass().getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Unrecognized type: "
|
||||
+ propTypeClass
|
||||
+ " for method: "
|
||||
+ method.getDeclaringClass().getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,43 +407,38 @@ import javax.annotation.Nullable;
|
||||
String[] names = annotation.names();
|
||||
if (propTypeClass == Dynamic.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new DynamicPropSetter(annotation, method, i));
|
||||
props.put(names[i], new DynamicPropSetter(annotation, method, i));
|
||||
}
|
||||
} else if (propTypeClass == int.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new IntPropSetter(annotation, method, i, annotation.defaultInt()));
|
||||
props.put(names[i], new IntPropSetter(annotation, method, i, annotation.defaultInt()));
|
||||
}
|
||||
} else if (propTypeClass == float.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new FloatPropSetter(annotation, method, i, annotation.defaultFloat()));
|
||||
props.put(names[i], new FloatPropSetter(annotation, method, i, annotation.defaultFloat()));
|
||||
}
|
||||
} else if (propTypeClass == double.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new DoublePropSetter(annotation, method, i, annotation.defaultDouble()));
|
||||
names[i], new DoublePropSetter(annotation, method, i, annotation.defaultDouble()));
|
||||
}
|
||||
} else if (propTypeClass == Integer.class) {
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
props.put(
|
||||
names[i],
|
||||
new BoxedIntPropSetter(annotation, method, i));
|
||||
props.put(names[i], new BoxedIntPropSetter(annotation, method, i));
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Unrecognized type: " + propTypeClass + " for method: " +
|
||||
method.getDeclaringClass().getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Unrecognized type: "
|
||||
+ propTypeClass
|
||||
+ " for method: "
|
||||
+ method.getDeclaringClass().getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static void extractPropSettersFromViewManagerClassDefinition(
|
||||
Class<? extends ViewManager> cls,
|
||||
Map<String, PropSetter> props) {
|
||||
Class<? extends ViewManager> cls, Map<String, PropSetter> props) {
|
||||
Method[] declaredMethods = cls.getDeclaredMethods();
|
||||
for (int i = 0; i < declaredMethods.length; i++) {
|
||||
Method method = declaredMethods[i];
|
||||
@@ -433,30 +446,42 @@ import javax.annotation.Nullable;
|
||||
if (annotation != null) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 2) {
|
||||
throw new RuntimeException("Wrong number of args for prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Wrong number of args for prop setter: " + cls.getName() + "#" + method.getName());
|
||||
}
|
||||
if (!View.class.isAssignableFrom(paramTypes[0])) {
|
||||
throw new RuntimeException("First param should be a view subclass to be updated: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"First param should be a view subclass to be updated: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
props.put(annotation.name(), createPropSetter(annotation, method, paramTypes[1]));
|
||||
}
|
||||
|
||||
ReactPropGroup groupAnnotation = method.getAnnotation(ReactPropGroup.class);
|
||||
if (groupAnnotation != null) {
|
||||
Class<?> [] paramTypes = method.getParameterTypes();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 3) {
|
||||
throw new RuntimeException("Wrong number of args for group prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Wrong number of args for group prop setter: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
if (!View.class.isAssignableFrom(paramTypes[0])) {
|
||||
throw new RuntimeException("First param should be a view subclass to be updated: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"First param should be a view subclass to be updated: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
if (paramTypes[1] != int.class) {
|
||||
throw new RuntimeException("Second argument should be property index: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Second argument should be property index: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
createPropSetters(groupAnnotation, method, paramTypes[2], props);
|
||||
}
|
||||
@@ -464,29 +489,34 @@ import javax.annotation.Nullable;
|
||||
}
|
||||
|
||||
private static void extractPropSettersFromShadowNodeClassDefinition(
|
||||
Class<? extends ReactShadowNode> cls,
|
||||
Map<String, PropSetter> props) {
|
||||
Class<? extends ReactShadowNode> cls, Map<String, PropSetter> props) {
|
||||
for (Method method : cls.getDeclaredMethods()) {
|
||||
ReactProp annotation = method.getAnnotation(ReactProp.class);
|
||||
if (annotation != null) {
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) {
|
||||
throw new RuntimeException("Wrong number of args for prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Wrong number of args for prop setter: " + cls.getName() + "#" + method.getName());
|
||||
}
|
||||
props.put(annotation.name(), createPropSetter(annotation, method, paramTypes[0]));
|
||||
}
|
||||
|
||||
ReactPropGroup groupAnnotation = method.getAnnotation(ReactPropGroup.class);
|
||||
if (groupAnnotation != null) {
|
||||
Class<?> [] paramTypes = method.getParameterTypes();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 2) {
|
||||
throw new RuntimeException("Wrong number of args for group prop setter: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Wrong number of args for group prop setter: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
if (paramTypes[0] != int.class) {
|
||||
throw new RuntimeException("Second argument should be property index: " +
|
||||
cls.getName() + "#" + method.getName());
|
||||
throw new RuntimeException(
|
||||
"Second argument should be property index: "
|
||||
+ cls.getName()
|
||||
+ "#"
|
||||
+ method.getName());
|
||||
}
|
||||
createPropSetters(groupAnnotation, method, paramTypes[1], props);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user