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
+83
-87
@@ -13,6 +13,7 @@ import static javax.tools.Diagnostic.Kind.WARNING;
|
||||
|
||||
import com.facebook.infer.annotation.SuppressFieldNotInitialized;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.DynamicFromObject;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
@@ -55,9 +56,9 @@ import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* This annotation processor crawls subclasses of ReactShadowNode and ViewManager and finds their
|
||||
* exported properties with the @ReactProp or @ReactGroupProp annotation. It generates a class
|
||||
* per shadow node/view manager that is named {@code <classname>$$PropSetter}. This class contains methods
|
||||
* to retrieve the name and type of all methods and a way to set these properties without
|
||||
* exported properties with the @ReactProp or @ReactGroupProp annotation. It generates a class per
|
||||
* shadow node/view manager that is named {@code <classname>$$PropSetter}. This class contains
|
||||
* methods to retrieve the name and type of all methods and a way to set these properties without
|
||||
* reflection.
|
||||
*/
|
||||
@SupportedAnnotationTypes("com.facebook.react.uimanager.annotations.ReactPropertyHolder")
|
||||
@@ -68,10 +69,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private static final TypeName PROPS_TYPE =
|
||||
ClassName.get("com.facebook.react.uimanager", "ReactStylesDiffMap");
|
||||
private static final TypeName OBJECT_TYPE = TypeName.get(Object.class);
|
||||
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 DYNAMIC_FROM_OBJECT_TYPE = TypeName.get(DynamicFromObject.class);
|
||||
|
||||
private static final TypeName VIEW_MANAGER_TYPE =
|
||||
ClassName.get("com.facebook.react.uimanager", "ViewManager");
|
||||
@@ -80,14 +83,10 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private static final ClassName VIEW_MANAGER_SETTER_TYPE =
|
||||
ClassName.get(
|
||||
"com.facebook.react.uimanager",
|
||||
"ViewManagerPropertyUpdater",
|
||||
"ViewManagerSetter");
|
||||
"com.facebook.react.uimanager", "ViewManagerPropertyUpdater", "ViewManagerSetter");
|
||||
private static final ClassName SHADOW_NODE_SETTER_TYPE =
|
||||
ClassName.get(
|
||||
"com.facebook.react.uimanager",
|
||||
"ViewManagerPropertyUpdater",
|
||||
"ShadowNodeSetter");
|
||||
"com.facebook.react.uimanager", "ViewManagerPropertyUpdater", "ShadowNodeSetter");
|
||||
|
||||
private static final TypeName PROPERTY_MAP_TYPE =
|
||||
ParameterizedTypeName.get(Map.class, String.class, String.class);
|
||||
@@ -96,14 +95,10 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private final Map<ClassName, ClassInfo> mClasses;
|
||||
|
||||
@SuppressFieldNotInitialized
|
||||
private Filer mFiler;
|
||||
@SuppressFieldNotInitialized
|
||||
private Messager mMessager;
|
||||
@SuppressFieldNotInitialized
|
||||
private Elements mElements;
|
||||
@SuppressFieldNotInitialized
|
||||
private Types mTypes;
|
||||
@SuppressFieldNotInitialized private Filer mFiler;
|
||||
@SuppressFieldNotInitialized private Messager mMessager;
|
||||
@SuppressFieldNotInitialized private Elements mElements;
|
||||
@SuppressFieldNotInitialized private Types mTypes;
|
||||
|
||||
static {
|
||||
DEFAULT_TYPES = new HashMap<>();
|
||||
@@ -165,7 +160,8 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
if (!shouldIgnoreClass(classInfo)) {
|
||||
// Sort by name
|
||||
Collections.sort(
|
||||
classInfo.mProperties, new Comparator<PropertyInfo>() {
|
||||
classInfo.mProperties,
|
||||
new Comparator<PropertyInfo>() {
|
||||
@Override
|
||||
public int compare(PropertyInfo a, PropertyInfo b) {
|
||||
return a.mProperty.name().compareTo(b.mProperty.name());
|
||||
@@ -219,8 +215,8 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
classInfo.addProperty(propertyBuilder.build(element, new RegularProperty(prop)));
|
||||
} else if (propGroup != null) {
|
||||
for (int i = 0, size = propGroup.names().length; i < size; i++) {
|
||||
classInfo
|
||||
.addProperty(propertyBuilder.build(element, new GroupProperty(propGroup, i)));
|
||||
classInfo.addProperty(
|
||||
propertyBuilder.build(element, new GroupProperty(propGroup, i)));
|
||||
}
|
||||
}
|
||||
} catch (ReactPropertyException e) {
|
||||
@@ -251,29 +247,32 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private void generateCode(ClassInfo classInfo, List<PropertyInfo> properties)
|
||||
throws IOException, ReactPropertyException {
|
||||
MethodSpec getMethods = MethodSpec.methodBuilder("getProperties")
|
||||
.addModifiers(PUBLIC)
|
||||
.addAnnotation(Override.class)
|
||||
.addParameter(PROPERTY_MAP_TYPE, "props")
|
||||
.returns(TypeName.VOID)
|
||||
.addCode(generateGetProperties(properties))
|
||||
.build();
|
||||
MethodSpec getMethods =
|
||||
MethodSpec.methodBuilder("getProperties")
|
||||
.addModifiers(PUBLIC)
|
||||
.addAnnotation(Override.class)
|
||||
.addParameter(PROPERTY_MAP_TYPE, "props")
|
||||
.returns(TypeName.VOID)
|
||||
.addCode(generateGetProperties(properties))
|
||||
.build();
|
||||
|
||||
TypeName superType = getSuperType(classInfo);
|
||||
ClassName className = classInfo.mClassName;
|
||||
|
||||
String holderClassName =
|
||||
getClassName((TypeElement) classInfo.mElement, className.packageName()) + "$$PropsSetter";
|
||||
TypeSpec holderClass = TypeSpec.classBuilder(holderClassName)
|
||||
.addSuperinterface(superType)
|
||||
.addModifiers(PUBLIC)
|
||||
.addMethod(generateSetPropertySpec(classInfo, properties))
|
||||
.addMethod(getMethods)
|
||||
.build();
|
||||
TypeSpec holderClass =
|
||||
TypeSpec.classBuilder(holderClassName)
|
||||
.addSuperinterface(superType)
|
||||
.addModifiers(PUBLIC)
|
||||
.addMethod(generateSetPropertySpec(classInfo, properties))
|
||||
.addMethod(getMethods)
|
||||
.build();
|
||||
|
||||
JavaFile javaFile = JavaFile.builder(className.packageName(), holderClass)
|
||||
.addFileComment("Generated by " + getClass().getName())
|
||||
.build();
|
||||
JavaFile javaFile =
|
||||
JavaFile.builder(className.packageName(), holderClass)
|
||||
.addFileComment("Generated by " + getClass().getName())
|
||||
.build();
|
||||
|
||||
javaFile.writeTo(mFiler);
|
||||
}
|
||||
@@ -287,9 +286,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
switch (classInfo.getType()) {
|
||||
case VIEW_MANAGER:
|
||||
return ParameterizedTypeName.get(
|
||||
VIEW_MANAGER_SETTER_TYPE,
|
||||
classInfo.mClassName,
|
||||
classInfo.mViewType);
|
||||
VIEW_MANAGER_SETTER_TYPE, classInfo.mClassName, classInfo.mViewType);
|
||||
case SHADOW_NODE:
|
||||
return ParameterizedTypeName.get(SHADOW_NODE_SETTER_TYPE, classInfo.mClassName);
|
||||
default:
|
||||
@@ -298,12 +295,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private static MethodSpec generateSetPropertySpec(
|
||||
ClassInfo classInfo,
|
||||
List<PropertyInfo> properties) {
|
||||
MethodSpec.Builder builder = MethodSpec.methodBuilder("setProperty")
|
||||
.addModifiers(PUBLIC)
|
||||
.addAnnotation(Override.class)
|
||||
.returns(TypeName.VOID);
|
||||
ClassInfo classInfo, List<PropertyInfo> properties) {
|
||||
MethodSpec.Builder builder =
|
||||
MethodSpec.methodBuilder("setProperty")
|
||||
.addModifiers(PUBLIC)
|
||||
.addAnnotation(Override.class)
|
||||
.returns(TypeName.VOID);
|
||||
|
||||
switch (classInfo.getType()) {
|
||||
case VIEW_MANAGER:
|
||||
@@ -312,14 +309,13 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
.addParameter(classInfo.mViewType, "view");
|
||||
break;
|
||||
case SHADOW_NODE:
|
||||
builder
|
||||
.addParameter(classInfo.mClassName, "node");
|
||||
builder.addParameter(classInfo.mClassName, "node");
|
||||
break;
|
||||
}
|
||||
|
||||
return builder
|
||||
.addParameter(STRING_TYPE, "name")
|
||||
.addParameter(PROPS_TYPE, "props")
|
||||
.addParameter(OBJECT_TYPE, "value")
|
||||
.addCode(generateSetProperty(classInfo, properties))
|
||||
.build();
|
||||
}
|
||||
@@ -334,9 +330,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
builder.add("switch (name) {\n").indent();
|
||||
for (int i = 0, size = properties.size(); i < size; i++) {
|
||||
PropertyInfo propertyInfo = properties.get(i);
|
||||
builder
|
||||
.add("case \"$L\":\n", propertyInfo.mProperty.name())
|
||||
.indent();
|
||||
builder.add("case \"$L\":\n", propertyInfo.mProperty.name()).indent();
|
||||
|
||||
switch (info.getType()) {
|
||||
case VIEW_MANAGER:
|
||||
@@ -350,14 +344,12 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
builder.add("$L, ", ((GroupProperty) propertyInfo.mProperty).mGroupIndex);
|
||||
}
|
||||
if (BOXED_PRIMITIVES.contains(propertyInfo.propertyType)) {
|
||||
builder.add("props.isNull(name) ? null : ");
|
||||
builder.add("value == null ? null : ");
|
||||
}
|
||||
getPropertyExtractor(propertyInfo, builder);
|
||||
builder.addStatement(")");
|
||||
|
||||
builder
|
||||
.addStatement("break")
|
||||
.unindent();
|
||||
builder.addStatement("break").unindent();
|
||||
}
|
||||
builder.unindent().add("}\n");
|
||||
|
||||
@@ -365,17 +357,16 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
private static CodeBlock.Builder getPropertyExtractor(
|
||||
PropertyInfo info,
|
||||
CodeBlock.Builder builder) {
|
||||
PropertyInfo info, CodeBlock.Builder builder) {
|
||||
TypeName propertyType = info.propertyType;
|
||||
if (propertyType.equals(STRING_TYPE)) {
|
||||
return builder.add("props.getString(name)");
|
||||
return builder.add("($L)value", STRING_TYPE);
|
||||
} else if (propertyType.equals(READABLE_ARRAY_TYPE)) {
|
||||
return builder.add("props.getArray(name)");
|
||||
return builder.add("($L)value", READABLE_ARRAY_TYPE); // TODO: use real type but needs import
|
||||
} else if (propertyType.equals(READABLE_MAP_TYPE)) {
|
||||
return builder.add("props.getMap(name)");
|
||||
return builder.add("($L)value", READABLE_MAP_TYPE);
|
||||
} else if (propertyType.equals(DYNAMIC_TYPE)) {
|
||||
return builder.add("props.getDynamic(name)");
|
||||
return builder.add("new $L(value)", DYNAMIC_FROM_OBJECT_TYPE);
|
||||
}
|
||||
|
||||
if (BOXED_PRIMITIVES.contains(propertyType)) {
|
||||
@@ -383,25 +374,27 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
if (propertyType.equals(TypeName.BOOLEAN)) {
|
||||
return builder.add("props.getBoolean(name, $L)", info.mProperty.defaultBoolean());
|
||||
} if (propertyType.equals(TypeName.DOUBLE)) {
|
||||
return builder.add("value == null ? $L : (boolean) value", info.mProperty.defaultBoolean());
|
||||
}
|
||||
if (propertyType.equals(TypeName.DOUBLE)) {
|
||||
double defaultDouble = info.mProperty.defaultDouble();
|
||||
if (Double.isNaN(defaultDouble)) {
|
||||
return builder.add("props.getDouble(name, $T.NaN)", Double.class);
|
||||
return builder.add("value == null ? $T.NaN : (double) value", Double.class);
|
||||
} else {
|
||||
return builder.add("props.getDouble(name, $Lf)", defaultDouble);
|
||||
return builder.add("value == null ? $Lf : (double) value", defaultDouble);
|
||||
}
|
||||
}
|
||||
if (propertyType.equals(TypeName.FLOAT)) {
|
||||
float defaultFloat = info.mProperty.defaultFloat();
|
||||
if (Float.isNaN(defaultFloat)) {
|
||||
return builder.add("props.getFloat(name, $T.NaN)", Float.class);
|
||||
return builder.add("value == null ? $T.NaN : ((Double)value).floatValue()", Float.class);
|
||||
} else {
|
||||
return builder.add("props.getFloat(name, $Lf)", defaultFloat);
|
||||
return builder.add("value == null ? $Lf : ((Double)value).floatValue()", defaultFloat);
|
||||
}
|
||||
}
|
||||
if (propertyType.equals(TypeName.INT)) {
|
||||
return builder.add("props.getInt(name, $L)", info.mProperty.defaultInt());
|
||||
return builder.add(
|
||||
"value == null ? $L : ((Double)value).intValue()", info.mProperty.defaultInt());
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
@@ -424,20 +417,20 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private static String getPropertypTypeName(Property property, TypeName propertyType) {
|
||||
String defaultType = DEFAULT_TYPES.get(propertyType);
|
||||
String useDefaultType = property instanceof RegularProperty ?
|
||||
ReactProp.USE_DEFAULT_TYPE : ReactPropGroup.USE_DEFAULT_TYPE;
|
||||
String useDefaultType =
|
||||
property instanceof RegularProperty
|
||||
? ReactProp.USE_DEFAULT_TYPE
|
||||
: ReactPropGroup.USE_DEFAULT_TYPE;
|
||||
return useDefaultType.equals(property.customType()) ? defaultType : property.customType();
|
||||
}
|
||||
|
||||
private static void checkElement(Element element) throws ReactPropertyException {
|
||||
if (element.getKind() == ElementKind.METHOD
|
||||
&& element.getModifiers().contains(PUBLIC)) {
|
||||
if (element.getKind() == ElementKind.METHOD && element.getModifiers().contains(PUBLIC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ReactPropertyException(
|
||||
"@ReactProp and @ReachPropGroup annotation must be on a public method",
|
||||
element);
|
||||
"@ReactProp and @ReachPropGroup annotation must be on a public method", element);
|
||||
}
|
||||
|
||||
private static boolean shouldIgnoreClass(ClassInfo classInfo) {
|
||||
@@ -464,10 +457,15 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
|
||||
private interface Property {
|
||||
String name();
|
||||
|
||||
String customType();
|
||||
|
||||
double defaultDouble();
|
||||
|
||||
float defaultFloat();
|
||||
|
||||
int defaultInt();
|
||||
|
||||
boolean defaultBoolean();
|
||||
}
|
||||
|
||||
@@ -575,9 +573,13 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
String name = propertyInfo.mProperty.name();
|
||||
if (checkPropertyExists(name)) {
|
||||
throw new ReactPropertyException(
|
||||
"Module " + mClassName + " has already registered a property named \"" +
|
||||
name + "\". If you want to override a property, don't add" +
|
||||
"the @ReactProp annotation to the property in the subclass", propertyInfo);
|
||||
"Module "
|
||||
+ mClassName
|
||||
+ " has already registered a property named \""
|
||||
+ name
|
||||
+ "\". If you want to override a property, don't add"
|
||||
+ "the @ReactProp annotation to the property in the subclass",
|
||||
propertyInfo);
|
||||
}
|
||||
|
||||
mProperties.add(propertyInfo);
|
||||
@@ -601,10 +603,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
public final Property mProperty;
|
||||
|
||||
private PropertyInfo(
|
||||
String methodName,
|
||||
TypeName propertyType,
|
||||
Element element,
|
||||
Property property) {
|
||||
String methodName, TypeName propertyType, Element element, Property property) {
|
||||
this.methodName = methodName;
|
||||
this.propertyType = propertyType;
|
||||
this.element = element;
|
||||
@@ -622,8 +621,7 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
mClassInfo = classInfo;
|
||||
}
|
||||
|
||||
public PropertyInfo build(Element element, Property property)
|
||||
throws ReactPropertyException {
|
||||
public PropertyInfo build(Element element, Property property) throws ReactPropertyException {
|
||||
String methodName = element.getSimpleName().toString();
|
||||
|
||||
ExecutableElement method = (ExecutableElement) element;
|
||||
@@ -645,16 +643,14 @@ public class ReactPropertyProcessor extends AbstractProcessor {
|
||||
TypeName indexType = TypeName.get(parameters.get(index++).asType());
|
||||
if (!indexType.equals(TypeName.INT)) {
|
||||
throw new ReactPropertyException(
|
||||
"Argument " + index + " must be an int for @ReactPropGroup",
|
||||
element);
|
||||
"Argument " + index + " must be an int for @ReactPropGroup", element);
|
||||
}
|
||||
}
|
||||
|
||||
TypeName propertyType = TypeName.get(parameters.get(index++).asType());
|
||||
if (!DEFAULT_TYPES.containsKey(propertyType)) {
|
||||
throw new ReactPropertyException(
|
||||
"Argument " + index + " must be of a supported type",
|
||||
element);
|
||||
"Argument " + index + " must be of a supported type", element);
|
||||
}
|
||||
|
||||
return new PropertyInfo(methodName, propertyType, element, property);
|
||||
|
||||
Reference in New Issue
Block a user