diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index a5d042faf374d097999e16727ca2c7b1cb009846..378c3219ae107baa59c2e42256b5a50579516fdb 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -54,6 +54,7 @@ import com.oracle.truffle.r.nodes.builtin.helpers.DebugHandling; import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling; import com.oracle.truffle.r.nodes.control.AbstractLoopNode; import com.oracle.truffle.r.nodes.control.BlockNode; +import com.oracle.truffle.r.nodes.control.ForNode; import com.oracle.truffle.r.nodes.control.IfNode; import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; @@ -532,7 +533,9 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { return true; } else { // single statement block, variable parent - return parent instanceof FunctionDefinitionNode || parent instanceof IfNode || parent instanceof AbstractLoopNode; + // note: RepeatingNode is not a RSyntaxElement but the body of a loop is + // under the repeating node ! + return parent instanceof FunctionDefinitionNode || parent instanceof IfNode || parent instanceof AbstractLoopNode || ForNode.isLoopBody(node); } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java index 80f96d66aa3d6895cfc9aee862ccb5d78126473b..75684febae6ee8df37d7d1fd758169b6a918569f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/helpers/DebugHandling.java @@ -45,6 +45,7 @@ import com.oracle.truffle.r.nodes.control.AbstractLoopNode; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; import com.oracle.truffle.r.nodes.instrumentation.RInstrumentation; import com.oracle.truffle.r.nodes.instrumentation.RSyntaxTags; +import com.oracle.truffle.r.runtime.JumpToTopLevelException; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RError; @@ -186,17 +187,18 @@ public class DebugHandling { @Override protected Void visit(RSyntaxFunction element) { + accept(element.getSyntaxBody()); return null; } }.accept(fdn); return fser; } - private static void ensureSingleStep(FunctionDefinitionNode fdn) { + private static FunctionStatementsEventListener ensureSingleStep(FunctionDefinitionNode fdn) { FunctionStatementsEventListener fser = getFunctionStatementsEventListener(fdn); if (fser == null) { // attach a "once" listener - fser = attachDebugHandler(fdn, null, null, true, false); + fser = attachDebugHandler(fdn, null, null, true, true); } else { if (fser.disabled()) { fser.enable(); @@ -204,6 +206,7 @@ public class DebugHandling { fser.enabledForStepInto = true; } } + return fser; } private abstract static class DebugEventListener implements ExecutionEventListener { @@ -293,8 +296,9 @@ public class DebugHandling { @TruffleBoundary private void attachStepInto() { - stepIntoInstrument = RInstrumentation.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(), - new StepIntoInstrumentListener(getFunctionStatementsEventListener(functionDefinitionNode))); + FunctionStatementsEventListener parentListener = getFunctionStatementsEventListener(functionDefinitionNode); + parentListener.stepIntoInstrument = RInstrumentation.getInstrumenter().attachListener(SourceSectionFilter.newBuilder().tagIs(StandardTags.RootTag.class).build(), + new StepIntoInstrumentListener(parentListener)); } @@ -426,7 +430,7 @@ public class DebugHandling { public void onReturnValue(EventContext context, VirtualFrame frame, Object result) { if (!disabled()) { CompilerDirectives.transferToInterpreter(); - returnCleanup(frame); + returnCleanup(frame, false); } } @@ -434,12 +438,12 @@ public class DebugHandling { public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { if (!disabled()) { CompilerDirectives.transferToInterpreter(); - returnCleanup(frame); + returnCleanup(frame, exception instanceof JumpToTopLevelException); } } - private void returnCleanup(VirtualFrame frame) { - if (!implicit) { + private void returnCleanup(VirtualFrame frame, boolean jumpToTopLevel) { + if (!implicit && !once && !jumpToTopLevel) { print("exiting from: ", false); printCall(frame); } @@ -502,7 +506,7 @@ public class DebugHandling { return; } printNode(node, false); - browserInteract(context.getInstrumentedNode(), frame); + browserInteract(node, frame); } } @@ -511,6 +515,9 @@ public class DebugHandling { } } + /** + * Handles the loop header and there is one instance registered for each loop. + */ private static class LoopStatementEventListener extends StatementEventListener { private boolean finishing; @@ -529,7 +536,7 @@ public class DebugHandling { @Override public void onEnter(EventContext context, VirtualFrame frame) { - if (!disabled()) { + if (!disabled() && context.getInstrumentedNode() == loopNode) { super.onEnter(context, frame); } } @@ -544,7 +551,7 @@ public class DebugHandling { @Override public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { - if (!disabled()) { + if (!disabled() && context.getInstrumentedNode() == loopNode) { CompilerDirectives.transferToInterpreter(); returnCleanup(); } @@ -552,7 +559,7 @@ public class DebugHandling { @Override public void onReturnValue(EventContext context, VirtualFrame frame, Object result) { - if (!disabled()) { + if (!disabled() && context.getInstrumentedNode() == loopNode) { CompilerDirectives.transferToInterpreter(); returnCleanup(); } @@ -592,9 +599,10 @@ public class DebugHandling { if (!RContext.getInstance().stateInstrumentation.debugGloballyDisabled()) { CompilerDirectives.transferToInterpreter(); FunctionDefinitionNode fdn = (FunctionDefinitionNode) context.getInstrumentedNode().getRootNode(); - ensureSingleStep(fdn); + FunctionStatementsEventListener ensureSingleStep = ensureSingleStep(fdn); + functionStatementsEventListener.clearStepInstrument(); - functionStatementsEventListener.onEnter(context, frame); + ensureSingleStep.onEnter(context, frame); } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java index d50558d6209356a2ae00089374cf22c521aea35f..27575a9f1759ab5d3f3cec301318bc2c55573205 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ForNode.java @@ -24,6 +24,7 @@ package com.oracle.truffle.r.nodes.control; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode; import com.oracle.truffle.api.nodes.LoopNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RepeatingNode; @@ -159,4 +160,16 @@ public final class ForNode extends AbstractLoopNode implements RSyntaxNode, RSyn public ArgumentsSignature getSyntaxSignature() { return ArgumentsSignature.empty(3); } + + /** + * Tests if the provided node is a loop-body node (also considering wrappers). + */ + public static boolean isLoopBody(Node n) { + Node parent = n.getParent(); + if (parent instanceof WrapperNode) { + Node grandparent = parent.getParent(); + return grandparent instanceof ForRepeatingNode && ((ForRepeatingNode) grandparent).body == parent; + } + return parent instanceof ForRepeatingNode && ((ForRepeatingNode) parent).body == n; + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java index 41ea6dacdaef4fa6b2282d0bc842ecc33a2f4a50..e1ef2d2fc9afa9196652aa5e6cec3fb6600fdc12 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RSource.java @@ -172,6 +172,13 @@ public class RSource { return Source.newBuilder(file).name(file.getName()).mimeType(RRuntime.R_APP_MIME).build(); } + /** + * Create a source from the file system path denoted by {@code file}. + */ + public static Source fromTempFile(File file) throws IOException { + return Source.newBuilder(file).name(file.getName()).mimeType(RRuntime.R_APP_MIME).internal().build(); + } + /** * Create an (external) source from {@code url}. */ diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 3ab87815debd8fab265f490b00b710d2c0efc683..9d4f0c63c3bc00a0e5221bc8df84c6586300ebd2 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -143303,6 +143303,63 @@ x + x ~ y + log(y) #update.formula(x ~ y, ~ . + x2) x ~ y + x2 +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testConditionalBreakpoint# +#fun <- function(x) { cat('x='); cat(x); cat('\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)<<<NEWLINE>>>; fun(11)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +[1] "fun" +Tracing fun(10) on entry +x=10 +Error: unexpected ';' in ";" + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testContinue# +#fun0 <- function() { print('fun0') }; fun1 <- function() { print('enter fun1'); fun0(); print('exit fun1') }; fun2 <- function() { print('enter fun2'); fun1(); print('exit fun2') }; debug(fun2); fun2()<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>c<<<NEWLINE>>>c<<<NEWLINE>>>c<<<NEWLINE>>> +debugging in: fun2() +debug at #1: { + print("enter fun2") + fun1() + print("exit fun2") +} +debug at #1: print("enter fun2") +[1] "enter fun2" +debug at #1: fun1() +debugging in: fun1() +debug at #1: { + print("enter fun1") + fun0() + print("exit fun1") +} +debug at #1: print("enter fun1") +[1] "enter fun1" +debug at #1: fun0() +debugging in: fun0() +debug at #1: { + print("fun0") +} +[1] "fun0" +debug at #1: print("exit fun1") +[1] "exit fun1" +debug at #1: print("exit fun2") +[1] "exit fun2" +exiting from: fun2() + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testDebugOnce# +#fun0 <- function() { print('fun0') }; debugonce(fun0); fun0()<<<NEWLINE>>>c<<<NEWLINE>>> +debugging in: fun0() +debug at #1: { + print("fun0") +} +[1] "fun0" + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testDebugOnce# +#fun0 <- function() { print('fun0') }; fun1 <- function() { print('en'); fun0(); fun0(); print('ex') }; debugonce(fun0); fun1()<<<NEWLINE>>>c<<<NEWLINE>>> +[1] "en" +debugging in: fun0() +debug at #1: { + print("fun0") +} +[1] "fun0" +[1] "fun0" +[1] "ex" + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testInvalidName# #f <- function(x) {<<<NEWLINE>>> `123t` <- x + 1<<<NEWLINE>>> print(`123t`)<<<NEWLINE>>> `123t`}<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>>n<<<NEWLINE>>>`123t`<<<NEWLINE>>>n<<<NEWLINE>>>n debugging in: f(5) @@ -143320,6 +143377,120 @@ debug at #4: `123t` exiting from: f(5) [1] 6 +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop# +#fun <- function(x) { for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debugging in: fun(3) +debug at #1: { + for (i in seq(x)) cat(i) + cat(5) +} +debug at #1: for (i in seq(x)) cat(i) +debug at #1: cat(i) +1debug at #1: cat(i) +2debug at #1: cat(i) +3debug at #1: cat(5) +5exiting from: fun(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop# +#fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debugging in: fun(3) +debug at #1: { + for (i in seq(x)) { + cat(i) + } + cat(5) +} +debug at #1: for (i in seq(x)) { + cat(i) +} +debug at #1: cat(i) +1debug at #1: cat(i) +2debug at #1: cat(i) +3debug at #1: cat(5) +5exiting from: fun(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop# +#fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>> +debugging in: fun(3) +debug at #1: { + for (i in seq(x)) { + cat(i) + } + cat(5) +} +debug at #1: for (i in seq(x)) { + cat(i) +} +debug at #1: cat(i) +1debug at #1: cat(i) +23debug at #1: cat(5) +5exiting from: fun(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop# +#fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debugging in: fun(3) +debug at #1: { + for (j in seq(2)) for (i in seq(x)) cat(i) + cat(5) +} +debug at #1: for (j in seq(2)) for (i in seq(x)) cat(i) +debug at #1: for (i in seq(x)) cat(i) +debug at #1: cat(i) +1debug at #1: cat(i) +2debug at #1: cat(i) +3debug at #1: for (i in seq(x)) cat(i) +debug at #1: cat(i) +1debug at #1: cat(i) +2debug at #1: cat(i) +3debug at #1: cat(5) +5exiting from: fun(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testLoop# +#fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>> +debugging in: fun(3) +debug at #1: { + for (j in seq(2)) for (i in seq(x)) cat(i) + cat(5) +} +debug at #1: for (j in seq(2)) for (i in seq(x)) cat(i) +debug at #1: for (i in seq(x)) cat(i) +debug at #1: cat(i) +123debug at #1: for (i in seq(x)) cat(i) +123debug at #1: cat(5) +5exiting from: fun(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNestedDebugging#Output.IgnoreDebugPath# +#foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo("bar <- function() { print('bar') }; debug(bar); bar()")<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debugging in: foo("bar <- function() { print('bar') }; debug(bar); bar()") +debug at #1: { + eval(parse(text = rCode)) + print("foo done") +} +debug at #1: eval(parse(text = rCode)) +debugging in: bar() +debug at <text>#1: { + print("bar") +} +debug at <text>#1: print("bar") +[1] "bar" +exiting from: bar() +debug at #1: print("foo done") +[1] "foo done" +exiting from: foo("bar <- function() { print('bar') }; debug(bar); bar()") + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNestedDebugging#Output.IgnoreDebugPath# +#foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo("bar <- function() { print('bar') }; debug(bar); bar()")<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>Q<<<NEWLINE>>> +debugging in: foo("bar <- function() { print('bar') }; debug(bar); bar()") +debug at #1: { + eval(parse(text = rCode)) + print("foo done") +} +debug at #1: eval(parse(text = rCode)) +debugging in: bar() +debug at <text>#1: { + print("bar") +} + ##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testNoBracket# #f <- function(x) print(x)<<<NEWLINE>>>debug(f)<<<NEWLINE>>>f(5)<<<NEWLINE>>>x<<<NEWLINE>>>n<<<NEWLINE>>> debugging in: f(5) @@ -143345,6 +143516,74 @@ debug at #4: t exiting from: f(5) [1] 6 +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto# +#bar <- function(x) { cat(x); cat('\n') }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>> +debugging in: foo(3) +debug at #1: { + cat("foo entry\n") + bar(x) + cat("foo exit\n") +} +debug at #1: cat("foo entry\n") +foo entry +debug at #1: bar(x) +debugging in: bar(x) +debug at #1: { + cat(x) + cat("\n") +} +debug at #1: cat(x) +3debug at #1: cat("\n") + +debug at #1: cat("foo exit\n") +foo exit +exiting from: foo(3) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto# +#bar <- function(x) { cat(x); cat('\n') }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(3)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>>Q<<<NEWLINE>>> +debugging in: foo(3) +debug at #1: { + cat("foo entry\n") + bar(x) + cat("foo exit\n") +} +debug at #1: cat("foo entry\n") +foo entry +debug at #1: bar(x) +debugging in: bar(x) +debug at #1: { + cat(x) + cat("\n") +} +debug at #1: cat(x) + +##com.oracle.truffle.r.test.library.utils.TestInteractiveDebug.testStepInto# +#bar <- function(x) { for(i in seq(x)) print(x) }; foo <- function(x) { cat('foo entry\n'); bar(x); cat('foo exit\n') }; debug(foo); foo(5)<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>s<<<NEWLINE>>>n<<<NEWLINE>>><<<NEWLINE>>><<<NEWLINE>>>f<<<NEWLINE>>><<<NEWLINE>>> +debugging in: foo(5) +debug at #1: { + cat("foo entry\n") + bar(x) + cat("foo exit\n") +} +debug at #1: cat("foo entry\n") +foo entry +debug at #1: bar(x) +debugging in: bar(x) +debug at #1: { + for (i in seq(x)) print(x) +} +debug at #1: for (i in seq(x)) print(x) +debug at #1: print(x) +[1] 5 +debug at #1: print(x) +[1] 5 +[1] 5 +[1] 5 +[1] 5 +debug at #1: cat("foo exit\n") +foo exit +exiting from: foo(5) + ##com.oracle.truffle.r.test.library.utils.TestTrace.testCondTrace# #f <- function(x) {}; (if (exists('.fastr.trace')) .fastr.trace else trace)(f, tracer=quote(if (x == 3 || x == 7) print(x))); g <- function() for (i in 1:10) f(i); g() [1] "f" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java index b0ed490c9833296de281c67815cde0a615ea7d8e..21d6e39b32118f9e58899eee2962eff22cb8e6ad 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestBase.java @@ -77,7 +77,9 @@ public class TestBase { MayIgnoreWarningContext, ContainsReferences, // replaces references in form of 0xbcdef1 for numbers IgnoreWhitespace, // removes all whitespace from the whole output - IgnoreCase; // ignores upper/lower case differences + IgnoreCase, // ignores upper/lower case differences + IgnoreDebugPath, // ignores <path> in debug output like "debug at <path> #..." + IgnoreDebugDepth; // ignores call depth printed by the debugger ("Browse[<call depth>]") @Override public String getName() { @@ -608,6 +610,12 @@ public class TestBase { if (output.contains(Output.ContainsReferences)) { return convertReferencesInOutput(out); } + if (output.contains(Output.IgnoreDebugPath)) { + return convertDebugOutput(out); + } + if (output.contains(Output.IgnoreDebugDepth)) { + return removeDebugCallDepth(out); + } return out; } } @@ -731,6 +739,29 @@ public class TestBase { return result; } + private static String convertDebugOutput(String out) { + String prefix = "debug at "; + return removeAllOccurrencesBetween(out, prefix, prefix.length(), "#", 0); + } + + private static String removeDebugCallDepth(String out) { + String prefix = "Browse["; + return removeAllOccurrencesBetween(out, prefix, prefix.length(), "]", 0); + } + + private static String removeAllOccurrencesBetween(String out, String prefix, int prefixOffset, String suffix, int suffixOffset) { + StringBuilder sb = new StringBuilder(out); + + int idxPrefix = -1; + int idxSuffix = -1; + + while ((idxPrefix = sb.indexOf(prefix, idxPrefix + 1)) > 0 && (idxSuffix = sb.indexOf(suffix, idxPrefix)) > idxPrefix) { + sb.replace(idxPrefix + prefixOffset, idxSuffix + suffixOffset, ""); + } + + return sb.toString(); + } + private boolean searchWhiteLists(WhiteList[] whiteLists, String input, String expected, String result, TestTraitsSet testTraits) { if (whiteLists == null) { return false; diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java index eec8974833cac7fa384d397106682b90355d237b..0b51a3d92da5c30b1872b21f4e31c429aeaa6403 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/generate/FastRSession.java @@ -36,6 +36,7 @@ import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.r.runtime.ExitException; +import com.oracle.truffle.r.runtime.JumpToTopLevelException; import com.oracle.truffle.r.runtime.RCmdOptions; import com.oracle.truffle.r.runtime.RCmdOptions.Client; import com.oracle.truffle.r.runtime.RError; @@ -224,8 +225,9 @@ public final class FastRSession implements RSession { } } catch (ParseException e) { e.report(consoleHandler); - } catch (ExitException e) { - // exit exceptions are legitimate if a test case calls "q()" + } catch (ExitException | JumpToTopLevelException e) { + // exit and jumpToTopLevel exceptions are legitimate if a test case calls "q()" or "Q" + // during debugging } catch (RError e) { // nothing to do } catch (Throwable t) { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java index 7d32146ecb76c6ce69082ef78ed32e6e1b54ff2c..e74647fcf24fff7e8ffb7e4de186873b31aa74d4 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/utils/TestInteractiveDebug.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 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 @@ -26,6 +26,7 @@ import org.junit.Test; import com.oracle.truffle.r.test.TestBase; +// Checkstyle: stop line length check public class TestInteractiveDebug extends TestBase { @Test public void testSimple() { @@ -41,4 +42,44 @@ public class TestInteractiveDebug extends TestBase { public void testNoBracket() { assertEval("f <- function(x) print(x)\ndebug(f)\nf(5)\nx\nn\n"); } + + @Test + public void testLoop() { + assertEval("fun <- function(x) { for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n"); + assertEval("fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n"); + assertEval("fun <- function(x) { for(i in seq(x)) { cat(i) }; cat(5) }; debug(fun); fun(3)\n\n\n\nf\n\n"); + assertEval("fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\n\n\n\n\n\n\n\n\n"); + assertEval("fun <- function(x) { for(j in seq(2)) for(i in seq(x)) cat(i); cat(5) }; debug(fun); fun(3)\n\n\n\nf\nf\n\n"); + } + + @Test + public void testStepInto() { + assertEval("bar <- function(x) { cat(x); cat('\\n') }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(3)\n\n\ns\nn\n\n\n\n"); + assertEval("bar <- function(x) { cat(x); cat('\\n') }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(3)\n\n\ns\nn\nQ\n"); + assertEval("bar <- function(x) { for(i in seq(x)) print(x) }; foo <- function(x) { cat('foo entry\\n'); bar(x); cat('foo exit\\n') }; debug(foo); foo(5)\n\n\ns\nn\n\n\nf\n\n"); + } + + @Test + public void testNestedDebugging() { + assertEval(Output.IgnoreDebugPath, + "foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo(\"bar <- function() { print('bar') }; debug(bar); bar()\")\n\n\n\n\n\n"); + assertEval(Output.IgnoreDebugPath, + "foo <- function(rCode) { eval(parse(text=rCode)); print('foo done') }; debug(foo); foo(\"bar <- function() { print('bar') }; debug(bar); bar()\")\n\n\nQ\n"); + } + + @Test + public void testConditionalBreakpoint() { + assertEval("fun <- function(x) { cat('x='); cat(x); cat('\\n') }; trace(fun, quote(if (x > 10) browser())); fun(10)\n; fun(11)\n\n\n\n\n\n"); + } + + @Test + public void testContinue() { + assertEval("fun0 <- function() { print('fun0') }; fun1 <- function() { print('enter fun1'); fun0(); print('exit fun1') }; fun2 <- function() { print('enter fun2'); fun1(); print('exit fun2') }; debug(fun2); fun2()\n\n\ns\nn\n\ns\nc\nc\nc\n"); + } + + @Test + public void testDebugOnce() { + assertEval("fun0 <- function() { print('fun0') }; fun1 <- function() { print('en'); fun0(); fun0(); print('ex') }; debugonce(fun0); fun1()\nc\n"); + assertEval("fun0 <- function() { print('fun0') }; debugonce(fun0); fun0()\nc\n"); + } }