diff options
author | Googler <noreply@google.com> | 2018-05-18 06:59:57 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-05-18 07:01:46 -0700 |
commit | 72d62bcaf58056db343f357c4591fbfd3baf999f (patch) | |
tree | 4aa14fbf696eb431648ee78951466e814831ee3c /src | |
parent | 7a71ce816bc8a67a63c2d147ac953ac451ebd23b (diff) |
Skylark debug server: add serialization code
Small self-contained part of the debug server (see unknown commit for the
larger picture).
PiperOrigin-RevId: 197140094
Diffstat (limited to 'src')
7 files changed, 394 insertions, 0 deletions
@@ -351,6 +351,7 @@ genrule( srcs = [ "//src/main/protobuf:dist_jars", "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:dist_jars", + "//src/main/java/com/google/devtools/build/lib/skylarkdebug/proto:dist_jars", "@googleapis//:dist_jars", ], outs = ["derived_java_srcs.zip"], diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 432a4d2ac4..b7d86d6f6d 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -60,6 +60,7 @@ filegroup( "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/cpp:srcs", "//src/main/java/com/google/devtools/build/lib/skylarkbuildapi/java:srcs", "//src/main/java/com/google/devtools/build/lib/skylarkdebug/proto:srcs", + "//src/main/java/com/google/devtools/build/lib/skylarkdebug/server:srcs", "//src/main/java/com/google/devtools/build/lib/skylarkinterface/processor:srcs", "//src/main/java/com/google/devtools/build/lib/ssd:srcs", "//src/main/java/com/google/devtools/build/lib/standalone:srcs", diff --git a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD new file mode 100644 index 0000000000..42768bb69d --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD @@ -0,0 +1,20 @@ +package(default_visibility = ["//src:__subpackages__"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), + visibility = ["//src/main/java/com/google/devtools/build/lib:__pkg__"], +) + +java_library( + name = "server", + srcs = glob(["*.java"]), + deps = [ + "//src/main/java/com/google/devtools/build/lib:skylarkinterface", + "//src/main/java/com/google/devtools/build/lib:syntax", + "//src/main/java/com/google/devtools/build/lib/collect/nestedset", + "//src/main/java/com/google/devtools/build/lib/skylarkdebug/proto:skylark_debugging_java_proto", + "//third_party:guava", + "//third_party:jsr305", + ], +) diff --git a/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerialization.java b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerialization.java new file mode 100644 index 0000000000..70d716332a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerialization.java @@ -0,0 +1,140 @@ +// 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.skylarkdebug.server; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Ordering; +import com.google.devtools.build.lib.collect.nestedset.NestedSetView; +import com.google.devtools.build.lib.skylarkdebugging.SkylarkDebuggingProtos; +import com.google.devtools.build.lib.skylarkdebugging.SkylarkDebuggingProtos.Value; +import com.google.devtools.build.lib.syntax.ClassObject; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.Printer; +import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import java.lang.reflect.Array; +import java.util.Map; + +/** Helper class for creating {@link SkylarkDebuggingProtos.Value} from skylark objects. */ +final class DebuggerSerialization { + + static Value getValueProto(String label, Object value) { + // TODO(bazel-team): prune cycles, and provide a way to limit breadth/depth of children reported + return Value.newBuilder() + .setLabel(label) + // TODO(bazel-team): omit type details for non-Skylark values + .setType(EvalUtils.getDataTypeName(value)) + .setDescription(Printer.repr(value)) + .addAllChild(getChildren(value)) + .build(); + } + + private static Value errorValue(String errorMessage) { + return Value.newBuilder().setLabel("Error").setDescription(errorMessage).build(); + } + + private static ImmutableList<Value> getChildren(Object value) { + // TODO(bazel-team): move child-listing logic to SkylarkValue where practical + if (value instanceof ClassObject) { + return getChildren((ClassObject) value); + } + if (value instanceof SkylarkNestedSet) { + return getChildren((SkylarkNestedSet) value); + } + if (value instanceof NestedSetView) { + return getChildren((NestedSetView) value); + } + if (value instanceof Map) { + return getChildren(((Map) value).entrySet()); + } + if (value instanceof Map.Entry) { + return getChildren((Map.Entry) value); + } + if (value instanceof Iterable) { + return getChildren((Iterable) value); + } + if (value.getClass().isArray()) { + return getArrayChildren(value); + } + // fallback to assuming there are no children + return ImmutableList.of(); + } + + private static ImmutableList<Value> getChildren(ClassObject classObject) { + ImmutableList.Builder<Value> builder = ImmutableList.builder(); + ImmutableList<String> keys; + try { + keys = Ordering.natural().immutableSortedCopy(classObject.getFieldNames()); + } catch (EvalException e) { + return ImmutableList.of(errorValue("Error retrieving field names: " + e.getMessage())); + } + for (String key : keys) { + Object value; + try { + value = classObject.getValue(key); + } catch (EvalException e) { + return ImmutableList.of( + errorValue( + String.format("Error retrieving value for field '%s': %s", key, e.getMessage()))); + } + if (value != null) { + builder.add(getValueProto(key, value)); + } + } + return builder.build(); + } + + private static ImmutableList<Value> getChildren(SkylarkNestedSet nestedSet) { + Class<?> type = nestedSet.getContentType().getType(); + return ImmutableList.<Value>builder() + .add( + Value.newBuilder() + .setLabel("order") + .setType("Traversal order") + .setDescription(nestedSet.getOrder().getSkylarkName()) + .build()) + .addAll(getChildren(new NestedSetView<>(nestedSet.getSet(type)))) + .build(); + } + + private static ImmutableList<Value> getChildren(NestedSetView<?> nestedSet) { + return ImmutableList.of( + getValueProto("directs", nestedSet.directs()), + getValueProto("transitives", nestedSet.transitives())); + } + + private static ImmutableList<Value> getChildren(Map.Entry<?, ?> entry) { + return ImmutableList.of( + getValueProto("key", entry.getKey()), getValueProto("value", entry.getValue())); + } + + private static ImmutableList<Value> getChildren(Iterable<?> iterable) { + ImmutableList.Builder<Value> builder = ImmutableList.builder(); + int index = 0; + for (Object value : iterable) { + builder.add(getValueProto(String.format("[%d]", index++), value)); + } + return builder.build(); + } + + private static ImmutableList<Value> getArrayChildren(Object array) { + ImmutableList.Builder<Value> builder = ImmutableList.builder(); + int index = 0; + for (int i = 0; i < Array.getLength(array); i++) { + builder.add(getValueProto(String.format("[%d]", index++), Array.get(array, i))); + } + return builder.build(); + } +} diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD index 52c95b19cc..887620485a 100644 --- a/src/test/java/com/google/devtools/build/lib/BUILD +++ b/src/test/java/com/google/devtools/build/lib/BUILD @@ -58,6 +58,7 @@ filegroup( "//src/test/java/com/google/devtools/build/lib/skyframe/serialization:srcs", "//src/test/java/com/google/devtools/build/lib/skyframe:srcs", "//src/test/java/com/google/devtools/build/lib/skylark:srcs", + "//src/test/java/com/google/devtools/build/lib/skylarkdebug/server:srcs", "//src/test/java/com/google/devtools/build/lib/skylarkinterface/processor:srcs", ], visibility = ["//src:__pkg__"], diff --git a/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD new file mode 100644 index 0000000000..5d9f881575 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD @@ -0,0 +1,26 @@ +package( + default_testonly = 1, + default_visibility = ["//src:__subpackages__"], +) + +filegroup( + name = "srcs", + testonly = 0, + srcs = glob(["**"]), + visibility = ["//src/test/java/com/google/devtools/build/lib:__pkg__"], +) + +java_test( + name = "DebuggerSerializationTest", + srcs = glob(["DebuggerSerializationTest.java"]), + deps = [ + "//src/main/java/com/google/devtools/build/lib:syntax", + "//src/main/java/com/google/devtools/build/lib/collect/nestedset", + "//src/main/java/com/google/devtools/build/lib/skylarkdebug/proto:skylark_debugging_java_proto", + "//src/main/java/com/google/devtools/build/lib/skylarkdebug/server", + "//third_party:guava", + "//third_party:jsr305", + "//third_party:junit4", + "//third_party:truth", + ], +) diff --git a/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerializationTest.java b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerializationTest.java new file mode 100644 index 0000000000..525f1ce005 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/DebuggerSerializationTest.java @@ -0,0 +1,205 @@ +// 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.skylarkdebug.server; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.collect.nestedset.NestedSetView; +import com.google.devtools.build.lib.skylarkdebugging.SkylarkDebuggingProtos.Value; +import com.google.devtools.build.lib.syntax.EvalUtils; +import com.google.devtools.build.lib.syntax.Printer; +import com.google.devtools.build.lib.syntax.SkylarkNestedSet; +import java.util.Map; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link DebuggerSerialization}. */ +@RunWith(JUnit4.class) +public final class DebuggerSerializationTest { + + @Test + public void testSimpleNestedSet() { + Set<String> children = ImmutableSet.of("a", "b"); + SkylarkNestedSet set = + SkylarkNestedSet.of(Object.class, NestedSetBuilder.stableOrder().addAll(children).build()); + + Value value = DebuggerSerialization.getValueProto("name", set); + + assertTypeAndDescription(set, value); + assertThat(value.getChildList()).hasSize(3); + assertThat(value.getChild(0)) + .isEqualTo( + Value.newBuilder() + .setLabel("order") + .setType("Traversal order") + .setDescription("default") + .build()); + assertEqualIgnoringTypeAndDescription( + value.getChild(1), DebuggerSerialization.getValueProto("directs", children)); + assertEqualIgnoringTypeAndDescription( + value.getChild(2), DebuggerSerialization.getValueProto("transitives", ImmutableList.of())); + } + + @Test + public void testNestedSetWithNestedChildren() { + NestedSet<String> innerNestedSet = + NestedSetBuilder.<String>stableOrder().add("inner1").add("inner2").build(); + ImmutableSet<String> directChildren = ImmutableSet.of("a", "b"); + SkylarkNestedSet outerSet = + SkylarkNestedSet.of( + String.class, + NestedSetBuilder.<String>linkOrder() + .addAll(directChildren) + .addTransitive(innerNestedSet) + .build()); + + Value value = DebuggerSerialization.getValueProto("name", outerSet); + + assertTypeAndDescription(outerSet, value); + assertThat(value.getChildList()).hasSize(3); + assertThat(value.getChild(0)) + .isEqualTo( + Value.newBuilder() + .setLabel("order") + .setType("Traversal order") + .setDescription("topological") + .build()); + assertEqualIgnoringTypeAndDescription( + value.getChild(1), DebuggerSerialization.getValueProto("directs", directChildren)); + assertEqualIgnoringTypeAndDescription( + value.getChild(2), + DebuggerSerialization.getValueProto( + "transitives", ImmutableList.of(new NestedSetView<>(innerNestedSet)))); + } + + @Test + public void testSimpleMap() { + Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 2); + + Value value = DebuggerSerialization.getValueProto("name", map); + + assertTypeAndDescription(map, value); + assertThat(value.getChildList()).hasSize(2); + assertThat(value.getChild(0).getLabel()).isEqualTo("[0]"); + assertThat(value.getChild(0).getChildList()) + .isEqualTo( + ImmutableList.of( + DebuggerSerialization.getValueProto("key", "a"), + DebuggerSerialization.getValueProto("value", 1))); + assertThat(value.getChild(1).getLabel()).isEqualTo("[1]"); + assertThat(value.getChild(1).getChildList()) + .isEqualTo( + ImmutableList.of( + DebuggerSerialization.getValueProto("key", "b"), + DebuggerSerialization.getValueProto("value", 2))); + } + + @Test + public void testNestedMap() { + Set<String> set = ImmutableSet.of("a", "b"); + Map<String, Object> map = ImmutableMap.of("a", set); + + Value value = DebuggerSerialization.getValueProto("name", map); + + assertTypeAndDescription(map, value); + assertThat(value.getChildList()).hasSize(1); + assertThat(value.getChild(0).getLabel()).isEqualTo("[0]"); + assertThat(value.getChild(0).getChildList()) + .isEqualTo( + ImmutableList.of( + DebuggerSerialization.getValueProto("key", "a"), + DebuggerSerialization.getValueProto("value", set))); + } + + @Test + public void testSimpleIterable() { + Iterable<Integer> iter = ImmutableList.of(1, 2); + + Value value = DebuggerSerialization.getValueProto("name", iter); + + assertTypeAndDescription(iter, value); + assertThat(value.getChildList()).hasSize(2); + assertThat(value.getChild(0)).isEqualTo(DebuggerSerialization.getValueProto("[0]", 1)); + assertThat(value.getChild(1)).isEqualTo(DebuggerSerialization.getValueProto("[1]", 2)); + } + + @Test + public void testNestedIterable() { + Iterable<Object> iter = ImmutableList.of(ImmutableList.of(1, 2)); + + Value value = DebuggerSerialization.getValueProto("name", iter); + + assertTypeAndDescription(iter, value); + assertThat(value.getChildList()).hasSize(1); + assertThat(value.getChild(0)) + .isEqualTo(DebuggerSerialization.getValueProto("[0]", ImmutableList.of(1, 2))); + } + + @Test + public void testSimpleArray() { + int[] array = new int[] {1, 2}; + + Value value = DebuggerSerialization.getValueProto("name", array); + + assertTypeAndDescription(array, value); + assertThat(value.getChildList()).hasSize(2); + assertThat(value.getChild(0)).isEqualTo(DebuggerSerialization.getValueProto("[0]", 1)); + assertThat(value.getChild(1)).isEqualTo(DebuggerSerialization.getValueProto("[1]", 2)); + } + + @Test + public void testNestedArray() { + Object[] array = new Object[] {1, ImmutableList.of(2, 3)}; + + Value value = DebuggerSerialization.getValueProto("name", array); + + assertTypeAndDescription(array, value); + assertThat(value.getChildList()).hasSize(2); + assertThat(value.getChild(0)).isEqualTo(DebuggerSerialization.getValueProto("[0]", 1)); + assertThat(value.getChild(1)) + .isEqualTo(DebuggerSerialization.getValueProto("[1]", ImmutableList.of(2, 3))); + } + + @Test + public void testUnrecognizedObjectOrSkylarkPrimitiveHasNoChildren() { + assertThat(DebuggerSerialization.getValueProto("name", 1).getChildList()).isEmpty(); + assertThat(DebuggerSerialization.getValueProto("name", "string").getChildList()).isEmpty(); + assertThat(DebuggerSerialization.getValueProto("name", new Object()).getChildList()).isEmpty(); + } + + private static void assertTypeAndDescription(Object object, Value value) { + assertThat(value.getType()).isEqualTo(EvalUtils.getDataTypeName(object)); + assertThat(value.getDescription()).isEqualTo(Printer.repr(object)); + } + + /** + * Type and description are implementation dependent (e.g. NestedSetView#directs returns a list + * instead of a set if there are no duplicates, which changes both 'type' and 'description'). + */ + private static void assertEqualIgnoringTypeAndDescription(Value value1, Value value2) { + assertThat(value1.getLabel()).isEqualTo(value2.getLabel()); + assertThat(value1.getChildCount()).isEqualTo(value2.getChildCount()); + for (int i = 0; i < value1.getChildCount(); i++) { + assertEqualIgnoringTypeAndDescription(value1.getChild(i), value2.getChild(i)); + } + } +} |