diff options
Diffstat (limited to 'src/main/java/com')
6 files changed, 491 insertions, 52 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java index 6bb0e5f829..caa5c5ec25 100644 --- a/src/main/java/com/google/devtools/build/lib/cmdline/Label.java +++ b/src/main/java/com/google/devtools/build/lib/cmdline/Label.java @@ -61,15 +61,16 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu * Package names that aren't made relative to the current repository because they mean special * things to Bazel. */ - public static final ImmutableSet<PathFragment> ABSOLUTE_PACKAGE_NAMES = ImmutableSet.of( - // Used for select - PathFragment.create("conditions"), - // dependencies that are a function of the configuration - PathFragment.create("tools/defaults"), - // Visibility is labels aren't actually targets - PathFragment.create("visibility"), - // There is only one //external package - Label.EXTERNAL_PACKAGE_NAME); + public static final ImmutableSet<PathFragment> ABSOLUTE_PACKAGE_NAMES = + ImmutableSet.of( + // Used for select + PathFragment.create("conditions"), + // dependencies that are a function of the configuration + PathFragment.create("tools/defaults"), + // Visibility is labels aren't actually targets + PathFragment.create("visibility"), + // There is only one //external package + Label.EXTERNAL_PACKAGE_NAME); public static final PackageIdentifier EXTERNAL_PACKAGE_IDENTIFIER = PackageIdentifier.createInMainRepo(EXTERNAL_PACKAGE_NAME); @@ -78,10 +79,13 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu public static final SkyFunctionName TRANSITIVE_TRAVERSAL = SkyFunctionName.create("TRANSITIVE_TRAVERSAL"); + public static final LabelCodec CODEC = LabelCodec.INSTANCE; + private static final Interner<Label> LABEL_INTERNER = BlazeInterners.newWeakInterner(); /** * Factory for Labels from absolute string form. e.g. + * * <pre> * //foo/bar * //foo/bar:quux @@ -98,6 +102,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu /** * Factory for Labels from absolute string form. e.g. + * * <pre> * //foo/bar * //foo/bar:quux @@ -106,8 +111,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu * {@literal @}foo//bar:baz * </pre> * - * @param defaultToMain Treat labels in the default repository as being in the main - * one instead. + * @param defaultToMain Treat labels in the default repository as being in the main one instead. */ public static Label parseAbsolute(String absName, boolean defaultToMain) throws LabelSyntaxException { @@ -156,11 +160,10 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu /** * Factory for Labels from separate components. * - * @param packageName The name of the package. The package name does - * <b>not</b> include {@code //}. Must be valid according to - * {@link LabelValidator#validatePackageName}. - * @param targetName The name of the target within the package. Must be - * valid according to {@link LabelValidator#validateTargetName}. + * @param packageName The name of the package. The package name does <b>not</b> include {@code + * //}. Must be valid according to {@link LabelValidator#validatePackageName}. + * @param targetName The name of the target within the package. Must be valid according to {@link + * LabelValidator#validateTargetName}. * @throws LabelSyntaxException if either of the arguments was invalid. */ public static Label create(String packageName, String targetName) throws LabelSyntaxException { @@ -168,8 +171,8 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu } /** - * Similar factory to above, but takes a package identifier to allow external repository labels - * to be created. + * Similar factory to above, but takes a package identifier to allow external repository labels to + * be created. */ public static Label create(PackageIdentifier packageId, String targetName) throws LabelSyntaxException { @@ -182,7 +185,6 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu * <p>Only call this method if you know what you're doing; in particular, don't call it on * arbitrary {@code targetName} inputs */ - public static Label createUnvalidated(PackageIdentifier packageId, String targetName) { return LABEL_INTERNER.intern(new Label(packageId, StringCanonicalizer.intern(targetName))); } @@ -190,6 +192,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu /** * Resolves a relative label using a workspace-relative path to the current working directory. The * method handles these cases: + * * <ul> * <li>The label is absolute. * <li>The label starts with a colon. @@ -246,8 +249,8 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu } /** - * Validates the given package name and returns a canonical {@link PackageIdentifier} instance - * if it is valid. Otherwise it throws a SyntaxException. + * Validates the given package name and returns a canonical {@link PackageIdentifier} instance if + * it is valid. Otherwise it throws a SyntaxException. */ private static PackageIdentifier validatePackageName(String packageIdentifier, String name) throws LabelSyntaxException { @@ -285,9 +288,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu this.hashCode = hashCode(this.name, this.packageIdentifier); } - /** - * A specialization of Arrays.HashCode() that does not require constructing a 2-element array. - */ + /** A specialization of Arrays.HashCode() that does not require constructing a 2-element array. */ private static final int hashCode(Object obj1, Object obj2) { int result = 31 + (obj1 == null ? 0 : obj1.hashCode()); return 31 * result + (obj2 == null ? 0 : obj2.hashCode()); @@ -309,10 +310,14 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu * Returns the name of the package in which this rule was declared (e.g. {@code * //file/base:fileutils_test} returns {@code file/base}). */ - @SkylarkCallable(name = "package", structField = true, - doc = "The package part of this label. " - + "For instance:<br>" - + "<pre class=language-python>Label(\"//pkg/foo:abc\").package == \"pkg/foo\"</pre>") + @SkylarkCallable( + name = "package", + structField = true, + doc = + "The package part of this label. " + + "For instance:<br>" + + "<pre class=language-python>Label(\"//pkg/foo:abc\").package == \"pkg/foo\"</pre>" + ) public String getPackageName() { return packageIdentifier.getPackageFragment().getPathString(); } @@ -322,11 +327,15 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu * {@code @repo//pkg:b}, it will returns {@code external/repo/pkg} and for label {@code //pkg:a}, * it will returns an empty string. */ - @SkylarkCallable(name = "workspace_root", structField = true, - doc = "Returns the execution root for the workspace of this label, relative to the execroot. " - + "For instance:<br>" - + "<pre class=language-python>Label(\"@repo//pkg/foo:abc\").workspace_root ==" - + " \"external/repo\"</pre>") + @SkylarkCallable( + name = "workspace_root", + structField = true, + doc = + "Returns the execution root for the workspace of this label, relative to the execroot. " + + "For instance:<br>" + + "<pre class=language-python>Label(\"@repo//pkg/foo:abc\").workspace_root ==" + + " \"external/repo\"</pre>" + ) public String getWorkspaceRoot() { return packageIdentifier.getRepository().getSourceRoot().toString(); } @@ -343,21 +352,23 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu return packageIdentifier.getPackageFragment(); } - /** - * Returns the label as a path fragment, using the package and the label name. - */ + /** Returns the label as a path fragment, using the package and the label name. */ public PathFragment toPathFragment() { return packageIdentifier.getPackageFragment().getRelative(name); } /** - * Returns the name by which this rule was declared (e.g. {@code //foo/bar:baz} - * returns {@code baz}). + * Returns the name by which this rule was declared (e.g. {@code //foo/bar:baz} returns {@code + * baz}). */ - @SkylarkCallable(name = "name", structField = true, - doc = "The name of this label within the package. " - + "For instance:<br>" - + "<pre class=language-python>Label(\"//pkg/foo:abc\").name == \"abc\"</pre>") + @SkylarkCallable( + name = "name", + structField = true, + doc = + "The name of this label within the package. " + + "For instance:<br>" + + "<pre class=language-python>Label(\"//pkg/foo:abc\").name == \"abc\"</pre>" + ) public String getName() { return name; } @@ -382,13 +393,16 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu } public String getUnambiguousCanonicalForm() { - return packageIdentifier.getRepository() + "//" + packageIdentifier.getPackageFragment() - + ":" + name; + return packageIdentifier.getRepository() + + "//" + + packageIdentifier.getPackageFragment() + + ":" + + name; } /** - * Renders this label in canonical form, except with labels in the main and default - * repositories conflated. + * Renders this label in canonical form, except with labels in the main and default repositories + * conflated. */ public String getDefaultCanonicalForm() { String repository; @@ -397,8 +411,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu } else { repository = packageIdentifier.getRepository().getName(); } - return repository + "//" + packageIdentifier.getPackageFragment() - + ":" + name; + return repository + "//" + packageIdentifier.getPackageFragment() + ":" + name; } /** @@ -517,9 +530,7 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu return hashCode; } - /** - * Two labels are equal iff both their name and their package name are equal. - */ + /** Two labels are equal iff both their name and their package name are equal. */ @Override public boolean equals(Object other) { if (!(other instanceof Label)) { @@ -527,7 +538,8 @@ public final class Label implements Comparable<Label>, Serializable, SkylarkValu } Label otherLabel = (Label) other; // Perform the equality comparisons in order from least likely to most likely. - return hashCode == otherLabel.hashCode && name.equals(otherLabel.name) + return hashCode == otherLabel.hashCode + && name.equals(otherLabel.name) && packageIdentifier.equals(otherLabel.packageIdentifier); } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java index c03e09184f..10291a6798 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TestSuiteExpansionValue.java @@ -23,6 +23,7 @@ import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.skyframe.serialization.NotSerializableRuntimeException; +import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; import com.google.devtools.build.skyframe.SkyFunctionName; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; @@ -76,6 +77,9 @@ public final class TestSuiteExpansionValue implements SkyValue { /** A list of targets of which all test suites should be expanded. */ @ThreadSafe static final class TestSuiteExpansionKey implements SkyKey { + public static final ObjectCodec<TestSuiteExpansionKey> CODEC = + TestSuiteExpansionKeyCodec.INSTANCE; + private final ImmutableSortedSet<Label> targets; public TestSuiteExpansionKey(ImmutableSortedSet<Label> targets) { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD index bb25cbcffd..af255e7b40 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/BUILD @@ -3,6 +3,7 @@ package(default_visibility = ["//src:__subpackages__"]) filegroup( name = "srcs", srcs = glob(["**"]) + [ + "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec:srcs", "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/testutils:srcs", ], ) 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 new file mode 100644 index 0000000000..17e3224fbf --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodec.java @@ -0,0 +1,55 @@ +// Copyright 2017 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 java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Specifies that AutoCodec should generate a codec implementation for the annotated abstract class. + * + * <p>Example: + * + * <pre>{@code + * @AutoCodec + * abstract class Codec implements ObjectCodec<Target> { + * static Codec create() { + * return new AutoCodec_Target(); + * } + * } + * }</pre> + * + * The {@code AutoCodec_} prefix is added to the {@Target} to obtain the generated class name. + */ +@Target(ElementType.TYPE) +public @interface AutoCodec { + /** + * AutoCodec recursively derives a codec using the public interfaces of the class. + * + * <p>Specific strategies are described below. + */ + public static enum Strategy { + /** + * Uses the constructor to infer serialization code. + * + * <p>Each constructor parameter is expected to have a corresponding getter. These pairs are + * used for serialization and deserialization. + */ + CONSTRUCTOR, + // TODO(shahan): Add a strategy that serializes from public members. + } + + Strategy strategy() default Strategy.CONSTRUCTOR; +} 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 new file mode 100644 index 0000000000..29df3cb46e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java @@ -0,0 +1,323 @@ +// Copyright 2017 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.auto.service.AutoService; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSortedSet; +import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; +import com.google.devtools.build.lib.skyframe.serialization.SerializationException; +import com.google.protobuf.CodedInputStream; +import com.google.protobuf.CodedOutputStream; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import java.io.IOException; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; +import javax.tools.Diagnostic; + +/** + * Javac annotation processor (compiler plugin) for generating {@link ObjectCodec} implementations. + * + * <p>User code must never reference this class. + */ +@AutoService(Processor.class) +public class AutoCodecProcessor extends AbstractProcessor { + // Synthesized classes will be prefixed with AutoCodec_. + public static final String GENERATED_CLASS_NAME_PREFIX = "AutoCodec"; + private static final Class<AutoCodec> ANNOTATION = AutoCodec.class; + + /** + * Passing {@code --javacopt=-Aautocodec_print_generated} to {@code blaze build} tells AutoCodec + * to print the generated code. + */ + private static final String PRINT_GENERATED_OPTION = "autocodec_print_generated"; + + private ProcessingEnvironment env; // Captured from `init` method. + + @Override + public Set<String> getSupportedOptions() { + return ImmutableSet.of(PRINT_GENERATED_OPTION); + } + + @Override + public Set<String> getSupportedAnnotationTypes() { + return ImmutableSet.of(ANNOTATION.getCanonicalName()); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); // Supports all versions of Java. + } + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + this.env = processingEnv; + } + + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + for (Element element : roundEnv.getElementsAnnotatedWith(ANNOTATION)) { + AutoCodec annotation = element.getAnnotation(ANNOTATION); + switch (annotation.strategy()) { + case CONSTRUCTOR: + buildCodecUsingConstructor((TypeElement) element); + break; + default: + throw new IllegalArgumentException("Unknown strategy: " + annotation.strategy()); + } + } + return true; + } + + /** + * Uses the first constructor of the class to synthesize a codec. + * + * <p>This strategy depends on + * + * <ul> + * <li>the class constructor taking all serialized fields as parameters + * <li>and each serialized field having a corresponding getter. + * </ul> + * + * For example, a constructor having parameter, {@code target}, should having a matching getter, + * {@code getTarget()}. + * + * <p>The first constructor is the first ocurring in the source code. + */ + private void buildCodecUsingConstructor(TypeElement classElement) { + TypeSpec.Builder codecClassBuilder = + TypeSpec.classBuilder(getCodecName(classElement)) + .superclass(TypeName.get(classElement.asType())); + + TypeElement encodedType = getEncodedType(classElement); + + // Generates the getEncodedClass method. + codecClassBuilder.addMethod( + MethodSpec.methodBuilder("getEncodedClass") + .addModifiers(Modifier.PUBLIC) + .addAnnotation(Override.class) + .returns( + ParameterizedTypeName.get( + ClassName.get(Class.class), TypeName.get(encodedType.asType()))) + .addStatement("return $T.class", TypeName.get(encodedType.asType())) + .build()); + + // In Java, every class has a constructor, so this always succeeds. + ExecutableElement constructor = + ElementFilter.constructorsIn(encodedType.getEnclosedElements()).get(0); + List<? extends VariableElement> constructorParameters = constructor.getParameters(); + addSerializeMethodUsingConstructor(codecClassBuilder, encodedType, constructorParameters); + addDeserializeMethodUsingConstructor(codecClassBuilder, encodedType, constructorParameters); + + String packageName = + env.getElementUtils().getPackageOf(classElement).getQualifiedName().toString(); + try { + JavaFile file = JavaFile.builder(packageName, codecClassBuilder.build()).build(); + file.writeTo(env.getFiler()); + if (env.getOptions().containsKey("autocodec_print_generated")) { + note("AutoCodec generated codec for " + classElement + ":\n" + file); + } + } catch (IOException e) { + env.getMessager() + .printMessage(Diagnostic.Kind.ERROR, "Failed to generate output file: " + e.getMessage()); + } + } + + /** + * Heuristic that converts a constructor parameter to a getter. + * + * <p>For example, a parameter called {@code target} results in {@code getTarget()}. + */ + private static String paramNameAsAccessor(String name) { + return "get" + name.substring(0, 1).toUpperCase() + name.substring(1) + "()"; + } + + /** + * Name of the generated codec class. + * + * <p>For {@code Foo.Bar.Codec} this is {@code AutoCodec_Foo_Bar_Codec}. + */ + private static String getCodecName(Element element) { + ImmutableList.Builder<String> classNamesBuilder = new ImmutableList.Builder<>(); + do { + classNamesBuilder.add(element.getSimpleName().toString()); + element = element.getEnclosingElement(); + } while (element instanceof TypeElement); + classNamesBuilder.add(GENERATED_CLASS_NAME_PREFIX); + return classNamesBuilder.build().reverse().stream().collect(Collectors.joining("_")); + } + + private void addSerializeMethodUsingConstructor( + TypeSpec.Builder codecClassBuilder, + TypeElement encodedType, + List<? extends VariableElement> constructorParameters) { + MethodSpec.Builder serializeBuilder = + MethodSpec.methodBuilder("serialize") + .addModifiers(Modifier.PUBLIC) + .returns(void.class) + .addParameter(TypeName.get(encodedType.asType()), "input") + .addParameter(CodedOutputStream.class, "codedOut") + .addAnnotation(Override.class) + .addException(SerializationException.class) + .addException(IOException.class); + for (VariableElement parameter : constructorParameters) { + buildSerializeBody( + serializeBuilder, + (DeclaredType) parameter.asType(), + "input." + paramNameAsAccessor(parameter.getSimpleName().toString())); + } + codecClassBuilder.addMethod(serializeBuilder.build()); + } + + private void addDeserializeMethodUsingConstructor( + TypeSpec.Builder codecClassBuilder, + TypeElement encodedType, + List<? extends VariableElement> constructorParameters) { + MethodSpec.Builder deserializeBuilder = + MethodSpec.methodBuilder("deserialize") + .addModifiers(Modifier.PUBLIC) + .returns(TypeName.get(encodedType.asType())) + .addParameter(CodedInputStream.class, "codedIn") + .addAnnotation(Override.class) + .addException(SerializationException.class) + .addException(IOException.class); + for (VariableElement parameter : constructorParameters) { + buildDeserializeBody( + deserializeBuilder, + (DeclaredType) parameter.asType(), + parameter.getSimpleName().toString()); + } + // Invokes the constructor and returns the value. + deserializeBuilder.addStatement( + "return new $T($L)", + TypeName.get(encodedType.asType()), + constructorParameters + .stream() + .map(p -> p.getSimpleName().toString()) + .collect(Collectors.joining(", "))); + codecClassBuilder.addMethod(deserializeBuilder.build()); + } + + /** + * Appends code statements to {@code builder} to serialize a pre-declared variable named {@code + * accessor}. + * + * @param type the type of {@code accessor} + */ + private void buildSerializeBody(MethodSpec.Builder builder, DeclaredType type, String accessor) { + builder.beginControlFlow("if ($L != null)", accessor); // Begin if not null block. + builder.addStatement("codedOut.writeBoolNoTag(true)"); + // TODO(shahan): Add support for more types. + if (matchesErased(type, ImmutableSortedSet.class)) { + // Writes the target count to the stream so deserialization knows when to stop. + builder.addStatement("codedOut.writeInt32NoTag($L.size())", accessor); + DeclaredType repeatedType = (DeclaredType) type.getTypeArguments().get(0); + // TODO(shahan): consider introducing a depth parameter to avoid shadowing here. + builder.beginControlFlow("for ($T repeated : $L)", TypeName.get(repeatedType), accessor); + buildSerializeBody(builder, repeatedType, "repeated"); + builder.endControlFlow(); + } else { + // Otherwise use the type's CODEC. + builder.addStatement("$T.CODEC.serialize($L, codedOut)", TypeName.get(type), accessor); + } + builder.nextControlFlow("else"); + builder.addStatement("codedOut.writeBoolNoTag(false)"); + builder.endControlFlow(); // End if not null. + } + + /** + * Appends code statements to {@code builder} declaring a variable called {@code name} and + * initializing it by deserialization. + * + * @param type the type of {@code name} + */ + private void buildDeserializeBody(MethodSpec.Builder builder, DeclaredType type, String name) { + builder.addStatement("$T $L = null", TypeName.get(type), name); + builder.beginControlFlow("if (codedIn.readBool())"); // Begin null-handling block. + // TODO(shahan): Add support for more types. + if (matchesErased(type, ImmutableSortedSet.class)) { + DeclaredType repeatedType = (DeclaredType) type.getTypeArguments().get(0); + builder.addStatement( + "$T<$T> builder = new $T<>($T.naturalOrder())", + ImmutableSortedSet.Builder.class, + TypeName.get(repeatedType), + ImmutableSortedSet.Builder.class, + Comparator.class); + builder.addStatement("int length = codedIn.readInt32()"); + builder.beginControlFlow("for (int i = 0; i < length; ++i)"); + buildDeserializeBody(builder, repeatedType, "repeated"); + builder.addStatement("builder.add(repeated)"); + builder.endControlFlow(); + builder.addStatement("$L = builder.build()", name); + } else { + // Otherwise, use the type's CODEC value. + builder.addStatement("$L = $T.CODEC.deserialize(codedIn)", name, TypeName.get(type)); + } + builder.endControlFlow(); // End null-handling block. + } + + /** + * Gets the type parameter of ObjectCodec, i.e., the type being encoded. + * + * <p>{@code element} must implement ObjectCodec. + */ + private TypeElement getEncodedType(TypeElement element) { + for (TypeMirror implementedInterface : element.getInterfaces()) { + if (matchesErased(implementedInterface, ObjectCodec.class)) { + return (TypeElement) + env.getTypeUtils() + .asElement(((DeclaredType) implementedInterface).getTypeArguments().get(0)); + } + } + throw new IllegalArgumentException(element + " does not implement ObjectCodec!"); + } + + /** True when erasure of {@code type} matches erasure of {@code clazz}. */ + private boolean matchesErased(TypeMirror type, Class<?> clazz) { + return env.getTypeUtils() + .isSameType( + env.getTypeUtils().erasure(type), + env.getTypeUtils() + .erasure( + env.getElementUtils().getTypeElement((clazz.getCanonicalName())).asType())); + } + + /** Emits a note to BUILD log during annotation processing for debugging. */ + private void note(String note) { + env.getMessager().printMessage(Diagnostic.Kind.NOTE, note); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD new file mode 100644 index 0000000000..5807597e65 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/BUILD @@ -0,0 +1,44 @@ +package(default_visibility = ["//src:__subpackages__"]) + +filegroup( + name = "srcs", + srcs = glob( + ["**"], + ), +) + +# @AutoCodec annotation and plugin. Used by clients. +java_library( + name = "autocodec", + exported_plugins = [":autocodec-plugin"], + exports = [":autocodec-annotation"], +) + +# @AutoCodec annotation only. Used by clients and the processor. +java_library( + name = "autocodec-annotation", + srcs = ["AutoCodec.java"], +) + +# Installs the @AutoCodec annotation processor as a compiler plugin. +java_plugin( + name = "autocodec-plugin", + processor_class = "com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodecProcessor", + deps = [ + ":autocodec-processor", + ], +) + +# @AutoCodec annotation processor implementation. +java_library( + name = "autocodec-processor", + srcs = ["AutoCodecProcessor.java"], + deps = [ + ":autocodec-annotation", + "//src/main/java/com/google/devtools/build/lib/skyframe/serialization", + "//third_party:auto_service", + "//third_party:guava", + "//third_party/java/javapoet", + "//third_party/protobuf:protobuf_java", + ], +) |