From eecb9302220e52b9850b3217366c16c386bb1789 Mon Sep 17 00:00:00 2001 From: Kevin Gozali Date: Mon, 17 Aug 2020 19:39:45 -0700 Subject: [PATCH] Codegen: JavaGenerator [3] - parse aliases and reserved value types Summary: This parses the JSON schema's aliases and reserved function valye types. It also assigns TypeId correctly now: * TypeId is an identifier for a specific type, that can be referred by others * This means only aliases should have TypeId.typeName. Properties' names are not typeNames * NativeModule spec's typeName is hardcoded to `Spec`, see T71955395 This way, whenever we encounter an `AliasType`, we can just lookup the actual Type by a quick Map lookup with TypeId as the key. Changelog: [Internal] Reviewed By: mdvacca Differential Revision: D23181432 fbshipit-source-id: 9d0ea17dbf601589d8f3fc1955e0c9406a80e244 --- .../gradlePlugin/build.gradle | 3 +- .../codegen/generator/JavaGenerator.java | 5 ++ .../codegen/generator/SchemaJsonParser.java | 77 ++++++++++++------- .../generator/model/NativeModuleType.java | 7 +- .../codegen/generator/model/RawSchema.java | 8 +- .../react/codegen/generator/model/TypeId.java | 2 +- 6 files changed, 65 insertions(+), 37 deletions(-) diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle index 5bd9f4d416f..e19a60c87dd 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/build.gradle @@ -20,7 +20,8 @@ gradlePlugin { dependencies { implementation 'com.android.tools.build:gradle:3.5.3' - implementation 'com.google.code.gson:gson:2.8.6' + // Use the same Gson version that `com.android.tools.build:gradle` depends on. + implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.google.guava:guava:29.0-jre' implementation 'com.squareup:javapoet:1.13.0' } diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java index 05aa1e9bb4b..2ad0aa51cd0 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/JavaGenerator.java @@ -7,6 +7,7 @@ package com.facebook.react.codegen.generator; +import com.facebook.react.codegen.generator.model.RawSchema; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; @@ -28,6 +29,10 @@ public final class JavaGenerator { } public void build() throws FileNotFoundException, IOException { + final RawSchema rawSchema = SchemaJsonParser.parse(mSchemaFile); + // TODO (T71663018): remove - this is for debugging + System.out.println(rawSchema); + final MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java index 526c3134c3e..fa9c67352c9 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/SchemaJsonParser.java @@ -37,7 +37,6 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -47,7 +46,7 @@ public final class SchemaJsonParser { final JsonParser parser = new JsonParser(); final JsonElement rootElement = parser.parse(new FileReader(schemaFile)); - final Map> collection = new HashMap<>(); + final Map> collection = new HashMap<>(); if (rootElement.isJsonObject()) { final JsonObject root = rootElement.getAsJsonObject(); @@ -67,13 +66,16 @@ public final class SchemaJsonParser { collection.put( jsModuleName, nativeModules.entrySet().stream() - .map( - nativeModuleEntry -> { - return parseNativeModule( - TypeId.of(jsModuleName, nativeModuleEntry.getKey()), - nativeModuleEntry.getValue().getAsJsonObject()); - }) - .collect(Collectors.toList())); + .collect( + Collectors.toMap( + Map.Entry::getKey, + e -> { + return parseNativeModule( + // TODO (T71955395): NativeModule spec type name does not + // exist in the schema. For now assume it's "Spec". + TypeId.of(jsModuleName, "Spec"), + e.getValue().getAsJsonObject()); + }))); }); } @@ -84,9 +86,21 @@ public final class SchemaJsonParser { final JsonObject aliases = json.getAsJsonObject("aliases"); final JsonArray properties = json.getAsJsonArray("properties"); - // TODO: parse aliases + final ImmutableList collectedAliases = + ImmutableList.copyOf( + aliases.entrySet().stream() + .map( + entry -> { + final String typeName = entry.getKey(); + final JsonObject typeAnnotation = entry.getValue().getAsJsonObject(); + // The alias name is the type name that other types can refer to. + return parseTypeAnnotation( + TypeId.of(typeId.moduleName, typeName), typeAnnotation); + }) + .collect(Collectors.toList())); - ImmutableList.Builder methods = new ImmutableList.Builder<>(); + ImmutableList.Builder collectedPropertiesBuilder = + new ImmutableList.Builder<>(); properties.forEach( p -> { final JsonObject node = p.getAsJsonObject(); @@ -94,13 +108,13 @@ public final class SchemaJsonParser { final JsonObject typeAnnotation = node.getAsJsonObject("typeAnnotation"); // TODO (T71845349): "optional" field shouldn't be part of the Function's typeAnnotation. final boolean optional = typeAnnotation.get("optional").getAsBoolean(); - final TypeId propertyTypeId = TypeId.of(typeId.moduleName, name); - methods.add( + final TypeId propertyTypeId = TypeId.of(typeId.moduleName); + collectedPropertiesBuilder.add( new NativeModuleType.Property( name, parseTypeAnnotation(propertyTypeId, typeAnnotation), optional)); }); - return new NativeModuleType(typeId, methods.build()); + return new NativeModuleType(typeId, collectedAliases, collectedPropertiesBuilder.build()); } // Parse type information from a JSON "typeAnnotation" node. @@ -115,8 +129,7 @@ public final class SchemaJsonParser { switch (type) { case AliasType.TYPE_NAME: - // TODO - parsedType = new AliasType(typeId, typeId); + parsedType = parseAliasTypeAnnotation(typeId, typeAnnotation); break; case AnyType.TYPE_NAME: parsedType = new AnyType(typeId); @@ -152,9 +165,8 @@ public final class SchemaJsonParser { parsedType = new PromiseType(typeId); break; case ReservedFunctionValueType.TYPE_NAME: - // TODO - parsedType = - new ReservedFunctionValueType(typeId, ReservedFunctionValueType.ReservedName.RootTag); + parsedType = parseReservedFunctionValueTypeAnnotation(typeId, typeAnnotation); + break; case StringType.TYPE_NAME: parsedType = new StringType(typeId); break; @@ -167,19 +179,20 @@ public final class SchemaJsonParser { return maybeCreateNullableType(nullable, parsedType); } + private static Type parseAliasTypeAnnotation( + final TypeId typeId, final JsonObject typeAnnotation) { + // For now, assume the alias lives inside the same file. + return new AliasType( + typeId, TypeId.of(typeId.moduleName, typeAnnotation.get("name").getAsString())); + } + private static Type parseArrayTypeAnnotation( final TypeId typeId, final JsonObject typeAnnotation) { final JsonObject elementTypeAnnotation = typeAnnotation.getAsJsonObject("elementType"); // TODO (T71847026): Some array types are missing elementType annotation. final Type elementType = elementTypeAnnotation != null - ? parseTypeAnnotation( - TypeId.of( - typeId.moduleName, - elementTypeAnnotation.has("name") - ? elementTypeAnnotation.get("name").getAsString() - : null), - elementTypeAnnotation) + ? parseTypeAnnotation(TypeId.of(typeId.moduleName), elementTypeAnnotation) : new AnyType(TypeId.of(typeId.moduleName)); return new ArrayType(typeId, elementType); @@ -201,8 +214,7 @@ public final class SchemaJsonParser { FunctionType.createArgument( name, parseTypeAnnotation( - TypeId.of(typeId.moduleName, name), - node.getAsJsonObject("typeAnnotation")))); + TypeId.of(typeId.moduleName), node.getAsJsonObject("typeAnnotation")))); }); } @@ -227,7 +239,7 @@ public final class SchemaJsonParser { final String name = node.has("name") ? node.get("name").getAsString() : null; final boolean optional = node.get("optional").getAsBoolean(); final JsonObject propertyTypeAnnotation = node.getAsJsonObject("typeAnnotation"); - final TypeId propertyTypeId = TypeId.of(typeId.moduleName, name); + final TypeId propertyTypeId = TypeId.of(typeId.moduleName); // TODO (T67898313): Some object properties are missing typeAnnotation. final Type propertyType = @@ -241,6 +253,13 @@ public final class SchemaJsonParser { return new ObjectType(typeId, propertiesList.build()); } + private static Type parseReservedFunctionValueTypeAnnotation( + final TypeId typeId, final JsonObject typeAnnotation) { + return new ReservedFunctionValueType( + typeId, + ReservedFunctionValueType.ReservedName.valueOf(typeAnnotation.get("name").getAsString())); + } + private static Type maybeCreateNullableType(final boolean nullable, final Type original) { if (!nullable || original instanceof VoidType) { return original; diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/NativeModuleType.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/NativeModuleType.java index b529e0e1178..2f3a41034ff 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/NativeModuleType.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/NativeModuleType.java @@ -13,6 +13,7 @@ import java.util.List; public final class NativeModuleType extends Type { public static String TYPE_NAME = ""; // Not an actual type in the schema. + public final List aliases; public final List properties; public static class Property { @@ -33,13 +34,15 @@ public final class NativeModuleType extends Type { } } - public NativeModuleType(final TypeId typeId, final List properties) { + public NativeModuleType( + final TypeId typeId, final List aliases, final List properties) { super(typeId); + this.aliases = Collections.unmodifiableList(aliases); this.properties = Collections.unmodifiableList(properties); } @Override public String toString() { - return getTypeId() + " -> " + properties; + return getTypeId() + "\n aliases: " + aliases + "\n properties: " + properties; } } diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java index 76fff67e1e0..969164e4a56 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/RawSchema.java @@ -7,16 +7,16 @@ package com.facebook.react.codegen.generator.model; -import java.util.List; +import java.util.Collections; import java.util.Map; /** Represents the parsed JSON schema without any type resolution. */ public final class RawSchema { - public final Map> modules; + public final Map> modules; - public RawSchema(final Map> modules) { - this.modules = modules; + public RawSchema(final Map> modules) { + this.modules = Collections.unmodifiableMap(modules); } @Override diff --git a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java index 5a24bec9f73..a8c5208c02d 100644 --- a/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java +++ b/packages/react-native-codegen/android/gradlePlugin-build/gradlePlugin/src/main/java/com/facebook/react/codegen/generator/model/TypeId.java @@ -46,7 +46,7 @@ public final class TypeId { public String toString() { return String.format( "", - moduleName, EMPTY_TYPE_NAME.equals(typeName) ? moduleName : typeName); + moduleName, EMPTY_TYPE_NAME.equals(typeName) ? "\"\"" : typeName); } @Override