blob: 2b084d3ead4975287dac445bd178a600684a63bb [file] [log] [blame]
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.skyframe.serialization.autocodec;
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 com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.util.List;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
/** Defines an abstract strategy for generating {@link ObjectCodec} implementations. */
// TODO(b/297857068): migrate other types to this class.
abstract class CodecGenerator {
final ProcessingEnvironment env;
CodecGenerator(ProcessingEnvironment env) {
this.env = env;
}
/** Creates the codec by delegating lower level implementation methods. */
final TypeSpec defineCodec(
TypeElement encodedType, AutoCodec annotation, ExecutableElement instantiator)
throws SerializationProcessingException {
List<? extends FieldGenerator> fieldGenerators = getFieldGenerators(encodedType);
TypeSpec.Builder classBuilder = initializeCodecClassBuilder(encodedType, env);
performAdditionalCodecInitialization(classBuilder, encodedType, instantiator);
MethodSpec.Builder constructor = initializeConstructor(!fieldGenerators.isEmpty());
MethodSpec.Builder serialize = initializeSerializeMethodBuilder(encodedType, annotation, env);
MethodSpec.Builder deserialize = initializeDeserializeMethod(encodedType);
for (FieldGenerator generator : fieldGenerators) {
generator.generateOffsetMember(classBuilder, constructor);
generator.generateAdditionalMemberVariables(classBuilder);
generator.generateConstructorCode(constructor);
generator.generateSerializeCode(serialize);
generator.generateDeserializeCode(deserialize);
}
addImplementationToEndOfMethods(
instantiator, constructor, deserialize, !fieldGenerators.isEmpty());
return classBuilder
.addMethod(constructor.build())
.addMethod(serialize.build())
.addMethod(deserialize.build())
.build();
}
/**
* 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;
/**
* Initializes the field-independent parts of the constructor.
*
* @param hasFields true if there are any fields to serialize. Exception handling logic may depend
* on the presence of fields.
*/
abstract MethodSpec.Builder initializeConstructor(boolean hasFields);
/** Initializes the method that performs deserialization work. */
abstract MethodSpec.Builder initializeDeserializeMethod(TypeElement encodedType);
/**
* 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.
*/
abstract void addImplementationToEndOfMethods(
ExecutableElement instantiator,
MethodSpec.Builder constructor,
MethodSpec.Builder deserialize,
boolean hasFields);
}