# HG changeset patch # User Michael Van De Vanter # Date 1427853667 25200 # Node ID 2e3cc2a277110851a6079fa35b4c5910e9d2d06a # Parent e73096245a4caac948a891d54607723172974655 Truffle/Instrumentation: a new flavor of Instrument that lazily provides an AST fragment to be attached/adopted directly into a running AST, and to which execution event notifications will be routed. Important use cases so far include conditional breakpoints (with optimizeable conditions) and Ruby set_trace_func. diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Tue Mar 31 18:58:36 2015 -0700 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Tue Mar 31 19:01:07 2015 -0700 @@ -205,6 +205,47 @@ } @Test + public void constantValueInertToolNodeInstrumentListener() { + FrameDescriptor fd = new FrameDescriptor(); + AbstractTestNode result = new ConstantTestNode(42); + RootTestNode root = new RootTestNode(fd, "constantValue", result); + root.adoptChildren(); + Probe probe = result.probe(); + // A listener that could insert a "tool node" into the AST, but which never does. + Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertPartialEvalEquals("constant42", root); + } + + @Test + public void constantValueInertToolNode() { + FrameDescriptor fd = new FrameDescriptor(); + AbstractTestNode result = new ConstantTestNode(42); + RootTestNode root = new RootTestNode(fd, "constantValue", result); + root.adoptChildren(); + Probe probe = result.probe(); + // A listener that inserts a "tool node" with empty methods into the AST. + Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return new ToolNode() { + }; + } + + }, null); + probe.attach(instrument); + + assertPartialEvalEquals("constant42", root); + } + + @Test public void instrumentDeopt() { final FrameDescriptor fd = new FrameDescriptor(); final AbstractTestNode result = new ConstantTestNode(42); diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Tue Mar 31 18:58:36 2015 -0700 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Tue Mar 31 19:01:07 2015 -0700 @@ -35,6 +35,11 @@ import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; /** *

AST Instrumentation

@@ -339,142 +344,6 @@ } - private abstract class TestLanguageNode extends Node { - public abstract Object execute(VirtualFrame vFrame); - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new TestLanguageWrapperNode(this); - } - } - - @NodeInfo(cost = NodeCost.NONE) - private class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { - @Child private TestLanguageNode child; - @Child private ProbeNode probeNode; - - public TestLanguageWrapperNode(TestLanguageNode child) { - assert !(child instanceof TestLanguageWrapperNode); - this.child = child; - } - - @Override - public String instrumentationInfo() { - return "Wrapper node for testing"; - } - - @Override - public boolean isInstrumentable() { - return false; - } - - @Override - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; - } - - @Override - public Probe getProbe() { - return probeNode.getProbe(); - } - - @Override - public Node getChild() { - return child; - } - - @Override - public Object execute(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); - Object result; - - try { - result = child.execute(vFrame); - probeNode.returnValue(child, vFrame, result); - } catch (KillException e) { - throw (e); - } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); - throw (e); - } - - return result; - } - } - - /** - * A simple node for our test language to store a value. - */ - private class TestValueNode extends TestLanguageNode { - private final int value; - - public TestValueNode(int value) { - this.value = value; - } - - @Override - public Object execute(VirtualFrame vFrame) { - return new Integer(this.value); - } - } - - /** - * A node for our test language that adds up two {@link TestValueNode}s. - */ - private class TestAdditionNode extends TestLanguageNode { - @Child private TestLanguageNode leftChild; - @Child private TestLanguageNode rightChild; - - public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) { - this.leftChild = insert(leftChild); - this.rightChild = insert(rightChild); - } - - @Override - public Object execute(VirtualFrame vFrame) { - return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue()); - } - } - - /** - * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST - * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle - * completes an AST. The root nodes serves as our entry point into a program. - */ - private class TestRootNode extends RootNode { - @Child private TestLanguageNode body; - - /** - * This constructor emulates the global machinery that applies registered probers to every - * newly created AST. Global registry is not used, since that would interfere with other - * tests run in the same environment. - */ - public TestRootNode(TestLanguageNode body) { - super(null); - this.body = body; - } - - @Override - public Object execute(VirtualFrame vFrame) { - return body.execute(vFrame); - } - - @Override - public boolean isCloningAllowed() { - return true; - } - - @Override - public void applyInstrumentation() { - Probe.applyASTProbers(body); - } - } - private interface TestCounter { int enterCount(); diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Tue Mar 31 19:01:07 2015 -0700 @@ -0,0 +1,172 @@ +/* + * 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. + * + * 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.instrument; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.*; + +/** + * Tests instrumentation where a client can attach a node that gets attached into the AST. + */ +class InstrumentationTestNodes { + + abstract static class TestLanguageNode extends Node { + public abstract Object execute(VirtualFrame vFrame); + + @Override + public boolean isInstrumentable() { + return true; + } + + @Override + public WrapperNode createWrapperNode() { + return new TestLanguageWrapperNode(this); + } + } + + @NodeInfo(cost = NodeCost.NONE) + static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { + @Child private TestLanguageNode child; + @Child private ProbeNode probeNode; + + public TestLanguageWrapperNode(TestLanguageNode child) { + assert !(child instanceof TestLanguageWrapperNode); + this.child = child; + } + + @Override + public String instrumentationInfo() { + return "Wrapper node for testing"; + } + + @Override + public boolean isInstrumentable() { + return false; + } + + @Override + public void insertProbe(ProbeNode newProbeNode) { + this.probeNode = newProbeNode; + } + + @Override + public Probe getProbe() { + return probeNode.getProbe(); + } + + @Override + public Node getChild() { + return child; + } + + @Override + public Object execute(VirtualFrame vFrame) { + probeNode.enter(child, vFrame); + Object result; + + try { + result = child.execute(vFrame); + probeNode.returnValue(child, vFrame, result); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + probeNode.returnExceptional(child, vFrame, e); + throw (e); + } + + return result; + } + } + + /** + * A simple node for our test language to store a value. + */ + static class TestValueNode extends TestLanguageNode { + private final int value; + + public TestValueNode(int value) { + this.value = value; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(this.value); + } + } + + /** + * A node for our test language that adds up two {@link TestValueNode}s. + */ + static class TestAdditionNode extends TestLanguageNode { + @Child private TestLanguageNode leftChild; + @Child private TestLanguageNode rightChild; + + public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) { + this.leftChild = insert(leftChild); + this.rightChild = insert(rightChild); + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue()); + } + } + + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + static class TestRootNode extends RootNode { + @Child private TestLanguageNode body; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public TestRootNode(TestLanguageNode body) { + super(null); + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + Probe.applyASTProbers(body); + } + } + +} diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java Tue Mar 31 19:01:07 2015 -0700 @@ -0,0 +1,96 @@ +/* + * 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.instrument; + +import static org.junit.Assert.*; + +import org.junit.*; + +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.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; + +/** + * Tests instrumentation where a client can attach a node that gets attached into the AST. + */ +public class ToolNodeInstrumentationTest { + + @Test + public void testToolNodeListener() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Ensure it executes correctly + assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + assertEquals(13, callTarget1.call()); + + // Attach a listener that never actually attaches a node. + final Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertEquals(13, callTarget1.call()); + + final int[] count = new int[1]; + + // Attach a listener that never actually attaches a node. + probe.attach(Instrument.create(new ToolNodeInstrumentListener() { + + public ToolNode getToolNode(Probe p) { + return new ToolNode() { + + @Override + public void enter(Node node, VirtualFrame vFrame) { + count[0] = count[0] + 1; + } + }; + } + + }, null)); + assertEquals(0, count[0]); + + assertEquals(13, callTarget1.call()); + + assertEquals(1, count[0]); + + } + +} diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java Tue Mar 31 18:58:36 2015 -0700 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/tools/TestNodes.java Tue Mar 31 19:01:07 2015 -0700 @@ -32,7 +32,7 @@ /** * Nodes and an {@linkplain CallTarget executable ASTs} for testing. */ -public class TestNodes { +class TestNodes { /** * A fake source used for testing: empty line 1, expression on line 2. @@ -44,7 +44,7 @@ /** * An executable addition expression that evaluates to 13. */ - public static CallTarget createExpr13TestCallTarget() { + static CallTarget createExpr13TestCallTarget() { final RootNode rootNode = createExpr13TestRootNode(); return Truffle.getRuntime().createCallTarget(rootNode); } @@ -52,7 +52,7 @@ /** * Root holding an addition expression that evaluates to 13. */ - public static RootNode createExpr13TestRootNode() { + static RootNode createExpr13TestRootNode() { final TestLanguageNode ast = createExpr13AST(); final TestRootNode rootNode = new TestRootNode(ast); rootNode.adoptChildren(); @@ -62,7 +62,7 @@ /** * Addition expression that evaluates to 13, with faked source attribution. */ - public static TestLanguageNode createExpr13AST() { + static TestLanguageNode createExpr13AST() { final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1); final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection); final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1); @@ -71,7 +71,7 @@ return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection); } - public abstract static class TestLanguageNode extends Node { + abstract static class TestLanguageNode extends Node { public abstract Object execute(VirtualFrame frame); public TestLanguageNode() { @@ -93,7 +93,7 @@ } @NodeInfo(cost = NodeCost.NONE) - public static class TestWrapperNode extends TestLanguageNode implements WrapperNode { + static class TestWrapperNode extends TestLanguageNode implements WrapperNode { @Child private TestLanguageNode child; @Child private ProbeNode probeNode; @@ -151,7 +151,7 @@ * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle * completes an AST. The root nodes serves as our entry point into a program. */ - public static class TestRootNode extends RootNode { + static class TestRootNode extends RootNode { @Child private TestLanguageNode body; /** @@ -180,7 +180,7 @@ } } - public static class TestValueNode extends TestLanguageNode { + static class TestValueNode extends TestLanguageNode { private final int value; public TestValueNode(int value) { @@ -198,7 +198,7 @@ } } - public static class TestAddNode extends TestLanguageNode { + static class TestAddNode extends TestLanguageNode { @Child private TestLanguageNode leftChild; @Child private TestLanguageNode rightChild; diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Mar 31 18:58:36 2015 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Mar 31 19:01:07 2015 -0700 @@ -194,6 +194,19 @@ return new ASTInstrument(astListener, instrumentInfo); } + /** + * Creates an instrument that, when executed the first time in any particular AST location, + * invites the tool to provide an AST fragment for attachment/adoption into the running AST. + * + * @param toolNodeListener a listener for the tool that can request an AST fragment + * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful + * for debugging. + * @return a new instrument, ready for attachment at a probe. + */ + public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + return new ToolNodeInstrument(toolNodeListener, instrumentInfo); + } + // TODO (mlvdv) experimental /** * For implementation testing. @@ -278,7 +291,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(BasicInstrument.this); @@ -298,29 +311,29 @@ public void enter(Node node, VirtualFrame vFrame) { BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe); - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe); - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result); - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { BasicInstrument.this.instrumentListener.returnExceptional(BasicInstrument.this.probe, exception); - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -362,7 +375,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(ASTInstrument.this); @@ -382,29 +395,29 @@ public void enter(Node node, VirtualFrame vFrame) { ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame); - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { ASTInstrument.this.astListener.returnVoid(ASTInstrument.this.probe, node, vFrame); - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { ASTInstrument.this.astListener.returnValue(ASTInstrument.this.probe, node, vFrame, result); - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { ASTInstrument.this.astListener.returnExceptional(ASTInstrument.this.probe, node, vFrame, exception); - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -416,6 +429,104 @@ } + /** + * An instrument that propagates events to an instance of {@link ASTInstrumentListener}. + */ + private static final class ToolNodeInstrument extends Instrument { + + /** + * Tool-supplied listener for AST events. + */ + private final ToolNodeInstrumentListener toolNodeListener; + + private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + super(instrumentInfo); + this.toolNodeListener = toolNodeListener; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new ToolInstrumentNode(nextNode); + } + + @Override + AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) { + boolean found = false; + if (instrumentNode != null) { + if (instrumentNode.getInstrument() == this) { + // Found the match at the head of the chain + return instrumentNode.nextInstrumentNode; + } + // Match not at the head of the chain; remove it. + found = instrumentNode.removeFromChain(ToolNodeInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + @NodeInfo(cost = NodeCost.NONE) + private final class ToolInstrumentNode extends AbstractInstrumentNode { + + @Child ToolNode toolNode; + + private ToolInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + } + + public void enter(Node node, VirtualFrame vFrame) { + if (toolNode == null) { + final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe); + if (newToolNode != null) { + toolNode = newToolNode; + adoptChildren(); + ToolNodeInstrument.this.probe.invalidateProbeUnchanged(); + } + } + if (toolNode != null) { + toolNode.enter(node, vFrame); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + public void returnVoid(Node node, VirtualFrame vFrame) { + if (toolNode != null) { + toolNode.returnVoid(node, vFrame); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + if (toolNode != null) { + toolNode.returnValue(node, vFrame, result); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + if (toolNode != null) { + toolNode.returnExceptional(node, vFrame, exception); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : toolNodeListener.getClass().getSimpleName(); + } + } + + } + public interface TruffleOptListener { void notifyIsCompiled(boolean isCompiled); } @@ -440,7 +551,7 @@ if (instrumentNode != null) { if (instrumentNode.getInstrument() == this) { // Found the match at the head of the chain - return instrumentNode.nextInstrument; + return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. found = instrumentNode.removeFromChain(TruffleOptInstrument.this); @@ -466,26 +577,26 @@ this.isCompiled = CompilerDirectives.inCompiledCode(); TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled); } - if (nextInstrument != null) { - nextInstrument.enter(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); } } public void returnVoid(Node node, VirtualFrame vFrame) { - if (nextInstrument != null) { - nextInstrument.returnVoid(node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); } } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (nextInstrument != null) { - nextInstrument.returnValue(node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); } } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (nextInstrument != null) { - nextInstrument.returnExceptional(node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); } } @@ -500,10 +611,10 @@ @NodeInfo(cost = NodeCost.NONE) abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode { - @Child protected AbstractInstrumentNode nextInstrument; + @Child protected AbstractInstrumentNode nextInstrumentNode; protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) { - this.nextInstrument = nextNode; + this.nextInstrumentNode = nextNode; } @Override @@ -527,21 +638,21 @@ */ private boolean removeFromChain(Instrument instrument) { assert getInstrument() != instrument; - if (nextInstrument == null) { + if (nextInstrumentNode == null) { return false; } - if (nextInstrument.getInstrument() == instrument) { + if (nextInstrumentNode.getInstrument() == instrument) { // Next is the one to remove - if (nextInstrument.nextInstrument == null) { + if (nextInstrumentNode.nextInstrumentNode == null) { // Next is at the tail; just forget - nextInstrument = null; + nextInstrumentNode = null; } else { // Replace next with its successor - nextInstrument.replace(nextInstrument.nextInstrument); + nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode); } return true; } - return nextInstrument.removeFromChain(instrument); + return nextInstrumentNode.removeFromChain(instrument); } protected String getInstrumentInfo() { diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNode.java Tue Mar 31 19:01:07 2015 -0700 @@ -0,0 +1,55 @@ +/* + * 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.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Root of a tool-provided AST fragment that can be attached directly into an executing AST via + * {@link Instrument#create(ToolNodeInstrumentListener, String)}. + *

+ * Note: Instances of this class will in some situations be cloned by the + * instrumentation platform for attachment at equivalent locations in cloned parent ASTs. + */ +public abstract class ToolNode extends Node implements InstrumentationNode.TruffleEvents, InstrumentationNode { + + public void enter(Node node, VirtualFrame vFrame) { + } + + public void returnVoid(Node node, VirtualFrame vFrame) { + } + + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + } + + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + } + + public String instrumentationInfo() { + return null; + } + +} diff -r e73096245a4c -r 2e3cc2a27711 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolNodeInstrumentListener.java Tue Mar 31 19:01:07 2015 -0700 @@ -0,0 +1,58 @@ +/* + * 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.instrument; + +/** + * Instrument listener for a tool that works by providing an AST to be attached/adopted directly + * into the AST. + */ +public interface ToolNodeInstrumentListener { + + /** + * Receive notification that a probed AST node to which the {@link Instrument} is attached is + * about to be executed for the first time. This is a lazy opportunity for the tool to + * optionally add the root of a newly created AST fragment that will be attached/adopted + * directly into the executing AST. The new AST fragment will immediately begin receiving + * {@link InstrumentationNode.TruffleEvents}, beginning with the current execution event. + *

+ * AST fragments must be written to Truffle conventions. Some of these conventions are + * especially important if the fragment is to be fully optimized along with it's new parent AST. + *

+ * If this method returns {@code null} then it will be called again the next time the probed + * node is about to be executed. + *

+ * In some situations, this method will be called more than once for a particular Probe, and a + * new instance must be supplied each time. Each instance will be attached at the equivalent + * location in clones of the AST, and so should be behave as if equivalent for most purposes. + *

+ * In some situations the AST fragment supplied by this method maybe cloned for attachment to + * equivalent locations in cloned AST, so care should be taken about any state local to each + * instance of the AST fragment. + * + * @see Instrument + */ + ToolNode getToolNode(Probe probe); + +}