aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-12-27 18:41:40 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2017-12-27 18:43:23 -0800
commit3c0f67c07498cc97053505ded617320ec459436c (patch)
tree81036567ba9a602be984a426c97f2733f8c21189 /src/main/java/com/google/devtools/build/lib
parenta0d84f68c3555b600828695affaacf6872d4a886 (diff)
AutoCodec features.
* Support for Optional, Iterable, Collection and proto types. * Support for getters that omit 'get' prefix. * Support for constructors that throw exceptions. PiperOrigin-RevId: 180235121
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/AutoCodecProcessor.java43
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java75
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();
}
}