Skip to content

Commit

Permalink
Make CodecGenerator pass FieldGenerators in more places.
Browse files Browse the repository at this point in the history
Slightly rework the way constructors are setup.

PiperOrigin-RevId: 653413266
Change-Id: I9b007113b59c81db3b2223be1cd87755dae299d6
  • Loading branch information
aoeui authored and copybara-github committed Jul 18, 2024
1 parent a36c09f commit 489142b
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ java_library(
"//third_party:auto_service",
"//third_party:auto_value",
"//third_party:guava",
"//third_party:jsr305",
"//third_party/java/javapoet",
"//third_party/protobuf:protobuf_java",
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@

import static com.google.devtools.build.lib.skyframe.serialization.autocodec.Initializers.initializeCodecClassBuilder;
import static com.google.devtools.build.lib.skyframe.serialization.autocodec.Initializers.initializeSerializeMethodBuilder;
import static com.google.devtools.build.lib.skyframe.serialization.autocodec.TypeOperations.getErasure;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
Expand All @@ -37,14 +40,16 @@ abstract class CodecGenerator {
final TypeSpec defineCodec(
TypeElement encodedType, AutoCodec annotation, ExecutableElement instantiator)
throws SerializationProcessingException {
List<? extends FieldGenerator> fieldGenerators = getFieldGenerators(encodedType);
ImmutableList<FieldGenerator> fieldGenerators = getFieldGenerators(encodedType);

TypeSpec.Builder classBuilder = initializeCodecClassBuilder(encodedType, env);
performAdditionalCodecInitialization(classBuilder, encodedType, instantiator);
TypeName encodedTypeName = getErasure(encodedType, env);
performAdditionalCodecInitialization(
classBuilder, encodedTypeName, instantiator, fieldGenerators);

MethodSpec.Builder constructor = initializeConstructor(encodedType, fieldGenerators.size());
MethodSpec.Builder constructor = initializeConstructor(encodedType, fieldGenerators);
MethodSpec.Builder serialize = initializeSerializeMethodBuilder(encodedType, annotation, env);
MethodSpec.Builder deserialize = initializeDeserializeMethod(encodedType);
MethodSpec.Builder deserialize = initializeDeserializeMethod(encodedTypeName);

for (FieldGenerator generator : fieldGenerators) {
generator.generateHandleMember(classBuilder, constructor);
Expand All @@ -54,8 +59,7 @@ final TypeSpec defineCodec(
generator.generateDeserializeCode(deserialize);
}

addImplementationToEndOfMethods(
instantiator, constructor, deserialize, !fieldGenerators.isEmpty());
addImplementationToEndOfMethods(constructor, deserialize, fieldGenerators);

return classBuilder
.addMethod(constructor.build())
Expand All @@ -64,39 +68,47 @@ final TypeSpec defineCodec(
.build();
}

/** Creates {@link FieldGenerator} instances that generate code for serialized fields. */
abstract ImmutableList<FieldGenerator> getFieldGenerators(TypeElement type)
throws SerializationProcessingException;

/**
* Performs additional initialization steps on the codec being created.
*
* <p>Adds the correct superclass. May define additional field-independent methods.
*/
abstract void performAdditionalCodecInitialization(
TypeSpec.Builder classBuilder, TypeElement encodedType, ExecutableElement instantiator);

/** Creates {@link FieldGenerator} instances that generate code for serialized fields. */
abstract List<? extends FieldGenerator> getFieldGenerators(TypeElement type)
throws SerializationProcessingException;
TypeSpec.Builder classBuilder,
TypeName encodedTypeName,
ExecutableElement instantiator,
List<? extends FieldGenerator> fieldGenerators);

/**
* Initializes the field-independent parts of the constructor.
*
* @param fieldCount number of fields to serialize. This is used in two ways. 1. Exception
* handling logic may depend on the presence of fields. 2. We cross check the number of fields
* at runtime.
*/
abstract MethodSpec.Builder initializeConstructor(TypeElement type, int fieldCount);
abstract void generateConstructorPreamble(
TypeElement encodedType,
ImmutableList<FieldGenerator> fieldGenerators,
MethodSpec.Builder constructor);

/** Initializes the method that performs deserialization work. */
abstract MethodSpec.Builder initializeDeserializeMethod(TypeElement encodedType);
abstract MethodSpec.Builder initializeDeserializeMethod(TypeName typeName);

/**
* Adds field-independent code at the end of methods after per-field code is added.
*
* @param hasFields true if there are any fields to serialize, based on the result of {@link
* #getFieldGenerators}. Exception handling logic may depend on the presence of fields.
*/
/** Adds field-independent code at the end of methods after per-field code is added. */
abstract void addImplementationToEndOfMethods(
ExecutableElement instantiator,
MethodSpec.Builder constructor,
MethodSpec.Builder deserialize,
boolean hasFields);
ImmutableList<FieldGenerator> fieldGenerators);

/** Initializes the (mostly) field-independent parts of the constructor. */
private final MethodSpec.Builder initializeConstructor(
TypeElement encodedType, ImmutableList<FieldGenerator> fieldGenerators) {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder();
generateConstructorPreamble(encodedType, fieldGenerators, constructor);

if (fieldGenerators.stream().anyMatch(g -> g.getGetterName() == null)) {
// If there are any fields not retrieved by getters, the per-field section of the constructor
// will perform reflective operations to obtain handles to the variables. These are enclosed
// in a common try-catch block.
constructor.beginControlFlow("try");
}
return constructor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import javax.annotation.Nullable;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
Expand Down Expand Up @@ -55,6 +56,11 @@ abstract class FieldGenerator {
this.namePrefix = variable.getSimpleName() + GENERATED_TAG + hierarchyLevel;
}

/** Name of the field being serialized. */
final Name getParameterName() {
return variable.getSimpleName();
}

/** Any created member variables should start with this prefix. */
final String getNamePrefix() {
return namePrefix;
Expand All @@ -77,13 +83,10 @@ final String getHandleName() {
return namePrefix + HANDLE_SUFFIX;
}

/**
* Name of the field being serialized.
*
* <p>Implementations may refer to this for reflection.
*/
final Name getParameterName() {
return variable.getSimpleName();
/** Getter name, if a getter is used to retrieve the field. */
@Nullable
String getGetterName() {
return null;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
Expand Down Expand Up @@ -65,10 +66,12 @@ ImmutableList<FieldGenerator> getFieldGenerators(TypeElement type)

@Override
void performAdditionalCodecInitialization(
TypeSpec.Builder classBuilder, TypeElement encodedType, ExecutableElement internMethod) {
TypeName returnType = getErasure(encodedType, env);
TypeSpec.Builder classBuilder,
TypeName encodedTypeName,
ExecutableElement internMethod,
List<? extends FieldGenerator> unusedFieldGenerators) {
classBuilder.superclass(
ParameterizedTypeName.get(ClassName.get(InterningObjectCodec.class), returnType));
ParameterizedTypeName.get(ClassName.get(InterningObjectCodec.class), encodedTypeName));

// Defines the `InterningObjectCodec.intern` implementation.
VariableElement param = getOnlyElement(internMethod.getParameters());
Expand All @@ -77,40 +80,35 @@ void performAdditionalCodecInitialization(
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(getErasure(param.asType(), env), "value")
.returns(returnType)
.addStatement(
"return $T.$L(value)", getErasure(encodedType, env), internMethod.getSimpleName())
.returns(encodedTypeName)
.addStatement("return $T.$L(value)", encodedTypeName, internMethod.getSimpleName())
.build());
}

@Override
MethodSpec.Builder initializeConstructor(TypeElement encodedType, int fieldCount) {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder();

TypeName typeName = getErasure(encodedType, env);
void generateConstructorPreamble(
TypeElement encodedType,
ImmutableList<FieldGenerator> fieldGenerators,
MethodSpec.Builder constructor) {
TypeName encodedTypeName = getErasure(encodedType, env);
constructor.addStatement(
"int runtimeFieldCount = $T.getSerializableFieldCount($T.class)",
RuntimeHelpers.class,
typeName);
encodedTypeName);
constructor
.beginControlFlow("if (runtimeFieldCount != $L)", fieldCount)
.beginControlFlow("if (runtimeFieldCount != $L)", fieldGenerators.size())
.addStatement(
"throw new IllegalStateException(\"$T's AutoCodec expected $L fields, but there were"
+ " \" + runtimeFieldCount + \" serializable fields at runtime. See"
+ " b/319301818 for explanation and workaround.\")",
typeName,
fieldCount)
encodedTypeName,
fieldGenerators.size())
.endControlFlow();
if (fieldCount > 0) {
constructor.beginControlFlow("try");
}
return constructor;
}

/** Initializes the {@link InterningObjectCodec#deserializeInterned} method. */
@Override
MethodSpec.Builder initializeDeserializeMethod(TypeElement encodedType) {
TypeName typeName = getErasure(encodedType, env);
MethodSpec.Builder initializeDeserializeMethod(TypeName typeName) {
return MethodSpec.methodBuilder("deserializeInterned")
.addModifiers(Modifier.PUBLIC)
.returns(typeName)
Expand All @@ -133,11 +131,10 @@ MethodSpec.Builder initializeDeserializeMethod(TypeElement encodedType) {

@Override
void addImplementationToEndOfMethods(
ExecutableElement unusedInterner,
MethodSpec.Builder constructor,
MethodSpec.Builder deserialize,
boolean hasFields) {
if (hasFields) {
ImmutableList<FieldGenerator> fieldGenerators) {
if (!fieldGenerators.isEmpty()) {
constructor
.nextControlFlow("catch ($T e)", NoSuchFieldException.class)
.addStatement("throw new $T(e)", AssertionError.class)
Expand Down

0 comments on commit 489142b

Please sign in to comment.