From d2acedc4ca87156a6aa6f96008652c2070dfd071 Mon Sep 17 00:00:00 2001 From: shahan Date: Wed, 3 Jan 2018 10:18:05 -0800 Subject: Adds the @AutoCodec.Constructor annotation for selecting constructors. Uses the constructor having the @AutoCodec.Constructor annotation to generate a codec (instead of choosing the first in source code). This annotation is required when a class has more than one constructor. PiperOrigin-RevId: 180685902 --- .../serialization/autocodec/AutoCodec.java | 21 ++++++++++------ .../autocodec/AutoCodecProcessor.java | 29 +++++++++++++++++++--- .../serialization/autocodec/Marshallers.java | 1 + .../devtools/build/lib/util/RegexFilter.java | 1 + 4 files changed, 42 insertions(+), 10 deletions(-) (limited to 'src/main/java/com') 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 index b36dd4effb..2d67bedf6c 100644 --- 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 @@ -40,19 +40,17 @@ public @interface AutoCodec { */ public static enum Strategy { /** - * Uses the first constructor of the class to synthesize a codec. + * Uses a constructor of the class to synthesize a codec. * *

This strategy depends on * *

* - * For example, a constructor having parameter, {@code target}, should having a matching getter, - * {@code getTarget()}. - * - *

The first constructor is the first ocurring in the source code. + *

If there is a unique constructor, that is the designated constructor, otherwise one must + * be selected using the {@link AutoCodec.Constructor} annotation. */ CONSTRUCTOR, /** @@ -71,5 +69,14 @@ public @interface AutoCodec { POLYMORPHIC, } + /** + * Marks a specific constructor when using the CONSTRUCTOR strategy. + * + *

Indicates a constructor for codec generation. A compile-time error will result if multiple + * constructors are thus tagged. + */ + @Target(ElementType.CONSTRUCTOR) + public static @interface Constructor {} + 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 index c6d0df9dac..8df0f8743e 100644 --- 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 @@ -14,7 +14,10 @@ package com.google.devtools.build.lib.skyframe.serialization.autocodec; +import static com.google.common.collect.ImmutableList.toImmutableList; + import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec; import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs; @@ -127,9 +130,29 @@ public class AutoCodecProcessor extends AbstractProcessor { private void buildClassWithConstructorStrategy( TypeSpec.Builder codecClassBuilder, TypeElement encodedType) { - // In Java, every class has a constructor, so this always succeeds. - ExecutableElement constructor = - ElementFilter.constructorsIn(encodedType.getEnclosedElements()).get(0); + List constructors = + ElementFilter.constructorsIn(encodedType.getEnclosedElements()); + ImmutableList markedConstructors = + constructors + .stream() + .filter(c -> c.getAnnotation(AutoCodec.Constructor.class) != null) + .collect(toImmutableList()); + ExecutableElement constructor = null; + if (markedConstructors.isEmpty()) { + // If nothing is marked, see if there is a unique constructor. + if (constructors.size() > 1) { + throw new IllegalArgumentException( + encodedType.getQualifiedName() + + " has multiple constructors but no Constructor annotation."); + } + // In Java, every class has at least one constructor, so this never fails. + constructor = constructors.get(0); + } else if (markedConstructors.size() == 1) { + constructor = markedConstructors.get(0); + } else { + throw new IllegalArgumentException( + encodedType.getQualifiedName() + " has multiple Constructor annotations."); + } List constructorParameters = constructor.getParameters(); initializeUnsafeOffsets(codecClassBuilder, encodedType, constructorParameters); codecClassBuilder.addMethod( diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java index 052d856461..e3c7898c58 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java @@ -68,6 +68,7 @@ class Marshallers { new Marshaller() { @Override public boolean matches(DeclaredType type) { + // TODO(shahan): check for getCodec or CODEC. // CODEC is the final fallback for all Marshallers so this returns true. return true; } diff --git a/src/main/java/com/google/devtools/build/lib/util/RegexFilter.java b/src/main/java/com/google/devtools/build/lib/util/RegexFilter.java index 220ff0235c..4d3b7ef670 100644 --- a/src/main/java/com/google/devtools/build/lib/util/RegexFilter.java +++ b/src/main/java/com/google/devtools/build/lib/util/RegexFilter.java @@ -91,6 +91,7 @@ public final class RegexFilter { *

Null {@code inclusionPattern} or {@code exclusionPattern} means that inclusion or exclusion * matching will not be applied, respectively. */ + @AutoCodec.Constructor RegexFilter(@Nullable Pattern inclusionPattern, @Nullable Pattern exclusionPattern) { this.inclusionPattern = inclusionPattern; this.exclusionPattern = exclusionPattern; -- cgit v1.2.3