# HG changeset patch # User Jaroslav Tulach # Date 1437235416 -7200 # Node ID 5bc7f7b867abc0cc464205d8884d6a20e21c803b # Parent 324997830dc92631f7d77c107a6da2b74283504b Making debugger always on for each TruffleVM execution. Introducing EventConsumer to process such debugger events. Requesting each RootNode to be associated with a TruffleLanguage, so debugger can find out proper context for each Node where executions gets suspended. diff -r 324997830dc9 -r 5bc7f7b867ab mx.truffle/suite.py --- a/mx.truffle/suite.py Thu Jul 16 19:11:31 2015 +0200 +++ b/mx.truffle/suite.py Sat Jul 18 18:03:36 2015 +0200 @@ -269,19 +269,10 @@ "workingSets" : "Truffle,Tools", }, - "com.oracle.truffle.tools.debug.engine" : { - "subDir" : "truffle", - "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.tools"], - "checkstyle" : "com.oracle.truffle.api", - "javaCompliance" : "1.7", - "workingSets" : "Truffle,Tools", - }, - "com.oracle.truffle.tools.debug.shell" : { "subDir" : "truffle", "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.tools.debug.engine", + "dependencies" : ["com.oracle.truffle.tools", "JLINE"], "checkstyle" : "com.oracle.truffle.api", "javaCompliance" : "1.7", @@ -306,7 +297,7 @@ "sourceDirs" : ["src"], "dependencies" : [ "com.oracle.truffle.tck", - "com.oracle.truffle.sl" + "com.oracle.truffle.sl", ], "checkstyle" : "com.oracle.truffle.sl", "javaCompliance" : "1.7", diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -35,7 +35,7 @@ @Test public void testNode1() { final TestNode1 node = TestNode1NodeGen.create(null); - RootNode root = new RootNode() { + RootNode root = new RootNode(TestingLanguage.class, null, null) { @Child TestNode1 test = node; @Override diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestHelper.java Sat Jul 18 18:03:36 2015 +0200 @@ -42,7 +42,7 @@ // make nodes replacable public static T createRoot(final T node) { - new RootNode() { + new RootNode(TestingLanguage.class, null, null) { @Child T child = node; @Override diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 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. + * + * 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.dsl.test; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.source.Source; +import java.io.IOException; + +public final class TestingLanguage extends TruffleLanguage { + public static final TruffleLanguage INSTANCE = new TestingLanguage(); + + private TestingLanguage() { + super(null); + } + + @Override + protected Object eval(Source code) throws IOException { + throw new IOException(); + } + + @Override + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected ToolSupportProvider getToolSupport() { + return null; + } + + @Override + protected DebugSupportProvider getDebugSupport() { + return null; + } + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TypeSystemTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -148,7 +148,7 @@ @Child private E node; public TestRootNode(E node) { - super(null); + super(TestingLanguage.class, null, null); this.node = node; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/ExampleNode.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/ExampleNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/ExampleNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -25,6 +25,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.dsl.internal.*; +import com.oracle.truffle.api.dsl.test.TestingLanguage; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; @@ -68,6 +69,7 @@ @Child ExampleNode child; public ExampleRootNode(ExampleNode child) { + super(TestingLanguage.class, null, null); this.child = child; } @@ -105,6 +107,7 @@ private final int argumentIndex; public DummyCallRootNode(int argumentIndex) { + super(TestingLanguage.class, null, null); this.argumentIndex = argumentIndex; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java --- a/truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/Specialization.java Sat Jul 18 18:03:36 2015 +0200 @@ -244,12 +244,9 @@ * Example usage: * *
-     * static boolean acceptOperand(int operand) {
-     *     assert operand <= 42;
-     *     return operand & 1 == 1;
-     * }
-     * @Specialization(guards = {"operand <= 42", "acceptOperand(operand)"})
-     * void doSpecialization(int operand) {...}
+     * static boolean acceptOperand(int operand) { assert operand <= 42; return operand & 1 ==
+     * 1; } @Specialization(guards = {"operand <= 42", "acceptOperand(operand)"}) void
+     * doSpecialization(int operand) {...}
      * 
* *

@@ -287,16 +284,11 @@ * Example usage: * *
-     * static abstract class DynamicObject() {
-     *      abstract Shape getShape();
-     *      ...
-     * }
-     * static abstract class Shape() {
-     *      abstract Assumption getUnmodifiedAssuption();
-     *      ...
-     * }
-     * @Specialization(guards = "operand.getShape() == cachedShape", assumptions = "cachedShape.getUnmodifiedAssumption()")
-     * void doAssumeUnmodifiedShape(DynamicObject operand, @Cached("operand.getShape()") Shape cachedShape) {...}
+     * static abstract class DynamicObject() { abstract Shape getShape(); ... } static
+     * abstract class Shape() { abstract Assumption getUnmodifiedAssuption(); ... }
+     * @Specialization(guards = "operand.getShape() == cachedShape", assumptions =
+     * "cachedShape.getUnmodifiedAssumption()") void doAssumeUnmodifiedShape(DynamicObject
+     * operand, @Cached("operand.getShape()") Shape cachedShape) {...}
      * 
* *

@@ -335,11 +327,10 @@ * Example usage: * *
-     * static int getCacheLimit() {
-     *     return Integer.parseInt(System.getProperty("language.cacheLimit"));
-     * }
-     * @Specialization(guards = "operand == cachedOperand", limit = "getCacheLimit()")
-     * void doCached(Object operand, @Cached("operand") Object cachedOperand) {...}
+     * static int getCacheLimit() { return
+     * Integer.parseInt(System.getProperty("language.cacheLimit")); } @Specialization(guards =
+     * "operand == cachedOperand", limit = "getCacheLimit()") void doCached(Object
+     * operand, @Cached("operand") Object cachedOperand) {...}
      * 
* *

diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/TypeSystem.java --- a/truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/TypeSystem.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl/src/com/oracle/truffle/api/dsl/TypeSystem.java Sat Jul 18 18:03:36 2015 +0200 @@ -61,12 +61,12 @@ * * {@literal @}TypeSystem(types = {boolean.class, int.class, double.class}) * public abstract class ExampleTypeSystem { - * + * * {@literal @}TypeCheck * public boolean isInteger(Object value) { * return value instanceof Integer || value instanceof Double; * } - * + * * {@literal @}TypeCast * public double asInteger(Object value) { * return ((Number)value).doubleValue(); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/SymbolInvokerImpl.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/SymbolInvokerImpl.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/impl/SymbolInvokerImpl.java Sat Jul 18 18:03:36 2015 +0200 @@ -38,7 +38,7 @@ static final FrameDescriptor UNUSED_FRAMEDESCRIPTOR = new FrameDescriptor(); @Override - protected Object invoke(Object symbol, Object... arr) throws IOException { + protected Object invoke(TruffleLanguage lang, Object symbol, Object... arr) throws IOException { if (symbol instanceof String) { return symbol; } @@ -49,7 +49,7 @@ return symbol; } Node executeMain = Message.createExecute(arr.length).createNode(); - CallTarget callTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(executeMain, (TruffleObject) symbol, arr)); + CallTarget callTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(lang, executeMain, (TruffleObject) symbol, arr)); VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(arr, UNUSED_FRAMEDESCRIPTOR); Object ret = callTarget.call(frame); if (ret instanceof TruffleObject) { @@ -57,20 +57,20 @@ Object isBoxedResult; try { Node isBoxed = Message.IS_BOXED.createNode(); - CallTarget isBoxedTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(isBoxed, tret)); + CallTarget isBoxedTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(lang, isBoxed, tret)); isBoxedResult = isBoxedTarget.call(frame); } catch (IllegalArgumentException ex) { isBoxedResult = false; } if (Boolean.TRUE.equals(isBoxedResult)) { Node unbox = Message.UNBOX.createNode(); - CallTarget unboxTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(unbox, tret)); + CallTarget unboxTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(lang, unbox, tret)); Object unboxResult = unboxTarget.call(frame); return unboxResult; } else { try { Node isNull = Message.IS_NULL.createNode(); - CallTarget isNullTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(isNull, tret)); + CallTarget isNullTarget = Truffle.getRuntime().createCallTarget(new TemporaryRoot(lang, isNull, tret)); Object isNullResult = isNullTarget.call(frame); if (Boolean.TRUE.equals(isNullResult)) { return null; @@ -88,7 +88,8 @@ private final TruffleObject function; private final Object[] args; - public TemporaryRoot(Node foreignAccess, TruffleObject function, Object... args) { + public TemporaryRoot(TruffleLanguage lang, Node foreignAccess, TruffleObject function, Object... args) { + super(lang.getClass(), null, null); this.foreignAccess = foreignAccess; this.function = function; this.args = args; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/package-info.java --- a/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop/src/com/oracle/truffle/api/interop/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,26 +1,23 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2014, 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 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 + * 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. + * 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. + * 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. */ /** @@ -28,15 +25,15 @@ * {@link com.oracle.truffle.api.TruffleLanguage Truffle languages}. *

* Languages can exchange primitive Java type wrapper objects (e.g., {@link java.lang.Integer}, - * {@link java.lang.Double}, {@link java.lang.String}, etc) as well as any type - * implementing {@link com.oracle.truffle.api.interop.TruffleObject}. Foreign objects are - * precisely those implementing {@link com.oracle.truffle.api.interop.TruffleObject}. + * {@link java.lang.Double}, {@link java.lang.String}, etc) as well as any type implementing + * {@link com.oracle.truffle.api.interop.TruffleObject}. Foreign objects are precisely those + * implementing {@link com.oracle.truffle.api.interop.TruffleObject}. *

- * To use a {@link com.oracle.truffle.api.interop.TruffleObject} from a different language, - * you need to ask the language to build appropriate AST for a given + * To use a {@link com.oracle.truffle.api.interop.TruffleObject} from a different language, you need + * to ask the language to build appropriate AST for a given * {@link com.oracle.truffle.api.interop.Message} with - * {@link com.oracle.truffle.api.interop.Message#createNode}. The message can then - * be executed with {@link com.oracle.truffle.api.interop.ForeignAccess#execute}. + * {@link com.oracle.truffle.api.interop.Message#createNode}. The message can then be executed with + * {@link com.oracle.truffle.api.interop.ForeignAccess#execute}. */ package com.oracle.truffle.api.interop; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -65,7 +65,7 @@ @Children private final TestArgumentNode[] children; TestRootNode(TestArgumentNode[] children) { - super(null); + super(TestingLanguage.class, null, null); this.children = children; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -60,7 +60,7 @@ private final CallTarget secondTarget; DualCallNode(CallTarget firstTarget, CallTarget secondTarget) { - super(null); + super(TestingLanguage.class, null, null); this.firstTarget = firstTarget; this.secondTarget = secondTarget; } @@ -76,7 +76,7 @@ private final int value; public ConstantRootNode(int value) { - super(null); + super(TestingLanguage.class, null, null); this.value = value; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -76,7 +76,7 @@ @Child private TestChildNode right; public TestRootNode(TestChildNode left, TestChildNode right) { - super(null); + super(TestingLanguage.class, null, null); this.left = left; this.right = right; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -71,7 +71,7 @@ @Children private final TestChildNode[] children; public TestRootNode(TestChildNode[] children) { - super(null); + super(TestingLanguage.class, null, null); this.children = children; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -65,7 +65,7 @@ @Children private final TestChildNode[] children; public TestRootNode(TestChildNode[] children) { - super(null); + super(TestingLanguage.class, null, null); this.children = children; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -63,7 +63,7 @@ @Child TestChildNode right; public TestRootNode(FrameDescriptor descriptor, TestChildNode left, TestChildNode right) { - super(null, descriptor); + super(TestingLanguage.class, null, descriptor); this.left = left; this.right = right; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -87,7 +87,7 @@ @Child TestChildNode right; public TestRootNode(FrameDescriptor descriptor, TestChildNode left, TestChildNode right) { - super(null, descriptor); + super(TestingLanguage.class, null, descriptor); this.left = left; this.right = right; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -81,7 +81,7 @@ @Child private TestChildInterface child; public TestRootNode(TestChildInterface child) { - super(null); + super(TestingLanguage.class, null, null); this.child = child; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -84,7 +84,7 @@ @Children private final ValueNode[] children; public TestRootNode(ValueNode[] children) { - super(null); + super(TestingLanguage.class, null, null); this.children = children; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -62,7 +62,7 @@ @Child TestChildNode right; public TestRootNode(FrameDescriptor descriptor, TestChildNode left, TestChildNode right) { - super(null, descriptor); + super(TestingLanguage.class, null, descriptor); this.left = left; this.right = right; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -60,7 +60,7 @@ class TestRootNode extends RootNode { public TestRootNode() { - super(null); + super(TestingLanguage.class, null, null); } @Override diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015, 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. + * + * 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.test; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.source.Source; +import java.io.IOException; + +public final class TestingLanguage extends TruffleLanguage { + public static final TruffleLanguage INSTANCE = new TestingLanguage(); + + private TestingLanguage() { + super(null); + } + + @Override + protected Object eval(Source code) throws IOException { + throw new IOException(); + } + + @Override + protected Object findExportedSymbol(String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal() { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected ToolSupportProvider getToolSupport() { + return null; + } + + @Override + protected DebugSupportProvider getDebugSupport() { + return null; + } + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ThreadSafetyTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ThreadSafetyTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ThreadSafetyTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -84,7 +84,7 @@ @Child private ValueNode child; public TestRootNode(ValueNode child) { - super(null); + super(TestingLanguage.class, null, null); this.child = child; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -59,7 +59,7 @@ } private static RootNode createTestRootNode() { - return new RootNode() { + return new RootNode(TestingLanguage.class, null, null) { @Override public Object execute(VirtualFrame frame) { return 42; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Sat Jul 18 18:03:36 2015 +0200 @@ -27,6 +27,7 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.TestingLanguage; /** * Tests instrumentation where a client can attach a node that gets attached into the AST. @@ -147,7 +148,7 @@ * tests run in the same environment. */ public TestRootNode(TestLanguageNode body) { - super(null); + super(TestingLanguage.class, null, null); this.body = body; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -31,6 +31,7 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.TestingLanguage; public class NodeUtilTest { @@ -84,6 +85,10 @@ private int visited; + public TestRootNode() { + super(TestingLanguage.class, null, null); + } + @Override public Object execute(VirtualFrame frame) { return null; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/SafeReplaceTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/SafeReplaceTest.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/SafeReplaceTest.java Sat Jul 18 18:03:36 2015 +0200 @@ -28,6 +28,7 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.TestingLanguage; /** * Tests optional method for ensuring that a node replacement is type safe. Ordinary node @@ -81,6 +82,10 @@ private int executed; + public TestRootNode() { + super(TestingLanguage.class, null, null); + } + @Override public Object execute(VirtualFrame frame) { executed++; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/package-info.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,50 +1,62 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2012, 2014, 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. + * 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. * - * 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 + * 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. + * 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. + * 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. */ /** - *

This package contains basic tests of the Truffle API and serves at the same - * time as an introduction to the Truffle API for language implementors. Every test gives an example on how to use the construct explained in the class description.

+ *

+ * This package contains basic tests of the Truffle API and serves at the same time as an + * introduction to the Truffle API for language implementors. Every test gives an example on how to + * use the construct explained in the class description. + *

* *

- * Truffle is a language implementation framework. A guest language method is represented as a tree of executable nodes. - * The framework provides mechanisms for those trees to call each other. Additionally it contains dedicated data structures for storing data local to a tree invocation. + * Truffle is a language implementation framework. A guest language method is represented as a tree + * of executable nodes. The framework provides mechanisms for those trees to call each other. + * Additionally it contains dedicated data structures for storing data local to a tree invocation. *

* *

* This introduction to Truffle contains items in the following recommended order: * *

* * diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2015, 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; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.source.*; +import java.io.IOException; + +public abstract class Breakpoint { + + /** + * A general model of the states occupied by a breakpoint during its lifetime. + */ + public enum State { + + /** + * Not attached, enabled. + *

+ * Created for a source location but not yet attached: perhaps just created and the source + * hasn't been loaded yet; perhaps source has been loaded, but the line location isn't + * probed so a breakpoint cannot be attached. Can be either enabled or disabled. + */ + ENABLED_UNRESOLVED("Enabled/Unresolved"), + + /** + * Not attached, disabled. + *

+ * Created for a source location but not yet attached: perhaps just created and the source + * hasn't been loaded yet; perhaps source has been loaded, but the line location isn't + * probed so a breakpoint cannot be attached. + */ + DISABLED_UNRESOLVED("Disabled/Unresolved"), + + /** + * Attached, instrument enabled. + *

+ * Is currently implemented by some {@link Instrument}, which is attached to a {@link Probe} + * at a specific node in the AST, and the breakpoint is enabled. + */ + ENABLED("Enabled"), + + /** + * Attached, instrument disabled. + *

+ * Is currently implemented by some {@link Instrument}, which is attached to a {@link Probe} + * at a specific node in the AST, and the breakpoint is disabled. + */ + DISABLED("Disabled"), + + /** + * Not attached, instrument is permanently disabled. + */ + DISPOSED("Disposed"); + + private final String name; + + State(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + } + + private final boolean isOneShot; + + private int ignoreCount; + + private int hitCount; + + private State state; + + Breakpoint(State state, int ignoreCount, boolean isOneShot) { + this.state = state; + this.isOneShot = isOneShot; + this.ignoreCount = ignoreCount; + } + + /** + * Enables or disables this breakpoint's AST instrumentation. The breakpoint is enabled by + * default. + * + * @param enabled true to activate the instrumentation, false to + * deactivate the instrumentation so that it has no effect. + */ + public abstract void setEnabled(boolean enabled); + + /** + * Is this breakpoint active? + */ + public abstract boolean isEnabled(); + + /** + * Sets the condition on this breakpoint, {@code null} to make it unconditional. + * + * @param expr if non{@code -null}, a boolean expression, expressed in the guest language, to be + * evaluated in the lexical context at the breakpoint location. + * @throws IOException if condition is invalid + * @throws UnsupportedOperationException if the breakpoint does not support conditions + */ + public abstract void setCondition(String expr) throws IOException; + + /** + * Gets the string, expressed in the Guest Language, that defines the current condition on this + * breakpoint; {@code null} if this breakpoint is currently unconditional. + */ + public String getCondition() { + return null; + } + + /** + * Does this breakpoint remove itself after first activation? + */ + public final boolean isOneShot() { + return isOneShot; + } + + /** + * Gets the number of hits left to be ignored before halting. + */ + public final int getIgnoreCount() { + return ignoreCount; + } + + /** + * Change the threshold for when this breakpoint should start causing a break. When both an + * ignore count and a {@linkplain #setCondition(String) condition} are specified, the condition + * is evaluated first: if {@code false} it is not considered to be a hit. In other words, the + * ignore count is for successful conditions only. + */ + public final void setIgnoreCount(int ignoreCount) { + this.ignoreCount = ignoreCount; + } + + /** + * Number of times this breakpoint has reached, with one exception; if the breakpoint has a + * condition that evaluates to {@code false}, it does not count as a hit. + */ + public final int getHitCount() { + return hitCount; + } + + /** + * Disables this breakpoint and removes any associated instrumentation; it becomes permanently + * inert. + */ + public abstract void dispose(); + + /** + * Gets a human-sensible description of this breakpoint's location in a {@link Source}. + */ + public abstract String getLocationDescription(); + + public final State getState() { + return state; + } + + final void assertState(State s) { + assert state == s; + } + + final void setState(State state) { + this.state = state; + } + + /** + * Assumes that all conditions for causing the break have been satisfied, so increments the + * hit count. Then checks if the ignore count has been exceeded, and if so + * returns {@code true}. If not, it still counts as a hit but should be ignored. + * + * @return whether to proceed + */ + final boolean incrHitCountCheckIgnore() { + return ++hitCount > ignoreCount; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(getClass().getSimpleName()); + sb.append(" state="); + sb.append(getState() == null ? "" : getState().getName()); + if (isOneShot()) { + sb.append(", " + "One-Shot"); + } + if (getCondition() != null) { + sb.append(", condition=\"" + getCondition() + "\""); + } + return sb.toString(); + } +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java Sat Jul 18 18:03:36 2015 +0200 @@ -33,15 +33,6 @@ * Access to language-specific information and execution services to enable debugging. */ public interface DebugSupportProvider extends ToolSupportProvider { - - /** - * Runs source code. - * - * @param source code - * @throws DebugSupportException if unable to run successfully - */ - void run(Source source) throws DebugSupportException; - /** * Runs source code in a halted execution context, or at top level. * diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2015, 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; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.TruffleVM; + +/** + * Language-agnostic engine for running Truffle languages under debugging control. + */ +public final class Debugger { + + private static final boolean TRACE = false; + private static final String TRACE_PREFIX = "DEBUG ENGINE: "; + + private static final PrintStream OUT = System.out; + + private static final SyntaxTag STEPPING_TAG = StandardSyntaxTag.STATEMENT; + private static final SyntaxTag CALL_TAG = StandardSyntaxTag.CALL; + + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(TRACE_PREFIX + String.format(format, args)); + } + } + + private final TruffleVM vm; + private Source lastSource; + + interface BreakpointCallback { + + /** + * Passes control to the debugger with execution suspended. + */ + void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason); + } + + interface WarningLog { + + /** + * Logs a warning that is kept until the start of the next execution. + */ + void addWarning(String warning); + } + + /** + * Implementation of line-oriented breakpoints. + */ + private final LineBreakpointFactory lineBreaks; + + /** + * Implementation of tag-oriented breakpoints. + */ + private final TagBreakpointFactory tagBreaks; + + /** + * Head of the stack of executions. + */ + private DebugExecutionContext debugContext; + + Debugger(TruffleVM vm) { + this.vm = vm; + + Source.setFileCaching(true); + + // Initialize execution context stack + debugContext = new DebugExecutionContext(null, null); + prepareContinue(); + debugContext.contextTrace("START EXEC DEFAULT"); + + final BreakpointCallback breakpointCallback = new BreakpointCallback() { + + @TruffleBoundary + public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) { + debugContext.halt(astNode, mFrame, true, haltReason); + } + }; + + final WarningLog warningLog = new WarningLog() { + + public void addWarning(String warning) { + assert debugContext != null; + debugContext.logWarning(warning); + } + }; + + this.lineBreaks = new LineBreakpointFactory(this, breakpointCallback, warningLog); + this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); + } + + TruffleVM vm() { + return vm; + } + + /** + * Sets a breakpoint to halt at a source line. + * + * @param ignoreCount number of hits to ignore before halting + * @param lineLocation where to set the breakpoint (source, line number) + * @param oneShot breakpoint disposes itself after fist hit, if {@code true} + * @return a new breakpoint, initially enabled + * @throws IOException if the breakpoint can not be set. + */ + @TruffleBoundary + public Breakpoint setLineBreakpoint(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + return lineBreaks.create(ignoreCount, lineLocation, oneShot); + } + + /** + * Sets a breakpoint to halt at any node holding a specified {@link SyntaxTag}. + * + * @param ignoreCount number of hits to ignore before halting + * @param oneShot if {@code true} breakpoint removes it self after a hit + * @return a new breakpoint, initially enabled + * @throws IOException if the breakpoint already set + */ + @TruffleBoundary + public Breakpoint setTagBreakpoint(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { + return tagBreaks.create(ignoreCount, tag, oneShot); + } + + /** + * Gets all existing breakpoints, whatever their status, in natural sorted order. Modification + * save. + */ + @TruffleBoundary + public Collection getBreakpoints() { + final Collection result = new ArrayList<>(); + result.addAll(lineBreaks.getAll()); + result.addAll(tagBreaks.getAll()); + return result; + } + + /** + * Prepare to execute in Continue mode when guest language program execution resumes. In this + * mode: + *

+ */ + @TruffleBoundary + void prepareContinue() { + debugContext.setStrategy(new Continue()); + } + + /** + * Prepare to execute in StepInto mode when guest language program execution resumes. In this + * mode: + * + * + * @param stepCount the number of times to perform StepInto before halting + * @throws IllegalArgumentException if the specified number is {@code <= 0} + */ + @TruffleBoundary + void prepareStepInto(int stepCount) { + if (stepCount <= 0) { + throw new IllegalArgumentException(); + } + debugContext.setStrategy(new StepInto(stepCount)); + } + + /** + * Prepare to execute in StepOut mode when guest language program execution resumes. In this + * mode: + * + */ + @TruffleBoundary + void prepareStepOut() { + debugContext.setStrategy(new StepOut()); + } + + /** + * Prepare to execute in StepOver mode when guest language program execution resumes. In this + * mode: + * + * + * @param stepCount the number of times to perform StepInto before halting + * @throws IllegalArgumentException if the specified number is {@code <= 0} + */ + @TruffleBoundary + void prepareStepOver(int stepCount) { + if (stepCount <= 0) { + throw new IllegalArgumentException(); + } + debugContext.setStrategy(new StepOver(stepCount)); + } + + /** + * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot} + * that, when executed, computes the result of a textual expression in the language; used to + * create an + * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * Advanced Instrument}. + * + * @param expr a guest language expression + * @param resultListener optional listener for the result of each evaluation. + * @return a new factory + * @throws IOException if the factory cannot be created, for example if the expression is badly + * formed. + */ + AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Probe probe, String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + try { + Class langugageClass = ACCESSOR.findLanguage(probe); + TruffleLanguage l = ACCESSOR.findLanguage(vm, langugageClass); + DebugSupportProvider dsp = ACCESSOR.getDebugSupport(l); + return dsp.createAdvancedInstrumentRootFactory(expr, resultListener); + } catch (DebugSupportException ex) { + throw new IOException(ex); + } + } + + /** + * A mode of user navigation from a current code location to another, e.g "step in" vs. + * "step over". + */ + private abstract class StepStrategy { + + private DebugExecutionContext context; + protected final String strategyName; + + protected StepStrategy() { + this.strategyName = getClass().getSimpleName(); + } + + final String getName() { + return strategyName; + } + + /** + * Reconfigure the debugger so that when execution continues the program will halt at the + * location specified by this strategy. + */ + final void enable(DebugExecutionContext c, int stackDepth) { + this.context = c; + setStrategy(stackDepth); + } + + /** + * Return the debugger to the default navigation mode. + */ + final void disable() { + unsetStrategy(); + } + + @TruffleBoundary + final void halt(Node astNode, MaterializedFrame mFrame, boolean before) { + context.halt(astNode, mFrame, before, this.getClass().getSimpleName()); + } + + @TruffleBoundary + final void replaceStrategy(StepStrategy newStrategy) { + context.setStrategy(newStrategy); + } + + @TruffleBoundary + protected final void strategyTrace(String action, String format, Object... args) { + if (TRACE) { + context.contextTrace("%s (%s) %s", action, strategyName, String.format(format, args)); + } + } + + @TruffleBoundary + protected final void suspendUserBreakpoints() { + lineBreaks.setActive(false); + tagBreaks.setActive(false); + } + + @SuppressWarnings("unused") + protected final void restoreUserBreakpoints() { + lineBreaks.setActive(true); + tagBreaks.setActive(true); + } + + /** + * Reconfigure the debugger so that when execution continues, it will do so using this mode + * of navigation. + */ + protected abstract void setStrategy(int stackDepth); + + /** + * Return to the debugger to the default mode of navigation. + */ + protected abstract void unsetStrategy(); + } + + /** + * Strategy: the null stepping strategy. + * + */ + private final class Continue extends StepStrategy { + + @Override + protected void setStrategy(int stackDepth) { + } + + @Override + protected void unsetStrategy() { + } + } + + /** + * Strategy: per-statement stepping. + * + * + * @see Debugger#prepareStepInto(int) + */ + private final class StepInto extends StepStrategy { + private int unfinishedStepCount; + + StepInto(int stepCount) { + super(); + this.unfinishedStepCount = stepCount; + } + + @Override + protected void setStrategy(final int stackDepth) { + Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + // HALT: just before statement + --unfinishedStepCount; + strategyTrace("TRAP BEFORE", "unfinished steps=%d", unfinishedStepCount); + // Should run in fast path + if (unfinishedStepCount <= 0) { + halt(node, mFrame, true); + } + strategyTrace("RESUME BEFORE", ""); + } + }); + Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + --unfinishedStepCount; + strategyTrace(null, "TRAP AFTER unfinished steps=%d", unfinishedStepCount); + if (currentStackDepth() < stackDepth) { + // HALT: just "stepped out" + if (unfinishedStepCount <= 0) { + halt(node, mFrame, false); + } + } + strategyTrace("RESUME AFTER", ""); + } + }); + } + + @Override + protected void unsetStrategy() { + Probe.setBeforeTagTrap(null); + Probe.setAfterTagTrap(null); + } + } + + /** + * Strategy: execution to nearest enclosing call site. + * + * + * @see Debugger#prepareStepOut() + */ + private final class StepOut extends StepStrategy { + + @Override + protected void setStrategy(final int stackDepth) { + Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + + @TruffleBoundary + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + // HALT: + final int currentStackDepth = currentStackDepth(); + strategyTrace("TRAP AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth); + if (currentStackDepth < stackDepth) { + halt(node, mFrame, false); + } + strategyTrace("RESUME AFTER", ""); + } + }); + } + + @Override + protected void unsetStrategy() { + Probe.setAfterTagTrap(null); + } + } + + /** + * Strategy: per-statement stepping, so long as not nested in method calls (i.e. at original + * stack depth). + * + */ + private final class StepOver extends StepStrategy { + private int unfinishedStepCount; + + StepOver(int stepCount) { + this.unfinishedStepCount = stepCount; + } + + @Override + protected void setStrategy(final int stackDepth) { + Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + final int currentStackDepth = currentStackDepth(); + if (currentStackDepth <= stackDepth) { + // HALT: stack depth unchanged or smaller; treat like StepInto + --unfinishedStepCount; + if (TRACE) { + strategyTrace("TRAP BEFORE", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); + } + // Test should run in fast path + if (unfinishedStepCount <= 0) { + halt(node, mFrame, true); + } + } else { + // CONTINUE: Stack depth increased; don't count as a step + strategyTrace("STEP INTO", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); + // Stop treating like StepInto, start treating like StepOut + replaceStrategy(new StepOverNested(unfinishedStepCount, stackDepth)); + } + strategyTrace("RESUME BEFORE", ""); + } + }); + + Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + final int currentStackDepth = currentStackDepth(); + if (currentStackDepth < stackDepth) { + // HALT: just "stepped out" + --unfinishedStepCount; + strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); + // Should run in fast path + if (unfinishedStepCount <= 0) { + halt(node, mFrame, false); + } + strategyTrace("RESUME AFTER", ""); + } + } + }); + } + + @Override + protected void unsetStrategy() { + Probe.setBeforeTagTrap(null); + Probe.setAfterTagTrap(null); + } + } + + /** + * Strategy: per-statement stepping, not into method calls, in effect while at increased stack + * depth + * + */ + private final class StepOverNested extends StepStrategy { + private int unfinishedStepCount; + private final int startStackDepth; + + StepOverNested(int stepCount, int startStackDepth) { + this.unfinishedStepCount = stepCount; + this.startStackDepth = startStackDepth; + } + + @Override + protected void setStrategy(final int stackDepth) { + Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + + @Override + public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + final int currentStackDepth = currentStackDepth(); + if (currentStackDepth <= startStackDepth) { + // At original step depth (or smaller) after being nested + --unfinishedStepCount; + strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); + if (unfinishedStepCount <= 0) { + halt(node, mFrame, false); + } + // TODO (mlvdv) fixme for multiple steps + strategyTrace("RESUME BEFORE", ""); + } + } + }); + } + + @Override + protected void unsetStrategy() { + Probe.setBeforeTagTrap(null); + } + } + + /** + * Information and debugging state for a single Truffle execution (which make take place over + * one or more suspended executions). This holds interaction state, for example what is + * executing (e.g. some {@link Source}), what the execution mode is ("stepping" or + * "continuing"). When not running, this holds a cache of the Truffle stack for this particular + * execution, effectively hiding the Truffle stack for any currently suspended executions (down + * the stack). + */ + private final class DebugExecutionContext { + + // Previous halted context in stack + private final DebugExecutionContext predecessor; + + // The current execution level; first is 0. + private final int level; // Number of contexts suspended below + private final Source source; + private final int contextStackBase; // Where the stack for this execution starts + private final List warnings = new ArrayList<>(); + + private boolean running; + + /** + * The stepping strategy currently configured in the debugger. + */ + private StepStrategy strategy; + + /** + * Where halted; null if running. + */ + private Node haltedNode; + + /** + * Where halted; null if running. + */ + private MaterializedFrame haltedFrame; + + private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) { + this.source = executionSource; + this.predecessor = previousContext; + this.level = previousContext == null ? 0 : previousContext.level + 1; + + // "Base" is the number of stack frames for all nested (halted) executions. + this.contextStackBase = currentStackDepth(); + this.running = true; + contextTrace("NEW CONTEXT"); + } + + /** + * Sets up a strategy for the next resumption of execution. + * + * @param stepStrategy + */ + void setStrategy(StepStrategy stepStrategy) { + if (this.strategy == null) { + this.strategy = stepStrategy; + this.strategy.enable(this, currentStackDepth()); + if (TRACE) { + contextTrace("SET MODE -->" + stepStrategy.getName()); + } + } else { + strategy.disable(); + strategy = stepStrategy; + strategy.enable(this, currentStackDepth()); + contextTrace("SWITCH MODE %s-->%s", strategy.getName(), stepStrategy.getName()); + } + } + + void clearStrategy() { + if (strategy != null) { + final StepStrategy oldStrategy = strategy; + strategy.disable(); + strategy = null; + contextTrace("CLEAR MODE %s-->", oldStrategy.getName()); + } + } + + /** + * Handle a program halt, caused by a breakpoint, stepping strategy, or other cause. + * + * @param astNode the guest language node at which execution is halted + * @param mFrame the current execution frame where execution is halted + * @param before {@code true} if halted before the node, else after. + */ + @TruffleBoundary + void halt(Node astNode, MaterializedFrame mFrame, boolean before, String haltReason) { + assert running; + assert haltedNode == null; + assert haltedFrame == null; + + haltedNode = astNode; + haltedFrame = mFrame; + running = false; + + clearStrategy(); + + // Clean up, just in cased the one-shot breakpoints got confused + lineBreaks.disposeOneShots(); + + final int contextStackDepth = currentStackDepth() - contextStackBase; + if (TRACE) { + final String reason = haltReason == null ? "" : haltReason + ""; + final String where = before ? "BEFORE" : "AFTER"; + contextTrace("HALT %s : (%s) stack base=%d", where, reason, contextStackBase); + contextTrace("CURRENT STACK:"); + // printStack(OUT); + } + + final List recentWarnings = new ArrayList<>(warnings); + warnings.clear(); + + try { + // Pass control to the debug client with current execution suspended + ACCESSOR.dispatchEvent(vm, new SuspendedEvent(Debugger.this, astNode, mFrame, recentWarnings, contextStackDepth)); + // Debug client finished normally, execution resumes + // Presume that the client has set a new strategy (or default to Continue) + running = true; + } catch (KillException e) { + contextTrace("KILL"); + throw e; + } finally { + haltedNode = null; + haltedFrame = null; + } + + } + + void logWarning(String warning) { + warnings.add(warning); + } + + /* + * private void printStack(PrintStream stream) { getFrames(); if (frames == null) { + * stream.println(""); } else { final Visualizer visualizer = + * provider.getVisualizer(); for (FrameDebugDescription frameDesc : frames) { final + * StringBuilder sb = new StringBuilder(" frame " + Integer.toString(frameDesc.index())); + * sb.append(":at " + visualizer.displaySourceLocation(frameDesc.node())); sb.append(":in '" + * + visualizer.displayMethodName(frameDesc.node()) + "'"); stream.println(sb.toString()); } + * } } + */ + + void contextTrace(String format, Object... args) { + if (TRACE) { + final String srcName = (source != null) ? source.getName() : "no source"; + Debugger.trace("<%d> %s (%s)", level, String.format(format, args), srcName); + } + } + } + + // TODO (mlvdv) wish there were fast-path access to stack depth + /** + * Depth of current Truffle stack, including nested executions. Includes the top/current frame, + * which the standard iterator does not count: {@code 0} if no executions. + */ + @TruffleBoundary + private static int currentStackDepth() { + final int[] count = {0}; + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + @Override + public Void visitFrame(FrameInstance frameInstance) { + count[0] = count[0] + 1; + return null; + } + }); + return count[0] == 0 ? 0 : count[0] + 1; + + } + + void executionStarted(Source source) { + Source execSource = source; + if (execSource == null) { + execSource = lastSource; + } else { + lastSource = execSource; + } + // Push a new execution context onto stack + debugContext = new DebugExecutionContext(execSource, debugContext); + prepareContinue(); + debugContext.contextTrace("START EXEC "); + ACCESSOR.dispatchEvent(vm, new ExecutionEvent(this)); + } + + void executionEnded() { + lineBreaks.disposeOneShots(); + tagBreaks.disposeOneShots(); + debugContext.clearStrategy(); + debugContext.contextTrace("END EXEC "); + // Pop the stack of execution contexts. + debugContext = debugContext.predecessor; + } + + private static final class AccessorDebug extends Accessor { + @Override + protected Closeable executionStart(TruffleVM vm, Debugger[] fillIn, Source s) { + final Debugger d; + if (fillIn[0] == null) { + d = fillIn[0] = new Debugger(vm); + } else { + d = fillIn[0]; + } + d.executionStarted(s); + return new Closeable() { + @Override + public void close() throws IOException { + d.executionEnded(); + } + }; + } + + @Override + protected Class findLanguage(Probe probe) { + return super.findLanguage(probe); + } + + @Override + protected TruffleLanguage findLanguage(TruffleVM vm, Class languageClass) { + return super.findLanguage(vm, languageClass); + } + + @Override + protected DebugSupportProvider getDebugSupport(TruffleLanguage l) { + return super.getDebugSupport(l); + } + + @Override + protected void dispatchEvent(TruffleVM vm, Object event) { + super.dispatchEvent(vm, event); + } + } + + // registers into Accessor.DEBUG + private static final AccessorDebug ACCESSOR = new AccessorDebug(); +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/ExecutionEvent.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 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; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.vm.TruffleVM; +import com.oracle.truffle.api.vm.TruffleVM.Builder; + +/** + * This event is delivered to all {@link Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) + * registered event handlers} when an execution is about to be started. The event is the intended + * place to initialize debugger - e.g. set + * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean) + * breakpoints} or specify to execution should halt on the {@link #prepareStepInto() first possible + * occurence}. Methods in this event can only be used while the handlers process the event. Then the + * state of the event becomes invalid and subsequent calls to the event methods yield + * {@link IllegalStateException}. One can however obtain reference to {@link Debugger} instance and + * keep it to further manipulate with debugging capabilities of the {@link TruffleVM} when it is + * running. + */ +public final class ExecutionEvent { + private final Debugger debugger; + + ExecutionEvent(Debugger prepares) { + this.debugger = prepares; + } + + /** + * Debugger associated with the execution. This debuger remains valid after the event is + * processed, it is possible and suggested to keep a reference to it and use it any time later + * when evaluating sources in the {@link TruffleVM}. + * + * @return instance of debugger associated with the just starting execution and any subsequent + * ones in the same {@link TruffleVM}. + */ + public Debugger getDebugger() { + return debugger; + } + + /** + * Prepare to execute in Continue mode when guest language program execution resumes. In this + * mode: + *
    + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node to which an enabled breakpoint is attached, + * or:
    2. + *
    3. execution completes.
    4. + *
    + *
+ */ + public void prepareContinue() { + debugger.prepareContinue(); + } + + /** + * Prepare to execute in StepInto mode when guest language program execution resumes. In this + * mode: + *
    + *
  • User breakpoints are disabled.
  • + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT + * STATMENT}, or:
    2. + *
    3. execution completes.
    4. + *
    + *
  • StepInto mode persists only through one resumption (i.e. {@code stepIntoCount} steps), + * and reverts by default to Continue mode.
  • + *
+ * + * @throws IllegalArgumentException if the specified number is {@code <= 0} + */ + public void prepareStepInto() { + debugger.prepareStepInto(1); + } +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015, 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; + +import com.oracle.truffle.api.source.*; + +// TODO (mlvdv) generic? +/** + * A breakpoint associated with a {@linkplain LineLocation source line location}. + * + * @see Debugger + */ +abstract class LineBreakpoint extends Breakpoint { + + LineBreakpoint(State state, int ignoreCount, boolean isOneShot) { + super(state, ignoreCount, isOneShot); + } + + /** + * Gets the {@linkplain LineLocation source line location} that specifies where this breakpoint + * will trigger. + */ + public abstract LineLocation getLineLocation(); + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2015, 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; + +import static com.oracle.truffle.api.debug.Breakpoint.State.*; + +import java.io.*; +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.debug.Debugger.BreakpointCallback; +import com.oracle.truffle.api.debug.Debugger.WarningLog; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.utilities.*; + +//TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport + +/** + * Support class for creating and managing all existing ordinary (user visible) line breakpoints. + *

+ * Notes: + *

    + *
  1. Line breakpoints can only be set at nodes tagged as {@link StandardSyntaxTag#STATEMENT}.
  2. + *
  3. A newly created breakpoint looks for probes matching the location, attaches to them if found + * by installing an {@link Instrument} that calls back to the breakpoint.
  4. + *
  5. When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be + * copied along with the rest of the AST and will call back to the same breakpoint.
  6. + *
  7. When notification is received of a new Node being tagged as a statement, and if a + * breakpoint's line location matches the Probe's line location, then the breakpoint will attach a + * new Instrument at the probe to activate the breakpoint at that location.
  8. + *
  9. A breakpoint may have multiple Instruments deployed, one attached to each Probe that matches + * the breakpoint's line location; this might happen when a source is reloaded.
  10. + *
+ * + */ +final class LineBreakpointFactory { + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static final String BREAKPOINT_NAME = "LINE BREAKPOINT"; + + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(String.format("%s: %s", BREAKPOINT_NAME, String.format(format, args))); + } + } + + private static final Comparator> BREAKPOINT_COMPARATOR = new Comparator>() { + + @Override + public int compare(Entry entry1, Entry entry2) { + final LineLocation line1 = entry1.getKey(); + final LineLocation line2 = entry2.getKey(); + final int nameOrder = line1.getSource().getShortName().compareTo(line2.getSource().getShortName()); + if (nameOrder != 0) { + return nameOrder; + } + return Integer.compare(line1.getLineNumber(), line2.getLineNumber()); + } + }; + + private final Debugger executionSupport; + private final BreakpointCallback breakpointCallback; + private final WarningLog warningLog; + + /** + * Map: Source lines ==> attached breakpoints. There may be no more than one line breakpoint + * associated with a line. + */ + private final Map lineToBreakpoint = new HashMap<>(); + + /** + * A map of {@link LineLocation} to a collection of {@link Probe}s. This list must be + * initialized and filled prior to being used by this class. + */ + private final LineToProbesMap lineToProbesMap; + + /** + * Globally suspends all line breakpoint activity when {@code false}, ignoring whether + * individual breakpoints are enabled. + */ + @CompilationFinal private boolean breakpointsActive = true; + private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); + + LineBreakpointFactory(Debugger executionSupport, BreakpointCallback breakpointCallback, final WarningLog warningLog) { + this.executionSupport = executionSupport; + this.breakpointCallback = breakpointCallback; + this.warningLog = warningLog; + + lineToProbesMap = new LineToProbesMap(); + lineToProbesMap.install(); + + Probe.addProbeListener(new DefaultProbeListener() { + + @Override + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + if (tag == StandardSyntaxTag.STATEMENT) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (lineLocation != null) { + // A Probe with line location tagged STATEMENT we haven't seen before. + final LineBreakpointImpl breakpoint = lineToBreakpoint.get(lineLocation); + if (breakpoint != null) { + try { + breakpoint.attach(probe); + } catch (IOException e) { + warningLog.addWarning(BREAKPOINT_NAME + " failure attaching to newly tagged Probe: " + e.getMessage()); + if (TRACE) { + OUT.println(BREAKPOINT_NAME + " failure: " + e.getMessage()); + } + } + } + } + } + } + } + }); + } + + /** + * Globally enables line breakpoint activity; all breakpoints are ignored when set to + * {@code false}. When set to {@code true}, the enabled/disabled status of each breakpoint + * determines whether it will trigger when flow of execution reaches it. + * + * @param breakpointsActive + */ + void setActive(boolean breakpointsActive) { + if (this.breakpointsActive != breakpointsActive) { + breakpointsActiveUnchanged.invalidate(); + this.breakpointsActive = breakpointsActive; + } + } + + /** + * Gets all current line breakpoints,regardless of status; sorted and modification safe. + */ + List getAll() { + ArrayList> entries = new ArrayList<>(lineToBreakpoint.entrySet()); + Collections.sort(entries, BREAKPOINT_COMPARATOR); + + final ArrayList breakpoints = new ArrayList<>(entries.size()); + for (Entry entry : entries) { + breakpoints.add(entry.getValue()); + } + return breakpoints; + } + + /** + * Creates a new line breakpoint if one doesn't already exist. If one does exist, then resets + * the ignore count. + * + * @param lineLocation where to set the breakpoint + * @param ignoreCount number of initial hits before the breakpoint starts causing breaks. + * @param oneShot whether the breakpoint should dispose itself after one hit + * @return a possibly new breakpoint + * @throws IOException if a breakpoint already exists at the location and the ignore count is + * the same + */ + LineBreakpoint create(int ignoreCount, LineLocation lineLocation, boolean oneShot) throws IOException { + + LineBreakpointImpl breakpoint = lineToBreakpoint.get(lineLocation); + + if (breakpoint == null) { + breakpoint = new LineBreakpointImpl(ignoreCount, lineLocation, oneShot); + + if (TRACE) { + trace("NEW " + breakpoint.getShortDescription()); + } + + lineToBreakpoint.put(lineLocation, breakpoint); + + for (Probe probe : lineToProbesMap.findProbes(lineLocation)) { + if (probe.isTaggedAs(StandardSyntaxTag.STATEMENT)) { + breakpoint.attach(probe); + break; + } + } + } else { + if (ignoreCount == breakpoint.getIgnoreCount()) { + throw new IOException(BREAKPOINT_NAME + " already set at line " + lineLocation); + } + breakpoint.setIgnoreCount(ignoreCount); + if (TRACE) { + trace("CHANGED ignoreCount %s", breakpoint.getShortDescription()); + } + } + return breakpoint; + } + + /** + * Returns the {@link LineBreakpoint} for a given line. There should only ever be one breakpoint + * per line. + * + * @param lineLocation The {@link LineLocation} to get the breakpoint for. + * @return The breakpoint for the given line. + */ + LineBreakpoint get(LineLocation lineLocation) { + return lineToBreakpoint.get(lineLocation); + } + + /** + * Removes the associated instrumentation for all one-shot breakpoints only. + */ + void disposeOneShots() { + List breakpoints = new ArrayList<>(lineToBreakpoint.values()); + for (LineBreakpointImpl breakpoint : breakpoints) { + if (breakpoint.isOneShot()) { + breakpoint.dispose(); + } + } + } + + /** + * Removes all knowledge of a breakpoint, presumed disposed. + */ + private void forget(LineBreakpointImpl breakpoint) { + lineToBreakpoint.remove(breakpoint.getLineLocation()); + } + + /** + * Concrete representation of a line breakpoint, implemented by attaching an instrument to a + * probe at the designated source location. + */ + private final class LineBreakpointImpl extends LineBreakpoint implements AdvancedInstrumentResultListener { + + private static final String SHOULD_NOT_HAPPEN = "LineBreakpointImpl: should not happen"; + + private final LineLocation lineLocation; + + // Cached assumption that the global status of line breakpoint activity has not changed. + private Assumption breakpointsActiveAssumption; + + // Whether this breakpoint is enable/disabled + @CompilationFinal private boolean isEnabled; + private Assumption enabledUnchangedAssumption; + + private String conditionExpr; + + /** + * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: + * {@code null} if not attached. + */ + private List instruments = new ArrayList<>(); + + public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) { + super(ENABLED_UNRESOLVED, ignoreCount, oneShot); + this.lineLocation = lineLocation; + + this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); + this.isEnabled = true; + this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); + } + + @Override + public boolean isEnabled() { + return isEnabled; + } + + @Override + public void setEnabled(boolean enabled) { + if (enabled != isEnabled) { + switch (getState()) { + case ENABLED: + assert !enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(false); + changeState(DISABLED); + break; + case ENABLED_UNRESOLVED: + assert !enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(false); + changeState(DISABLED_UNRESOLVED); + break; + case DISABLED: + assert enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(true); + changeState(ENABLED); + break; + case DISABLED_UNRESOLVED: + assert enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(true); + changeState(DISABLED_UNRESOLVED); + break; + case DISPOSED: + assert false : "breakpoint disposed"; + break; + default: + assert false : SHOULD_NOT_HAPPEN; + break; + } + } + } + + @Override + public void setCondition(String expr) throws IOException { + if (this.conditionExpr != null || expr != null) { + // De-instrument the Probes instrumented by this breakpoint + final ArrayList probes = new ArrayList<>(); + for (Instrument instrument : instruments) { + probes.add(instrument.getProbe()); + instrument.dispose(); + } + instruments.clear(); + this.conditionExpr = expr; + // Re-instrument the probes previously instrumented + for (Probe probe : probes) { + attach(probe); + } + } + } + + @Override + public String getCondition() { + return conditionExpr; + } + + @Override + public void dispose() { + if (getState() != DISPOSED) { + for (Instrument instrument : instruments) { + instrument.dispose(); + } + changeState(DISPOSED); + LineBreakpointFactory.this.forget(this); + } + } + + private void attach(Probe newProbe) throws IOException { + if (getState() == DISPOSED) { + throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); + } + Instrument newInstrument = null; + if (conditionExpr == null) { + newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); + } else { + newInstrument = Instrument.create(this, executionSupport.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + } + newProbe.attach(newInstrument); + instruments.add(newInstrument); + changeState(isEnabled ? ENABLED : DISABLED); + } + + private void doSetEnabled(boolean enabled) { + if (this.isEnabled != enabled) { + enabledUnchangedAssumption.invalidate(); + this.isEnabled = enabled; + } + } + + private String getShortDescription() { + return BREAKPOINT_NAME + "@" + getLineLocation().getShortDescription(); + } + + private void changeState(State after) { + if (TRACE) { + trace("STATE %s-->%s %s", getState().getName(), after.getName(), getShortDescription()); + } + setState(after); + } + + private void doBreak(Node node, VirtualFrame vFrame) { + if (incrHitCountCheckIgnore()) { + breakpointCallback.haltedAt(node, vFrame.materialize(), BREAKPOINT_NAME); + } + } + + /** + * Receives notification from the attached instrument that execution is about to enter node + * where the breakpoint is set. Designed so that when in the fast path, there is either an + * unconditional "halt" call to the debugger or nothing. + */ + private void nodeEnter(Node astNode, VirtualFrame vFrame) { + + // Deopt if the global active/inactive flag has changed + try { + this.breakpointsActiveAssumption.check(); + } catch (InvalidAssumptionException ex) { + this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); + } + + // Deopt if the enabled/disabled state of this breakpoint has changed + try { + this.enabledUnchangedAssumption.check(); + } catch (InvalidAssumptionException ex) { + this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption("LineBreakpoint enabled state unchanged"); + } + + if (LineBreakpointFactory.this.breakpointsActive && this.isEnabled) { + if (isOneShot()) { + dispose(); + } + LineBreakpointImpl.this.doBreak(astNode, vFrame); + } + } + + public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + final boolean condition = (Boolean) result; + if (TRACE) { + trace("breakpoint condition = %b %s", condition, getShortDescription()); + } + if (condition) { + nodeEnter(node, vFrame); + } + } + + @TruffleBoundary + public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); + if (TRACE) { + trace("breakpoint failure = %s %s", ex.toString(), getShortDescription()); + } + // Take the breakpoint if evaluation fails. + nodeEnter(node, vFrame); + } + + @Override + public String getLocationDescription() { + return "Line: " + lineLocation.getShortDescription(); + } + + @Override + public LineLocation getLineLocation() { + return lineLocation; + } + + private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { + + @Override + public void enter(Probe probe, Node node, VirtualFrame vFrame) { + LineBreakpointImpl.this.nodeEnter(node, vFrame); + } + } + } + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2014, 2015, 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; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.source.*; + +/** + * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST, + * indexed by {@link Source} and line number. + */ +final class LineToProbesMap extends InstrumentationTool { + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static void trace(String msg) { + OUT.println("LineToProbesMap: " + msg); + } + + /** + * Map: Source line ==> probes associated with source sections starting on the line. + */ + private final Map> lineToProbesMap = new HashMap<>(); + + private final ProbeListener probeListener; + + /** + * Create a map of {@link Probe}s that collects information on all probes added to subsequently + * created ASTs (once installed). + */ + public LineToProbesMap() { + this.probeListener = new LineToProbesListener(); + } + + @Override + protected boolean internalInstall() { + Probe.addProbeListener(probeListener); + return true; + } + + @Override + protected void internalReset() { + lineToProbesMap.clear(); + } + + @Override + protected void internalDispose() { + Probe.removeProbeListener(probeListener); + } + + /** + * Returns the {@link Probe}, if any, associated with a specific line of guest language code; if + * more than one, return the one with the first starting character location. + */ + public Probe findFirstProbe(LineLocation lineLocation) { + Probe probe = null; + final Collection probes = findProbes(lineLocation); + for (Probe probesOnLine : probes) { + if (probe == null) { + probe = probesOnLine; + } else if (probesOnLine.getProbedSourceSection().getCharIndex() < probe.getProbedSourceSection().getCharIndex()) { + probe = probesOnLine; + } + } + return probe; + } + + /** + * Returns all {@link Probe}s whose associated source begins at the given {@link LineLocation}, + * an empty list if none. + */ + public Collection findProbes(LineLocation line) { + final Collection probes = lineToProbesMap.get(line); + if (probes == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableCollection(probes); + } + + private class LineToProbesListener extends DefaultProbeListener { + + @Override + public void newProbeInserted(Probe probe) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null && sourceSection.getSource() != null) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (TRACE) { + trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); + } + Collection probes = lineToProbesMap.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbesMap.put(lineLocation, probes); + } else { + assert !probes.contains(probe); + } + probes.add(probe); + } + } + } +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/SuspendedEvent.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2015, 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; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.frame.FrameInstance; +import com.oracle.truffle.api.frame.FrameInstanceVisitor; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.vm.TruffleVM; +import com.oracle.truffle.api.vm.TruffleVM.Builder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This event is delivered to all {@link Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) + * registered event handlers} when an execution is suspended on a + * {@link Debugger#setLineBreakpoint(int, com.oracle.truffle.api.source.LineLocation, boolean) + * breakpoint} or during {@link #prepareStepInto(int) stepping}. Methods in this event can only be + * used while the handlers process the event. Then the state of the event becomes invalid and + * subsequent calls to the event methods yield {@link IllegalStateException}. + * + */ +public final class SuspendedEvent { + private final List recentWarnings; + private final MaterializedFrame mFrame; + private final Node astNode; + private final List frames; + private final Debugger debugger; + + SuspendedEvent(Debugger prepares, Node astNode, MaterializedFrame mFrame, List recentWarnings, final int stackDepth) { + this.debugger = prepares; + this.astNode = astNode; + this.mFrame = mFrame; + this.recentWarnings = recentWarnings; + + this.frames = new ArrayList<>(); + // Map the Truffle stack for this execution, ignore nested executions + // The top (current) frame is not produced by the iterator. + frames.add(Truffle.getRuntime().getCurrentFrame()); + Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { + int frameCount = 1; + + @Override + public FrameInstance visitFrame(FrameInstance frameInstance) { + if (frameCount < stackDepth) { + frames.add(frameInstance); + frameCount++; + return null; + } + return frameInstance; + } + }); + + } + + /** + * Debugger associated with the just suspended execution. This debuger remains valid after the + * event is processed, it is possible and suggested to keep a reference to it and use it any + * time later when evaluating sources in the {@link TruffleVM}. + * + * @return instance of debugger associated with the just suspended execution and any subsequent + * ones in the same {@link TruffleVM}. + */ + public Debugger getDebugger() { + return debugger; + } + + public Node getNode() { + return astNode; + } + + public MaterializedFrame getFrame() { + return mFrame; + } + + public List getRecentWarnings() { + return Collections.unmodifiableList(recentWarnings); + } + + /** + * Gets the stack frames from the currently halted {@link TruffleVM} execution. + * + * @return list of stack frames + */ + @CompilerDirectives.TruffleBoundary + public List getStack() { + return Collections.unmodifiableList(frames); + } + + /** + * Prepare to execute in Continue mode when guest language program execution resumes. In this + * mode: + *
    + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node to which an enabled breakpoint is attached, + * or:
    2. + *
    3. execution completes.
    4. + *
    + *
+ */ + public void prepareContinue() { + debugger.prepareContinue(); + } + + /** + * Prepare to execute in StepInto mode when guest language program execution resumes. In this + * mode: + *
    + *
  • User breakpoints are disabled.
  • + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT + * STATMENT}, or:
    2. + *
    3. execution completes.
    4. + *
    + *
  • StepInto mode persists only through one resumption (i.e. {@code stepIntoCount} steps), + * and reverts by default to Continue mode.
  • + *
+ * + * @param stepCount the number of times to perform StepInto before halting + * @throws IllegalArgumentException if the specified number is {@code <= 0} + */ + public void prepareStepInto(int stepCount) { + debugger.prepareStepInto(stepCount); + } + + /** + * Prepare to execute in StepOut mode when guest language program execution resumes. In this + * mode: + *
    + *
  • User breakpoints are enabled.
  • + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at the nearest enclosing call site on the stack, or
    2. + *
    3. execution completes.
    4. + *
    + *
  • StepOut mode persists only through one resumption, and reverts by default to Continue + * mode.
  • + *
+ */ + public void prepareStepOut() { + debugger.prepareStepOut(); + } + + /** + * Prepare to execute in StepOver mode when guest language program execution resumes. In this + * mode: + *
    + *
  • Execution will continue until either: + *
      + *
    1. execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT + * STATEMENT} when not nested in one or more function/method calls, or:
    2. + *
    3. execution arrives at a node to which a breakpoint is attached and when nested in one or + * more function/method calls, or:
    4. + *
    5. execution completes.
    6. + *
    + *
  • StepOver mode persists only through one resumption (i.e. {@code stepOverCount} steps), + * and reverts by default to Continue mode.
  • + *
+ * + * @param stepCount the number of times to perform StepInto before halting + * @throws IllegalArgumentException if the specified number is {@code <= 0} + */ + public void prepareStepOver(int stepCount) { + debugger.prepareStepOver(stepCount); + } +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015, 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; + +import com.oracle.truffle.api.instrument.*; + +// TODO (mlvdv) generic? +/** + * A breakpoint associated with a {@link SyntaxTag}. + * + * @see Debugger + */ +abstract class TagBreakpoint extends Breakpoint { + + TagBreakpoint(State state, int ignoreCount, boolean isOneShot) { + super(state, ignoreCount, isOneShot); + } + + /** + * Gets the tag that specifies where this breakpoint will trigger. + */ + public abstract SyntaxTag getTag(); + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2015, 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; + +import static com.oracle.truffle.api.debug.Breakpoint.State.*; + +import java.io.*; +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.debug.Debugger.BreakpointCallback; +import com.oracle.truffle.api.debug.Debugger.WarningLog; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; + +// TODO (mlvdv) some common functionality could be factored out of this and LineBreakpointSupport + +/** + * Support class for creating and managing "Tag Breakpoints". A Tag Breakpoint halts execution just + * before reaching any node whose Probe carries a specified {@linkplain SyntaxTag Tag}. + *

+ * The {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly into the + * Instrumentation Framework, does the same thing more efficiently, but there may only be one Tag + * Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but it would + * be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap. + *

+ * Notes: + *

    + *
  1. Only one Tag Breakpoint can be active for a specific {@linkplain SyntaxTag Tag}.
  2. + *
  3. A newly created breakpoint looks for probes matching the tag, attaches to them if found by + * installing an {@link Instrument}.
  4. + *
  5. When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be + * copied along with the rest of the AST and will call back to the same breakpoint.
  6. + *
  7. When notification is received that the breakpoint's Tag has been newly added to a Node, then + * the breakpoint will attach a new Instrument at the probe to activate the breakpoint at that + * location.
  8. + *
  9. A breakpoint may have multiple Instruments deployed, one attached to each Probe that holds + * the breakpoint's tag; this might happen when a source is reloaded.
  10. + *
+ */ +final class TagBreakpointFactory { + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static final String BREAKPOINT_NAME = "TAG BREAKPOINT"; + + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(String.format("%s: %s", BREAKPOINT_NAME, String.format(format, args))); + } + } + + private static final Comparator> BREAKPOINT_COMPARATOR = new Comparator>() { + + @Override + public int compare(Entry entry1, Entry entry2) { + return entry1.getKey().name().compareTo(entry2.getKey().name()); + } + }; + + private final Debugger executionSupport; + private final BreakpointCallback breakpointCallback; + private final WarningLog warningLog; + + /** + * Map: Tags ==> Tag Breakpoints. There may be no more than one breakpoint per Tag. + */ + private final Map tagToBreakpoint = new HashMap<>(); + + /** + * Globally suspends all line breakpoint activity when {@code false}, ignoring whether + * individual breakpoints are enabled. + */ + @CompilationFinal private boolean breakpointsActive = true; + private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); + + TagBreakpointFactory(Debugger executionSupport, BreakpointCallback breakpointCallback, final WarningLog warningLog) { + this.executionSupport = executionSupport; + this.breakpointCallback = breakpointCallback; + this.warningLog = warningLog; + + Probe.addProbeListener(new DefaultProbeListener() { + + @Override + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + final TagBreakpointImpl breakpoint = tagToBreakpoint.get(tag); + if (breakpoint != null) { + try { + breakpoint.attach(probe); + } catch (IOException e) { + warningLog.addWarning(BREAKPOINT_NAME + " failure attaching to newly tagged Probe: " + e.getMessage()); + if (TRACE) { + OUT.println(BREAKPOINT_NAME + " failure: " + e.getMessage()); + } + } + } + } + }); + } + + /** + * Globally enables tag breakpoint activity; all breakpoints are ignored when set to + * {@code false}. When set to {@code true}, the enabled/disabled status of each breakpoint + * determines whether it will trigger when flow of execution reaches it. + * + * @param breakpointsActive + */ + void setActive(boolean breakpointsActive) { + if (this.breakpointsActive != breakpointsActive) { + breakpointsActiveUnchanged.invalidate(); + this.breakpointsActive = breakpointsActive; + } + } + + /** + * Gets all current tag breakpoints,regardless of status; sorted and modification safe. + */ + List getAll() { + ArrayList> entries = new ArrayList<>(tagToBreakpoint.entrySet()); + Collections.sort(entries, BREAKPOINT_COMPARATOR); + + final ArrayList breakpoints = new ArrayList<>(entries.size()); + for (Entry entry : entries) { + breakpoints.add(entry.getValue()); + } + return breakpoints; + } + + /** + * Creates a new tag breakpoint if one doesn't already exist. If one does exist, then resets the + * ignore count. + * + * @param tag where to set the breakpoint + * @param ignoreCount number of initial hits before the breakpoint starts causing breaks. + * @param oneShot whether the breakpoint should dispose itself after one hit + * @return a possibly new breakpoint + * @throws IOException if a breakpoint already exists for the tag and the ignore count is the + * same + */ + TagBreakpoint create(int ignoreCount, SyntaxTag tag, boolean oneShot) throws IOException { + + TagBreakpointImpl breakpoint = tagToBreakpoint.get(tag); + + if (breakpoint == null) { + breakpoint = new TagBreakpointImpl(ignoreCount, tag, oneShot); + + if (TRACE) { + trace("NEW " + breakpoint.getShortDescription()); + } + + tagToBreakpoint.put(tag, breakpoint); + + for (Probe probe : Probe.findProbesTaggedAs(tag)) { + breakpoint.attach(probe); + } + } else { + if (ignoreCount == breakpoint.getIgnoreCount()) { + throw new IOException(BREAKPOINT_NAME + " already set for tag " + tag.name()); + } + breakpoint.setIgnoreCount(ignoreCount); + if (TRACE) { + trace("CHANGED ignoreCount %s", breakpoint.getShortDescription()); + } + } + return breakpoint; + } + + /** + * Returns the {@link TagBreakpoint} for a given tag, {@code null} if none. + */ + TagBreakpoint get(SyntaxTag tag) { + return tagToBreakpoint.get(tag); + } + + /** + * Removes the associated instrumentation for all one-shot breakpoints only. + */ + void disposeOneShots() { + List breakpoints = new ArrayList<>(tagToBreakpoint.values()); + for (TagBreakpointImpl breakpoint : breakpoints) { + if (breakpoint.isOneShot()) { + breakpoint.dispose(); + } + } + } + + /** + * Removes all knowledge of a breakpoint, presumed disposed. + */ + private void forget(TagBreakpointImpl breakpoint) { + tagToBreakpoint.remove(breakpoint.getTag()); + } + + /** + * Concrete representation of a line breakpoint, implemented by attaching an instrument to a + * probe at the designated source location. + */ + private final class TagBreakpointImpl extends TagBreakpoint implements AdvancedInstrumentResultListener { + + private static final String SHOULD_NOT_HAPPEN = "TagBreakpointImpl: should not happen"; + + private final SyntaxTag tag; + + // Cached assumption that the global status of tag breakpoint activity has not changed. + private Assumption breakpointsActiveAssumption; + + // Whether this breakpoint is enable/disabled + @CompilationFinal private boolean isEnabled; + private Assumption enabledUnchangedAssumption; + + private String conditionExpr; + + /** + * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: + * {@code null} if not attached. + */ + private List instruments = new ArrayList<>(); + + private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) { + super(ENABLED, ignoreCount, oneShot); + this.tag = tag; + this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); + this.isEnabled = true; + this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); + } + + @Override + public boolean isEnabled() { + return isEnabled; + } + + @Override + public void setEnabled(boolean enabled) { + // Tag Breakpoints are never unresolved + if (enabled != isEnabled) { + switch (getState()) { + case ENABLED: + assert !enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(false); + changeState(DISABLED); + break; + case DISABLED: + assert enabled : SHOULD_NOT_HAPPEN; + doSetEnabled(true); + changeState(ENABLED); + break; + case DISPOSED: + assert false : "breakpoint disposed"; + break; + case ENABLED_UNRESOLVED: + case DISABLED_UNRESOLVED: + default: + assert false : SHOULD_NOT_HAPPEN; + break; + } + } + } + + @Override + public void setCondition(String expr) throws IOException { + if (this.conditionExpr != null || expr != null) { + // De-instrument the Probes instrumented by this breakpoint + final ArrayList probes = new ArrayList<>(); + for (Instrument instrument : instruments) { + probes.add(instrument.getProbe()); + instrument.dispose(); + } + instruments.clear(); + this.conditionExpr = expr; + // Re-instrument the probes previously instrumented + for (Probe probe : probes) { + attach(probe); + } + } + } + + @Override + public String getCondition() { + return conditionExpr; + } + + @Override + public void dispose() { + if (getState() != DISPOSED) { + for (Instrument instrument : instruments) { + instrument.dispose(); + } + changeState(DISPOSED); + TagBreakpointFactory.this.forget(this); + } + } + + private void attach(Probe newProbe) throws IOException { + if (getState() == DISPOSED) { + throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); + } + Instrument newInstrument = null; + if (conditionExpr == null) { + newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); + } else { + newInstrument = Instrument.create(this, executionSupport.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + } + newProbe.attach(newInstrument); + instruments.add(newInstrument); + changeState(isEnabled ? ENABLED : DISABLED); + } + + private void doSetEnabled(boolean enabled) { + if (this.isEnabled != enabled) { + enabledUnchangedAssumption.invalidate(); + this.isEnabled = enabled; + } + } + + private String getShortDescription() { + return BREAKPOINT_NAME + "@" + tag.name(); + } + + private void changeState(State after) { + if (TRACE) { + trace("STATE %s-->%s %s", getState().getName(), after.getName(), getShortDescription()); + } + setState(after); + } + + private void doBreak(Node node, VirtualFrame vFrame) { + if (incrHitCountCheckIgnore()) { + breakpointCallback.haltedAt(node, vFrame.materialize(), BREAKPOINT_NAME); + } + } + + /** + * Receives notification from the attached instrument that execution is about to enter node + * where the breakpoint is set. Designed so that when in the fast path, there is either an + * unconditional "halt" call to the debugger or nothing. + */ + private void nodeEnter(Node astNode, VirtualFrame vFrame) { + + // Deopt if the global active/inactive flag has changed + try { + this.breakpointsActiveAssumption.check(); + } catch (InvalidAssumptionException ex) { + this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); + } + + // Deopt if the enabled/disabled state of this breakpoint has changed + try { + this.enabledUnchangedAssumption.check(); + } catch (InvalidAssumptionException ex) { + this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption("LineBreakpoint enabled state unchanged"); + } + + if (TagBreakpointFactory.this.breakpointsActive && this.isEnabled) { + if (isOneShot()) { + dispose(); + } + TagBreakpointImpl.this.doBreak(astNode, vFrame); + } + + } + + public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + final boolean condition = (Boolean) result; + if (TRACE) { + trace("breakpoint condition = %b %s", condition, getShortDescription()); + } + if (condition) { + nodeEnter(node, vFrame); + } + } + + public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); + if (TRACE) { + trace("breakpoint failure = %s %s", ex.toString(), getShortDescription()); + } + // Take the breakpoint if evaluation fails. + nodeEnter(node, vFrame); + } + + @Override + public String getLocationDescription() { + return "Tag " + tag.name(); + } + + @Override + public SyntaxTag getTag() { + return tag; + } + + private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { + + @Override + public void enter(Probe probe, Node node, VirtualFrame vFrame) { + TagBreakpointImpl.this.nodeEnter(node, vFrame); + } + } + + } + +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,35 +1,31 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** * Representation of a {@link com.oracle.truffle.api.frame.Frame stack frame}. While - * {@link com.oracle.truffle.api.TruffleLanguage based language} - * is executing its stack parameters - * and local variables are represented by {@link com.oracle.truffle.api.frame.VirtualFrame}. Such a frame - * can be converted into {@link com.oracle.truffle.api.frame.MaterializedFrame} stored on a heap for - * later access when the actual stack is elsewhere. + * {@link com.oracle.truffle.api.TruffleLanguage based language} is executing its stack parameters + * and local variables are represented by {@link com.oracle.truffle.api.frame.VirtualFrame}. Such a + * frame can be converted into {@link com.oracle.truffle.api.frame.MaterializedFrame} stored on a + * heap for later access when the actual stack is elsewhere. */ package com.oracle.truffle.api.frame; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Sat Jul 18 18:03:36 2015 +0200 @@ -31,6 +31,8 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.debug.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.vm.*; @@ -40,6 +42,10 @@ public abstract class Accessor { private static Accessor API; private static Accessor SPI; + private static Accessor NODES; + private static Accessor INSTRUMENT; + private static Accessor DEBUG; + static { TruffleLanguage lng = new TruffleLanguage(null) { @Override @@ -73,6 +79,14 @@ } }; lng.hashCode(); + new Node(null) { + }.getRootNode(); + + try { + Class.forName(Debugger.class.getName(), true, Debugger.class.getClassLoader()); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException(ex); + } } protected Accessor() { @@ -81,6 +95,21 @@ throw new IllegalStateException(); } API = this; + } else if (this.getClass().getSimpleName().endsWith("Nodes")) { + if (NODES != null) { + throw new IllegalStateException(); + } + NODES = this; + } else if (this.getClass().getSimpleName().endsWith("Instrument")) { + if (INSTRUMENT != null) { + throw new IllegalStateException(); + } + INSTRUMENT = this; + } else if (this.getClass().getSimpleName().endsWith("Debug")) { + if (DEBUG != null) { + throw new IllegalStateException(); + } + DEBUG = this; } else { if (SPI != null) { throw new IllegalStateException(); @@ -117,16 +146,36 @@ return API.getDebugSupport(l); } - protected Object invoke(Object obj, Object[] args) throws IOException { + protected Object invoke(TruffleLanguage lang, Object obj, Object[] args) throws IOException { for (SymbolInvoker si : ServiceLoader.load(SymbolInvoker.class)) { - return si.invoke(obj, args); + return si.invoke(lang, obj, args); } throw new IOException("No symbol invoker found!"); } + protected Class findLanguage(RootNode n) { + return NODES.findLanguage(n); + } + + protected Class findLanguage(Probe probe) { + return INSTRUMENT.findLanguage(probe); + } + + protected TruffleLanguage findLanguage(TruffleVM vm, Class languageClass) { + return API.findLanguage(vm, languageClass); + } + + protected Closeable executionStart(TruffleVM vm, Debugger[] fillIn, Source s) { + return DEBUG.executionStart(vm, fillIn, s); + } + + protected void dispatchEvent(TruffleVM vm, Object event) { + SPI.dispatchEvent(vm, event); + } + /** * Don't call me. I am here only to let NetBeans debug any Truffle project. - * + * * @param args */ public static void main(String... args) { diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/SymbolInvoker.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/SymbolInvoker.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/SymbolInvoker.java Sat Jul 18 18:03:36 2015 +0200 @@ -24,6 +24,7 @@ */ package com.oracle.truffle.api.impl; +import com.oracle.truffle.api.TruffleLanguage; import java.io.*; /** @@ -31,5 +32,5 @@ * associated nodes too much. */ public abstract class SymbolInvoker { - protected abstract Object invoke(Object symbol, Object... args) throws IOException; + protected abstract Object invoke(TruffleLanguage lang, Object symbol, Object... args) throws IOException; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Sat Jul 18 18:03:36 2015 +0200 @@ -42,8 +42,8 @@ * *

* Client-oriented documentation for the use of Instruments is available online at https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events + * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https:// + * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events *

* The implementation of Instruments is complicated by the requirement that Truffle be able to clone * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Sat Jul 18 18:03:36 2015 +0200 @@ -30,6 +30,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; @@ -47,8 +48,8 @@ * *

* Client-oriented documentation for the use of Probes is available online at https://wiki.openjdk.java.net/display/Graal/Finding+Probes + * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes" >https://wiki.openjdk.java. + * net/display/Graal/Finding+Probes *

*

Implementation notes:

*

@@ -100,6 +101,7 @@ * @see SyntaxTag */ public final class Probe { + private final Class language; private static final boolean TRACE = false; private static final String TRACE_PREFIX = "PROBE: "; @@ -303,7 +305,7 @@ /** * Intended for use only by {@link ProbeNode}. */ - Probe(ProbeNode probeNode, SourceSection sourceSection) { + Probe(Class l, ProbeNode probeNode, SourceSection sourceSection) { this.sourceSection = sourceSection; probes.add(new WeakReference<>(this)); registerProbeNodeClone(probeNode); @@ -314,6 +316,7 @@ for (ProbeListener listener : probeListeners) { listener.newProbeInserted(this); } + this.language = l; } /** @@ -483,4 +486,18 @@ sb.append("]"); return sb.toString(); } + + static final class AccessorInstrument extends Accessor { + @Override + protected Class findLanguage(RootNode n) { + return super.findLanguage(n); + } + + @Override + protected Class findLanguage(Probe probe) { + return probe.language; + } + } + + static final AccessorInstrument ACCESSOR = new AccessorInstrument(); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.Instrument.AbstractInstrumentNode; import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; @@ -120,7 +121,8 @@ public static Probe insertProbe(WrapperNode wrapper) { final SourceSection sourceSection = wrapper.getChild().getSourceSection(); final ProbeNode probeNode = new ProbeNode(); // private constructor - probeNode.probe = new Probe(probeNode, sourceSection); // package private access + Class l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); + probeNode.probe = new Probe(l, probeNode, sourceSection); // package private access wrapper.insertProbe(probeNode); return probeNode.probe; } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,26 +1,23 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java Sat Jul 18 18:03:36 2015 +0200 @@ -245,7 +245,7 @@ protected void setNodeProperty(Object node, String propertyName, Object value) { Element nodeElem = getElementByObject(node); Element propElem = getPropertyElement(node, propertyName); // if property exists, replace - // its value + // its value if (null == propElem) { // if property doesn't exist, create one propElem = dom.createElement("p"); propElem.setAttribute("name", propertyName); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Sat Jul 18 18:03:36 2015 +0200 @@ -30,6 +30,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.source.*; @@ -39,7 +40,6 @@ * Abstract base class for all Truffle nodes. */ public abstract class Node implements NodeInterface, Cloneable { - private final NodeClass nodeClass; @CompilationFinal private Node parent; @CompilationFinal private SourceSection sourceSection; @@ -587,4 +587,14 @@ IN_ATOMIC_BLOCK.set(IN_ATOMIC_BLOCK.get() - 1); return true; } + + private static final class AccessorNodes extends Accessor { + @Override + protected Class findLanguage(RootNode n) { + return n.language; + } + } + + // registers into Accessor.NODES + @SuppressWarnings("unused") private static final AccessorNodes ACCESSOR = new AccessorNodes(); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -37,20 +37,57 @@ * {@link TruffleRuntime#createCallTarget(RootNode)}. */ public abstract class RootNode extends Node { - + final Class language; private RootCallTarget callTarget; @CompilationFinal private FrameDescriptor frameDescriptor; + /** + * @deprecated each RootNode should be associated with a {@link TruffleLanguage} use constructor + * that allows you to specify it. This method will be removed on Aug 15, 2015. + */ + @Deprecated protected RootNode() { - this(null, null); + this(null, null, null, false); + } + + /** + * @deprecated each RootNode should be associated with a {@link TruffleLanguage} use constructor + * that allows you to specify it. This method will be removed on Aug 15, 2015. + */ + @Deprecated + protected RootNode(SourceSection sourceSection) { + this(null, sourceSection, null, false); } - protected RootNode(SourceSection sourceSection) { - this(sourceSection, null); + /** + * @deprecated each RootNode should be associated with a {@link TruffleLanguage} use constructor + * that allows you to specify it. This method will be removed on Aug 15, 2015. + */ + @Deprecated + protected RootNode(SourceSection sourceSection, FrameDescriptor frameDescriptor) { + this(null, sourceSection, frameDescriptor, false); } - protected RootNode(SourceSection sourceSection, FrameDescriptor frameDescriptor) { + /** + * Creates new root node. Each {@link RootNode} is associated with a particular language - if + * the root node represents a method it is assumed the method is written in such language. + * + * @param language the language of the node, cannot be null + * @param sourceSection a part of source associated with this node, can be null + * @param frameDescriptor descriptor of slots, can be null + */ + protected RootNode(Class language, SourceSection sourceSection, FrameDescriptor frameDescriptor) { + this(language, sourceSection, frameDescriptor, true); + } + + private RootNode(Class language, SourceSection sourceSection, FrameDescriptor frameDescriptor, boolean checkLanguage) { super(sourceSection); + if (checkLanguage) { + if (!TruffleLanguage.class.isAssignableFrom(language)) { + throw new IllegalStateException(); + } + } + this.language = language; if (frameDescriptor == null) { this.frameDescriptor = new FrameDescriptor(); } else { diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,26 +1,23 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,31 +1,28 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** - * Do you want to implement your own {@link com.oracle.truffle.api.TruffleLanguage Truffle based language}? - * In such case start {@link com.oracle.truffle.api.TruffleLanguage here}. + * Do you want to implement your own {@link com.oracle.truffle.api.TruffleLanguage Truffle based + * language}? In such case start {@link com.oracle.truffle.api.TruffleLanguage here}. */ package com.oracle.truffle.api; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,26 +1,23 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/EventConsumer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/EventConsumer.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014, 2015, 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.vm; + +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; + +/** + * {@link TruffleVM} generates various events and delivers them to + * {@link TruffleVM.Builder#onEvent(com.oracle.truffle.api.vm.EventConsumer) registered} handlers. + * Each handler is registered for a particular type of event. Examples of events include + * {@link ExecutionEvent} or {@link SuspendedEvent} useful when debugging {@link TruffleLanguage + * Truffle language}s. + * + * @param type of event to observe and handle + */ +public abstract class EventConsumer { + final Class type; + + /** + * Creates new handler for specified event type. + * + * @param eventType type of events to handle + */ + public EventConsumer(Class eventType) { + this.type = eventType; + } + + /** + * Called by the {@link TruffleVM} when event of requested type appears. + * + * @param event the instance of an event of the request type + */ + protected abstract void on(Event event); +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/TruffleVM.java Sat Jul 18 18:03:36 2015 +0200 @@ -63,6 +63,8 @@ private final Reader in; private final Writer err; private final Writer out; + private final EventConsumer[] handlers; + private Debugger debugger; /** * Private & temporary only constructor. @@ -73,6 +75,7 @@ this.err = null; this.out = null; this.langs = null; + this.handlers = null; } /** @@ -82,10 +85,11 @@ * @param err stderr * @param in stdin */ - private TruffleVM(Writer out, Writer err, Reader in) { + private TruffleVM(Writer out, Writer err, Reader in, EventConsumer[] handlers) { this.out = out; this.err = err; this.in = in; + this.handlers = handlers; this.initThread = Thread.currentThread(); this.langs = new HashMap<>(); Enumeration en; @@ -169,12 +173,13 @@ private Writer out; private Writer err; private Reader in; + private List> handlers = new ArrayList<>(); Builder() { } /** - * Changes the defaut output for languages running in to be created + * Changes the default output for languages running in to be created * {@link TruffleVM virtual machine}. The default is to use {@link System#out}. * * @param w the writer to use as output @@ -210,6 +215,19 @@ } /** + * Registers another instance of {@link EventConsumer} into the to be created + * {@link TruffleVM}. + * + * @param handler the handler to register + * @return instance of this builder + */ + public Builder onEvent(EventConsumer handler) { + handler.getClass(); + handlers.add(handler); + return this; + } + + /** * Creates the {@link TruffleVM Truffle virtual machine}. The configuration is taken from * values passed into configuration methods in this class. * @@ -225,7 +243,7 @@ if (in == null) { in = new InputStreamReader(System.in); } - return new TruffleVM(out, err, in); + return new TruffleVM(out, err, in, handlers.toArray(new EventConsumer[0])); } } @@ -273,7 +291,7 @@ if (l == null) { throw new IOException("No language for " + location + " with MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } - return SPI.eval(l, s); + return eval(l, s); } /** @@ -291,7 +309,7 @@ if (l == null) { throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } - return SPI.eval(l, Source.fromReader(reader, mimeType)); + return eval(l, Source.fromReader(reader, mimeType)); } /** @@ -309,7 +327,18 @@ if (l == null) { throw new IOException("No language for MIME type " + mimeType + " found. Supported types: " + langs.keySet()); } - return SPI.eval(l, Source.fromText(code, mimeType)); + return eval(l, Source.fromText(code, mimeType)); + } + + private Object eval(TruffleLanguage l, Source s) throws IOException { + TruffleVM.findDebuggerSupport(l); + Debugger[] fillIn = {debugger}; + try (Closeable d = SPI.executionStart(this, fillIn, s)) { + if (debugger == null) { + debugger = fillIn[0]; + } + return SPI.eval(l, s); + } } /** @@ -331,12 +360,14 @@ */ public Symbol findGlobalSymbol(String globalName) { checkThread(); + TruffleLanguage lang = null; Object obj = null; Object global = null; for (Language dl : langs.values()) { TruffleLanguage l = dl.getImpl(); obj = SPI.findExportedSymbol(l, globalName, true); if (obj != null) { + lang = l; global = SPI.languageGlobal(l); break; } @@ -346,12 +377,13 @@ TruffleLanguage l = dl.getImpl(); obj = SPI.findExportedSymbol(l, globalName, false); if (obj != null) { + lang = l; global = SPI.languageGlobal(l); break; } } } - return obj == null ? null : new Symbol(obj, global); + return obj == null ? null : new Symbol(lang, obj, global); } private void checkThread() { @@ -366,15 +398,46 @@ return l == null ? null : l.getImpl(); } + @SuppressWarnings("all") + void dispatch(Object ev) { + Class type = ev.getClass(); + if (type == SuspendedEvent.class) { + dispatchSuspendedEvent((SuspendedEvent) ev); + } + if (type == ExecutionEvent.class) { + dispatchExecutionEvent((ExecutionEvent) ev); + } + dispatch(type, ev); + } + + @SuppressWarnings("unused") + void dispatchSuspendedEvent(SuspendedEvent event) { + } + + @SuppressWarnings("unused") + void dispatchExecutionEvent(ExecutionEvent event) { + } + + @SuppressWarnings("all") + void dispatch(Class type, Event event) { + for (EventConsumer handler : handlers) { + if (handler.type == type) { + handler.on(event); + } + } + } + /** * Represents {@link TruffleVM#findGlobalSymbol(java.lang.String) global symbol} provided by one * of the initialized languages in {@link TruffleVM Truffle virtual machine}. */ public class Symbol { + private final TruffleLanguage language; private final Object obj; private final Object global; - Symbol(Object obj, Object global) { + Symbol(TruffleLanguage language, Object obj, Object global) { + this.language = language; this.obj = obj; this.global = global; } @@ -392,16 +455,22 @@ * @throws IOException signals problem during execution */ public Object invoke(Object thiz, Object... args) throws IOException { - List arr = new ArrayList<>(); - if (thiz == null) { - if (global != null) { - arr.add(global); + Debugger[] fillIn = {debugger}; + try (Closeable c = SPI.executionStart(TruffleVM.this, fillIn, null)) { + if (debugger == null) { + debugger = fillIn[0]; } - } else { - arr.add(thiz); + List arr = new ArrayList<>(); + if (thiz == null) { + if (global != null) { + arr.add(global); + } + } else { + arr.add(thiz); + } + arr.addAll(Arrays.asList(args)); + return SPI.invoke(language, obj, arr.toArray()); } - arr.addAll(Arrays.asList(args)); - return SPI.invoke(obj, arr.toArray()); } } @@ -472,14 +541,6 @@ return shortName; } - public ToolSupportProvider getToolSupport() { - return SPI.getToolSupport(getImpl()); - } - - public DebugSupportProvider getDebugSupport() { - return SPI.getDebugSupport(getImpl()); - } - TruffleLanguage getImpl() { if (impl == null) { String n = props.getProperty(prefix + "className"); @@ -500,6 +561,25 @@ } } // end of Language + // + // Accessor helper methods + // + + TruffleLanguage findLanguage(Probe probe) { + Class languageClazz = SPI.findLanguage(probe); + for (Map.Entry entrySet : langs.entrySet()) { + Language languageDescription = entrySet.getValue(); + if (languageClazz.isInstance(languageDescription.impl)) { + return languageDescription.impl; + } + } + throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); + } + + static DebugSupportProvider findDebuggerSupport(TruffleLanguage l) { + return SPI.getDebugSupport(l); + } + private static class SPIAccessor extends Accessor { @Override public Object importSymbol(TruffleVM vm, TruffleLanguage ownLang, String globalName) { @@ -548,8 +628,8 @@ } @Override - public Object invoke(Object obj, Object[] args) throws IOException { - return super.invoke(obj, args); + protected Object invoke(TruffleLanguage lang, Object obj, Object[] args) throws IOException { + return super.invoke(lang, obj, args); } @Override @@ -561,5 +641,20 @@ public DebugSupportProvider getDebugSupport(TruffleLanguage l) { return super.getDebugSupport(l); } + + @Override + protected Class findLanguage(Probe probe) { + return super.findLanguage(probe); + } + + @Override + protected Closeable executionStart(TruffleVM aThis, Debugger[] fillIn, Source s) { + return super.executionStart(aThis, fillIn, s); + } + + @Override + protected void dispatchEvent(TruffleVM vm, Object event) { + vm.dispatch(event); + } } // end of SPIAccessor } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package-info.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/vm/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,31 +1,28 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2014, 2015, 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 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 + * 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. + * 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. + * 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. */ /** - * Central place to create and control {@link com.oracle.truffle.api.vm.TruffleVM Truffle Virtual Machine} - * and all languages hosted in it. + * Central place to create and control {@link com.oracle.truffle.api.vm.TruffleVM Truffle Virtual + * Machine} and all languages hosted in it. */ package com.oracle.truffle.api.vm; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/expression/DSLExpressionResolver.java --- a/truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/expression/DSLExpressionResolver.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/expression/DSLExpressionResolver.java Sat Jul 18 18:03:36 2015 +0200 @@ -187,8 +187,8 @@ sep = ", "; } // name mismatch - throw new InvalidExpressionException(String.format("The method %s in the type %s is not applicable for the arguments %s.", // - ElementUtils.getReadableSignature(foundWithName), // + throw new InvalidExpressionException(String.format("The method %s in the type %s is not applicable for the arguments %s.", // + ElementUtils.getReadableSignature(foundWithName), // ElementUtils.getSimpleName((TypeElement) foundWithName.getEnclosingElement()), arguments.toString())); } } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java --- a/truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/NodeGenFactory.java Sat Jul 18 18:03:36 2015 +0200 @@ -972,7 +972,7 @@ private List calculateReachableSpecializations() { List specializations = new ArrayList<>(); for (SpecializationData specialization : node.getSpecializations()) { - if (specialization.isReachable() && // + if (specialization.isReachable() && // (specialization.isSpecialized() // || (specialization.isFallback() && optimizeFallback(specialization)))) { specializations.add(specialization); @@ -1233,7 +1233,7 @@ if (limitExpression == null) { limitExpressionTree = CodeTreeBuilder.singleString("3"); } else { - limitExpressionTree = DSLExpressionGenerator.write(limitExpression, accessParent(null), // + limitExpressionTree = DSLExpressionGenerator.write(limitExpression, accessParent(null), // castBoundTypes(bindExpressionValues(limitExpression, specialization, currentValues))); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java --- a/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java Sat Jul 18 18:03:36 2015 +0200 @@ -204,6 +204,7 @@ } private static final ShapeProfiler shapeProf; + static { if (ObjectStorageOptions.Profile) { shapeProf = new ShapeProfiler(); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java --- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Sat Jul 18 18:03:36 2015 +0200 @@ -56,12 +56,9 @@ import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.impl.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.sl.factory.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.sl.nodes.instrument.*; import com.oracle.truffle.sl.nodes.local.*; -import com.oracle.truffle.sl.parser.*; -import com.oracle.truffle.sl.runtime.*; import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; /** @@ -80,7 +77,6 @@ private static final String ASSIGNMENT_VALUE_SUFFIX = "_assnCount"; private static final String LF = System.getProperty("line.separator"); - private static SLContext slContext; static class InstrumentTestCase { protected final Description name; @@ -227,10 +223,10 @@ // We use the name of the file to determine what visitor to attach to it. if (testCase.baseName.endsWith(ASSIGNMENT_VALUE_SUFFIX)) { // Set up the execution context for Simple and register our two listeners - slContext = SLContextFactory.create(new BufferedReader(new StringReader(testCase.testInput)), printer); + TruffleVM vm = TruffleVM.newVM().stdIn(new BufferedReader(new StringReader(testCase.testInput))).stdOut(printer).build(); - final Source source = Source.fromText(readAllLines(testCase.path), testCase.sourceName); - Parser.parseSL(slContext, source); + final String src = readAllLines(testCase.path); + vm.eval("application/x-sl", src); // Attach an instrument to every probe tagged as an assignment for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { @@ -238,8 +234,8 @@ probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value")); } - SLFunction main = slContext.getFunctionRegistry().lookup("main"); - main.getCallTarget().call(); + TruffleVM.Symbol main = vm.findGlobalSymbol("main"); + main.invoke(null); } else { notifier.fireTestFailure(new Failure(testCase.name, new UnsupportedOperationException("No instrumentation found."))); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java --- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLHandler.java Sat Jul 18 18:03:36 2015 +0200 @@ -40,10 +40,11 @@ */ package com.oracle.truffle.sl.tools.debug; +import java.io.*; import java.util.*; import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.tools.debug.shell.*; import com.oracle.truffle.tools.debug.shell.client.*; import com.oracle.truffle.tools.debug.shell.server.*; @@ -113,20 +114,27 @@ } }; + // TODO (mlvdv) re-implement stepInto when vm support replaced /** * Runs a source, optionally stepping into a specified tag. */ - private static REPLMessage[] loadHandler(REPLMessage request, REPLServerContext serverContext, boolean stepInto) { + private static REPLMessage[] loadHandler(REPLMessage request, REPLServerContext serverContext, @SuppressWarnings("unused") boolean stepInto) { final REPLMessage reply = new REPLMessage(REPLMessage.OP, REPLMessage.LOAD_RUN); final String fileName = request.get(REPLMessage.SOURCE_NAME); try { - final Source source = Source.fromFileName(fileName, true); - if (source == null) { + final File file = new File(fileName); + if (!file.canRead()) { return finishReplyFailed(reply, "can't find file \"" + fileName + "\""); } - serverContext.getDebugEngine().run(source, stepInto); - reply.put(REPLMessage.FILE_PATH, source.getPath()); - return finishReplySucceeded(reply, source.getName() + " exited"); + final TruffleVM vm = serverContext.vm(); + vm.eval(file.toURI()); + TruffleVM.Symbol main = vm.findGlobalSymbol("main"); + if (main != null) { + main.invoke(null); + } + final String path = file.getCanonicalPath(); + reply.put(REPLMessage.FILE_PATH, path); + return finishReplySucceeded(reply, fileName + " exited"); } catch (QuitException ex) { throw ex; } catch (KillException ex) { diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java --- a/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.tools/src/com/oracle/truffle/sl/tools/debug/SLREPLServer.java Sat Jul 18 18:03:36 2015 +0200 @@ -40,16 +40,19 @@ */ package com.oracle.truffle.sl.tools.debug; +import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; + import java.util.*; -import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.vm.*; import com.oracle.truffle.api.vm.TruffleVM.Language; import com.oracle.truffle.sl.*; -import com.oracle.truffle.tools.debug.engine.*; +import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; import com.oracle.truffle.tools.debug.shell.*; import com.oracle.truffle.tools.debug.shell.client.*; import com.oracle.truffle.tools.debug.shell.server.*; @@ -58,12 +61,11 @@ * Instantiation of the "server" side of the "REPL*" debugger for the Simple language. *

* The SL parser is not equipped to parse program fragments, so any debugging functions that depend - * on this are not supported, for example {@link DebugEngine#eval(Source, Node, MaterializedFrame)} - * and {@link Breakpoint#setCondition(String)}. + * on this are not supported, for example "eval" and breakpoint conditions. * * @see SimpleREPLClient */ -public final class SLREPLServer implements REPLServer { +public final class SLREPLServer extends REPLServer { // TODO (mlvdv) remove when there's a better way to express this dependency @SuppressWarnings("unused") private static final Class DYNAMIC_DEPENDENCY = com.oracle.truffle.sl.SLLanguage.class; @@ -83,7 +85,8 @@ } private final Language language; - private final DebugEngine slDebugEngine; + private final TruffleVM vm; + private Debugger db; private final String statusPrefix; private final Map handlerMap = new HashMap<>(); private SLServerContext currentServerContext; @@ -118,13 +121,25 @@ add(REPLHandler.TRUFFLE_HANDLER); add(REPLHandler.TRUFFLE_NODE_HANDLER); - TruffleVM vm = TruffleVM.newVM().build(); - this.language = vm.getLanguages().get("application/x-sl"); + EventConsumer onHalted = new EventConsumer(SuspendedEvent.class) { + @Override + protected void on(SuspendedEvent ev) { + SLREPLServer.this.haltedAt(ev); + } + }; + EventConsumer onExec = new EventConsumer(ExecutionEvent.class) { + @Override + protected void on(ExecutionEvent event) { + event.prepareStepInto(); + db = event.getDebugger(); + } + }; + + TruffleVM newVM = TruffleVM.newVM().onEvent(onHalted).onEvent(onExec).build(); + this.language = newVM.getLanguages().get("application/x-sl"); assert language != null; - final SLREPLDebugClient slDebugClient = new SLREPLDebugClient(language); - this.slDebugEngine = DebugEngine.create(slDebugClient, language); - + this.vm = newVM; this.statusPrefix = language.getShortName() + " REPL:"; } @@ -132,9 +147,10 @@ this.replClient = replClient; } + @Override public REPLMessage start() { - this.currentServerContext = new SLServerContext(null, null, null); + this.currentServerContext = new SLServerContext(null, null); // SL doesn't load modules (like other languages), so we just return a success final REPLMessage reply = new REPLMessage(); @@ -143,6 +159,7 @@ return reply; } + @Override public REPLMessage[] receive(REPLMessage request) { if (currentServerContext == null) { final REPLMessage message = new REPLMessage(); @@ -161,8 +178,8 @@ private final SLServerContext predecessor; - public SLServerContext(SLServerContext predecessor, Node astNode, MaterializedFrame mFrame) { - super(predecessor == null ? 0 : predecessor.getLevel() + 1, astNode, mFrame); + public SLServerContext(SLServerContext predecessor, SuspendedEvent event) { + super(predecessor == null ? 0 : predecessor.getLevel() + 1, event); this.predecessor = predecessor; } @@ -188,66 +205,68 @@ } @Override - public DebugEngine getDebugEngine() { - return slDebugEngine; + public Visualizer getVisualizer() { + return new SLDefaultVisualizer(); + } + + @Override + public TruffleVM vm() { + return vm; + } + + @Override + protected Debugger db() { + return db; + } + + @Override + public void registerBreakpoint(Breakpoint breakpoint) { + SLREPLServer.this.registerBreakpoint(breakpoint); + } + + @Override + public Breakpoint findBreakpoint(int id) { + return SLREPLServer.this.findBreakpoint(id); + } + + @Override + public int getBreakpointID(Breakpoint breakpoint) { + return SLREPLServer.this.getBreakpointID(breakpoint); } } - /** - * Specialize the standard SL debug context by notifying the REPL client when execution is - * halted, e.g. at a breakpoint. - *

- * Before notification, the server creates a new context at the halted location, in which - * subsequent evaluations take place until such time as the client says to "continue". - *

- * This implementation "cheats" the intended asynchronous architecture by calling back directly - * to the client with the notification. - */ - private final class SLREPLDebugClient implements DebugClient { - - private final Language language; - - SLREPLDebugClient(Language language) { - this.language = language; - } - - public void haltedAt(Node node, MaterializedFrame mFrame, List warnings) { - // Create and push a new debug context where execution is halted - currentServerContext = new SLServerContext(currentServerContext, node, mFrame); + void haltedAt(SuspendedEvent event) { + // Create and push a new debug context where execution is halted + currentServerContext = new SLServerContext(currentServerContext, event); - // Message the client that execution is halted and is in a new debugging context - final REPLMessage message = new REPLMessage(); - message.put(REPLMessage.OP, REPLMessage.STOPPED); - final SourceSection src = node.getSourceSection(); - final Source source = src.getSource(); - message.put(REPLMessage.SOURCE_NAME, source.getName()); - message.put(REPLMessage.FILE_PATH, source.getPath()); - message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); - message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); - message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); - if (!warnings.isEmpty()) { - final StringBuilder sb = new StringBuilder(); - for (String warning : warnings) { - sb.append(warning + "\n"); - } - message.put(REPLMessage.WARNINGS, sb.toString()); + // Message the client that execution is halted and is in a new debugging context + final REPLMessage message = new REPLMessage(); + message.put(REPLMessage.OP, REPLMessage.STOPPED); + final SourceSection src = event.getNode().getSourceSection(); + final Source source = src.getSource(); + message.put(REPLMessage.SOURCE_NAME, source.getName()); + message.put(REPLMessage.FILE_PATH, source.getPath()); + message.put(REPLMessage.LINE_NUMBER, Integer.toString(src.getStartLine())); + message.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); + message.put(REPLMessage.DEBUG_LEVEL, Integer.toString(currentServerContext.getLevel())); + List warnings = event.getRecentWarnings(); + if (!warnings.isEmpty()) { + final StringBuilder sb = new StringBuilder(); + for (String warning : warnings) { + sb.append(warning + "\n"); } - try { - // Cheat with synchrony: call client directly about entering a nested debugging - // context. - replClient.halted(message); - } finally { - // Returns when "continue" is called in the new debugging context + message.put(REPLMessage.WARNINGS, sb.toString()); + } + try { + // Cheat with synchrony: call client directly about entering a nested debugging + // context. + replClient.halted(message); + } finally { + // Returns when "continue" is called in the new debugging context - // Pop the debug context, and return so that the old context will continue - currentServerContext = currentServerContext.predecessor; - } - } - - public Language getLanguage() { - return language; + // Pop the debug context, and return so that the old context will continue + currentServerContext = currentServerContext.predecessor; } } - } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Sat Jul 18 18:03:36 2015 +0200 @@ -164,7 +164,7 @@ public SLLanguage(Env env) { super(env); - context = SLContextFactory.create(new BufferedReader(env().stdIn()), new PrintWriter(env().stdOut(), true)); + context = SLContextFactory.create(this, new BufferedReader(env().stdIn()), new PrintWriter(env().stdOut(), true)); LAST = this; for (NodeFactory builtin : builtins) { context.installBuiltin(builtin); @@ -489,17 +489,6 @@ } } - public void run(Source source) throws DebugSupportException { - // TODO (mlvdv) fix to run properly in the current VM - try { - SLLanguage.run(source); - } catch (QuitException ex) { - throw ex; - } catch (Exception e) { - throw new DebugSupportException(e); - } - } - public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { throw new DebugSupportException("evalInContext not supported in this language"); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java Sat Jul 18 18:03:36 2015 +0200 @@ -40,6 +40,7 @@ */ package com.oracle.truffle.sl.factory; +import com.oracle.truffle.sl.SLLanguage; import java.io.*; import com.oracle.truffle.sl.runtime.*; @@ -49,7 +50,7 @@ private SLContextFactory() { } - public static SLContext create(BufferedReader input, PrintWriter output) { - return new SLContext(input, output); + public static SLContext create(SLLanguage sl, BufferedReader input, PrintWriter output) { + return new SLContext(sl, input, output); } } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -45,6 +45,7 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.nodes.controlflow.*; import com.oracle.truffle.sl.runtime.*; @@ -70,7 +71,7 @@ @CompilationFinal private boolean isCloningAllowed; public SLRootNode(SLContext context, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, String name) { - super(null, frameDescriptor); + super(SLLanguage.class, null, frameDescriptor); this.bodyNode = bodyNode; this.name = name; this.context = context; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyCacheNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyCacheNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLReadPropertyCacheNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -69,8 +69,8 @@ */ @Specialization(limit = "CACHE_LIMIT", guards = {"longLocation != null", "shape.check(receiver)"}, assumptions = "shape.getValidAssumption()") @SuppressWarnings("unused") - protected long doCachedLong(DynamicObject receiver, // - @Cached("receiver.getShape()") Shape shape, // + protected long doCachedLong(DynamicObject receiver, // + @Cached("receiver.getShape()") Shape shape, // @Cached("getLongLocation(shape)") LongLocation longLocation) { return longLocation.getLong(receiver, true); } @@ -88,8 +88,8 @@ * contain all long cache entries. */ @Specialization(limit = "CACHE_LIMIT", contains = "doCachedLong", guards = "shape.check(receiver)", assumptions = "shape.getValidAssumption()") - protected static Object doCachedObject(DynamicObject receiver, // - @Cached("receiver.getShape()") Shape shape, // + protected static Object doCachedObject(DynamicObject receiver, // + @Cached("receiver.getShape()") Shape shape, // @Cached("shape.getProperty(propertyName)") Property property) { if (property == null) { return SLNull.SINGLETON; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyCacheNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyCacheNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/access/SLWritePropertyCacheNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -57,7 +57,7 @@ public abstract void executeObject(DynamicObject receiver, Object value); @Specialization(guards = "location.isValid(receiver, value)", assumptions = "location.getAssumptions()") - public void writeCached(DynamicObject receiver, Object value, // + public void writeCached(DynamicObject receiver, Object value, // @Cached("createCachedWrite(receiver, value)") CachedWriteLocation location) { if (location.writeUnchecked(receiver, value)) { // write successful @@ -68,7 +68,7 @@ @Specialization(contains = "writeCached") @TruffleBoundary - public void writeGeneric(DynamicObject receiver, Object value, // + public void writeGeneric(DynamicObject receiver, Object value, // @Cached("new(createCachedWrite(receiver, value))") LRUCachedWriteLocation lru) { CachedWriteLocation location = lru.location; if (!location.isValid(receiver, value) || !location.areAssumptionsValid()) { diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDispatchNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDispatchNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/call/SLDispatchNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -95,8 +95,8 @@ * cachedFunction. */ @Specialization(limit = "INLINE_CACHE_SIZE", guards = "function == cachedFunction", assumptions = "cachedFunction.getCallTargetStable()") - protected static Object doDirect(VirtualFrame frame, SLFunction function, Object[] arguments, // - @Cached("function") SLFunction cachedFunction, // + protected static Object doDirect(VirtualFrame frame, SLFunction function, Object[] arguments, // + @Cached("function") SLFunction cachedFunction, // @Cached("create(cachedFunction.getCallTarget())") DirectCallNode callNode) { /* Inline cache hit, we are safe to execute the cached call target. */ return callNode.call(frame, arguments); @@ -108,7 +108,7 @@ * no method inlining is performed. */ @Specialization(contains = "doDirect") - protected static Object doIndirect(VirtualFrame frame, SLFunction function, Object[] arguments, // + protected static Object doIndirect(VirtualFrame frame, SLFunction function, Object[] arguments, // @Cached("create()") IndirectCallNode callNode) { /* * SL has a quite simple call lookup: just ask the function for the current call target, and diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java Sat Jul 18 18:03:36 2015 +0200 @@ -69,7 +69,9 @@ if (valueNode != null) { result = valueNode.executeGeneric(frame); } else { - /* Return statement that was not followed by an expression, so return the SL null value. */ + /* + * Return statement that was not followed by an expression, so return the SL null value. + */ result = SLNull.SINGLETON; } throw new SLReturnException(result); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLDefaultVisualizer.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLDefaultVisualizer.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLDefaultVisualizer.java Sat Jul 18 18:03:36 2015 +0200 @@ -74,7 +74,7 @@ RootNode root = node.getRootNode(); if (root instanceof SLRootNode) { SLRootNode slRootNode = (SLRootNode) root; - return slRootNode.toString(); + return slRootNode.getName(); } return "unknown"; @@ -92,7 +92,7 @@ @Override public String displayValue(Object value, int trim) { - if (value == SLNull.SINGLETON) { + if (value == null || value == SLNull.SINGLETON) { return "null"; } return trim(value.toString(), trim); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Sat Jul 18 18:03:36 2015 +0200 @@ -239,8 +239,8 @@ } /** - * Returns the corresponding subclass of {@link SLExpressionNode} for binary expressions. - *
These nodes are currently not instrumented. + * Returns the corresponding subclass of {@link SLExpressionNode} for binary expressions.
+ * These nodes are currently not instrumented. * * @param opToken The operator of the binary expression * @param leftNode The left node of the expression diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Sat Jul 18 18:03:36 2015 +0200 @@ -48,6 +48,7 @@ import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.object.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.nodes.*; import com.oracle.truffle.sl.nodes.local.*; @@ -66,12 +67,14 @@ public final class SLContext extends ExecutionContext { private static final Layout LAYOUT = Layout.createLayout(); + private final SLLanguage language; private final BufferedReader input; private final PrintWriter output; private final SLFunctionRegistry functionRegistry; private final Shape emptyShape; - public SLContext(BufferedReader input, PrintWriter output) { + public SLContext(SLLanguage language, BufferedReader input, PrintWriter output) { + this.language = language; this.input = input; this.output = output; this.functionRegistry = new SLFunctionRegistry(); @@ -103,6 +106,10 @@ return functionRegistry; } + public SLLanguage getLanguage() { + return language; + } + /** * Adds all builtin functions to the {@link SLFunctionRegistry}. This method lists all * {@link SLBuiltinNode builtin implementation classes}. diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLFunctionForeignAccess.java Sat Jul 18 18:03:36 2015 +0200 @@ -47,6 +47,7 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.nodes.call.SLDispatchNode; import com.oracle.truffle.sl.nodes.call.SLDispatchNodeGen; import java.math.BigInteger; @@ -80,6 +81,10 @@ private static class SLForeignCallerRootNode extends RootNode { @Child private SLDispatchNode dispatch = SLDispatchNodeGen.create(); + public SLForeignCallerRootNode() { + super(SLLanguage.class, null, null); + } + @Override public Object execute(VirtualFrame frame) { SLFunction function = (SLFunction) ForeignAccess.getReceiver(frame); @@ -106,6 +111,10 @@ } private static class SLForeignNullCheckNode extends RootNode { + public SLForeignNullCheckNode() { + super(SLLanguage.class, null, null); + } + @Override public Object execute(VirtualFrame frame) { Object receiver = ForeignAccess.getReceiver(frame); diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java --- a/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tck/src/com/oracle/truffle/tck/MaxMinObject.java Sat Jul 18 18:03:36 2015 +0200 @@ -27,6 +27,7 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; @@ -175,6 +176,7 @@ private final boolean max; MaxMinNode(boolean max) { + super(MMLanguage.class, null, null); this.max = max; } @@ -194,4 +196,9 @@ } } + private abstract class MMLanguage extends TruffleLanguage { + public MMLanguage(Env env) { + super(env); + } + } } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/Breakpoint.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/Breakpoint.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.source.*; - -public abstract class Breakpoint { - - /** - * A general model of the states occupied by a breakpoint during its lifetime. - */ - public enum BreakpointState { - - /** - * Not attached, enabled. - *

- * Created for a source location but not yet attached: perhaps just created and the source - * hasn't been loaded yet; perhaps source has been loaded, but the line location isn't - * probed so a breakpoint cannot be attached. Can be either enabled or disabled. - */ - ENABLED_UNRESOLVED("Enabled/Unresolved"), - - /** - * Not attached, disabled. - *

- * Created for a source location but not yet attached: perhaps just created and the source - * hasn't been loaded yet; perhaps source has been loaded, but the line location isn't - * probed so a breakpoint cannot be attached. - */ - DISABLED_UNRESOLVED("Disabled/Unresolved"), - - /** - * Attached, instrument enabled. - *

- * Is currently implemented by some {@link Instrument}, which is attached to a {@link Probe} - * at a specific node in the AST, and the breakpoint is enabled. - */ - ENABLED("Enabled"), - - /** - * Attached, instrument disabled. - *

- * Is currently implemented by some {@link Instrument}, which is attached to a {@link Probe} - * at a specific node in the AST, and the breakpoint is disabled. - */ - DISABLED("Disabled"), - - /** - * Not attached, instrument is permanently disabled. - */ - DISPOSED("Disposed"); - - private final String name; - - BreakpointState(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return name; - } - - } - - private static int nextBreakpointId = 0; - - private final int id; - private final int groupId; - private final boolean isOneShot; - - private int ignoreCount; - - private int hitCount = 0; - - private BreakpointState state; - - Breakpoint(BreakpointState state, int groupId, int ignoreCount, boolean isOneShot) { - this.state = state; - this.id = nextBreakpointId++; - this.groupId = groupId; - this.isOneShot = isOneShot; - this.ignoreCount = ignoreCount; - } - - /** - * Unique ID. - */ - public final int getId() { - return id; - } - - /** - * Group ID, set when created. - */ - public final int getGroupId() { - return groupId; - } - - /** - * Enables or disables this breakpoint's AST instrumentation. The breakpoint is enabled by - * default. - * - * @param enabled true to activate the instrumentation, false to - * deactivate the instrumentation so that it has no effect. - */ - public abstract void setEnabled(boolean enabled); - - /** - * Is this breakpoint active? - */ - public abstract boolean isEnabled(); - - /** - * Sets the condition on this breakpoint, {@code null} to make it unconditional. - * - * @param expr if non{@code -null}, a boolean expression, expressed in the guest language, to be - * evaluated in the lexical context at the breakpoint location. - * @throws DebugException if condition is invalid - * @throws UnsupportedOperationException if the breakpoint does not support conditions - */ - public abstract void setCondition(String expr) throws DebugException; - - /** - * Gets the string, expressed in the Guest Language, that defines the current condition on this - * breakpoint; {@code null} if this breakpoint is currently unconditional. - */ - public String getCondition() { - return null; - } - - /** - * Does this breakpoint remove itself after first activation? - */ - public final boolean isOneShot() { - return isOneShot; - } - - /** - * Gets the number of hits left to be ignored before halting. - */ - public final int getIgnoreCount() { - return ignoreCount; - } - - /** - * Change the threshold for when this breakpoint should start causing a break. When both an - * ignore count and a {@linkplain #setCondition(String) condition} are specified, the condition - * is evaluated first: if {@code false} it is not considered to be a hit. In other words, the - * ignore count is for successful conditions only. - */ - public final void setIgnoreCount(int ignoreCount) { - this.ignoreCount = ignoreCount; - } - - /** - * Number of times this breakpoint has reached, with one exception; if the breakpoint has a - * condition that evaluates to {@code false}, it does not count as a hit. - */ - public final int getHitCount() { - return hitCount; - } - - /** - * Disables this breakpoint and removes any associated instrumentation; it becomes permanently - * inert. - */ - public abstract void dispose(); - - /** - * Gets a human-sensible description of this breakpoint's location in a {@link Source}. - */ - public abstract String getLocationDescription(); - - public final BreakpointState getState() { - return state; - } - - final void assertState(BreakpointState s) { - assert state == s; - } - - final void setState(BreakpointState state) { - this.state = state; - } - - /** - * Assumes that all conditions for causing the break have been satisfied, so increments the - * hit count. Then checks if the ignore count has been exceeded, and if so - * returns {@code true}. If not, it still counts as a hit but should be ignored. - * - * @return whether to proceed - */ - final boolean incrHitCountCheckIgnore() { - return ++hitCount > ignoreCount; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder(getClass().getSimpleName()); - sb.append(" state="); - sb.append(getState() == null ? "" : getState().getName()); - if (isOneShot()) { - sb.append(", " + "One-Shot"); - } - if (getCondition() != null) { - sb.append(", condition=\"" + getCondition() + "\""); - } - return sb.toString(); - } -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugClient.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugClient.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import java.util.*; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.vm.TruffleVM.Language; - -/** - * A client of the debugger where certain events should be posted. - * - * @see DebugEngine - */ -public interface DebugClient { - - /** - * Notifies client that program execution has been halted at some location; execution will - * resume when this method returns. - * - * @param astNode AST node that is just about to be executed - * @param mFrame frame that will be passed to the node when executed - * @param warnings any warnings generated since the most recent halt. - */ - void haltedAt(Node astNode, MaterializedFrame mFrame, List warnings); - - /** - * Gets information and services for the language being debugged. - */ - Language getLanguage(); -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugEngine.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugEngine.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,851 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import java.io.*; -import java.util.*; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.TruffleVM.Language; -import com.oracle.truffle.tools.debug.engine.DebugExecutionSupport.DebugExecutionListener; - -/** - * Language-agnostic engine for running Truffle languages under debugging control. - */ -public final class DebugEngine { - - private static final boolean TRACE = false; - private static final String TRACE_PREFIX = "DEBUG ENGINE: "; - - private static final PrintStream OUT = System.out; - - private static final SyntaxTag STEPPING_TAG = StandardSyntaxTag.STATEMENT; - private static final SyntaxTag CALL_TAG = StandardSyntaxTag.CALL; - - private static void trace(String format, Object... args) { - if (TRACE) { - OUT.println(TRACE_PREFIX + String.format(format, args)); - } - } - - interface BreakpointCallback { - - /** - * Passes control to the debugger with execution suspended. - */ - void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason); - } - - interface WarningLog { - - /** - * Logs a warning that is kept until the start of the next execution. - */ - void addWarning(String warning); - } - - private final Language language; - - /** - * The client of this engine. - */ - private final DebugClient debugClient; - - private final DebugExecutionSupport executionSupport; - - /** - * Implementation of line-oriented breakpoints. - */ - private final LineBreakpointFactory lineBreaks; - - /** - * Implementation of tag-oriented breakpoints. - */ - private final TagBreakpointFactory tagBreaks; - - /** - * Head of the stack of executions. - */ - private DebugExecutionContext debugContext; - - /** - * @param debugClient - * @param language - */ - private DebugEngine(DebugClient debugClient, Language language) { - this.debugClient = debugClient; - this.language = language; - this.executionSupport = new DebugExecutionSupport(language.getShortName(), language.getDebugSupport()); - - Source.setFileCaching(true); - - // Initialize execution context stack - debugContext = new DebugExecutionContext(null, null); - prepareContinue(); - debugContext.contextTrace("START EXEC DEFAULT"); - - executionSupport.addExecutionListener(new DebugExecutionListener() { - - public void executionStarted(Source source, boolean stepInto) { - // Push a new execution context onto stack - DebugEngine.this.debugContext = new DebugExecutionContext(source, DebugEngine.this.debugContext); - if (stepInto) { - DebugEngine.this.prepareStepInto(1); - } else { - DebugEngine.this.prepareContinue(); - } - DebugEngine.this.debugContext.contextTrace("START EXEC "); - } - - public void executionEnded() { - DebugEngine.this.lineBreaks.disposeOneShots(); - DebugEngine.this.tagBreaks.disposeOneShots(); - DebugEngine.this.debugContext.clearStrategy(); - DebugEngine.this.debugContext.contextTrace("END EXEC "); - // Pop the stack of execution contexts. - DebugEngine.this.debugContext = DebugEngine.this.debugContext.predecessor; - } - }); - - final BreakpointCallback breakpointCallback = new BreakpointCallback() { - - @TruffleBoundary - public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) { - debugContext.halt(astNode, mFrame, true, haltReason); - } - }; - - final WarningLog warningLog = new WarningLog() { - - public void addWarning(String warning) { - assert debugContext != null; - debugContext.logWarning(warning); - } - }; - - this.lineBreaks = new LineBreakpointFactory(executionSupport, breakpointCallback, warningLog); - - this.tagBreaks = new TagBreakpointFactory(executionSupport, breakpointCallback, warningLog); - } - - public static DebugEngine create(DebugClient debugClient, Language language) { - return new DebugEngine(debugClient, language); - } - - /** - * Runs a script. If "StepInto" is requested, halts at the first location tagged as a - * {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. - * - * @throws DebugException if an unexpected failure occurs - */ - public void run(Source source, boolean stepInto) throws DebugException { - executionSupport.run(source, stepInto); - } - - /** - * Sets a breakpoint to halt at a source line. - * - * @param groupId - * @param ignoreCount number of hits to ignore before halting - * @param lineLocation where to set the breakpoint (source, line number) - * @param oneShot breakpoint disposes itself after fist hit, if {@code true} - * @return a new breakpoint, initially enabled - * @throws DebugException if the breakpoint can not be set. - */ - @TruffleBoundary - public LineBreakpoint setLineBreakpoint(int groupId, int ignoreCount, LineLocation lineLocation, boolean oneShot) throws DebugException { - return lineBreaks.create(groupId, ignoreCount, lineLocation, oneShot); - } - - /** - * Sets a breakpoint to halt at any node holding a specified {@link SyntaxTag}. - * - * @param groupId - * @param ignoreCount number of hits to ignore before halting - * @param oneShot if {@code true} breakpoint removes it self after a hit - * @return a new breakpoint, initially enabled - * @throws DebugException if the breakpoint already set - */ - @TruffleBoundary - public Breakpoint setTagBreakpoint(int groupId, int ignoreCount, SyntaxTag tag, boolean oneShot) throws DebugException { - return tagBreaks.create(groupId, ignoreCount, tag, oneShot); - } - - /** - * Finds a breakpoint created by this engine, but not yet disposed, by id. - */ - @TruffleBoundary - public Breakpoint findBreakpoint(long id) { - final Breakpoint breakpoint = lineBreaks.find(id); - return breakpoint == null ? tagBreaks.find(id) : breakpoint; - } - - /** - * Gets all existing breakpoints, whatever their status, in natural sorted order. Modification - * save. - */ - @TruffleBoundary - public Collection getBreakpoints() { - final Collection result = new ArrayList<>(); - result.addAll(lineBreaks.getAll()); - result.addAll(tagBreaks.getAll()); - return result; - } - - /** - * Prepare to execute in Continue mode when guest language program execution resumes. In this - * mode: - *

    - *
  • Execution will continue until either: - *
      - *
    1. execution arrives at a node to which an enabled breakpoint is attached, - * or:
    2. - *
    3. execution completes.
    4. - *
    - *
- */ - @TruffleBoundary - public void prepareContinue() { - debugContext.setStrategy(new Continue()); - } - - /** - * Prepare to execute in StepInto mode when guest language program execution resumes. In this - * mode: - *
    - *
  • User breakpoints are disabled.
  • - *
  • Execution will continue until either: - *
      - *
    1. execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT - * STATMENT}, or:
    2. - *
    3. execution completes.
    4. - *
    - *
  • - * StepInto mode persists only through one resumption (i.e. {@code stepIntoCount} steps), and - * reverts by default to Continue mode.
  • - *
- * - * @param stepCount the number of times to perform StepInto before halting - * @throws IllegalArgumentException if the specified number is {@code <= 0} - */ - @TruffleBoundary - public void prepareStepInto(int stepCount) { - if (stepCount <= 0) { - throw new IllegalArgumentException(); - } - debugContext.setStrategy(new StepInto(stepCount)); - } - - /** - * Prepare to execute in StepOut mode when guest language program execution resumes. In this - * mode: - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution will continue until either: - *
      - *
    1. execution arrives at the nearest enclosing call site on the stack, or
    2. - *
    3. execution completes.
    4. - *
    - *
  • StepOut mode persists only through one resumption, and reverts by default to Continue - * mode.
  • - *
- */ - @TruffleBoundary - public void prepareStepOut() { - debugContext.setStrategy(new StepOut()); - } - - /** - * Prepare to execute in StepOver mode when guest language program execution resumes. In this - * mode: - *
    - *
  • Execution will continue until either: - *
      - *
    1. execution arrives at a node with the tag {@linkplain StandardSyntaxTag#STATEMENT - * STATEMENT} when not nested in one or more function/method calls, or:
    2. - *
    3. execution arrives at a node to which a breakpoint is attached and when nested in one or - * more function/method calls, or:
    4. - *
    5. execution completes.
    6. - *
    - *
  • StepOver mode persists only through one resumption (i.e. {@code stepOverCount} steps), - * and reverts by default to Continue mode.
  • - *
- * - * @param stepCount the number of times to perform StepInto before halting - * @throws IllegalArgumentException if the specified number is {@code <= 0} - */ - @TruffleBoundary - public void prepareStepOver(int stepCount) { - if (stepCount <= 0) { - throw new IllegalArgumentException(); - } - debugContext.setStrategy(new StepOver(stepCount)); - } - - /** - * Gets the stack frames from the (topmost) halted Truffle execution; {@code null} null if no - * execution. - */ - @TruffleBoundary - public List getStack() { - return debugContext == null ? null : debugContext.getFrames(); - } - - /** - * Evaluates code in a halted execution context, at top-level if mFrame==null. - * - * @throws DebugException - */ - public Object eval(Source source, Node node, MaterializedFrame mFrame) throws DebugException { - return executionSupport.evalInContext(source, node, mFrame); - } - - /** - * A mode of user navigation from a current code location to another, e.g "step in" vs. - * "step over". - */ - private abstract class StepStrategy { - - private DebugExecutionContext context; - protected final String strategyName; - - protected StepStrategy() { - this.strategyName = getClass().getSimpleName(); - } - - final String getName() { - return strategyName; - } - - /** - * Reconfigure the debugger so that when execution continues the program will halt at the - * location specified by this strategy. - */ - final void enable(DebugExecutionContext c, int stackDepth) { - this.context = c; - setStrategy(stackDepth); - } - - /** - * Return the debugger to the default navigation mode. - */ - final void disable() { - unsetStrategy(); - } - - @TruffleBoundary - final void halt(Node astNode, MaterializedFrame mFrame, boolean before) { - context.halt(astNode, mFrame, before, this.getClass().getSimpleName()); - } - - @TruffleBoundary - final void replaceStrategy(StepStrategy newStrategy) { - context.setStrategy(newStrategy); - } - - @TruffleBoundary - protected final void strategyTrace(String action, String format, Object... args) { - if (TRACE) { - context.contextTrace("%s (%s) %s", action, strategyName, String.format(format, args)); - } - } - - @TruffleBoundary - protected final void suspendUserBreakpoints() { - lineBreaks.setActive(false); - tagBreaks.setActive(false); - } - - @SuppressWarnings("unused") - protected final void restoreUserBreakpoints() { - lineBreaks.setActive(true); - tagBreaks.setActive(true); - } - - /** - * Reconfigure the debugger so that when execution continues, it will do so using this mode - * of navigation. - */ - protected abstract void setStrategy(int stackDepth); - - /** - * Return to the debugger to the default mode of navigation. - */ - protected abstract void unsetStrategy(); - } - - /** - * Strategy: the null stepping strategy. - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution continues until either: - *
      - *
    1. execution arrives at a node with attached user breakpoint, or:
    2. - *
    3. execution completes.
    4. - *
    - *
- */ - private final class Continue extends StepStrategy { - - @Override - protected void setStrategy(int stackDepth) { - } - - @Override - protected void unsetStrategy() { - } - } - - /** - * Strategy: per-statement stepping. - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution continues until either: - *
      - *
    1. execution arrives at a STATEMENT node, or:
    2. - *
    3. execution returns to a CALL node and the call stack is smaller then when - * execution started, or:
    4. - *
    5. execution completes.
    6. - *
    - *
- * - * @see DebugEngine#prepareStepInto(int) - */ - private final class StepInto extends StepStrategy { - private int unfinishedStepCount; - - StepInto(int stepCount) { - super(); - this.unfinishedStepCount = stepCount; - } - - @Override - protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { - - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - // HALT: just before statement - --unfinishedStepCount; - strategyTrace("TRAP BEFORE", "unfinished steps=%d", unfinishedStepCount); - // Should run in fast path - if (unfinishedStepCount <= 0) { - halt(node, mFrame, true); - } - strategyTrace("RESUME BEFORE", ""); - } - }); - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { - - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - --unfinishedStepCount; - strategyTrace(null, "TRAP AFTER unfinished steps=%d", unfinishedStepCount); - if (currentStackDepth() < stackDepth) { - // HALT: just "stepped out" - if (unfinishedStepCount <= 0) { - halt(node, mFrame, false); - } - } - strategyTrace("RESUME AFTER", ""); - } - }); - } - - @Override - protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); - } - } - - /** - * Strategy: execution to nearest enclosing call site. - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution continues until either: - *
      - *
    1. execution arrives at a node with attached user breakpoint, or:
    2. - *
    3. execution returns to a CALL node and the call stack is smaller than when - * execution started, or:
    4. - *
    5. execution completes.
    6. - *
    - *
- * - * @see DebugEngine#prepareStepOut() - */ - private final class StepOut extends StepStrategy { - - @Override - protected void setStrategy(final int stackDepth) { - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { - - @TruffleBoundary - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - // HALT: - final int currentStackDepth = currentStackDepth(); - strategyTrace("TRAP AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth); - if (currentStackDepth < stackDepth) { - halt(node, mFrame, false); - } - strategyTrace("RESUME AFTER", ""); - } - }); - } - - @Override - protected void unsetStrategy() { - Probe.setAfterTagTrap(null); - } - } - - /** - * Strategy: per-statement stepping, so long as not nested in method calls (i.e. at original - * stack depth). - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution continues until either: - *
      - *
    1. execution arrives at a STATEMENT node with stack depth no more than when started - * or:
    2. - *
    3. the program completes.
    4. - *
    - *
- */ - private final class StepOver extends StepStrategy { - private int unfinishedStepCount; - - StepOver(int stepCount) { - this.unfinishedStepCount = stepCount; - } - - @Override - protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { - - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - final int currentStackDepth = currentStackDepth(); - if (currentStackDepth <= stackDepth) { - // HALT: stack depth unchanged or smaller; treat like StepInto - --unfinishedStepCount; - if (TRACE) { - strategyTrace("TRAP BEFORE", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); - } - // Test should run in fast path - if (unfinishedStepCount <= 0) { - halt(node, mFrame, true); - } - } else { - // CONTINUE: Stack depth increased; don't count as a step - strategyTrace("STEP INTO", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); - // Stop treating like StepInto, start treating like StepOut - replaceStrategy(new StepOverNested(unfinishedStepCount, stackDepth)); - } - strategyTrace("RESUME BEFORE", ""); - } - }); - - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { - - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - final int currentStackDepth = currentStackDepth(); - if (currentStackDepth < stackDepth) { - // HALT: just "stepped out" - --unfinishedStepCount; - strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); - // Should run in fast path - if (unfinishedStepCount <= 0) { - halt(node, mFrame, false); - } - strategyTrace("RESUME AFTER", ""); - } - } - }); - } - - @Override - protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); - } - } - - /** - * Strategy: per-statement stepping, not into method calls, in effect while at increased stack - * depth - *
    - *
  • User breakpoints are enabled.
  • - *
  • Execution continues until either: - *
      - *
    1. execution arrives at a STATEMENT node with stack depth no more than when started - * or:
    2. - *
    3. the program completes or:
    4. - *
    - *
- */ - private final class StepOverNested extends StepStrategy { - private int unfinishedStepCount; - private final int startStackDepth; - - StepOverNested(int stepCount, int startStackDepth) { - this.unfinishedStepCount = stepCount; - this.startStackDepth = startStackDepth; - } - - @Override - protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { - - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { - final int currentStackDepth = currentStackDepth(); - if (currentStackDepth <= startStackDepth) { - // At original step depth (or smaller) after being nested - --unfinishedStepCount; - strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); - if (unfinishedStepCount <= 0) { - halt(node, mFrame, false); - } - // TODO (mlvdv) fixme for multiple steps - strategyTrace("RESUME BEFORE", ""); - } - } - }); - } - - @Override - protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - } - } - - /** - * Information and debugging state for a single Truffle execution (which make take place over - * one or more suspended executions). This holds interaction state, for example what is - * executing (e.g. some {@link Source}), what the execution mode is ("stepping" or - * "continuing"). When not running, this holds a cache of the Truffle stack for this particular - * execution, effectively hiding the Truffle stack for any currently suspended executions (down - * the stack). - */ - private final class DebugExecutionContext { - - // Previous halted context in stack - private final DebugExecutionContext predecessor; - - // The current execution level; first is 0. - private final int level; // Number of contexts suspended below - private final Source source; - private final int contextStackBase; // Where the stack for this execution starts - private final List warnings = new ArrayList<>(); - - private boolean running; - - /** - * The stepping strategy currently configured in the debugger. - */ - private StepStrategy strategy; - - /** - * Where halted; null if running. - */ - private Node haltedNode; - - /** - * Where halted; null if running. - */ - private MaterializedFrame haltedFrame; - - /** - * Cached list of stack frames when halted; null if running. - */ - private List frames = new ArrayList<>(); - - private DebugExecutionContext(Source executionSource, DebugExecutionContext previousContext) { - this.source = executionSource; - this.predecessor = previousContext; - this.level = previousContext == null ? 0 : previousContext.level + 1; - - // "Base" is the number of stack frames for all nested (halted) executions. - this.contextStackBase = currentStackDepth(); - this.running = true; - contextTrace("NEW CONTEXT"); - } - - /** - * Sets up a strategy for the next resumption of execution. - * - * @param stepStrategy - */ - void setStrategy(StepStrategy stepStrategy) { - if (this.strategy == null) { - this.strategy = stepStrategy; - this.strategy.enable(this, currentStackDepth()); - if (TRACE) { - contextTrace("SET MODE -->" + stepStrategy.getName()); - } - } else { - strategy.disable(); - strategy = stepStrategy; - strategy.enable(this, currentStackDepth()); - contextTrace("SWITCH MODE %s-->%s", strategy.getName(), stepStrategy.getName()); - } - } - - void clearStrategy() { - if (strategy != null) { - final StepStrategy oldStrategy = strategy; - strategy.disable(); - strategy = null; - contextTrace("CLEAR MODE %s-->", oldStrategy.getName()); - } - } - - /** - * Handle a program halt, caused by a breakpoint, stepping strategy, or other cause. - * - * @param astNode the guest language node at which execution is halted - * @param mFrame the current execution frame where execution is halted - * @param before {@code true} if halted before the node, else after. - */ - @TruffleBoundary - void halt(Node astNode, MaterializedFrame mFrame, boolean before, String haltReason) { - assert running; - assert frames.isEmpty(); - assert haltedNode == null; - assert haltedFrame == null; - - haltedNode = astNode; - haltedFrame = mFrame; - running = false; - - clearStrategy(); - - // Clean up, just in cased the one-shot breakpoints got confused - lineBreaks.disposeOneShots(); - - // Map the Truffle stack for this execution, ignore nested executions - // The top (current) frame is not produced by the iterator. - frames.add(new FrameDebugDescription(0, haltedNode, Truffle.getRuntime().getCurrentFrame())); - final int contextStackDepth = currentStackDepth() - contextStackBase; - final int[] frameCount = {1}; - Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { - @Override - public FrameInstance visitFrame(FrameInstance frameInstance) { - if (frameCount[0] < contextStackDepth) { - frames.add(new FrameDebugDescription(frameCount[0], frameInstance.getCallNode(), frameInstance)); - frameCount[0] = frameCount[0] + 1; - return null; - } - return frameInstance; - } - }); - - if (TRACE) { - final String reason = haltReason == null ? "" : haltReason + ""; - final String where = before ? "BEFORE" : "AFTER"; - contextTrace("HALT %s : (%s) stack base=%d", where, reason, contextStackBase); - contextTrace("CURRENT STACK:"); - printStack(OUT); - } - - final List recentWarnings = new ArrayList<>(warnings); - warnings.clear(); - - try { - // Pass control to the debug client with current execution suspended - debugClient.haltedAt(astNode, mFrame, recentWarnings); - // Debug client finished normally, execution resumes - // Presume that the client has set a new strategy (or default to Continue) - running = true; - } catch (KillException e) { - contextTrace("KILL"); - throw e; - } finally { - haltedNode = null; - haltedFrame = null; - frames.clear(); - } - - } - - List getFrames() { - return Collections.unmodifiableList(frames); - } - - void logWarning(String warning) { - warnings.add(warning); - } - - // For tracing - private void printStack(PrintStream stream) { - getFrames(); - if (frames == null) { - stream.println(""); - } else { - final Visualizer visualizer = language.getDebugSupport().getVisualizer(); - for (FrameDebugDescription frameDesc : frames) { - final StringBuilder sb = new StringBuilder(" frame " + Integer.toString(frameDesc.index())); - sb.append(":at " + visualizer.displaySourceLocation(frameDesc.node())); - sb.append(":in '" + visualizer.displayMethodName(frameDesc.node()) + "'"); - stream.println(sb.toString()); - } - } - } - - void contextTrace(String format, Object... args) { - if (TRACE) { - final String srcName = (source != null) ? source.getName() : "no source"; - DebugEngine.trace("<%d> %s (%s)", level, String.format(format, args), srcName); - } - } - } - - // TODO (mlvdv) wish there were fast-path access to stack depth - /** - * Depth of current Truffle stack, including nested executions. Includes the top/current frame, - * which the standard iterator does not count: {@code 0} if no executions. - */ - @TruffleBoundary - private static int currentStackDepth() { - final int[] count = {0}; - Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor() { - @Override - public Void visitFrame(FrameInstance frameInstance) { - count[0] = count[0] + 1; - return null; - } - }); - return count[0] == 0 ? 0 : count[0] + 1; - - } -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugException.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugException.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -/** - * An unexpected failure in the operation of the {@link DebugEngine}. - */ -public class DebugException extends Exception { - - public DebugException(String string) { - super(string); - } - - public DebugException(Exception ex) { - super(ex); - } - - private static final long serialVersionUID = 3307454453821997224L; - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugExecutionSupport.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/DebugExecutionSupport.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import java.util.*; - -import com.oracle.truffle.api.debug.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; - -/** - * Access to language-specific support for debugging. - */ -final class DebugExecutionSupport { - - interface DebugExecutionListener { - - /** - * Notifies that execution is about to start and requests initial execution mode. - */ - void executionStarted(Source source, boolean stepInto); - - /** - * Notification that the current execution has just ended. - */ - void executionEnded(); - } - - private final String languageName; - private final DebugSupportProvider provider; - private final List listeners = new ArrayList<>(); - - DebugExecutionSupport(String languageName, DebugSupportProvider provider) { - this.languageName = languageName; - this.provider = provider; - } - - void addExecutionListener(DebugExecutionListener listener) { - assert listener != null; - listeners.add(listener); - } - - String getLanguageName() { - return languageName; - } - - Visualizer getVisualizer() { - return provider.getVisualizer(); - } - - /** - * Runs a script. If "StepInto" is specified, halts at the first location tagged as a - * {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. - */ - void run(Source source, boolean stepInto) throws DebugException { - for (DebugExecutionListener listener : listeners) { - listener.executionStarted(source, stepInto); - } - try { - provider.run(source); - } catch (DebugSupportException ex) { - throw new DebugException(ex); - } finally { - for (DebugExecutionListener listener : listeners) { - listener.executionEnded(); - } - } - } - - /** - * Evaluates string of language code in a halted execution context, at top level if - * mFrame==null. - * - * @throws DebugException - */ - Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugException { - for (DebugExecutionListener listener : listeners) { - listener.executionStarted(source, false); - } - try { - return provider.evalInContext(source, node, mFrame); - } catch (DebugSupportException ex) { - throw new DebugException(ex); - } finally { - for (DebugExecutionListener listener : listeners) { - listener.executionEnded(); - } - } - } - - /** - * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot} - * that, when executed, computes the result of a textual expression in the language; used to - * create an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) - * Advanced Instrument}. - * - * @param expr a guest language expression - * @param resultListener optional listener for the result of each evaluation. - * @return a new factory - * @throws DebugException if the factory cannot be created, for example if the expression is - * badly formed. - */ - AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugException { - try { - return provider.createAdvancedInstrumentRootFactory(expr, resultListener); - } catch (DebugSupportException ex) { - throw new DebugException(ex); - } - } - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/FrameDebugDescription.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/FrameDebugDescription.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; - -/** - * A summary description of a Truffle {@link Frame} in a particular stack context. - * - * @see DebugEngine - */ -public final class FrameDebugDescription { - - private final int index; - private final Node node; - private final FrameInstance frameInstance; - - FrameDebugDescription(int index, Node node, FrameInstance frameInstance) { - this.index = index; - this.node = node; - this.frameInstance = frameInstance; - } - - /** - * Position in the current stack: {@code 0} at the top. - */ - public int index() { - return index; - } - - /** - * AST location. - */ - public Node node() { - return node; - } - - /** - * Access to the Truffle {@link Frame}. - */ - public FrameInstance frameInstance() { - return frameInstance; - } -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/LineBreakpoint.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/LineBreakpoint.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import com.oracle.truffle.api.source.*; - -// TODO (mlvdv) generic? -/** - * A breakpoint associated with a {@linkplain LineLocation source line location}. - * - * @see DebugEngine - */ -public abstract class LineBreakpoint extends Breakpoint { - - LineBreakpoint(BreakpointState state, int groupId, int ignoreCount, boolean isOneShot) { - super(state, groupId, ignoreCount, isOneShot); - } - - /** - * Gets the {@linkplain LineLocation source line location} that specifies where this breakpoint - * will trigger. - */ - public abstract LineLocation getLineLocation(); - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/LineBreakpointFactory.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/LineBreakpointFactory.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,478 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import static com.oracle.truffle.tools.debug.engine.Breakpoint.BreakpointState.*; - -import java.io.*; -import java.util.*; -import java.util.Map.Entry; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.instrument.impl.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.utilities.*; -import com.oracle.truffle.tools.*; -import com.oracle.truffle.tools.debug.engine.DebugEngine.BreakpointCallback; -import com.oracle.truffle.tools.debug.engine.DebugEngine.WarningLog; - -//TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport - -/** - * Support class for creating and managing all existing ordinary (user visible) line breakpoints. - *

- * Notes: - *

    - *
  1. Line breakpoints can only be set at nodes tagged as {@link StandardSyntaxTag#STATEMENT}.
  2. - *
  3. A newly created breakpoint looks for probes matching the location, attaches to them if found - * by installing an {@link Instrument} that calls back to the breakpoint.
  4. - *
  5. When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be - * copied along with the rest of the AST and will call back to the same breakpoint.
  6. - *
  7. When notification is received of a new Node being tagged as a statement, and if a - * breakpoint's line location matches the Probe's line location, then the breakpoint will attach a - * new Instrument at the probe to activate the breakpoint at that location.
  8. - *
  9. A breakpoint may have multiple Instruments deployed, one attached to each Probe that matches - * the breakpoint's line location; this might happen when a source is reloaded.
  10. - *
- * - */ -final class LineBreakpointFactory { - - private static final boolean TRACE = false; - private static final PrintStream OUT = System.out; - - private static final String BREAKPOINT_NAME = "LINE BREAKPOINT"; - - private static void trace(String format, Object... args) { - if (TRACE) { - OUT.println(String.format("%s: %s", BREAKPOINT_NAME, String.format(format, args))); - } - } - - private static final Comparator> BREAKPOINT_COMPARATOR = new Comparator>() { - - @Override - public int compare(Entry entry1, Entry entry2) { - final LineLocation line1 = entry1.getKey(); - final LineLocation line2 = entry2.getKey(); - final int nameOrder = line1.getSource().getShortName().compareTo(line2.getSource().getShortName()); - if (nameOrder != 0) { - return nameOrder; - } - return Integer.compare(line1.getLineNumber(), line2.getLineNumber()); - } - }; - - private final DebugExecutionSupport executionSupport; - private final BreakpointCallback breakpointCallback; - private final WarningLog warningLog; - - /** - * Map: Source lines ==> attached breakpoints. There may be no more than one line breakpoint - * associated with a line. - */ - private final Map lineToBreakpoint = new HashMap<>(); - - /** - * A map of {@link LineLocation} to a collection of {@link Probe}s. This list must be - * initialized and filled prior to being used by this class. - */ - private final LineToProbesMap lineToProbesMap; - - /** - * Globally suspends all line breakpoint activity when {@code false}, ignoring whether - * individual breakpoints are enabled. - */ - @CompilationFinal private boolean breakpointsActive = true; - private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - - LineBreakpointFactory(DebugExecutionSupport executionSupport, BreakpointCallback breakpointCallback, final WarningLog warningLog) { - this.executionSupport = executionSupport; - this.breakpointCallback = breakpointCallback; - this.warningLog = warningLog; - - lineToProbesMap = new LineToProbesMap(); - lineToProbesMap.install(); - - Probe.addProbeListener(new DefaultProbeListener() { - - @Override - public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { - if (tag == StandardSyntaxTag.STATEMENT) { - final SourceSection sourceSection = probe.getProbedSourceSection(); - if (sourceSection != null) { - final LineLocation lineLocation = sourceSection.getLineLocation(); - if (lineLocation != null) { - // A Probe with line location tagged STATEMENT we haven't seen before. - final LineBreakpointImpl breakpoint = lineToBreakpoint.get(lineLocation); - if (breakpoint != null) { - try { - breakpoint.attach(probe); - } catch (DebugException e) { - warningLog.addWarning(BREAKPOINT_NAME + " failure attaching to newly tagged Probe: " + e.getMessage()); - if (TRACE) { - OUT.println(BREAKPOINT_NAME + " failure: " + e.getMessage()); - } - } - } - } - } - } - } - }); - } - - /** - * Globally enables line breakpoint activity; all breakpoints are ignored when set to - * {@code false}. When set to {@code true}, the enabled/disabled status of each breakpoint - * determines whether it will trigger when flow of execution reaches it. - * - * @param breakpointsActive - */ - void setActive(boolean breakpointsActive) { - if (this.breakpointsActive != breakpointsActive) { - breakpointsActiveUnchanged.invalidate(); - this.breakpointsActive = breakpointsActive; - } - } - - /** - * Returns the (not yet disposed) breakpoint by id; null if none. - */ - LineBreakpoint find(long id) { - for (LineBreakpoint breakpoint : lineToBreakpoint.values()) { - if (breakpoint.getId() == id) { - return breakpoint; - } - } - return null; - } - - /** - * Gets all current line breakpoints,regardless of status; sorted and modification safe. - */ - List getAll() { - ArrayList> entries = new ArrayList<>(lineToBreakpoint.entrySet()); - Collections.sort(entries, BREAKPOINT_COMPARATOR); - - final ArrayList breakpoints = new ArrayList<>(entries.size()); - for (Entry entry : entries) { - breakpoints.add(entry.getValue()); - } - return breakpoints; - } - - /** - * Creates a new line breakpoint if one doesn't already exist. If one does exist, then resets - * the ignore count. - * - * @param lineLocation where to set the breakpoint - * @param ignoreCount number of initial hits before the breakpoint starts causing breaks. - * @param oneShot whether the breakpoint should dispose itself after one hit - * @return a possibly new breakpoint - * @throws DebugException if a breakpoint already exists at the location and the ignore count is - * the same - */ - LineBreakpoint create(int groupId, int ignoreCount, LineLocation lineLocation, boolean oneShot) throws DebugException { - - LineBreakpointImpl breakpoint = lineToBreakpoint.get(lineLocation); - - if (breakpoint == null) { - breakpoint = new LineBreakpointImpl(groupId, ignoreCount, lineLocation, oneShot); - - if (TRACE) { - trace("NEW " + breakpoint.getShortDescription()); - } - - lineToBreakpoint.put(lineLocation, breakpoint); - - for (Probe probe : lineToProbesMap.findProbes(lineLocation)) { - if (probe.isTaggedAs(StandardSyntaxTag.STATEMENT)) { - breakpoint.attach(probe); - break; - } - } - } else { - if (ignoreCount == breakpoint.getIgnoreCount()) { - throw new DebugException(BREAKPOINT_NAME + " already set at line " + lineLocation); - } - breakpoint.setIgnoreCount(ignoreCount); - if (TRACE) { - trace("CHANGED ignoreCount %s", breakpoint.getShortDescription()); - } - } - return breakpoint; - } - - /** - * Returns the {@link LineBreakpoint} for a given line. There should only ever be one breakpoint - * per line. - * - * @param lineLocation The {@link LineLocation} to get the breakpoint for. - * @return The breakpoint for the given line. - */ - LineBreakpoint get(LineLocation lineLocation) { - return lineToBreakpoint.get(lineLocation); - } - - /** - * Removes the associated instrumentation for all one-shot breakpoints only. - */ - void disposeOneShots() { - List breakpoints = new ArrayList<>(lineToBreakpoint.values()); - for (LineBreakpointImpl breakpoint : breakpoints) { - if (breakpoint.isOneShot()) { - breakpoint.dispose(); - } - } - } - - /** - * Removes all knowledge of a breakpoint, presumed disposed. - */ - private void forget(LineBreakpointImpl breakpoint) { - lineToBreakpoint.remove(breakpoint.getLineLocation()); - } - - /** - * Concrete representation of a line breakpoint, implemented by attaching an instrument to a - * probe at the designated source location. - */ - private final class LineBreakpointImpl extends LineBreakpoint implements AdvancedInstrumentResultListener { - - private static final String SHOULD_NOT_HAPPEN = "LineBreakpointImpl: should not happen"; - - private final LineLocation lineLocation; - - // Cached assumption that the global status of line breakpoint activity has not changed. - private Assumption breakpointsActiveAssumption; - - // Whether this breakpoint is enable/disabled - @CompilationFinal private boolean isEnabled; - private Assumption enabledUnchangedAssumption; - - private String conditionExpr; - - /** - * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: - * {@code null} if not attached. - */ - private List instruments = new ArrayList<>(); - - public LineBreakpointImpl(int groupId, int ignoreCount, LineLocation lineLocation, boolean oneShot) { - super(ENABLED_UNRESOLVED, groupId, ignoreCount, oneShot); - this.lineLocation = lineLocation; - - this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); - this.isEnabled = true; - this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); - } - - @Override - public boolean isEnabled() { - return isEnabled; - } - - @Override - public void setEnabled(boolean enabled) { - if (enabled != isEnabled) { - switch (getState()) { - case ENABLED: - assert !enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(false); - changeState(DISABLED); - break; - case ENABLED_UNRESOLVED: - assert !enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(false); - changeState(DISABLED_UNRESOLVED); - break; - case DISABLED: - assert enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(true); - changeState(ENABLED); - break; - case DISABLED_UNRESOLVED: - assert enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(true); - changeState(DISABLED_UNRESOLVED); - break; - case DISPOSED: - assert false : "breakpoint disposed"; - break; - default: - assert false : SHOULD_NOT_HAPPEN; - break; - } - } - } - - @Override - public void setCondition(String expr) throws DebugException { - if (this.conditionExpr != null || expr != null) { - // De-instrument the Probes instrumented by this breakpoint - final ArrayList probes = new ArrayList<>(); - for (Instrument instrument : instruments) { - probes.add(instrument.getProbe()); - instrument.dispose(); - } - instruments.clear(); - this.conditionExpr = expr; - // Re-instrument the probes previously instrumented - for (Probe probe : probes) { - attach(probe); - } - } - } - - @Override - public String getCondition() { - return conditionExpr; - } - - @Override - public void dispose() { - if (getState() != DISPOSED) { - for (Instrument instrument : instruments) { - instrument.dispose(); - } - changeState(DISPOSED); - LineBreakpointFactory.this.forget(this); - } - } - - private void attach(Probe newProbe) throws DebugException { - if (getState() == DISPOSED) { - throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); - } - Instrument newInstrument = null; - if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); - } else { - newInstrument = Instrument.create(this, executionSupport.createAdvancedInstrumentRootFactory(conditionExpr, this), Boolean.class, BREAKPOINT_NAME); - } - newProbe.attach(newInstrument); - instruments.add(newInstrument); - changeState(isEnabled ? ENABLED : DISABLED); - } - - private void doSetEnabled(boolean enabled) { - if (this.isEnabled != enabled) { - enabledUnchangedAssumption.invalidate(); - this.isEnabled = enabled; - } - } - - private String getShortDescription() { - return BREAKPOINT_NAME + "@" + getLineLocation().getShortDescription(); - } - - private void changeState(BreakpointState after) { - if (TRACE) { - trace("STATE %s-->%s %s", getState().getName(), after.getName(), getShortDescription()); - } - setState(after); - } - - private void doBreak(Node node, VirtualFrame vFrame) { - if (incrHitCountCheckIgnore()) { - breakpointCallback.haltedAt(node, vFrame.materialize(), BREAKPOINT_NAME); - } - } - - /** - * Receives notification from the attached instrument that execution is about to enter node - * where the breakpoint is set. Designed so that when in the fast path, there is either an - * unconditional "halt" call to the debugger or nothing. - */ - private void nodeEnter(Node astNode, VirtualFrame vFrame) { - - // Deopt if the global active/inactive flag has changed - try { - this.breakpointsActiveAssumption.check(); - } catch (InvalidAssumptionException ex) { - this.breakpointsActiveAssumption = LineBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); - } - - // Deopt if the enabled/disabled state of this breakpoint has changed - try { - this.enabledUnchangedAssumption.check(); - } catch (InvalidAssumptionException ex) { - this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption("LineBreakpoint enabled state unchanged"); - } - - if (LineBreakpointFactory.this.breakpointsActive && this.isEnabled) { - if (isOneShot()) { - dispose(); - } - LineBreakpointImpl.this.doBreak(astNode, vFrame); - } - } - - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { - final boolean condition = (Boolean) result; - if (TRACE) { - trace("breakpoint condition = %b %s", condition, getShortDescription()); - } - if (condition) { - nodeEnter(node, vFrame); - } - } - - @TruffleBoundary - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { - warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); - if (TRACE) { - trace("breakpoint failure = %s %s", ex.toString(), getShortDescription()); - } - // Take the breakpoint if evaluation fails. - nodeEnter(node, vFrame); - } - - @Override - public String getLocationDescription() { - return "Line: " + lineLocation.getShortDescription(); - } - - @Override - public LineLocation getLineLocation() { - return lineLocation; - } - - private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { - - @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - LineBreakpointImpl.this.nodeEnter(node, vFrame); - } - } - } - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/TagBreakpoint.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/TagBreakpoint.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import com.oracle.truffle.api.instrument.*; - -// TODO (mlvdv) generic? -/** - * A breakpoint associated with a {@link SyntaxTag}. - * - * @see DebugEngine - */ -public abstract class TagBreakpoint extends Breakpoint { - - TagBreakpoint(BreakpointState state, int groupId, int ignoreCount, boolean isOneShot) { - super(state, groupId, ignoreCount, isOneShot); - } - - /** - * Gets the tag that specifies where this breakpoint will trigger. - */ - public abstract SyntaxTag getTag(); - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/TagBreakpointFactory.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/TagBreakpointFactory.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -/* - * Copyright (c) 2015, 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.tools.debug.engine; - -import static com.oracle.truffle.tools.debug.engine.Breakpoint.BreakpointState.*; - -import java.io.*; -import java.util.*; -import java.util.Map.Entry; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.instrument.impl.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.utilities.*; -import com.oracle.truffle.tools.debug.engine.DebugEngine.BreakpointCallback; -import com.oracle.truffle.tools.debug.engine.DebugEngine.WarningLog; - -// TODO (mlvdv) some common functionality could be factored out of this and LineBreakpointSupport - -/** - * Support class for creating and managing "Tag Breakpoints". A Tag Breakpoint halts execution just - * before reaching any node whose Probe carries a specified {@linkplain SyntaxTag Tag}. - *

- * The {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly into the - * Instrumentation Framework, does the same thing more efficiently, but there may only be one Tag - * Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but it would - * be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap. - *

- * Notes: - *

    - *
  1. Only one Tag Breakpoint can be active for a specific {@linkplain SyntaxTag Tag}.
  2. - *
  3. A newly created breakpoint looks for probes matching the tag, attaches to them if found by - * installing an {@link Instrument}.
  4. - *
  5. When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be - * copied along with the rest of the AST and will call back to the same breakpoint.
  6. - *
  7. When notification is received that the breakpoint's Tag has been newly added to a Node, then - * the breakpoint will attach a new Instrument at the probe to activate the breakpoint at that - * location.
  8. - *
  9. A breakpoint may have multiple Instruments deployed, one attached to each Probe that holds - * the breakpoint's tag; this might happen when a source is reloaded.
  10. - *
- */ -final class TagBreakpointFactory { - - private static final boolean TRACE = false; - private static final PrintStream OUT = System.out; - - private static final String BREAKPOINT_NAME = "TAG BREAKPOINT"; - - private static void trace(String format, Object... args) { - if (TRACE) { - OUT.println(String.format("%s: %s", BREAKPOINT_NAME, String.format(format, args))); - } - } - - private static final Comparator> BREAKPOINT_COMPARATOR = new Comparator>() { - - @Override - public int compare(Entry entry1, Entry entry2) { - return entry1.getKey().name().compareTo(entry2.getKey().name()); - } - }; - - private final DebugExecutionSupport executionSupport; - private final BreakpointCallback breakpointCallback; - private final WarningLog warningLog; - - /** - * Map: Tags ==> Tag Breakpoints. There may be no more than one breakpoint per Tag. - */ - private final Map tagToBreakpoint = new HashMap<>(); - - /** - * Globally suspends all line breakpoint activity when {@code false}, ignoring whether - * individual breakpoints are enabled. - */ - @CompilationFinal private boolean breakpointsActive = true; - private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - - TagBreakpointFactory(DebugExecutionSupport executionSupport, BreakpointCallback breakpointCallback, final WarningLog warningLog) { - this.executionSupport = executionSupport; - this.breakpointCallback = breakpointCallback; - this.warningLog = warningLog; - - Probe.addProbeListener(new DefaultProbeListener() { - - @Override - public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { - final TagBreakpointImpl breakpoint = tagToBreakpoint.get(tag); - if (breakpoint != null) { - try { - breakpoint.attach(probe); - } catch (DebugException e) { - warningLog.addWarning(BREAKPOINT_NAME + " failure attaching to newly tagged Probe: " + e.getMessage()); - if (TRACE) { - OUT.println(BREAKPOINT_NAME + " failure: " + e.getMessage()); - } - } - } - } - }); - } - - /** - * Globally enables tag breakpoint activity; all breakpoints are ignored when set to - * {@code false}. When set to {@code true}, the enabled/disabled status of each breakpoint - * determines whether it will trigger when flow of execution reaches it. - * - * @param breakpointsActive - */ - void setActive(boolean breakpointsActive) { - if (this.breakpointsActive != breakpointsActive) { - breakpointsActiveUnchanged.invalidate(); - this.breakpointsActive = breakpointsActive; - } - } - - /** - * Returns the (not yet disposed) breakpoint by id, if any; null if none. - */ - TagBreakpoint find(long id) { - for (TagBreakpointImpl breakpoint : tagToBreakpoint.values()) { - if (breakpoint.getId() == id) { - return breakpoint; - } - } - return null; - } - - /** - * Gets all current tag breakpoints,regardless of status; sorted and modification safe. - */ - List getAll() { - ArrayList> entries = new ArrayList<>(tagToBreakpoint.entrySet()); - Collections.sort(entries, BREAKPOINT_COMPARATOR); - - final ArrayList breakpoints = new ArrayList<>(entries.size()); - for (Entry entry : entries) { - breakpoints.add(entry.getValue()); - } - return breakpoints; - } - - /** - * Creates a new tag breakpoint if one doesn't already exist. If one does exist, then resets the - * ignore count. - * - * @param tag where to set the breakpoint - * @param ignoreCount number of initial hits before the breakpoint starts causing breaks. - * @param oneShot whether the breakpoint should dispose itself after one hit - * @return a possibly new breakpoint - * @throws DebugException if a breakpoint already exists for the tag and the ignore count is the - * same - */ - TagBreakpoint create(int groupId, int ignoreCount, SyntaxTag tag, boolean oneShot) throws DebugException { - - TagBreakpointImpl breakpoint = tagToBreakpoint.get(tag); - - if (breakpoint == null) { - breakpoint = new TagBreakpointImpl(groupId, ignoreCount, tag, oneShot); - - if (TRACE) { - trace("NEW " + breakpoint.getShortDescription()); - } - - tagToBreakpoint.put(tag, breakpoint); - - for (Probe probe : Probe.findProbesTaggedAs(tag)) { - breakpoint.attach(probe); - } - } else { - if (ignoreCount == breakpoint.getIgnoreCount()) { - throw new DebugException(BREAKPOINT_NAME + " already set for tag " + tag.name()); - } - breakpoint.setIgnoreCount(ignoreCount); - if (TRACE) { - trace("CHANGED ignoreCount %s", breakpoint.getShortDescription()); - } - } - return breakpoint; - } - - /** - * Returns the {@link TagBreakpoint} for a given tag, {@code null} if none. - */ - TagBreakpoint get(SyntaxTag tag) { - return tagToBreakpoint.get(tag); - } - - /** - * Removes the associated instrumentation for all one-shot breakpoints only. - */ - void disposeOneShots() { - List breakpoints = new ArrayList<>(tagToBreakpoint.values()); - for (TagBreakpointImpl breakpoint : breakpoints) { - if (breakpoint.isOneShot()) { - breakpoint.dispose(); - } - } - } - - /** - * Removes all knowledge of a breakpoint, presumed disposed. - */ - private void forget(TagBreakpointImpl breakpoint) { - tagToBreakpoint.remove(breakpoint.getTag()); - } - - /** - * Concrete representation of a line breakpoint, implemented by attaching an instrument to a - * probe at the designated source location. - */ - private final class TagBreakpointImpl extends TagBreakpoint implements AdvancedInstrumentResultListener { - - private static final String SHOULD_NOT_HAPPEN = "TagBreakpointImpl: should not happen"; - - private final SyntaxTag tag; - - // Cached assumption that the global status of tag breakpoint activity has not changed. - private Assumption breakpointsActiveAssumption; - - // Whether this breakpoint is enable/disabled - @CompilationFinal private boolean isEnabled; - private Assumption enabledUnchangedAssumption; - - private String conditionExpr; - - /** - * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: - * {@code null} if not attached. - */ - private List instruments = new ArrayList<>(); - - private TagBreakpointImpl(int groupId, int ignoreCount, SyntaxTag tag, boolean oneShot) { - super(ENABLED, groupId, ignoreCount, oneShot); - this.tag = tag; - this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); - this.isEnabled = true; - this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption(BREAKPOINT_NAME + " enabled state unchanged"); - } - - @Override - public boolean isEnabled() { - return isEnabled; - } - - @Override - public void setEnabled(boolean enabled) { - // Tag Breakpoints are never unresolved - if (enabled != isEnabled) { - switch (getState()) { - case ENABLED: - assert !enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(false); - changeState(DISABLED); - break; - case DISABLED: - assert enabled : SHOULD_NOT_HAPPEN; - doSetEnabled(true); - changeState(ENABLED); - break; - case DISPOSED: - assert false : "breakpoint disposed"; - break; - case ENABLED_UNRESOLVED: - case DISABLED_UNRESOLVED: - default: - assert false : SHOULD_NOT_HAPPEN; - break; - } - } - } - - @Override - public void setCondition(String expr) throws DebugException { - if (this.conditionExpr != null || expr != null) { - // De-instrument the Probes instrumented by this breakpoint - final ArrayList probes = new ArrayList<>(); - for (Instrument instrument : instruments) { - probes.add(instrument.getProbe()); - instrument.dispose(); - } - instruments.clear(); - this.conditionExpr = expr; - // Re-instrument the probes previously instrumented - for (Probe probe : probes) { - attach(probe); - } - } - } - - @Override - public String getCondition() { - return conditionExpr; - } - - @Override - public void dispose() { - if (getState() != DISPOSED) { - for (Instrument instrument : instruments) { - instrument.dispose(); - } - changeState(DISPOSED); - TagBreakpointFactory.this.forget(this); - } - } - - private void attach(Probe newProbe) throws DebugException { - if (getState() == DISPOSED) { - throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); - } - Instrument newInstrument = null; - if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); - } else { - newInstrument = Instrument.create(this, executionSupport.createAdvancedInstrumentRootFactory(conditionExpr, this), Boolean.class, BREAKPOINT_NAME); - } - newProbe.attach(newInstrument); - instruments.add(newInstrument); - changeState(isEnabled ? ENABLED : DISABLED); - } - - private void doSetEnabled(boolean enabled) { - if (this.isEnabled != enabled) { - enabledUnchangedAssumption.invalidate(); - this.isEnabled = enabled; - } - } - - private String getShortDescription() { - return BREAKPOINT_NAME + "@" + tag.name(); - } - - private void changeState(BreakpointState after) { - if (TRACE) { - trace("STATE %s-->%s %s", getState().getName(), after.getName(), getShortDescription()); - } - setState(after); - } - - private void doBreak(Node node, VirtualFrame vFrame) { - if (incrHitCountCheckIgnore()) { - breakpointCallback.haltedAt(node, vFrame.materialize(), BREAKPOINT_NAME); - } - } - - /** - * Receives notification from the attached instrument that execution is about to enter node - * where the breakpoint is set. Designed so that when in the fast path, there is either an - * unconditional "halt" call to the debugger or nothing. - */ - private void nodeEnter(Node astNode, VirtualFrame vFrame) { - - // Deopt if the global active/inactive flag has changed - try { - this.breakpointsActiveAssumption.check(); - } catch (InvalidAssumptionException ex) { - this.breakpointsActiveAssumption = TagBreakpointFactory.this.breakpointsActiveUnchanged.getAssumption(); - } - - // Deopt if the enabled/disabled state of this breakpoint has changed - try { - this.enabledUnchangedAssumption.check(); - } catch (InvalidAssumptionException ex) { - this.enabledUnchangedAssumption = Truffle.getRuntime().createAssumption("LineBreakpoint enabled state unchanged"); - } - - if (TagBreakpointFactory.this.breakpointsActive && this.isEnabled) { - if (isOneShot()) { - dispose(); - } - TagBreakpointImpl.this.doBreak(astNode, vFrame); - } - - } - - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { - final boolean condition = (Boolean) result; - if (TRACE) { - trace("breakpoint condition = %b %s", condition, getShortDescription()); - } - if (condition) { - nodeEnter(node, vFrame); - } - } - - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { - warningLog.addWarning(String.format("Exception in %s: %s", getShortDescription(), ex.getMessage())); - if (TRACE) { - trace("breakpoint failure = %s %s", ex.toString(), getShortDescription()); - } - // Take the breakpoint if evaluation fails. - nodeEnter(node, vFrame); - } - - @Override - public String getLocationDescription() { - return "Tag " + tag.name(); - } - - @Override - public SyntaxTag getTag() { - return tag; - } - - private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { - - @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - TagBreakpointImpl.this.nodeEnter(node, vFrame); - } - } - - } - -} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/package-info.java --- a/truffle/com.oracle.truffle.tools.debug.engine/src/com/oracle/truffle/tools/debug/engine/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ - -/** - * This package contains the shared (language-agnostic) support for implementing debuggers - * that work with Truffle-implemented languages. - *

- * This implementation is made possible by the general purpose Instrumentation Framework built - * into the Truffle platform. Some online documentation for the Instrumentation Framework is available - * online: - * - * https://wiki.openjdk.java.net/display/Graal/Instrumentation+API - * - *

- * Debugging services for a Truffle-implemented language are provided by creating an instance - * of {@link com.oracle.truffle.tools.debug.engine.DebugEngine} specialized for a specific language. The DebugEngine can: - *

    - *
  • Load and run sources in the language
  • - *
  • Set breakpoints possibly with conditions and other attributes, on source lines
  • - *
  • Navigate by Continue, StepIn, StepOver, or StepOut
  • - *
  • Examine the execution stack
  • - *
  • Examine the contents of a stack frame
  • - *
  • Evaluate a code fragment in the context of a stack frame
  • - *
- *

- * Specialization of the DebugEngine for a Truffle-implemented language takes several forms: - *

    - *
  1. A specification from the language implementor that adds Instrumentation "tags" to the nodes - * that a debugger should know about, for example Statements, Calls, and Throws
  2. - *
  3. Methods to run programs/scripts generally, and more specifically run text fragments in the context of - * a particular frame/Node in a halted Truffle execution
  4. - *
  5. Utility methods, such as providing textual displays of Objects that represent values in the language
  6. - *
- *

- * Note: Both the functionality and API for this package are under active development. - *

- * @see com.oracle.truffle.api.instrument - */ -package com.oracle.truffle.tools.debug.engine; - diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLMessage.java Sat Jul 18 18:03:36 2015 +0200 @@ -46,7 +46,6 @@ public static final String BREAK_AT_THROW = "break-at-throw"; public static final String BREAK_AT_THROW_ONCE = "break-at-throw-once"; public static final String BREAKPOINT_CONDITION = "breakpoint-condition"; - public static final String BREAKPOINT_GROUP_ID = "breakpoint-group-id"; public static final String BREAKPOINT_HIT_COUNT = "breakpoint-hit-count"; public static final String BREAKPOINT_ID = "breakpoint-id"; public static final String BREAKPOINT_IGNORE_COUNT = "breakpoint-ignore-count"; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/REPLServer.java Sat Jul 18 18:03:36 2015 +0200 @@ -24,22 +24,46 @@ */ package com.oracle.truffle.tools.debug.shell; +import com.oracle.truffle.api.debug.Breakpoint; +import java.util.*; + /** * The server side of a simple message-based protocol for a possibly remote language * Read-Eval-Print-Loop. */ -public interface REPLServer { +public abstract class REPLServer { /** * Starts up a server; status returned in a message. */ - REPLMessage start(); + public abstract REPLMessage start(); /** * Ask the server to handle a request. Return a non-empty array of messages to simulate remote * operation where the protocol has possibly multiple messages being returned asynchronously in * response to each request. */ - REPLMessage[] receive(REPLMessage request); + public abstract REPLMessage[] receive(REPLMessage request); + + private int breakpointCounter; + private Map breakpoints = new WeakHashMap<>(); + + protected final synchronized void registerBreakpoint(Breakpoint breakpoint) { + breakpoints.put(breakpoint, breakpointCounter++); + } + + protected final synchronized Breakpoint findBreakpoint(int id) { + for (Map.Entry entrySet : breakpoints.entrySet()) { + if (id == entrySet.getValue()) { + return entrySet.getKey(); + } + } + return null; + } + + protected final synchronized int getBreakpointID(Breakpoint breakpoint) { + final Integer id = breakpoints.get(breakpoint); + return id == null ? -1 : id; + } } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/client/SimpleREPLClient.java Sat Jul 18 18:03:36 2015 +0200 @@ -896,7 +896,6 @@ sb.append("@" + message.get(REPLMessage.INFO_VALUE)); sb.append(" (state=" + message.get(REPLMessage.BREAKPOINT_STATE)); if (verboseBreakpointInfoOption.getBool()) { - sb.append(", group=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_GROUP_ID))); sb.append(", hits=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_HIT_COUNT))); sb.append(", ignore=" + Integer.parseInt(message.get(REPLMessage.BREAKPOINT_IGNORE_COUNT))); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/package-info.java Sat Jul 18 18:03:36 2015 +0200 @@ -1,58 +1,56 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * Copyright (c) 2015, 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 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 + * 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. + * 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. + * 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. */ /** - * This package contains an experimental framework for building simple command-line oriented debuggers - * that work with Truffle-implemented languages; it is used mainly for testing Truffle's built-in - * {@link com.oracle.truffle.tools.debug.engine.DebugEngine}, which actually provides the debugging services. + * This package contains an experimental framework for building simple command-line oriented + * debuggers that work with Truffle-implemented languages; it is used mainly for testing Truffle's + * built-in , which actually provides the + * debugging services. *

- * Truffle debugging is made possible by the general purpose Instrumentation Framework built - * into the Truffle platform. Some online documentation for the Instrumentation Framework is available + * Truffle debugging is made possible by the general purpose Instrumentation Framework built into + * the Truffle platform. Some online documentation for the Instrumentation Framework is available * online: - * - * https://wiki.openjdk.java.net/display/Graal/Instrumentation+API - * + * https://wiki. + * openjdk.java.net/display/Graal/Instrumentation+API *

* Building one of these command line debuggers requires creating language-specific instances of: *

    - *
  1. {@link com.oracle.truffle.tools.debug.engine.DebugEngine}, - * noting that this instance also depends on related services provided by the language implementation,
  2. - *
  3. {@link com.oracle.truffle.tools.debug.shell.REPLServer}, best accomplished by copying the implementation for - * Truffle's demonstration language "Simple" (a.k.a. "SL").
  4. + *
  5. DebugEngine, noting that this instance also + * depends on related services provided by the language implementation,
  6. + *
  7. {@link com.oracle.truffle.tools.debug.shell.REPLServer}, best accomplished by copying the + * implementation for Truffle's demonstration language "Simple" (a.k.a. "SL").
  8. *
* - * Disclaimer: although these command line debuggers are useful, they are - * not intended, and will not be maintained as, fully functioning debuggers. They should be - * considered valuable tools for the maintainers of the {@link com.oracle.truffle.tools.debug.engine.DebugEngine}, - * as well as for Truffle language implementors for whom concurrent access to any kind debugging services can - * be quite helpful. + * Disclaimer: although these command line debuggers are useful, they are not + * intended, and will not be maintained as, fully functioning debuggers. They should be considered + * valuable tools for the maintainers of the + * DebugEngine, as well as for Truffle language + * implementors for whom concurrent access to any kind debugging services can be quite helpful. *

- * Note: Both the functionality and API for this package are under active development. + * Note: Both the functionality and API for this package are under active + * development. *

+ * * @see com.oracle.truffle.api.instrument - * @see com.oracle.truffle.tools.debug.engine */ package com.oracle.truffle.tools.debug.shell; diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/FrameDebugDescription.java Sat Jul 18 18:03:36 2015 +0200 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, 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.tools.debug.shell.server; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +public final class FrameDebugDescription { + private final int index; + private final Node node; + private final FrameInstance frameInstance; + + FrameDebugDescription(int index, Node node, FrameInstance frameInstance) { + this.index = index; + this.node = node; + this.frameInstance = frameInstance; + } + + /** + * Position in the current stack: {@code 0} at the top. + */ + public int index() { + return index; + } + + /** + * AST location. + */ + public Node node() { + return node; + } + + /** + * Access to the Truffle {@link Frame}. + */ + public FrameInstance frameInstance() { + return frameInstance; + } +} diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLHandler.java Sat Jul 18 18:03:36 2015 +0200 @@ -24,14 +24,14 @@ */ package com.oracle.truffle.tools.debug.shell.server; +import com.oracle.truffle.api.debug.Breakpoint; +import java.io.*; import java.util.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.vm.TruffleVM.Language; -import com.oracle.truffle.tools.debug.engine.*; import com.oracle.truffle.tools.debug.shell.*; /** @@ -44,9 +44,6 @@ // TODO (mlvdv) add support for setting/using ignore count private static final int DEFAULT_IGNORE_COUNT = 0; - // TODO (mlvdv) add support for setting/using groupId - private static final int DEFAULT_GROUP_ID = 0; - private final String op; protected REPLHandler(String op) { @@ -99,11 +96,9 @@ return replies; } - protected static REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint) { + static final REPLMessage createBreakpointInfoMessage(Breakpoint breakpoint, REPLServerContext serverContext) { final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.BREAKPOINT_INFO); - infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpoint.getId())); - infoMessage.put(REPLMessage.BREAKPOINT_GROUP_ID, Integer.toString(breakpoint.getGroupId())); - infoMessage.put(REPLMessage.BREAKPOINT_STATE, breakpoint.getState().toString()); + infoMessage.put(REPLMessage.BREAKPOINT_ID, Integer.toString(serverContext.getBreakpointID(breakpoint))); infoMessage.put(REPLMessage.BREAKPOINT_HIT_COUNT, Integer.toString(breakpoint.getHitCount())); infoMessage.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, Integer.toString(breakpoint.getIgnoreCount())); infoMessage.put(REPLMessage.INFO_VALUE, breakpoint.getLocationDescription().toString()); @@ -115,7 +110,7 @@ } protected static REPLMessage createFrameInfoMessage(final REPLServerContext serverContext, FrameDebugDescription frame) { - final Visualizer visualizer = serverContext.getLanguage().getDebugSupport().getVisualizer(); + final Visualizer visualizer = serverContext.getVisualizer(); final REPLMessage infoMessage = new REPLMessage(REPLMessage.OP, REPLMessage.FRAME_INFO); infoMessage.put(REPLMessage.FRAME_NUMBER, Integer.toString(frame.index())); final Node node = frame.node(); @@ -127,11 +122,11 @@ SourceSection section = node.getSourceSection(); if (section == null) { section = node.getEncapsulatingSourceSection(); - if (section != null) { - infoMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath()); - infoMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine())); - infoMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine())); - } + } + if (section != null) { + infoMessage.put(REPLMessage.FILE_PATH, section.getSource().getPath()); + infoMessage.put(REPLMessage.LINE_NUMBER, Integer.toString(section.getStartLine())); + infoMessage.put(REPLMessage.SOURCE_LINE_TEXT, section.getSource().getCode(section.getStartLine())); } } infoMessage.put(REPLMessage.STATUS, REPLMessage.SUCCEEDED); @@ -144,7 +139,7 @@ public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); final ArrayList frameMessages = new ArrayList<>(); - for (FrameDebugDescription frame : serverContext.getDebugEngine().getStack()) { + for (FrameDebugDescription frame : serverContext.getStack()) { frameMessages.add(createFrameInfoMessage(serverContext, frame)); } if (frameMessages.size() > 0) { @@ -179,15 +174,16 @@ if (ignoreCount == null) { ignoreCount = 0; } - LineBreakpoint breakpoint; + Breakpoint breakpoint; try { - breakpoint = serverContext.getDebugEngine().setLineBreakpoint(DEFAULT_GROUP_ID, DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); + breakpoint = serverContext.db().setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), false); + serverContext.registerBreakpoint(breakpoint); } catch (Exception ex) { return finishReplyFailed(reply, ex.getMessage()); } reply.put(REPLMessage.SOURCE_NAME, fileName); reply.put(REPLMessage.FILE_PATH, source.getPath()); - reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpoint.getId())); + reply.put(REPLMessage.BREAKPOINT_ID, Integer.toString(serverContext.getBreakpointID(breakpoint))); reply.put(REPLMessage.LINE_NUMBER, Integer.toString(lineNumber)); reply.put(REPLMessage.BREAKPOINT_IGNORE_COUNT, ignoreCount.toString()); return finishReplySucceeded(reply, "Breakpoint set"); @@ -216,7 +212,8 @@ return finishReplyFailed(reply, "missing line number"); } try { - serverContext.getDebugEngine().setLineBreakpoint(DEFAULT_GROUP_ID, DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); + Breakpoint b = serverContext.db().setLineBreakpoint(DEFAULT_IGNORE_COUNT, source.createLineLocation(lineNumber), true); + serverContext.registerBreakpoint(b); } catch (Exception ex) { return finishReplyFailed(reply, ex.getMessage()); } @@ -233,7 +230,8 @@ public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); try { - serverContext.getDebugEngine().setTagBreakpoint(DEFAULT_GROUP_ID, DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); + Breakpoint b = serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, false); + serverContext.registerBreakpoint(b); return finishReplySucceeded(reply, "Breakpoint at any throw set"); } catch (Exception ex) { return finishReplyFailed(reply, ex.getMessage()); @@ -247,7 +245,7 @@ public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); try { - serverContext.getDebugEngine().setTagBreakpoint(DEFAULT_GROUP_ID, DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); + serverContext.db().setTagBreakpoint(DEFAULT_IGNORE_COUNT, StandardSyntaxTag.THROW, true); return finishReplySucceeded(reply, "One-shot breakpoint at any throw set"); } catch (Exception ex) { return finishReplyFailed(reply, ex.getMessage()); @@ -261,8 +259,8 @@ public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); final ArrayList infoMessages = new ArrayList<>(); - for (Breakpoint breakpoint : serverContext.getDebugEngine().getBreakpoints()) { - infoMessages.add(createBreakpointInfoMessage(breakpoint)); + for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { + infoMessages.add(createBreakpointInfoMessage(breakpoint, serverContext)); } if (infoMessages.size() > 0) { return infoMessages.toArray(new REPLMessage[0]); @@ -280,7 +278,7 @@ if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.getDebugEngine().findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -295,7 +293,7 @@ @Override public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); - serverContext.getDebugEngine().prepareContinue(); + serverContext.prepareContinue(); return finishReplySucceeded(reply, "Continue mode entered"); } }; @@ -306,7 +304,7 @@ public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); int deleteCount = 0; - for (Breakpoint breakpoint : serverContext.getDebugEngine().getBreakpoints()) { + for (Breakpoint breakpoint : serverContext.db().getBreakpoints()) { breakpoint.dispose(); deleteCount++; } @@ -326,7 +324,7 @@ if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.getDebugEngine().findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -345,7 +343,7 @@ if (breakpointNumber == null) { return finishReplyFailed(reply, "missing breakpoint number"); } - final Breakpoint breakpoint = serverContext.getDebugEngine().findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(reply, "no breakpoint number " + breakpointNumber); } @@ -395,15 +393,14 @@ if (frameNumber == null) { return finishReplyFailed(reply, "no frame number specified"); } - final List stack = serverContext.getDebugEngine().getStack(); + final List stack = serverContext.getStack(); if (frameNumber < 0 || frameNumber >= stack.size()) { return finishReplyFailed(reply, "frame number " + frameNumber + " out of range"); } final FrameDebugDescription frameDescription = stack.get(frameNumber); final REPLMessage frameMessage = createFrameInfoMessage(serverContext, frameDescription); final Frame frame = frameDescription.frameInstance().getFrame(FrameInstance.FrameAccess.READ_ONLY, true); - final Language language = serverContext.getLanguage(); - final Visualizer visualizer = language.getDebugSupport().getVisualizer(); + final Visualizer visualizer = serverContext.getVisualizer(); final FrameDescriptor frameDescriptor = frame.getFrameDescriptor(); try { final StringBuilder sb = new StringBuilder(); @@ -452,7 +449,7 @@ return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.getDebugEngine().findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } @@ -462,7 +459,7 @@ } try { breakpoint.setCondition(expr); - } catch (DebugException ex) { + } catch (IOException ex) { return finishReplyFailed(message, "invalid condition for " + breakpointNumber); } catch (UnsupportedOperationException ex) { return finishReplyFailed(message, "conditions not unsupported by breakpoint " + breakpointNumber); @@ -481,7 +478,7 @@ if (repeat == null) { repeat = 1; } - serverContext.getDebugEngine().prepareStepInto(repeat); + serverContext.prepareStepInto(repeat); return finishReplySucceeded(reply, "StepInto <" + repeat + "> enabled"); } }; @@ -490,7 +487,7 @@ @Override public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { - serverContext.getDebugEngine().prepareStepOut(); + serverContext.prepareStepOut(); return finishReplySucceeded(createReply(), "StepOut enabled"); } }; @@ -504,7 +501,7 @@ if (repeat == null) { repeat = 1; } - debugServerContextFrame.getDebugEngine().prepareStepOver(repeat); + debugServerContextFrame.prepareStepOver(repeat); return finishReplySucceeded(reply, "StepOver <" + repeat + "> enabled"); } }; @@ -514,10 +511,10 @@ @Override public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getLanguage().getDebugSupport().getVisualizer().getASTPrinter(); + final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); final String topic = request.get(REPLMessage.TOPIC); reply.put(REPLMessage.TOPIC, topic); - Node node = serverContext.getNode(); + Node node = serverContext.getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); } @@ -531,7 +528,7 @@ while (node.getParent() != null) { node = node.getParent(); } - final String astText = astPrinter.printTreeToString(node, depth, serverContext.getNode()); + final String astText = astPrinter.printTreeToString(node, depth, serverContext.getNodeAtHalt()); return finishReplySucceeded(reply, astText); case REPLMessage.SUBTREE: case REPLMessage.SUB: @@ -557,13 +554,13 @@ return finishReplyFailed(message, "missing breakpoint number"); } message.put(REPLMessage.BREAKPOINT_ID, Integer.toString(breakpointNumber)); - final Breakpoint breakpoint = serverContext.getDebugEngine().findBreakpoint(breakpointNumber); + final Breakpoint breakpoint = serverContext.findBreakpoint(breakpointNumber); if (breakpoint == null) { return finishReplyFailed(message, "no breakpoint number " + breakpointNumber); } try { breakpoint.setCondition(null); - } catch (DebugException e) { + } catch (IOException e) { return finishReplyFailed(message, e.getMessage()); } return finishReplyFailed(message, "Breakpoint " + breakpointNumber + " condition cleared"); @@ -575,8 +572,8 @@ @Override public REPLMessage[] receive(REPLMessage request, REPLServerContext serverContext) { final REPLMessage reply = createReply(); - final ASTPrinter astPrinter = serverContext.getLanguage().getDebugSupport().getVisualizer().getASTPrinter(); - final Node node = serverContext.getNode(); + final ASTPrinter astPrinter = serverContext.getVisualizer().getASTPrinter(); + final Node node = serverContext.getNodeAtHalt(); if (node == null) { return finishReplyFailed(reply, "no current AST node"); } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java --- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Sat Jul 18 18:03:36 2015 +0200 @@ -24,22 +24,27 @@ */ package com.oracle.truffle.tools.debug.shell.server; +import com.oracle.truffle.api.debug.Breakpoint; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.impl.DefaultVisualizer; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.vm.*; import com.oracle.truffle.api.vm.TruffleVM.Language; -import com.oracle.truffle.tools.debug.engine.*; import com.oracle.truffle.tools.debug.shell.*; +import java.util.ArrayList; +import java.util.List; public abstract class REPLServerContext { private final int level; - private final Node astNode; - private final MaterializedFrame mFrame; + private final SuspendedEvent event; - protected REPLServerContext(int level, Node astNode, MaterializedFrame mFrame) { + protected REPLServerContext(int level, SuspendedEvent event) { this.level = level; - this.astNode = astNode; - this.mFrame = mFrame; + this.event = event; } /** @@ -52,24 +57,65 @@ /** * The AST node where execution is halted in this context. */ - public Node getNode() { - return astNode; + public Node getNodeAtHalt() { + return event.getNode(); } /** * The frame where execution is halted in this context. */ - public MaterializedFrame getFrame() { - return mFrame; + public MaterializedFrame getFrameAtHalt() { + return event.getFrame(); } public abstract Language getLanguage(); - public abstract DebugEngine getDebugEngine(); + public Visualizer getVisualizer() { + return new DefaultVisualizer(); + } + + public abstract TruffleVM vm(); + + protected abstract Debugger db(); /** * Dispatches a REPL request to the appropriate handler. */ public abstract REPLMessage[] receive(REPLMessage request); + public abstract void registerBreakpoint(Breakpoint breakpoint); + + public abstract Breakpoint findBreakpoint(int id); + + public abstract int getBreakpointID(Breakpoint breakpoint); + + List getStack() { + List frames = new ArrayList<>(); + int frameCount = 1; + for (FrameInstance frameInstance : event.getStack()) { + if (frameCount == 1) { + frames.add(new FrameDebugDescription(frameCount, event.getNode(), frameInstance)); + } else { + frames.add(new FrameDebugDescription(frameCount, frameInstance.getCallNode(), frameInstance)); + } + frameCount++; + } + return frames; + } + + void prepareStepOut() { + event.prepareStepOut(); + } + + void prepareStepInto(int repeat) { + event.prepareStepInto(repeat); + } + + void prepareStepOver(int repeat) { + event.prepareStepOver(repeat); + } + + void prepareContinue() { + event.prepareContinue(); + } } diff -r 324997830dc9 -r 5bc7f7b867ab truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Thu Jul 16 19:11:31 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Sat Jul 18 18:03:36 2015 +0200 @@ -162,7 +162,7 @@ * tests run in the same environment. */ public TestRootNode(TestLanguageNode body) { - super(null); + super(TruffleLanguage.class, null, null); this.body = body; }