blob: 0f65d9c024ce34e86f3c52769aa50d27e757fe38 [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 com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
import com.google.devtools.build.lib.unsafe.UnsafeProvider;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
/**
* Generates code for a specific field.
*
* <p>Always stores the offset of the field in a variable named {@link #getOffsetName}.
*/
abstract class FieldGenerator {
/**
* A string that should never occur in user code identifiers.
*
* <p>This is used to create unique identifiers that won't collide.
*/
private static final String GENERATED_TAG = "$AutoCodec$";
private static final String OFFSET_SUFFIX = "_offset";
private final VariableElement variable;
private final ClassName parentName;
private final String namePrefix;
/**
* Constructor.
*
* @param variable the field being serialized. Note that {@link VariableElement} contains a
* reference to the enclosing class.
* @param hierarchyLevel a variable could occur in either the class being serialized or in one of
* its ancestor classes. This is 0 for the class itself, 1 for its superclass, and so on. It
* is used to avoid naming collisions, particularly in the case of shadowed variables.
*/
FieldGenerator(VariableElement variable, int hierarchyLevel) {
this.variable = variable;
this.parentName = ClassName.get((TypeElement) variable.getEnclosingElement());
this.namePrefix = variable.getSimpleName() + GENERATED_TAG + hierarchyLevel;
}
/** Any created member variables should start with this prefix. */
final String getNamePrefix() {
return namePrefix;
}
/** The name of the enclosing class, used in generated code. */
final ClassName getParentName() {
return parentName;
}
/**
* Generated codecs store the offset of every field of the serialized class in a member variable.
*
* @return name of the offset member variable
*/
final String getOffsetName() {
return namePrefix + OFFSET_SUFFIX;
}
/**
* Name of the field being serialized.
*
* <p>Implementations may refer to this for reflection.
*/
final Name getParameterName() {
return variable.getSimpleName();
}
final void generateOffsetMember(TypeSpec.Builder classBuilder, MethodSpec.Builder constructor) {
classBuilder.addField(long.class, getOffsetName(), Modifier.PRIVATE, Modifier.FINAL);
constructor.addStatement(
"this.$L = $T.unsafe().objectFieldOffset($T.class.getDeclaredField(\"$L\"))",
getOffsetName(),
UnsafeProvider.class,
getParentName(),
variable.getSimpleName());
}
/**
* Generates any additional member variables needed for this field.
*
* <p>To avoid collisions, field specific field names should be prefixed with {@link #namePrefix}.
*
* <p>The *offset* field is already generated by {@link #generateOffsetMember}.
*/
void generateAdditionalMemberVariables(TypeSpec.Builder classBuilder) {}
/**
* Adds field specific code to the constructor.
*
* <p>Many implementations don't need to do anything here given that the offset is already
* initialized by {@link #generateOffsetMember}.
*/
void generateConstructorCode(MethodSpec.Builder constructor) {}
/**
* Adds {@link ObjectCodec#serialize} code to serialize this field.
*
* <p>Implementations may assume that the parameters of {@link ObjectCodec#serialize} are present
* in the generated method's scope and may be referenced.
*/
abstract void generateSerializeCode(MethodSpec.Builder serialize);
/**
* Adds field specific deserialize code.
*
* <p>Implementations may assume the presence of parameters of {@link
* AsyncObjectCodec#deserializeAsync}.
*/
abstract void generateDeserializeCode(MethodSpec.Builder deserialize);
}