diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java index 95cab7a8a759345a2f2e09c98eaabd59daa92f18..75d3e7118b616fad8a31e40c2b5a41b66aaf2303 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/TruffleRLanguage.java @@ -35,6 +35,7 @@ import com.oracle.truffle.api.instrumentation.ProvidedTags; import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.r.engine.interop.RForeignAccessFactoryImpl; import com.oracle.truffle.r.nodes.RASTBuilder; import com.oracle.truffle.r.nodes.builtin.RBuiltinPackages; @@ -51,8 +52,12 @@ import com.oracle.truffle.r.runtime.context.Engine.IncompleteSourceException; import com.oracle.truffle.r.runtime.context.Engine.ParseException; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.RCloseable; +import com.oracle.truffle.r.runtime.data.RFunction; +import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.ffi.Load_RFFIFactory; import com.oracle.truffle.r.runtime.ffi.RFFIFactory; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; /** * Only does the minimum for running under the debugger. It is not completely clear how to correctly @@ -147,6 +152,37 @@ public final class TruffleRLanguage extends TruffleLanguage<RContext> { return false; } + @Override + protected Object findMetaObject(RContext context, Object value) { + String ret = "Object"; + if (value instanceof RPromise) { + RPromise promise = (RPromise) value; + if (promise.isEvaluated()) { + value = promise.getValue(); + } + } + value = RRuntime.asAbstractVector(value); // Wrap scalars "Integer", "Double" etc. + if (value instanceof RTypedValue) { + ret = ((RTypedValue) value).getRType().getName(); + } + return ret; + } + + @Override + protected SourceSection findSourceLocation(RContext context, Object value) { + if (value instanceof RPromise) { + RPromise promise = (RPromise) value; + RBaseNode expr = promise.getClosure().getExpr(); + return expr.getEncapsulatingSourceSection(); + } + + if (value instanceof RFunction) { + RFunction f = (RFunction) value; + return f.getTarget().getRootNode().getSourceSection(); + } + return null; + } + @SuppressWarnings("try") @Override protected CallTarget parse(ParsingRequest request) throws Exception { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java index d8a04e48887dd7d5328d1f2abc712dec20ded2b3..bae8e4a3a619fd58c96c9e73c9f85c73c9d3c74a 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRDebugTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,6 +22,8 @@ */ package com.oracle.truffle.r.test.tck; +import com.oracle.truffle.api.debug.DebugStackFrame; +import com.oracle.truffle.api.debug.DebugValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -41,6 +43,7 @@ import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.api.vm.PolyglotEngine.Value; import com.oracle.truffle.r.runtime.RSource; @@ -185,6 +188,7 @@ public class FastRDebugTest { assertLocation(11, "res = n * nMOFact", "n", 2.0, "nMinusOne", 1.0, "nMOFact", 1.0); + assertMetaObjects(factorial, "n", "double", "nMOFact", "double", "nMinusOne", "double"); stepOver(1); assertLocation(12, "res", "n", 2.0, @@ -206,6 +210,39 @@ public class FastRDebugTest { assertEquals("Factorial computed OK", 2, n.intValue()); } + @Test + public void testFindMetaObjectAndSourceLocation() throws Throwable { + final Source source = RSource.fromTextInternal("main <- function() {\n" + + " i = 3L\n" + + " n = 15\n" + + " str = 'hello'\n" + + " i <- i + 1L\n" + + " i\n" + + "}\n", + RSource.Internal.DEBUGTEST_DEBUG); + engine.eval(source); + + // @formatter:on + run.addLast(new Runnable() { + @Override + public void run() { + assertNull(suspendedEvent); + assertNotNull(executionEvent); + executionEvent.prepareStepInto(); + } + }); + + stepInto(1); + stepOver(3); + assertLocation(6, "i", "i", 4, "n", 15.0, "str", "hello"); + assertMetaObjects(source, "i", "integer", "n", "double", "str", "character"); + stepOut(); + + final Source evalSource = RSource.fromTextInternal("main()\n", RSource.Internal.DEBUGTEST_EVAL); + engine.eval(evalSource); + assertExecutedOK(); + } + private void performWork() { try { if (ex == null && !run.isEmpty()) { @@ -308,4 +345,35 @@ public class FastRDebugTest { } assertTrue("Assuming all requests processed: " + run, run.isEmpty()); } + + private void assertMetaObjects(final Source expectedSource, final String... nameAndMetaObjectPairs) { + run.addLast((Runnable) () -> { + DebugStackFrame frame = suspendedEvent.getTopStackFrame(); + for (int i = 0; i < nameAndMetaObjectPairs.length;) { + String name = nameAndMetaObjectPairs[i++]; + String expectedMO = nameAndMetaObjectPairs[i++]; + boolean found = false; + for (DebugValue value : frame) { + if (name.equals(value.getName())) { + DebugValue moDV = value.getMetaObject(); + if (moDV != null || expectedMO != null) { + String mo = moDV.as(String.class); + Assert.assertEquals("MetaObjects of '" + name + "' differ:", expectedMO, mo); + } + found = true; + // Trigger findSourceLocation() call + SourceSection sourceLocation = value.getSourceLocation(); + if (sourceLocation != null) { + Assert.assertSame("Sources differ", expectedSource, sourceLocation.getSource()); + } + } + } + if (!found) { + Assert.fail("DebugValue named '" + name + "' not found."); + } + } + run.removeFirst().run(); + }); + } + }