aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodec.java91
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodec.java47
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec/Marshallers.java153
-rw-r--r--src/test/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodecTest.java61
-rw-r--r--src/test/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodecTest.java30
5 files changed, 253 insertions, 129 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodec.java
new file mode 100644
index 0000000000..9be4046daf
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodec.java
@@ -0,0 +1,91 @@
+// Copyright 2018 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;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.common.collect.Ordering;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * Encodes an {@link ImmutableMap}, which may be an ImmutableSortedMap. We handle both here because
+ * we cannot handle ImmutableSortedMap with any ordering other than the default, and so we degrade
+ * to ImmutableMap in that case, hoping that the caller does not notice.
+ */
+class ImmutableMapCodec implements ObjectCodec<ImmutableMap<?, ?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<ImmutableMap<?, ?>> getEncodedClass() {
+ // Because Java disallows converting from Class<ImmutableMap> to Class<ImmutableMap<?, ?>>
+ // directly.
+ return (Class<ImmutableMap<?, ?>>) ((Class<?>) ImmutableMap.class);
+ }
+
+ @Override
+ public void serialize(
+ SerializationContext context, ImmutableMap<?, ?> map, CodedOutputStream codedOut)
+ throws SerializationException, IOException {
+ codedOut.writeInt32NoTag(map.size());
+ boolean serializeAsSortedMap = false;
+ if (map instanceof ImmutableSortedMap) {
+ Comparator<?> comparator = ((ImmutableSortedMap<?, ?>) map).comparator();
+ // In practice the comparator seems to always be Ordering.natural(), but be flexible.
+ serializeAsSortedMap =
+ comparator.equals(Ordering.natural()) || comparator.equals(Comparator.naturalOrder());
+ }
+ codedOut.writeBoolNoTag(serializeAsSortedMap);
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ context.serialize(entry.getKey(), codedOut);
+ context.serialize(entry.getValue(), codedOut);
+ }
+ }
+
+ @Override
+ public ImmutableMap<?, ?> deserialize(DeserializationContext context, CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ int length = codedIn.readInt32();
+ if (length < 0) {
+ throw new SerializationException("Expected non-negative length: " + length);
+ }
+ if (codedIn.readBool()) {
+ return buildMap(ImmutableSortedMap.naturalOrder(), length, context, codedIn);
+ } else {
+ return buildMap(ImmutableMap.builderWithExpectedSize(length), length, context, codedIn);
+ }
+ }
+
+ private static <T> ImmutableMap<T, Object> buildMap(
+ ImmutableMap.Builder<T, Object> builder,
+ int length,
+ DeserializationContext context,
+ CodedInputStream codedIn)
+ throws IOException, SerializationException {
+ for (int i = 0; i < length; i++) {
+ T key = context.deserialize(codedIn);
+ Object value = context.deserialize(codedIn);
+ builder.put(key, value);
+ }
+ try {
+ return builder.build();
+ } catch (IllegalArgumentException e) {
+ throw new SerializationException(
+ "Duplicate keys during ImmutableMapCodec deserialization", e);
+ }
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodec.java
new file mode 100644
index 0000000000..549b9a56ae
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodec.java
@@ -0,0 +1,47 @@
+// Copyright 2018 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;
+
+import com.google.common.base.Optional;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+
+/** {@link ObjectCodec} for {@link Optional}. */
+class OptionalCodec implements ObjectCodec<Optional<?>> {
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<Optional<?>> getEncodedClass() {
+ return (Class<Optional<?>>) (Class<?>) Optional.class;
+ }
+
+ @Override
+ public void serialize(SerializationContext context, Optional<?> obj, CodedOutputStream codedOut)
+ throws SerializationException, IOException {
+ codedOut.writeBoolNoTag(obj.isPresent());
+ if (obj.isPresent()) {
+ context.serialize(obj.get(), codedOut);
+ }
+ }
+
+ @Override
+ public Optional<?> deserialize(DeserializationContext context, CodedInputStream codedIn)
+ throws SerializationException, IOException {
+ if (!codedIn.readBool()) {
+ return Optional.absent();
+ }
+ return Optional.of(context.deserialize(codedIn));
+ }
+}
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 af8a2da6bb..1ab908cfab 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,14 +14,11 @@
package com.google.devtools.build.lib.skyframe.serialization.autocodec;
-import com.google.common.base.Optional;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
@@ -44,7 +41,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
-import java.util.function.Consumer;
import java.util.regex.Pattern;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.ElementKind;
@@ -422,31 +418,6 @@ 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.getDeclaredType().getTypeArguments().get(0);
- writeSerializationCode(context.with(optionalType, context.name + ".orNull()"));
- }
-
- @Override
- public void addDeserializationCode(Context context) {
- DeclaredType optionalType =
- (DeclaredType) context.getDeclaredType().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 uuidMarshller =
new Marshaller() {
@Override
@@ -717,106 +688,33 @@ class Marshallers {
@Override
public void addDeserializationCode(Context context) {
- addMapDeserializationCode(
- context,
- (builderName, key, value) ->
- context.builder.addStatement(
- "$T<$T, $T> $L = new $T<>()",
- LinkedHashMap.class,
- key.getTypeName(),
- value.getTypeName(),
- builderName,
- LinkedHashMap.class),
- (builderName) -> context.builder.addStatement("$L = $L", context.name, builderName));
- }
- };
-
- private final Marshaller immutableMapMarshaller =
- new Marshaller() {
- @Override
- public boolean matches(DeclaredType type) {
- return matchesErased(type, ImmutableMap.class);
- }
-
- @Override
- public void addSerializationCode(Context context) {
- mapMarshaller.addSerializationCode(context);
- }
-
- @Override
- public void addDeserializationCode(Context context) {
- addMapDeserializationCode(
- context,
- (builderName, key, value) ->
- context.builder.addStatement(
- "$T<$T, $T> $L = new $T<>()",
- ImmutableMap.Builder.class,
- key.getTypeName(),
- value.getTypeName(),
- builderName,
- ImmutableMap.Builder.class),
- (builderName) ->
- context.builder.addStatement("$L = $L.build()", context.name, builderName));
- }
- };
-
- private final Marshaller immutableSortedMapMarshaller =
- new Marshaller() {
- @Override
- public boolean matches(DeclaredType type) {
- return matchesErased(type, ImmutableSortedMap.class);
- }
-
- @Override
- public void addSerializationCode(Context context) {
- mapMarshaller.addSerializationCode(context);
- }
-
- @Override
- public void addDeserializationCode(Context context) {
- addMapDeserializationCode(
- context,
- (builderName, key, value) ->
- context.builder.addStatement(
- "$T<$T, $T> $L = new $T<>($T.naturalOrder())",
- ImmutableSortedMap.Builder.class,
- key.getTypeName(),
- value.getTypeName(),
- builderName,
- ImmutableSortedMap.Builder.class,
- Comparator.class),
- (builderName) ->
- context.builder.addStatement("$L = $L.build()", context.name, builderName));
+ String builderName = context.makeName("builder");
+ Context key =
+ context.with(
+ context.getDeclaredType().getTypeArguments().get(0), context.makeName("key"));
+ Context value =
+ context.with(
+ context.getDeclaredType().getTypeArguments().get(1), context.makeName("value"));
+ context.builder.addStatement(
+ "$T<$T, $T> $L = new $T<>()",
+ LinkedHashMap.class,
+ key.getTypeName(),
+ value.getTypeName(),
+ builderName,
+ LinkedHashMap.class);
+ String lengthName = context.makeName("length");
+ context.builder.addStatement("int $L = codedIn.readInt32()", lengthName);
+ String indexName = context.makeName("i");
+ context.builder.beginControlFlow(
+ "for (int $L = 0; $L < $L; ++$L)", indexName, indexName, lengthName, indexName);
+ writeDeserializationCode(key);
+ writeDeserializationCode(value);
+ context.builder.addStatement("$L.put($L, $L)", builderName, key.name, value.name);
+ context.builder.endControlFlow();
+ context.builder.addStatement("$L = $L", context.name, builderName);
}
};
- @FunctionalInterface
- private static interface MapBuilderInitializer {
- void initialize(String builderName, Context key, Context value);
- }
-
- /** Helper for map marshallers. */
- private void addMapDeserializationCode(
- Context context, MapBuilderInitializer mapBuilderInitializer, Consumer<String> finisher) {
- String builderName = context.makeName("builder");
- Context key =
- context.with(context.getDeclaredType().getTypeArguments().get(0), context.makeName("key"));
- Context value =
- context.with(
- context.getDeclaredType().getTypeArguments().get(1), context.makeName("value"));
- mapBuilderInitializer.initialize(builderName, key, value);
- String lengthName = context.makeName("length");
- context.builder.addStatement("int $L = codedIn.readInt32()", lengthName);
- String indexName = context.makeName("i");
- context.builder.beginControlFlow(
- "for (int $L = 0; $L < $L; ++$L)", indexName, indexName, lengthName, indexName);
- writeDeserializationCode(key);
- writeDeserializationCode(value);
- context.builder.addStatement("$L.put($L, $L)", builderName, key.name, value.name);
- context.builder.endControlFlow();
- finisher.accept(builderName);
- }
-
private final Marshaller multimapMarshaller =
new Marshaller() {
@Override
@@ -1049,7 +947,6 @@ class Marshallers {
enumMarshaller,
stringMarshaller,
charSequenceMarshaller,
- optionalMarshaller,
supplierMarshaller,
uuidMarshller,
mapEntryMarshaller,
@@ -1057,8 +954,6 @@ class Marshallers {
immutableSetMarshaller,
immutableSortedSetMarshaller,
mapMarshaller,
- immutableMapMarshaller,
- immutableSortedMapMarshaller,
multimapMarshaller,
nestedSetMarshaller,
patternMarshaller,
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodecTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodecTest.java
new file mode 100644
index 0000000000..2791f0adcc
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/serialization/ImmutableMapCodecTest.java
@@ -0,0 +1,61 @@
+// Copyright 2018 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedMap;
+import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
+import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester.VerificationFunction;
+import com.google.devtools.build.lib.skyframe.serialization.testutils.TestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ImmutableMapCodec}. */
+@RunWith(JUnit4.class)
+public class ImmutableMapCodecTest {
+ @Test
+ public void smoke() throws Exception {
+ new SerializationTester(
+ ImmutableMap.of(),
+ ImmutableMap.of("A", "//foo:A"),
+ ImmutableMap.of("B", "//foo:B"),
+ ImmutableSortedMap.of(),
+ ImmutableSortedMap.of("A", "//foo:A"),
+ ImmutableSortedMap.of("B", "//foo:B"),
+ ImmutableSortedMap.reverseOrder().put("a", "b").put("c", "d").build())
+ // Check for order.
+ .setVerificationFunction(
+ (VerificationFunction<ImmutableMap<?, ?>>)
+ (deserialized, subject) -> {
+ assertThat(deserialized).isEqualTo(subject);
+ assertThat(deserialized).containsExactlyEntriesIn(subject).inOrder();
+ })
+ .runTests();
+ }
+
+ @Test
+ public void unnaturallySortedMapComesBackUnsortedInCorrectOrder() throws Exception {
+ ImmutableMap<?, ?> deserialized =
+ TestUtils.roundTrip(
+ ImmutableSortedMap.reverseOrder().put("a", "b").put("c", "d").build(),
+ ImmutableMap.of());
+ assertThat(deserialized).isInstanceOf(ImmutableMap.class);
+ assertThat(deserialized).isNotInstanceOf(ImmutableSortedMap.class);
+ assertThat(deserialized).containsExactly("c", "d", "a", "b").inOrder();
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodecTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodecTest.java
new file mode 100644
index 0000000000..302a50314b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/serialization/OptionalCodecTest.java
@@ -0,0 +1,30 @@
+// Copyright 2018 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;
+
+import com.google.common.base.Optional;
+import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link OptionalCodec}. */
+@RunWith(JUnit4.class)
+public class OptionalCodecTest {
+ @Test
+ public void smoke() throws Exception {
+ new SerializationTester(Optional.absent(), Optional.of("string")).runTests();
+ }
+}