Skip to content

Commit

Permalink
Delete unnecessary AutoCodec.Strategy.
Browse files Browse the repository at this point in the history
This was needed at one point for serializing classes with
AutoValue.Builder, but it's no longer used anywhere, likely because
of DynamicCodec.

Unmarks AutoCodec retention. The annotation is not needed at runtime.

PiperOrigin-RevId: 581348075
Change-Id: Idbd1250a517d7dee9d8d2fd8042c5aca7feab01b
  • Loading branch information
aoeui authored and copybara-github committed Nov 10, 2023
1 parent a4e218c commit ae99f81
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 281 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -44,49 +42,26 @@
* 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.
*
* <p>Specific strategies are described below.
*/
enum Strategy {
/**
* Uses a constructor or factory method of the class to synthesize a codec.
*
* <p>This strategy depends on
*
* <ul>
* <li>a designated constructor or factory method to inspect to generate the codec
* <li>each parameter must match a member field on name and the field will be interpreted as
* an instance of the parameter type.
* </ul>
*
* <p>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.
*
* <p>This marking is required when the class has more than one constructor.
*
* <p>Indicates an instantiator, either a constructor or factory method, for codec generation. A
* compile-time error will result if multiple methods are thus tagged.
*/
@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}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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(
Expand All @@ -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<? extends VariableElement> fields = constructor.getParameters();

Expand All @@ -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<ExecutableElement> 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<ExecutableElement> constructors =
Expand Down Expand Up @@ -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<ExecutableElement> findGettersFromType(
TypeElement encodedType, TypeElement builderTypeForFiltering)
throws SerializationProcessingFailedException {
List<ExecutableElement> 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<ExecutableElement> 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<ExecutableElement> methods =
ElementFilter.methodsIn(env.getElementUtils().getAllMembers(builderType));
String varName = getNameFromGetter(getter);
TypeMirror type = getter.getReturnType();
ImmutableSet<String> 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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -720,10 +483,6 @@ private Optional<FieldValueAndClass> 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);
Expand Down

0 comments on commit ae99f81

Please sign in to comment.