/* * Copyright (c) 2016, 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 * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.truffle.api.debug.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Iterator; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.junit.Assert; import org.junit.Test; import com.oracle.truffle.api.debug.Breakpoint; import com.oracle.truffle.api.debug.DebugStackFrame; import com.oracle.truffle.api.debug.DebugValue; import com.oracle.truffle.api.debug.DebuggerSession; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.instrumentation.test.InstrumentationTestLanguage; import com.oracle.truffle.api.source.Source; public class SuspendedEventTest extends AbstractDebugTest { @Test public void testStackFrames() throws Throwable { final Source source = testSource("ROOT(\n" + " DEFINE(bar, VARIABLE(bar0, 41), VARIABLE(bar1, 40), STATEMENT),\n" + " DEFINE(foo, ROOT(VARIABLE(foo0, 42), \n" + " STATEMENT(CALL(bar)))),\n" + " STATEMENT(VARIABLE(root0, 43)),\n" + " STATEMENT(CALL(foo))\n" + ")\n"); try (DebuggerSession session = startSession()) { session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 5, true, "STATEMENT(VARIABLE(root0, 43))").prepareStepOver(1); Iterator<DebugStackFrame> frameIterator = event.getStackFrames().iterator(); checkStack(frameIterator.next()); Assert.assertFalse(frameIterator.hasNext()); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 6, true, "STATEMENT(CALL(foo))", "root0", "43").prepareStepInto(1); Iterator<DebugStackFrame> frameIterator = event.getStackFrames().iterator(); checkStack(frameIterator.next(), "root0", "43"); Assert.assertFalse(frameIterator.hasNext()); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT(CALL(bar))", "foo0", "42").prepareStepInto(1); Iterator<DebugStackFrame> frameIterator = event.getStackFrames().iterator(); checkStack(frameIterator.next(), "foo0", "42"); checkStack(frameIterator.next(), "root0", "43"); Assert.assertFalse(frameIterator.hasNext()); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT", "bar0", "41", "bar1", "40").prepareContinue(); Iterator<DebugStackFrame> frameIterator = event.getStackFrames().iterator(); checkStack(frameIterator.next(), "bar0", "41", "bar1", "40"); checkStack(frameIterator.next(), "foo0", "42"); checkStack(frameIterator.next(), "root0", "43"); Assert.assertFalse(frameIterator.hasNext()); }); expectDone(); } } @Test public void testReturnValue() throws Throwable { final Source source = testSource("ROOT(\n" + " DEFINE(bar, STATEMENT(CONSTANT(42))), \n" + " DEFINE(foo, CALL(bar)), \n" + " STATEMENT(CALL(foo))\n" + ")\n"); try (DebuggerSession session = startSession()) { session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT(CALL(foo))").prepareStepInto(1); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT(CONSTANT(42))").prepareStepInto(1); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 3, false, "CALL(bar)").prepareStepInto(1); assertEquals("42", event.getReturnValue().as(String.class)); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, false, "CALL(foo)").prepareContinue(); assertEquals("42", event.getReturnValue().as(String.class)); }); expectDone(); } } @Test public void testIsInternal() throws Throwable { final Source source = Source.newBuilder("ROOT(\n" + " DEFINE(bar, ROOT(STATEMENT)),\n" + " DEFINE(foo, STATEMENT, \n" + " STATEMENT(CALL(bar))),\n" + " STATEMENT(CALL(foo))\n" + ")\n").mimeType(InstrumentationTestLanguage.MIME_TYPE).internal().name("internal test code").build(); try (DebuggerSession session = startSession()) { session.install(Breakpoint.newBuilder(source).lineIs(2).build()); startEval(source); expectSuspended((SuspendedEvent event) -> { Iterator<DebugStackFrame> frameIterator = event.getStackFrames().iterator(); DebugStackFrame frame = frameIterator.next(); assertTrue(frame.isInternal()); frame = frameIterator.next(); assertTrue(frame.isInternal()); frame = frameIterator.next(); assertTrue(frame.isInternal()); }); expectDone(); } } @Test public void testOtherThreadAccess() throws Throwable { final Source source = testSource("ROOT(\n" + " DEFINE(bar, VARIABLE(bar0, 41), VARIABLE(bar1, 40), STATEMENT),\n" + " DEFINE(foo, ROOT(VARIABLE(foo0, 42), \n" + " STATEMENT(CALL(bar)))),\n" + " STATEMENT(VARIABLE(root0, 43)),\n" + " STATEMENT(CALL(foo))\n" + ")\n"); try (DebuggerSession session = startSession()) { final Breakpoint breakpoint = session.install(Breakpoint.newBuilder(source).lineIs(4).build()); startEval(source); expectSuspended((SuspendedEvent event) -> { run(() -> event.getBreakpointConditionException(breakpoint)); run(() -> event.getSession()); run(() -> event.getSourceSection()); run(() -> event.getBreakpoints()); run(() -> event.isHaltedBefore()); run(() -> event.toString()); run(() -> { event.prepareStepInto(1); return null; }); run(() -> { event.prepareStepOut(1); return null; }); run(() -> { event.prepareStepOver(1); return null; }); run(() -> { event.prepareContinue(); return null; }); run(() -> { event.prepareKill(); return null; }); runExpectIllegalState(() -> event.getStackFrames()); runExpectIllegalState(() -> event.getTopStackFrame()); runExpectIllegalState(() -> event.getReturnValue()); for (DebugStackFrame frame : event.getStackFrames()) { for (DebugValue value : frame) { runExpectIllegalState(() -> value.as(String.class)); runExpectIllegalState(() -> { value.set(null); return null; }); runExpectIllegalState(() -> value.getName()); runExpectIllegalState(() -> value.isReadable()); runExpectIllegalState(() -> value.isWritable()); } run(() -> frame.getName()); run(() -> frame.getSourceSection()); run(() -> frame.isInternal()); run(() -> frame.toString()); runExpectIllegalState(() -> frame.getScope().getDeclaredValue("")); runExpectIllegalState(() -> frame.getScope().getDeclaredValues().iterator()); runExpectIllegalState(() -> frame.eval("")); } }); expectDone(); } } private static <T> T run(Callable<T> callable) { Future<T> future = Executors.newSingleThreadExecutor().submit(callable); try { return future.get(); } catch (Exception e) { throw new AssertionError(e); } } private static <T> void runExpectIllegalState(Callable<T> callable) { Future<T> future = Executors.newSingleThreadExecutor().submit(callable); try { future.get(); Assert.fail(); } catch (ExecutionException e) { if (!(e.getCause() instanceof IllegalStateException)) { throw new AssertionError(e); } } catch (InterruptedException e) { throw new AssertionError(e); } } }