diff options
3 files changed, 108 insertions, 22 deletions
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 index 0bc7b6fb01..75d7e00c8f 100644 --- 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 @@ -19,13 +19,16 @@ 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.skylarkinterface.SkylarkValue; 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.FuncallExpression; 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; +import java.util.Set; /** Helper class for creating {@link SkylarkDebuggingProtos.Value} from skylark objects. */ final class DebuggerSerialization { @@ -50,16 +53,7 @@ final class DebuggerSerialization { return Printer.repr(value); } - private static Value errorValue(String errorMessage) { - return Value.newBuilder().setLabel("Error").setDescription(errorMessage).build(); - } - private static boolean hasChildren(Object value) { - if (value instanceof ClassObject) { - // assuming ClassObject's have at least one child as a temporary optimization - // TODO(bazel-team): remove once child-listing logic is moved to SkylarkValue - return true; - } if (value instanceof SkylarkNestedSet) { return true; } @@ -78,15 +72,16 @@ final class DebuggerSerialization { if (value.getClass().isArray()) { return Array.getLength(value) > 0; } + if (value instanceof ClassObject || value instanceof SkylarkValue) { + // assuming ClassObject's have at least one child as a temporary optimization + // TODO(bazel-team): remove once child-listing logic is moved to SkylarkValue + return true; + } // fallback to assuming there are no children return false; } static ImmutableList<Value> getChildren(ThreadObjectMap objectMap, Object value) { - // TODO(bazel-team): move child-listing logic to SkylarkValue where practical - if (value instanceof ClassObject) { - return getChildren(objectMap, (ClassObject) value); - } if (value instanceof SkylarkNestedSet) { return getChildren(objectMap, (SkylarkNestedSet) value); } @@ -105,6 +100,13 @@ final class DebuggerSerialization { if (value.getClass().isArray()) { return getArrayChildren(objectMap, value); } + // TODO(bazel-team): move child-listing logic to SkylarkValue where practical + if (value instanceof ClassObject) { + return getChildren(objectMap, (ClassObject) value); + } + if (value instanceof SkylarkValue) { + return getChildren(objectMap, (SkylarkValue) value); + } // fallback to assuming there are no children return ImmutableList.of(); } @@ -115,26 +117,51 @@ final class DebuggerSerialization { ImmutableList<String> keys; try { keys = Ordering.natural().immutableSortedCopy(classObject.getFieldNames()); - } catch (EvalException e) { - return ImmutableList.of(errorValue("Error retrieving field names: " + e.getMessage())); + } catch (EvalException | IllegalArgumentException e) { + // silently return no children + return ImmutableList.of(); } 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(objectMap, key, value)); + if (value != null) { + builder.add(getValueProto(objectMap, key, value)); + } + } catch (EvalException | IllegalArgumentException e) { + // silently ignore errors } } return builder.build(); } private static ImmutableList<Value> getChildren( + ThreadObjectMap objectMap, SkylarkValue skylarkValue) { + Set<String> fieldNames; + try { + fieldNames = FuncallExpression.getStructFieldNames(skylarkValue.getClass()); + } catch (IllegalArgumentException e) { + // silently return no children + return ImmutableList.of(); + } + ImmutableList.Builder<Value> children = ImmutableList.builder(); + for (String fieldName : fieldNames) { + FuncallExpression.MethodDescriptor method = + FuncallExpression.getStructField(skylarkValue.getClass(), fieldName); + try { + children.add( + getValueProto( + objectMap, + fieldName, + FuncallExpression.invokeStructField(method, fieldName, skylarkValue))); + } catch (EvalException | InterruptedException | IllegalArgumentException e) { + // silently ignore errors + } + } + return children.build(); + } + + private static ImmutableList<Value> getChildren( ThreadObjectMap objectMap, SkylarkNestedSet nestedSet) { Class<?> type = nestedSet.getContentType().getType(); return ImmutableList.<Value>builder() 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 index 9f1ac0fa0b..376471f5f5 100644 --- a/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD +++ b/src/test/java/com/google/devtools/build/lib/skylarkdebug/server/BUILD @@ -63,6 +63,7 @@ java_test( name = "DebuggerSerializationTest", srcs = glob(["DebuggerSerializationTest.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", 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 index 52b208f076..4570ccb54b 100644 --- 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 @@ -23,6 +23,9 @@ 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.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter; +import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.Printer; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; @@ -204,6 +207,61 @@ public final class DebuggerSerializationTest { assertThat(getValueProto("name", new Object()).getHasChildren()).isFalse(); } + @Test + public void testSkylarkValue() { + DummyType dummy = new DummyType(); + + Value value = getValueProto("name", dummy); + assertTypeAndDescription(dummy, value); + assertThat(getChildren(value)).containsExactly(getValueProto("bool", true)); + } + + private static class DummyType implements SkylarkValue { + @Override + public void repr(SkylarkPrinter printer) { + printer.append("DummyType"); + } + + @SkylarkCallable(name = "bool", doc = "Returns True", structField = true) + public boolean bool() { + return true; + } + + public boolean anotherMethod() { + return false; + } + } + + @Test + public void testSkipSkylarkCallableThrowingException() { + DummyTypeWithException dummy = new DummyTypeWithException(); + + Value value = getValueProto("name", dummy); + assertTypeAndDescription(dummy, value); + assertThat(getChildren(value)).containsExactly(getValueProto("bool", true)); + } + + private static class DummyTypeWithException implements SkylarkValue { + @Override + public void repr(SkylarkPrinter printer) { + printer.append("DummyTypeWithException"); + } + + @SkylarkCallable(name = "bool", doc = "Returns True", structField = true) + public boolean bool() { + return true; + } + + @SkylarkCallable(name = "invalid", doc = "Throws exception!", structField = true) + public boolean invalid() { + throw new IllegalArgumentException(); + } + + public boolean anotherMethod() { + return false; + } + } + private static void assertTypeAndDescription(Object object, Value value) { assertThat(value.getType()).isEqualTo(EvalUtils.getDataTypeName(object)); assertThat(value.getDescription()).isEqualTo(Printer.repr(object)); |