diff options
Diffstat (limited to 'src/main')
2 files changed, 96 insertions, 22 deletions
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 95b9706187..6638116617 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 @@ -126,6 +126,37 @@ public class AutoCodecProcessor extends AbstractProcessor { return true; } + /** + * Heuristic for synthesizing a method getter. + * + * <p>Tries prepending {@code get} first, for example, a parameter called {@code target} results + * in {@code getTarget()}. + * + * <p>Falls back to {@code target()} if {@code getTarget()} doesn't exist. + */ + private class GetGetter implements UnaryOperator<String> { + private final TypeElement encodedType; + + GetGetter(TypeElement encodedType) { + this.encodedType = encodedType; + } + + @Override + public String apply(String name) { + String getMethodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); + List<ExecutableElement> methods = + ElementFilter.methodsIn(env.getElementUtils().getAllMembers(encodedType)); + if (methods.stream().anyMatch(p -> p.getSimpleName().contentEquals(getMethodName))) { + return getMethodName + "()"; + } + if (methods.stream().anyMatch(p -> p.getSimpleName().contentEquals(name))) { + return name + "()"; + } + throw new IllegalArgumentException( + encodedType.getQualifiedName() + " has no getter for " + name); + } + } + private void buildClassWithConstructorStrategy( TypeSpec.Builder codecClassBuilder, TypeElement encodedType) { // In Java, every class has a constructor, so this always succeeds. @@ -133,8 +164,7 @@ public class AutoCodecProcessor extends AbstractProcessor { ElementFilter.constructorsIn(encodedType.getEnclosedElements()).get(0); List<? extends VariableElement> constructorParameters = constructor.getParameters(); codecClassBuilder.addMethod( - buildSerializeMethod( - encodedType, constructorParameters, AutoCodecProcessor::paramNameAsGetter)); + buildSerializeMethod(encodedType, constructorParameters, new GetGetter(encodedType))); MethodSpec.Builder deserializeBuilder = AutoCodecUtil.initializeDeserializeMethodBuilder(encodedType); buildDeserializeBody(deserializeBuilder, constructorParameters); @@ -158,15 +188,6 @@ public class AutoCodecProcessor extends AbstractProcessor { codecClassBuilder.addMethod(deserializeBuilder.build()); } - /** - * Heuristic that converts a constructor parameter to a getter. - * - * <p>For example, a parameter called {@code target} results in {@code getTarget()}. - */ - private static String paramNameAsGetter(String name) { - return "get" + name.substring(0, 1).toUpperCase() + name.substring(1) + "()"; - } - private boolean isPublicField(VariableElement element) { if (matchesType(element.asType(), Void.class)) { return false; // Void types can't be instantiated, so the processor ignores them completely. 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 4a34cd6e81..e87395d520 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 @@ -14,13 +14,16 @@ package com.google.devtools.build.lib.skyframe.serialization.autocodec; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Maps; import com.google.devtools.build.lib.skyframe.serialization.autocodec.Marshaller.Context; import com.google.devtools.build.lib.skyframe.serialization.strings.StringCodecs; +import com.google.protobuf.GeneratedMessage; import com.google.protobuf.ProtocolMessageEnum; +import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map; @@ -136,6 +139,29 @@ class Marshallers { } }; + private final Marshaller optionalMarshaller = + new Marshaller() { + @Override + public boolean matches(DeclaredType type) { + return matchesErased(type, Optional.class); + } + + @Override + public void addSerializationCode(Context context) { + DeclaredType optionalType = (DeclaredType) context.type.getTypeArguments().get(0); + writeSerializationCode(context.with(optionalType, context.name + ".orNull()")); + } + + @Override + public void addDeserializationCode(Context context) { + DeclaredType optionalType = (DeclaredType) context.type.getTypeArguments().get(0); + String optionalName = context.makeName("optional"); + writeDeserializationCode(context.with(optionalType, optionalName)); + context.builder.addStatement( + "$L = $T.fromNullable($L)", context.name, Optional.class, optionalName); + } + }; + private final Marshaller mapEntryMarshaller = new Marshaller() { @Override @@ -168,9 +194,11 @@ class Marshallers { new Marshaller() { @Override public boolean matches(DeclaredType type) { - // TODO(shahan): List is more general than ImmutableList. Consider whether these - // two should have distinct marshallers. - return matchesErased(type, List.class) || matchesErased(type, ImmutableList.class); + // TODO(shahan): refine this as needed by splitting this into separate marshallers. + return matchesErased(type, Iterable.class) + || matchesErased(type, Collection.class) + || matchesErased(type, List.class) + || matchesErased(type, ImmutableList.class); } @Override @@ -311,30 +339,55 @@ class Marshallers { } }; + private final Marshaller protoMarshaller = + new Marshaller() { + @Override + public boolean matches(DeclaredType type) { + return isSubtype(type, GeneratedMessage.class); + } + + @Override + public void addSerializationCode(Context context) { + context.builder.addStatement("$L.writeTo(codedOut)", context.name); + } + + @Override + public void addDeserializationCode(Context context) { + context.builder.addStatement( + "$L = $T.parseFrom(codedIn)", context.name, context.getTypeName()); + } + }; + private final ImmutableList<Marshaller> marshallers = ImmutableList.of( enumMarshaller, stringMarshaller, + optionalMarshaller, mapEntryMarshaller, listMarshaller, immutableSortedSetMarshaller, mapMarshaller, + protoMarshaller, CODEC_MARSHALLER); /** True when {@code type} has the same type as {@code clazz}. */ private boolean matchesType(TypeMirror type, Class<?> clazz) { - return env.getTypeUtils() - .isSameType( - type, env.getElementUtils().getTypeElement((clazz.getCanonicalName())).asType()); + return env.getTypeUtils().isSameType(type, getType(clazz)); + } + + /** True when {@code type} is a subtype of {@code clazz}. */ + private boolean isSubtype(TypeMirror type, Class<?> clazz) { + return env.getTypeUtils().isSubtype(type, getType(clazz)); } /** 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())); + .isSameType(env.getTypeUtils().erasure(type), env.getTypeUtils().erasure(getType(clazz))); + } + + /** Returns the TypeMirror corresponding to {@code clazz}. */ + private TypeMirror getType(Class<?> clazz) { + return env.getElementUtils().getTypeElement((clazz.getCanonicalName())).asType(); } } |