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
This commit is contained in:
Kevin Gozali
2020-08-17 19:41:53 -07:00
committed by Facebook GitHub Bot
parent 4231551270
commit eecb930222
6 changed files with 65 additions and 37 deletions
@@ -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'
}
@@ -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)
@@ -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<String, List<NativeModuleType>> collection = new HashMap<>();
final Map<String, Map<String, NativeModuleType>> 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<Type> 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<NativeModuleType.Property> methods = new ImmutableList.Builder<>();
ImmutableList.Builder<NativeModuleType.Property> 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;
@@ -13,6 +13,7 @@ import java.util.List;
public final class NativeModuleType extends Type {
public static String TYPE_NAME = "<NONE>"; // Not an actual type in the schema.
public final List<Type> aliases;
public final List<Property> properties;
public static class Property {
@@ -33,13 +34,15 @@ public final class NativeModuleType extends Type {
}
}
public NativeModuleType(final TypeId typeId, final List<Property> properties) {
public NativeModuleType(
final TypeId typeId, final List<Type> aliases, final List<Property> 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;
}
}
@@ -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<String, List<NativeModuleType>> modules;
public final Map<String, Map<String, NativeModuleType>> modules;
public RawSchema(final Map<String, List<NativeModuleType>> modules) {
this.modules = modules;
public RawSchema(final Map<String, Map<String, NativeModuleType>> modules) {
this.modules = Collections.unmodifiableMap(modules);
}
@Override
@@ -46,7 +46,7 @@ public final class TypeId {
public String toString() {
return String.format(
"<moduleName = %s, typeName = %s>",
moduleName, EMPTY_TYPE_NAME.equals(typeName) ? moduleName : typeName);
moduleName, EMPTY_TYPE_NAME.equals(typeName) ? "\"\"" : typeName);
}
@Override