diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java index eb3a7ea0b1fd24..f7d8ffc15fe755 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java @@ -15,8 +15,6 @@ package com.google.devtools.build.lib.skyframe.serialization.autocodec; import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** @@ -44,40 +42,19 @@ * not need to directly access the generated class. */ @Target(ElementType.TYPE) -// TODO(janakr): remove once serialization is complete. -@Retention(RetentionPolicy.RUNTIME) public @interface AutoCodec { - /** - * AutoCodec recursively derives a codec using the public interfaces of the class. - * - *

Specific strategies are described below. - */ - enum Strategy { - /** - * Uses a constructor or factory method of the class to synthesize a codec. - * - *

This strategy depends on - * - *

- * - *

If there is a unique constructor, @AutoCodec may select that as the default instantiator, - * otherwise one must be selected using the {@link AutoCodec.Instantiator} annotation. - */ - INSTANTIATOR, - /** - * For use with {@link com.google.auto.value.AutoValue} classes with an {@link - * com.google.auto.value.AutoValue.Builder} static nested Builder class: uses the builder when - * deserializing. - */ - AUTO_VALUE_BUILDER, - } + // AutoCodec works by determining a unique *instantiator*, either a constructor or factory method, + // to serve as a specification for serialization. The @AutoCodec.Instantiator tag can be helpful + // for marking a specific instantiator. + // + // AutoCodec inspects the parameters of the instantiator and finds fields of the class + // corresponding in both name and type. For serialization, it generates code that reads those + // fields using reflection. For deserialization it generates code to invoke the instantiator. /** - * Marks a specific method when using the INSTANTIATOR strategy. + * Marks a specific method to use as the instantiator. + * + *

This marking is required when the class has more than one constructor. * *

Indicates an instantiator, either a constructor or factory method, for codec generation. A * compile-time error will result if multiple methods are thus tagged. @@ -85,8 +62,6 @@ enum Strategy { @Target({ElementType.CONSTRUCTOR, ElementType.METHOD}) @interface Instantiator {} - Strategy strategy() default Strategy.INSTANTIATOR; - /** * Checks whether or not this class is allowed to be serialized. See {@link * com.google.devtools.build.lib.skyframe.serialization.SerializationContext#checkClassExplicitlyAllowed}. diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java index 8c44d3faeaf6fe..60b77ff4b93f57 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.skyframe.serialization.autocodec; +import static com.google.common.base.Ascii.toLowerCase; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationProcessorUtil.sanitizeTypeParameter; import static com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationProcessorUtil.writeGeneratedClassToFile; @@ -22,7 +23,6 @@ import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; import com.google.devtools.build.lib.skyframe.serialization.SerializationException; import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationCodeGenerator.Marshaller; @@ -111,17 +111,7 @@ private void processInternal(RoundEnvironment roundEnv) for (Element element : roundEnv.getElementsAnnotatedWith(AutoCodecUtil.ANNOTATION)) { AutoCodec annotation = element.getAnnotation(AutoCodecUtil.ANNOTATION); TypeElement encodedType = (TypeElement) element; - TypeSpec.Builder codecClassBuilder; - switch (annotation.strategy()) { - case INSTANTIATOR: - codecClassBuilder = buildClassWithInstantiatorStrategy(encodedType, annotation); - break; - case AUTO_VALUE_BUILDER: - codecClassBuilder = buildClassWithAutoValueBuilderStrategy(encodedType, annotation); - break; - default: - throw new IllegalArgumentException("Unknown strategy: " + annotation.strategy()); - } + TypeSpec.Builder codecClassBuilder = buildClassWithInstantiator(encodedType, annotation); codecClassBuilder.addMethod( AutoCodecUtil.initializeGetEncodedClassMethod(encodedType, env) .addStatement( @@ -135,8 +125,8 @@ private void processInternal(RoundEnvironment roundEnv) } } - private TypeSpec.Builder buildClassWithInstantiatorStrategy( - TypeElement encodedType, AutoCodec annotation) throws SerializationProcessingFailedException { + private TypeSpec.Builder buildClassWithInstantiator(TypeElement encodedType, AutoCodec annotation) + throws SerializationProcessingFailedException { ExecutableElement constructor = selectInstantiator(encodedType); List fields = constructor.getParameters(); @@ -161,35 +151,6 @@ private TypeSpec.Builder buildClassWithInstantiatorStrategy( return codecClassBuilder; } - private TypeSpec.Builder buildClassWithAutoValueBuilderStrategy( - TypeElement encodedType, AutoCodec annotation) throws SerializationProcessingFailedException { - TypeElement builderType = findBuilderType(encodedType); - List getters = findGettersFromType(encodedType, builderType); - ExecutableElement builderCreationMethod = findBuilderCreationMethod(encodedType, builderType); - ExecutableElement buildMethod = findBuildMethod(encodedType, builderType); - TypeSpec.Builder codecClassBuilder = - AutoCodecUtil.initializeCodecClassBuilder(encodedType, env); - MethodSpec.Builder serializeBuilder = - AutoCodecUtil.initializeSerializeMethodBuilder(encodedType, annotation, env); - for (ExecutableElement getter : getters) { - marshallers.writeSerializationCode( - new Marshaller.Context( - serializeBuilder, - getter.getReturnType(), - turnGetterIntoExpression(getter.getSimpleName().toString()))); - } - codecClassBuilder.addMethod(serializeBuilder.build()); - MethodSpec.Builder deserializeBuilder = - AutoCodecUtil.initializeDeserializeMethodBuilder(encodedType, env); - String builderVarName = - buildDeserializeBodyWithBuilder( - encodedType, builderType, deserializeBuilder, getters, builderCreationMethod); - addReturnNew(deserializeBuilder, encodedType, buildMethod, builderVarName, env); - codecClassBuilder.addMethod(deserializeBuilder.build()); - - return codecClassBuilder; - } - private ExecutableElement selectInstantiator(TypeElement encodedType) throws SerializationProcessingFailedException { List constructors = @@ -228,204 +189,6 @@ private static boolean hasInstantiatorAnnotation(Element elt) { return elt.getAnnotation(AutoCodec.Instantiator.class) != null; } - private static TypeElement findBuilderType(TypeElement encodedType) - throws SerializationProcessingFailedException { - TypeElement builderType = null; - for (Element element : encodedType.getEnclosedElements()) { - if (element instanceof TypeElement - && element.getModifiers().contains(Modifier.STATIC) - && element.getAnnotation(AutoValue.Builder.class) != null) { - if (builderType != null) { - throw new SerializationProcessingFailedException( - encodedType, - "Type %s had multiple inner classes annotated as @AutoValue.Builder: %s and %s", - encodedType, - builderType, - element); - } - builderType = (TypeElement) element; - } - } - if (builderType == null) { - throw new SerializationProcessingFailedException( - encodedType, - "Couldn't find @AutoValue.Builder-annotated static class inside %s", - encodedType); - } - return builderType; - } - - private List findGettersFromType( - TypeElement encodedType, TypeElement builderTypeForFiltering) - throws SerializationProcessingFailedException { - List result = new ArrayList<>(); - for (ExecutableElement method : - ElementFilter.methodsIn(env.getElementUtils().getAllMembers(encodedType))) { - if (!method.getModifiers().contains(Modifier.STATIC) - && method.getModifiers().contains(Modifier.ABSTRACT) - && method.getParameters().isEmpty() - && method.getReturnType().getKind() != TypeKind.VOID - && (!method.getReturnType().getKind().equals(TypeKind.DECLARED) - || !builderTypeForFiltering.equals( - env.getTypeUtils().asElement(method.getReturnType())))) { - result.add(method); - } - } - if (result.isEmpty()) { - throw new SerializationProcessingFailedException( - encodedType, "Couldn't find any properties for %s", encodedType); - } - return result; - } - - private static String getNameFromGetter(ExecutableElement method) { - String name = method.getSimpleName().toString(); - if (name.startsWith("get")) { - return name.substring(3, 4).toLowerCase() + name.substring(4); - } else if (name.startsWith("is")) { - return name.substring(2, 3).toLowerCase() + name.substring(3); - } else { - return name; - } - } - - private ExecutableElement findBuilderCreationMethod( - TypeElement encodedType, TypeElement builderType) - throws SerializationProcessingFailedException { - ExecutableElement builderMethod = null; - for (ExecutableElement method : - ElementFilter.methodsIn(env.getElementUtils().getAllMembers(encodedType))) { - if (method.getModifiers().contains(Modifier.STATIC) - && !method.getModifiers().contains(Modifier.ABSTRACT) - && method.getParameters().isEmpty() - && isSameReturnType(method, builderType)) { - if (builderMethod != null) { - throw new SerializationProcessingFailedException( - encodedType, - "Type %s had multiple static methods to create an element of type %s: %s and %s", - encodedType, - builderType, - builderMethod, - method); - } - builderMethod = method; - } - } - if (builderMethod == null) { - throw new SerializationProcessingFailedException( - builderType, - "Couldn't find builder creation method for %s and %s", - encodedType, - builderType); - } - return builderMethod; - } - - private ExecutableElement findBuildMethod(TypeElement encodedType, TypeElement builderType) - throws SerializationProcessingFailedException { - ExecutableElement abstractBuildMethod = null; - for (ExecutableElement method : - ElementFilter.methodsIn(env.getElementUtils().getAllMembers(builderType))) { - if (method.getModifiers().contains(Modifier.STATIC)) { - continue; - } - if (method.getParameters().isEmpty() - && isSameReturnType(method, encodedType) - && method.getModifiers().contains(Modifier.ABSTRACT)) { - if (abstractBuildMethod != null) { - throw new SerializationProcessingFailedException( - builderType, - "Type %s had multiple abstract methods to create an element of type %s: %s and %s", - builderType, - encodedType, - abstractBuildMethod, - method); - } - abstractBuildMethod = method; - } - } - if (abstractBuildMethod == null) { - throw new SerializationProcessingFailedException( - builderType, "Couldn't find build method for %s and %s", encodedType, builderType); - } - return abstractBuildMethod; - } - - private String buildDeserializeBodyWithBuilder( - TypeElement encodedType, - TypeElement builderType, - MethodSpec.Builder builder, - List fields, - ExecutableElement builderCreationMethod) - throws SerializationProcessingFailedException { - String builderVarName = "objectBuilder"; - builder.addStatement( - "$T $L = $T.$L()", - builderCreationMethod.getReturnType(), - builderVarName, - encodedType, - builderCreationMethod.getSimpleName()); - for (ExecutableElement getter : fields) { - String paramName = getNameFromGetter(getter) + "_"; - marshallers.writeDeserializationCode( - new Marshaller.Context(builder, getter.getReturnType(), paramName)); - setValueInBuilder(builderType, getter, paramName, builderVarName, builder); - } - return builderVarName; - } - - private void setValueInBuilder( - TypeElement builderType, - ExecutableElement getter, - String paramName, - String builderVarName, - MethodSpec.Builder methodBuilder) - throws SerializationProcessingFailedException { - ExecutableElement setterMethod = findSetterGivenGetter(getter, builderType); - methodBuilder.addStatement( - "$L.$L($L)", builderVarName, setterMethod.getSimpleName(), paramName); - } - - private ExecutableElement findSetterGivenGetter(ExecutableElement getter, TypeElement builderType) - throws SerializationProcessingFailedException { - List methods = - ElementFilter.methodsIn(env.getElementUtils().getAllMembers(builderType)); - String varName = getNameFromGetter(getter); - TypeMirror type = getter.getReturnType(); - ImmutableSet setterNames = ImmutableSet.of(varName, addCamelCasePrefix(varName, "set")); - - ExecutableElement setterMethod = null; - for (ExecutableElement method : methods) { - if (!method.getModifiers().contains(Modifier.STATIC) - && !method.getModifiers().contains(Modifier.PRIVATE) - && setterNames.contains(method.getSimpleName().toString()) - && isSameReturnType(method, builderType) - && method.getParameters().size() == 1 - && env.getTypeUtils() - .isSubtype(type, Iterables.getOnlyElement(method.getParameters()).asType())) { - if (setterMethod != null) { - throw new SerializationProcessingFailedException( - builderType, - "Multiple setter methods for %s found in %s: %s and %s", - getter, - builderType, - setterMethod, - method); - } - setterMethod = method; - } - } - if (setterMethod != null) { - return setterMethod; - } - - throw new SerializationProcessingFailedException( - builderType, - "No setter found corresponding to getter %s, %s", - getter.getSimpleName(), - type); - } - private enum Relation { INSTANCE_OF, EQUAL_TO, @@ -515,7 +278,7 @@ private MethodSpec buildSerializeMethodWithInstantiator( parameter.getSimpleName(), sanitizeTypeParameter(parameter.asType(), env), UnsafeProvider.class, - typeKind.isPrimitive() ? firstLetterUpper(typeKind.toString().toLowerCase()) : "Object", + typeKind.isPrimitive() ? firstLetterUpper(toLowerCase(typeKind.toString())) : "Object", parameter.getSimpleName()); marshallers.writeSerializationCode( new SerializationCodeGenerator.Context( @@ -720,10 +483,6 @@ private Optional getFieldByNameRecursive(TypeElement type, S return Optional.empty(); } - private boolean isSameReturnType(ExecutableElement method, TypeElement typeElement) { - return env.getTypeUtils().isSameType(method.getReturnType(), typeElement.asType()); - } - /** Emits a note to BUILD log during annotation processing for debugging. */ private void note(String note) { env.getMessager().printMessage(Diagnostic.Kind.NOTE, note);