/* * Copyright (c) 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 * 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 org.junit.Assert; import org.junit.Test; import com.oracle.truffle.api.debug.Breakpoint; import com.oracle.truffle.api.debug.DebuggerSession; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.debug.SuspensionFilter; import com.oracle.truffle.api.instrumentation.test.InstrumentationTestLanguage; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.tck.DebuggerTester; public class SuspensionFilterTest extends AbstractDebugTest { @Test public void testSuspendInInitialization() { Source initSource = Source.newBuilder("STATEMENT(EXPRESSION)").name("<init>").mimeType(InstrumentationTestLanguage.MIME_TYPE).build(); resetContext(new DebuggerTester(engineBuilder -> { return engineBuilder.config(InstrumentationTestLanguage.MIME_TYPE, "initSource", initSource); })); final Source source = testSource("ROOT(\n" + " DEFINE(foo, \n" + " STATEMENT(CONSTANT(42))\n" + " ), \n" + " STATEMENT(CALL(foo))\n" + ")\n"); SuspensionFilter suspensionFilter = SuspensionFilter.newBuilder().build(); // Empty filter does not filter anything try (DebuggerSession session = startSession()) { session.setSteppingFilter(suspensionFilter); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 1, true, "STATEMENT(EXPRESSION)").prepareContinue(); Assert.assertFalse(event.isLanguageContextInitialized()); }); expectDone(); } } @Test public void testSuspendAfterInitialization() { Source initSource = Source.newBuilder("STATEMENT(EXPRESSION)").name("<init>").mimeType(InstrumentationTestLanguage.MIME_TYPE).build(); resetContext(new DebuggerTester(engineBuilder -> { return engineBuilder.config(InstrumentationTestLanguage.MIME_TYPE, "initSource", initSource); })); final Source source = testSource("ROOT(\n" + " STATEMENT(CONSTANT(42))\n" + ")\n"); SuspensionFilter suspensionFilter = SuspensionFilter.newBuilder().ignoreLanguageContextInitialization(true).build(); try (DebuggerSession session = startSession()) { session.setSteppingFilter(suspensionFilter); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT(CONSTANT(42))").prepareContinue(); Assert.assertTrue(event.isLanguageContextInitialized()); }); expectDone(); } } @Test public void testSuspendAfterInitialization2() { // Suspend after initialization code finishes, // but can step into the same code that was executed during initialization, later on. Source initSource = Source.newBuilder("STATEMENT(EXPRESSION)").name("<init>").mimeType(InstrumentationTestLanguage.MIME_TYPE).build(); resetContext(new DebuggerTester(engineBuilder -> { return engineBuilder.config(InstrumentationTestLanguage.MIME_TYPE, "initSource", initSource).config(InstrumentationTestLanguage.MIME_TYPE, "runInitAfterExec", true); })); final Source source = testSource("ROOT(\n" + " STATEMENT(CONSTANT(42))\n" + ")\n"); SuspensionFilter suspensionFilter = SuspensionFilter.newBuilder().ignoreLanguageContextInitialization(true).build(); try (DebuggerSession session = startSession()) { session.setSteppingFilter(suspensionFilter); session.suspendNextExecution(); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 2, true, "STATEMENT(CONSTANT(42))").prepareStepOver(1); Assert.assertTrue(event.isLanguageContextInitialized()); session.suspendNextExecution(); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 1, true, "STATEMENT(EXPRESSION)").prepareContinue(); Assert.assertTrue(event.isLanguageContextInitialized()); }); expectDone(); } } @Test public void testInitializationFilterChange() { // Set to skip the initialization, but put two breakpoints there. // Verify that step just skips the code to the next breakpoint. // After second breakpoint is hit, change the filter to allow stepping // in the initialization code. String initCode = "ROOT(\n" + " DEFINE(initFoo, \n" + " STATEMENT(EXPRESSION),\n" + // Skipped by suspensionFilter " STATEMENT(EXPRESSION),\n" + // l. 4 Breakpoint " STATEMENT(CONSTANT(2)),\n" + // Skipped by suspensionFilter " STATEMENT(EXPRESSION),\n" + // l. 6 Breakpoint, filter changed " LOOP(2,\n" + " STATEMENT(CONSTANT(1)))\n" + // l. 8 Step stops here " ), \n" + " STATEMENT(CALL(initFoo))\n" + ")\n"; Source initSource = Source.newBuilder(initCode).name("<init>").mimeType(InstrumentationTestLanguage.MIME_TYPE).build(); resetContext(new DebuggerTester(engineBuilder -> { return engineBuilder.config(InstrumentationTestLanguage.MIME_TYPE, "initSource", initSource); })); final Source source = testSource("ROOT(\n" + " STATEMENT(CONSTANT(42))\n" + ")\n"); SuspensionFilter.Builder filterBuilder = SuspensionFilter.newBuilder().ignoreLanguageContextInitialization(true); SuspensionFilter suspensionFilter = filterBuilder.build(); try (DebuggerSession session = startSession()) { session.setSteppingFilter(suspensionFilter); session.suspendNextExecution(); Breakpoint bp4 = Breakpoint.newBuilder(initSource).lineIs(4).build(); Breakpoint bp6 = Breakpoint.newBuilder(initSource).lineIs(6).build(); session.install(bp4); session.install(bp6); startEval(source); expectSuspended((SuspendedEvent event) -> { checkState(event, 4, true, "STATEMENT(EXPRESSION)"); Assert.assertFalse(event.isLanguageContextInitialized()); Assert.assertTrue(event.getBreakpoints().contains(bp4)); event.prepareStepOver(1); }); expectSuspended((SuspendedEvent event) -> { checkState(event, 6, true, "STATEMENT(EXPRESSION)"); Assert.assertFalse(event.isLanguageContextInitialized()); Assert.assertTrue(event.getBreakpoints().contains(bp6)); filterBuilder.ignoreLanguageContextInitialization(false); session.setSteppingFilter(filterBuilder.build()); event.prepareStepOver(1); }); expectSuspended((SuspendedEvent event) -> { Assert.assertFalse(event.isLanguageContextInitialized()); checkState(event, 8, true, "STATEMENT(CONSTANT(1))").prepareContinue(); }); expectDone(); } } }