Mercurial > hg > truffle
changeset 22266:0d36601f233e
Merge revised Instrumentation framework into the Polyglot API
- Required language implementation support now in TruffleLanguage
- Instrumentation services provided by the new Instrumenter class
- Reduced public API exposure; communication with other components via Accessor
- Several methods removed from the Node class
- Many test rewritten or using a new "test mode" because of limited access to Engine services
Merge with c66f520ad8562b906a878e9b3293aaf54270db90
line wrap: on
line diff
--- a/mx.truffle/suite.py Tue Sep 29 18:04:11 2015 +0200 +++ b/mx.truffle/suite.py Wed Sep 30 16:33:56 2015 -0700 @@ -84,6 +84,7 @@ "sourceDirs" : ["src"], "dependencies" : [ "com.oracle.truffle.dsl.processor", + "com.oracle.truffle.api.vm", "mx:JUNIT", ], "checkstyle" : "com.oracle.truffle.dsl.processor", @@ -127,6 +128,7 @@ "subDir" : "truffle", "sourceDirs" : ["src"], "dependencies" : [ + "com.oracle.truffle.api.interop.java", "com.oracle.truffle.api.vm", "mx:JUNIT" ], @@ -193,6 +195,7 @@ ], "checkstyle" : "com.oracle.truffle.api", "javaCompliance" : "1.7", + "annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"], "workingSets" : "Truffle,Tools", },
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,11 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.ImplicitCast; @@ -32,15 +37,24 @@ import com.oracle.truffle.api.dsl.TypeSystem; import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.dsl.test.ArrayTestFactory.TestNode1NodeGen; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.UnexpectedResultException; -import org.junit.Assert; -import org.junit.Test; public class ArrayTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testNode1() { final TestNode1 node = TestNode1NodeGen.create(null);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/AssumptionsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/AssumptionsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,18 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static com.oracle.truffle.api.dsl.test.TestHelper.getNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; @@ -36,17 +48,21 @@ import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.MethodTestFactory; import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.NodeFieldTest2Factory; import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.StaticFieldTestFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; -import static com.oracle.truffle.api.dsl.test.TestHelper.getNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import java.util.HashMap; -import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class AssumptionsTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testField() { CallTarget root = createCallTarget(FieldTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BadLongOverflowSpecializationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BadLongOverflowSpecializationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,18 +22,33 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.BadLongOverflowSpecializationTestFactory.ImplicitCastExclusionFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class BadLongOverflowSpecializationTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + /* Regression test for */ @NodeChild
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BinaryNodeTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BinaryNodeTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,20 +22,35 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.BinaryNodeTestFactory.AddNodeFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class BinaryNodeTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testAdd() { TestRootNode<AddNode> node = createRoot(AddNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,15 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertionsEnabled; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NodeChild; @@ -39,16 +48,22 @@ import com.oracle.truffle.api.dsl.test.CachedTestFactory.TestGuardWithJustCachedParameterFactory; import com.oracle.truffle.api.dsl.test.CachedTestFactory.TestMultipleCachesFactory; import com.oracle.truffle.api.dsl.test.CachedTestFactory.UnboundCacheFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertionsEnabled; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; @SuppressWarnings("unused") public class CachedTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testUnboundCache() { CallTarget root = createCallTarget(UnboundCacheFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CodeFormatTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CodeFormatTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,31 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.CodeFormatTestFactory.LineWrappingTestFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; /** * Tests the generated code compiles without warnings for unusual large guard names. */ public class CodeFormatTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { Assert.assertEquals(42, TestHelper.createCallTarget(LineWrappingTestFactory.create()).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,18 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.array; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; @@ -33,21 +45,24 @@ import com.oracle.truffle.api.dsl.test.ContainsTestFactory.Contains4Factory; import com.oracle.truffle.api.dsl.test.ContainsTestFactory.PolymorphicToMonomorphic0Factory; import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener; -import static com.oracle.truffle.api.dsl.test.TestHelper.array; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.NodeCost; -import static org.hamcrest.CoreMatchers.is; -import org.junit.Assert; -import static org.junit.Assert.assertThat; -import org.junit.Test; @SuppressWarnings("unused") public class ContainsTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + /* * Tests a simple monomorphic inclusion. */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,11 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; @@ -43,14 +48,23 @@ import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.UnexpectedResultException; -import org.junit.Assert; -import org.junit.Test; public class ExecuteEvaluatedTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testSingleEvaluated() { ArgumentNode arg0 = new ArgumentNode(0);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,16 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.array; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; @@ -30,23 +40,28 @@ import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback2Factory; import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback3Factory; import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback4Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.array; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeUtil; -import org.junit.Assert; -import org.junit.Test; public class FallbackTest { private static final Object UNKNOWN_OBJECT = new Object() { }; + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testFallback1() { assertRuns(Fallback1Factory.getInstance(), //
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,9 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.Assert; +import org.junit.Test; + import com.oracle.truffle.api.dsl.ImplicitCast; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; @@ -34,8 +37,6 @@ import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; import com.oracle.truffle.api.frame.VirtualFrame; -import org.junit.Assert; -import org.junit.Test; public class ImplicitCastTest {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,32 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.array; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.ImportGuardsTestFactory.ImportGuards6Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.array; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class ImportGuardsTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @ImportStatic(Imports0.class) @NodeChild("a") static class ImportGuards0 extends ValueNode {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/IntegerLiteralGuardsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/IntegerLiteralGuardsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,13 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; @@ -29,14 +36,22 @@ import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.DecimalLiteralTestFactory; import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.HexLiteralTestFactory; import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.OctalLiteralTestFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; @SuppressWarnings("unused") public class IntegerLiteralGuardsTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testDecimalLiteral() { CallTarget root = createCallTarget(DecimalLiteralTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,18 +22,34 @@ */ package com.oracle.truffle.api.dsl.test; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.LazyClassLoadingTestFactory.TestNodeFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class LazyClassLoadingTest { + + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { String factoryName = TestNodeFactory.class.getName();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LimitTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LimitTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,14 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NodeChild; @@ -31,15 +39,22 @@ import com.oracle.truffle.api.dsl.test.LimitTestFactory.DefaultLimit3TestFactory; import com.oracle.truffle.api.dsl.test.LimitTestFactory.LocalLimitTestFactory; import com.oracle.truffle.api.dsl.test.LimitTestFactory.MethodLimitTestFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; @SuppressWarnings("unused") public class LimitTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testDefaultLimit3() { CallTarget root = createCallTarget(DefaultLimit3TestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MergeSpecializationsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MergeSpecializationsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,18 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.concurrent.CountDownLatch; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeFactory; @@ -30,21 +42,25 @@ import com.oracle.truffle.api.dsl.internal.SpecializedNode; import com.oracle.truffle.api.dsl.test.MergeSpecializationsTestFactory.TestCachedNodeFactory; import com.oracle.truffle.api.dsl.test.MergeSpecializationsTestFactory.TestNodeFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.Node; -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.Test; public class MergeSpecializationsTest { private static final int THREADS = 50; + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @NodeChild @SuppressWarnings("unused") abstract static class TestNode extends ValueNode {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,15 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static com.oracle.truffle.api.dsl.test.TestHelper.getNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; @@ -45,16 +54,22 @@ import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardStaticFieldTestFactory; import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardStaticFinalFieldCompareTestFactory; import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardUnboundMethodTestFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; -import static com.oracle.truffle.api.dsl.test.TestHelper.getNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; @SuppressWarnings("unused") public class MethodGuardsTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testGuardEqual() { CallTarget root = createCallTarget(GuardEqualTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsWithArgumentsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsWithArgumentsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,14 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; @@ -32,15 +40,22 @@ import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsDouble2Factory; import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsDouble3Factory; import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsSingle2Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class MethodGuardsWithArgumentsTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testMArguments0() { TestRootNode<MArguments0> root = createRoot(MArguments0Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NegatedGuardsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NegatedGuardsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,32 @@ */ package com.oracle.truffle.api.dsl.test; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.dsl.test.NegatedGuardsTestFactory.NegatedGuardNodeFactory; import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.dsl.test.NegatedGuardsTestFactory.NegatedGuardNodeFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class NegatedGuardsTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testGuardGlobal() { TestRootNode<NegatedGuardNode> root = createRoot(NegatedGuardNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildNoNameTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildNoNameTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -29,12 +29,26 @@ import com.oracle.truffle.api.dsl.test.NodeChildNoNameTestFactory.ThreeArgsNoNameFactory; import com.oracle.truffle.api.dsl.test.NodeChildNoNameTestFactory.TwoArgsNoNameFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; public class NodeChildNoNameTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testOneArg() { ValueNode node = new ConstantNode();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,32 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.IntFieldTestNodeFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class NodeChildTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testIntField() { assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,13 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeField; import com.oracle.truffle.api.dsl.NodeFields; import com.oracle.truffle.api.dsl.Specialization; @@ -32,13 +39,21 @@ import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.RewriteTestNodeFactory; import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.StringFieldTestNodeFactory; import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.TestContainerFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class NodeFieldTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testIntField() { assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullLiteralGuardsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullLiteralGuardsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,19 +22,34 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareNotNullNodeFactory; import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareObjectsNullNodeFactory; import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareStringNullNodeFactory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; @SuppressWarnings("unused") public class NullLiteralGuardsTest { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testCompareObjectsNull() { CallTarget root = createCallTarget(CompareObjectsNullNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,19 +22,34 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.NullTestFactory.NullTest1Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class NullTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testGuardInvocations() { TestRootNode<NullTest1> root = createRoot(NullTest1Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,19 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.array; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; @@ -29,21 +42,23 @@ import com.oracle.truffle.api.dsl.test.PolymorphicTestFactory.Polymorphic2Factory; import com.oracle.truffle.api.dsl.test.PolymorphicTestFactory.Polymorphic3Factory; import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener; -import static com.oracle.truffle.api.dsl.test.TestHelper.array; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeUtil; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import org.junit.Assert; -import static org.junit.Assert.assertEquals; -import org.junit.Test; public class PolymorphicTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } private static void assertParent(Node expectedParent, Node child) { Node parent = child.getParent();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,32 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.BinaryNodeTest.BinaryNode; import com.oracle.truffle.api.dsl.test.PolymorphicTest2Factory.Polymorphic1Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.NodeCost; -import static org.junit.Assert.assertEquals; -import org.junit.Test; public class PolymorphicTest2 { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testMultipleTypes() { /* Tests the unexpected polymorphic case. */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,12 @@ */ package com.oracle.truffle.api.dsl.test; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.ImplicitCast; import com.oracle.truffle.api.dsl.NodeChild; @@ -36,11 +42,20 @@ import com.oracle.truffle.api.dsl.test.ShortCircuitTestFactory.VarArgsNodeFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class ShortCircuitTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testSingleChild1() { ArgumentNode arg0 = new ArgumentNode(0);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,22 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; +import static com.oracle.truffle.api.dsl.test.TestHelper.createRootPrefix; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.theories.DataPoints; +import org.junit.experimental.theories.Theories; +import org.junit.experimental.theories.Theory; +import org.junit.runner.RunWith; + import com.oracle.truffle.api.dsl.CreateCast; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; @@ -29,29 +45,28 @@ import com.oracle.truffle.api.dsl.internal.SpecializationNode; import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection0Factory; import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection1Factory; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot; -import static com.oracle.truffle.api.dsl.test.TestHelper.createRootPrefix; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.SourceSection; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.sameInstance; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import org.junit.Test; -import org.junit.experimental.theories.DataPoints; -import org.junit.experimental.theories.Theories; -import org.junit.experimental.theories.Theory; -import org.junit.runner.RunWith; @RunWith(Theories.class) public class SourceSectionTest { @DataPoints public static final int[] data = new int[]{1, 2, 3, 4}; + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Theory public void testSourceSections(int value0, int value1, int value2) { TestRootNode<SourceSection0> root = createRoot(SourceSection0Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationFallthroughTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationFallthroughTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,14 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.array; +import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; @@ -33,15 +41,22 @@ import com.oracle.truffle.api.dsl.test.SpecializationFallthroughTestFactory.FallthroughTest4Factory; import com.oracle.truffle.api.dsl.test.SpecializationFallthroughTestFactory.FallthroughTest5Factory; import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener; -import static com.oracle.truffle.api.dsl.test.TestHelper.array; -import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; public class SpecializationFallthroughTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testFallthrough0() { assertRuns(FallthroughTest0Factory.getInstance(), //
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,16 +22,20 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.test.SpecializationGroupingTestFactory.TestElseConnectionBug1Factory; import com.oracle.truffle.api.dsl.test.SpecializationGroupingTestFactory.TestElseConnectionBug2Factory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.SlowPathException; -import org.junit.Assert; -import org.junit.Test; /** * Tests execution counts of guards. While we do not make guarantees for guard invocation except for @@ -40,6 +44,16 @@ */ public class SpecializationGroupingTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testElseConnectionBug1() { CallTarget target = TestHelper.createCallTarget(TestElseConnectionBug1Factory.create(new GenericInt()));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,19 +22,34 @@ */ package com.oracle.truffle.api.dsl.test; +import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.Specialization; -import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith; import com.oracle.truffle.api.dsl.test.TestSerializationFactory.SerializedNodeFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.NodeCost; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import org.junit.Test; public class TestSerialization { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testUpdateRoot() { /* Tests the unexpected polymorphic case. */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,13 +22,17 @@ */ package com.oracle.truffle.api.dsl.test; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; 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.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public final class TestingLanguage extends TruffleLanguage<Object> { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -58,12 +62,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; }
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,13 @@ */ package com.oracle.truffle.api.dsl.test; +import java.util.List; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; import com.oracle.truffle.api.dsl.ShortCircuit; @@ -31,14 +38,22 @@ import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Unsupported1Factory; import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Unsupported2Factory; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeUtil; -import java.util.List; -import org.junit.Assert; -import org.junit.Test; public class UnsupportedSpecializationTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testUnsupported1() { TestRootNode<Unsupported1> root = TestHelper.createRoot(Unsupported1Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/FunctionCall.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/FunctionCall.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,19 +22,24 @@ */ package com.oracle.truffle.api.dsl.test.examples; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createDummyTarget; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createDummyTarget; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; import com.oracle.truffle.api.dsl.test.examples.FunctionCallFactory.FunctionCallNodeGen; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; /** * This example illustrates how {@link Cached} can be used to implement function calls that use @@ -46,6 +51,16 @@ @SuppressWarnings("unused") public class FunctionCall { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testFunctionCall() { assertEquals(2, FunctionCallNode.CACHE_SIZE);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/Interop.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/Interop.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,17 +22,22 @@ */ package com.oracle.truffle.api.dsl.test.examples; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; import com.oracle.truffle.api.dsl.test.examples.InteropFactory.UseInteropNodeGen; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import static org.junit.Assert.assertEquals; -import org.junit.Test; /** * This example aims to illustrate how the {@link Cached} annotation can be used to implement a @@ -40,6 +45,16 @@ */ public class Interop { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testInterop() { UseInterop node = UseInteropNodeGen.create(createArguments(2));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/MathPow.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/MathPow.java Wed Sep 30 16:33:56 2015 -0700 @@ -25,11 +25,18 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; + import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; + import com.oracle.truffle.api.dsl.test.examples.MathPowFactory.MathPowNodeGen; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.nodes.Node; + import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; /** @@ -42,6 +49,16 @@ @SuppressWarnings("unused") public class MathPow extends Node { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testPow() { MathPowNode node = MathPowNodeGen.create(createArguments(2));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,17 @@ */ package com.oracle.truffle.api.dsl.test.examples; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; +import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; +import static org.junit.Assert.assertEquals; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -29,21 +40,15 @@ import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.dsl.internal.SpecializedNode; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments; -import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget; import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyDispatchNodeGen; import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyHeadNodeGen; import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyLookupNodeGen; +import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.utilities.CyclicAssumption; -import java.util.HashMap; -import java.util.Map; -import static org.junit.Assert.assertEquals; -import org.junit.Test; /** * This example illustrates a simplified version of a Ruby function call semantics (RubyHeadNode). @@ -52,6 +57,16 @@ @SuppressWarnings("unused") public class RubyCall { + @Before + public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(true); + } + + @After + public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + InstrumentationTestMode.set(false); + } + @Test public void testCall() { RubyHeadNode node = RubyHeadNodeGen.create(createArguments(4));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,14 +22,18 @@ */ package com.oracle.truffle.api.dsl.test.processor; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.dsl.test.ExpectError; -import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public class LanguageRegistrationTest { @@ -76,12 +80,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; } @@ -120,12 +139,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; } @@ -160,12 +194,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; }
--- /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/utilities/InstrumentationTestMode.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,53 @@ +/* + * 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.utilities; + +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.vm.PolyglotEngine; + +public class InstrumentationTestMode { + + public static void set(boolean enable) { + + try { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter"); + instrumenterField.setAccessible(true); + final Object instrumenter = instrumenterField.get(vm); + final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM"); + testVMField.setAccessible(true); + if (enable) { + testVMField.set(instrumenter, vm); + } else { + testVMField.set(instrumenter, null); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + fail("Reflective access to Instrumenter for testing"); + } + + } +}
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -27,10 +27,13 @@ import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; + +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -51,6 +54,12 @@ public void initObjects() { obj = JavaInterop.asTruffleObject(ClassInteropTest.class); xyp = JavaInterop.asJavaObject(XYPlus.class, obj); + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); } @Test
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/InstrumentationTestMode.java Wed Sep 30 16:33:56 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.interop.java.test; + +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.vm.PolyglotEngine; + +public class InstrumentationTestMode { + + public static void set(boolean enable) { + + try { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter"); + instrumenterField.setAccessible(true); + final Object instrumenter = instrumenterField.get(vm); + final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM"); + testVMField.setAccessible(true); + if (enable) { + testVMField.set(instrumenter, vm); + } else { + testVMField.set(instrumenter, null); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + fail("Reflective access to Instrumenter for testing"); + } + + } +}
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,14 +24,17 @@ */ package com.oracle.truffle.api.interop.java.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Random; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; -import java.util.Random; -import org.junit.AfterClass; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import org.junit.BeforeClass; -import org.junit.Test; public class JavaInteropSpeedTest { private static final int REPEAT = 10000; @@ -41,6 +44,7 @@ @BeforeClass public static void beforeTesting() { + InstrumentationTestMode.set(true); arr = initArray(REPEAT); for (int i = 0; i < 1000; i++) { JavaInteropSpeedTest t = new JavaInteropSpeedTest(); @@ -50,6 +54,11 @@ } } + @AfterClass + public static void after() { + InstrumentationTestMode.set(false); + } + private int mmInOp; private int mmInJava;
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,6 +24,17 @@ */ package com.oracle.truffle.api.interop.java.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; @@ -35,13 +46,6 @@ import com.oracle.truffle.api.interop.java.MethodMessage; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import org.junit.Before; -import org.junit.Test; public class JavaInteropTest { public int x; @@ -67,6 +71,12 @@ public void initObjects() { obj = JavaInterop.asTruffleObject(this); xyp = JavaInterop.asJavaObject(XYPlus.class, obj); + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); } @Test
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,14 +24,18 @@ */ package com.oracle.truffle.api.interop.java.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.interop.java.MethodMessage; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import org.junit.Test; public class MethodMessageTest { interface MathWrap { @@ -39,6 +43,16 @@ MaxFunction max(); } + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + interface MaxFunction { @MethodMessage(message = "IS_NULL") boolean isNull();
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,9 +26,13 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; + import java.util.List; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; + +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -70,6 +74,12 @@ public void initObjects() { obj = JavaInterop.asTruffleObject(this); interop = JavaInterop.asJavaObject(ExactMatchInterop.class, obj); + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); } @Test
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,9 +26,13 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; + import java.util.List; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; + +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -79,6 +83,12 @@ public void initObjects() { obj = JavaInterop.asTruffleObject(this); interop = JavaInterop.asJavaObject(RawInterop.class, obj); + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); } @Test
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,14 +22,18 @@ */ package com.oracle.truffle.api.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleRuntime; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; /** * <h3>Passing Arguments</h3> @@ -54,6 +58,16 @@ */ public class ArgumentsTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,13 +22,17 @@ */ package com.oracle.truffle.api.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleRuntime; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.RootNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; /** * <h3>Calling Another Tree</h3> @@ -46,6 +50,16 @@ */ public class CallTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -29,8 +29,13 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + import java.util.Iterator; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -55,6 +60,16 @@ */ public class ChildNodeTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -28,8 +28,13 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + import java.util.Iterator; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -51,6 +56,16 @@ */ public class ChildrenNodesTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -28,7 +28,11 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -54,6 +58,16 @@ */ public class FinalFieldTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -32,7 +32,11 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -51,6 +55,16 @@ */ public class FrameSlotTypeSpecializationTest { + @Before + public void setInstrumentationTestMode() { + InstrumentationTestMode.set(true); + } + + @After + public void unsetInstrumentationTestMode() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,11 @@ */ package com.oracle.truffle.api.test; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleRuntime; @@ -33,8 +38,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -import org.junit.Assert; -import org.junit.Test; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; /** * <h3>Storing Values in Frame Slots</h3> @@ -69,8 +73,18 @@ */ public class FrameTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test - public void test() { + public void test() throws SecurityException, IllegalArgumentException { TruffleRuntime runtime = Truffle.getRuntime(); FrameDescriptor frameDescriptor = new FrameDescriptor(); String varName = "localVar";
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -30,8 +30,13 @@ import com.oracle.truffle.api.nodes.NodeInterface; import com.oracle.truffle.api.nodes.NodeUtil; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + import java.util.Iterator; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -39,6 +44,16 @@ */ public class InterfaceChildFieldTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testChild() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -28,9 +28,16 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + import java.util.Iterator; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; + import static org.junit.Assert.assertEquals; + import org.junit.Test; /** @@ -54,6 +61,16 @@ */ public class ReplaceTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -33,7 +33,11 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.UnexpectedResultException; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -51,6 +55,16 @@ */ public class ReturnTypeSpecializationTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -27,7 +27,11 @@ import com.oracle.truffle.api.TruffleRuntime; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + +import org.junit.After; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; /** @@ -50,6 +54,16 @@ */ public class RootNodeTest { + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void test() { TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,13 +22,17 @@ */ package com.oracle.truffle.api.test; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; 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.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public final class TestingLanguage extends TruffleLanguage<Object> { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -57,12 +61,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,6 +22,21 @@ */ package com.oracle.truffle.api.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleRuntime; @@ -30,17 +45,7 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import org.junit.Before; -import org.junit.Test; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; /** * <h3>Accessing the Truffle Runtime</h3> @@ -63,12 +68,18 @@ private TruffleRuntime runtime; @Before - public void setUp() { + public void before() { + InstrumentationTestMode.set(true); this.runtime = Truffle.getRuntime(); } - private static RootNode createTestRootNode() { - return new RootNode(TestingLanguage.class, null, null) { + @After + public void after() { + InstrumentationTestMode.set(false); + } + + private static RootNode createTestRootNode(SourceSection sourceSection) { + return new RootNode(TestingLanguage.class, sourceSection, null) { @Override public Object execute(VirtualFrame frame) { return 42; @@ -93,7 +104,7 @@ @Test public void testCreateCallTarget() { - RootNode rootNode = createTestRootNode(); + RootNode rootNode = createTestRootNode(null); RootCallTarget target = runtime.createCallTarget(rootNode); assertNotNull(target); assertEquals(target.call(), 42); @@ -102,14 +113,14 @@ @Test public void testGetCallTargets1() { - RootNode rootNode = createTestRootNode(); + RootNode rootNode = createTestRootNode(null); RootCallTarget target = runtime.createCallTarget(rootNode); assertTrue(runtime.getCallTargets().contains(target)); } @Test public void testGetCallTargets2() { - RootNode rootNode = createTestRootNode(); + RootNode rootNode = createTestRootNode(null); RootCallTarget target1 = runtime.createCallTarget(rootNode); RootCallTarget target2 = runtime.createCallTarget(rootNode); assertTrue(runtime.getCallTargets().contains(target1)); @@ -127,10 +138,8 @@ SourceSection sourceSection1 = source1.createSection("foo", 1); SourceSection sourceSection2 = source1.createSection("bar", 2); - RootNode rootNode1 = createTestRootNode(); - rootNode1.assignSourceSection(sourceSection1); - RootNode rootNode2 = createTestRootNode(); - rootNode2.assignSourceSection(sourceSection2); + RootNode rootNode1 = createTestRootNode(sourceSection1); + RootNode rootNode2 = createTestRootNode(sourceSection2); RootNode rootNode2Copy = NodeUtil.cloneNode(rootNode2); assertSame(rootNode2.getSourceSection(), rootNode2Copy.getSourceSection());
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,20 +22,26 @@ */ package com.oracle.truffle.api.test.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleRuntime; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdvancedInstrumentCounterRoot; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag; +import com.oracle.truffle.api.vm.PolyglotEngine; /** * Tests the kind of instrumentation where a client can provide an AST fragment to be @@ -44,47 +50,53 @@ public class AdvancedInstrumentTest { @Test - public void testAdvancedInstrumentListener() { - // 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); + public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + + instrumenter.registerASTProber(new InstrumentationTestingLanguage.TestASTProber()); + final Source source = Source.fromText("testAdvancedInstrumentListener text", "testAdvancedInstrumentListener").withMimeType("text/x-instTest"); + + final Probe[] addNodeProbe = new Probe[1]; + instrumenter.addProbeListener(new DefaultProbeListener() { - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); + @Override + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + if (tag == InstrumentTestTag.ADD_TAG) { + assertNull("only one add node", addNodeProbe[0]); + addNodeProbe[0] = probe; + } + } + }); + assertEquals(vm.eval(source).get(), 13); + assertNotNull("Add node should be probed", addNodeProbe[0]); - // Probe the addition node - final Probe probe = addNode.probe(); - - assertEquals(13, callTarget1.call()); - - // Attach a null factory; it never actually attaches a node. - final Instrument instrument = Instrument.create(null, new AdvancedInstrumentRootFactory() { + // Attach a factory that never actually attaches a node. + final AdvancedInstrumentRootFactory rootFactory1 = new AdvancedInstrumentRootFactory() { public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) { return null; } - }, null, "test AdvancedInstrument"); - probe.attach(instrument); + }; + instrumenter.attach(addNodeProbe[0], null, rootFactory1, null, "test AdvancedInstrument"); - assertEquals(13, callTarget1.call()); - - final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot(); + assertEquals(vm.eval(source).get(), 13); // Attach a factory that splices an execution counter into the AST. - probe.attach(Instrument.create(null, new AdvancedInstrumentRootFactory() { + final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot(); + final AdvancedInstrumentRootFactory rootFactory2 = new AdvancedInstrumentRootFactory() { public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) { return counter; } - }, null, "test AdvancedInstrument")); - assertEquals(0, counter.getCount()); + }; + instrumenter.attach(addNodeProbe[0], null, rootFactory2, null, "test AdvancedInstrument"); - assertEquals(13, callTarget1.call()); - + assertEquals(0, counter.getCount()); + assertEquals(vm.eval(source).get(), 13); assertEquals(1, counter.getCount()); } }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,181 +22,115 @@ */ package com.oracle.truffle.api.test.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleRuntime; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeException; -import com.oracle.truffle.api.instrument.ProbeFailure.Reason; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.ProbeInstrument; import com.oracle.truffle.api.instrument.SimpleInstrumentListener; import com.oracle.truffle.api.instrument.StandardInstrumentListener; import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; 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; -import java.util.Iterator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag; +import com.oracle.truffle.api.vm.PolyglotEngine; /** * <h3>AST Instrumentation</h3> * * Instrumentation allows the insertion into Truffle ASTs language-specific instances of * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of - * {@link Instrument} that might be attached to the particular probe by tools. - * <ol> - * <li>Creates a simple add AST</li> - * <li>Verifies its structure</li> - * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li> - * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li> - * <li>Verifies the structure of the probed AST</li> - * <li>Verifies the execution of the probed AST</li> - * <li>Verifies the results observed by the instrument.</li> - * </ol> - * To do these tests, several required classes have been implemented in their most basic form, only - * implementing the methods necessary for the tests to pass, with stubs elsewhere. + * {@link ProbeInstrument} that might be attached to the particular probe by tools. */ public class InstrumentationTest { - private static final SyntaxTag ADD_TAG = new SyntaxTag() { - - @Override - public String name() { - return "Addition"; - } - - @Override - public String getDescription() { - return "Test Language Addition Node"; - } - }; - - private static final SyntaxTag VALUE_TAG = new SyntaxTag() { - - @Override - public String name() { - return "Value"; - } + @Test + public void testProbing() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new TestASTProber(instrumenter)); + final Source source = Source.fromText("testProbing text", "testProbing").withMimeType("text/x-instTest"); - @Override - public String getDescription() { - return "Test Language Value Node"; - } - }; - - @Test - public void testInstrumentationStructure() { - // 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); - - try { - addNode.probe(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), Reason.NO_PARENT); - } - final TestRootNode rootNode = new TestRootNode(addNode); - - // Creating a call target sets the parent pointers in this tree and is necessary prior to - // checking any parent/child relationships - final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + final Probe[] probes = new Probe[3]; + instrumenter.addProbeListener(new DefaultProbeListener() { - // Check the tree structure - assertEquals(addNode, leftValueNode.getParent()); - assertEquals(addNode, rightValueNode.getParent()); - Iterator<Node> iterator = addNode.getChildren().iterator(); - assertEquals(leftValueNode, iterator.next()); - assertEquals(rightValueNode, iterator.next()); - assertFalse(iterator.hasNext()); - assertEquals(rootNode, addNode.getParent()); - iterator = rootNode.getChildren().iterator(); - assertEquals(addNode, iterator.next()); - assertFalse(iterator.hasNext()); - - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); - - // Probe the addition node - addNode.probe(); - - // Check the modified tree structure - assertEquals(addNode, leftValueNode.getParent()); - assertEquals(addNode, rightValueNode.getParent()); - iterator = addNode.getChildren().iterator(); - assertEquals(leftValueNode, iterator.next()); - assertEquals(rightValueNode, iterator.next()); - assertFalse(iterator.hasNext()); + @Override + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + if (tag == InstrumentTestTag.ADD_TAG) { + assertEquals(probes[0], null); + probes[0] = probe; + } else if (tag == InstrumentTestTag.VALUE_TAG) { + if (probes[1] == null) { + probes[1] = probe; + } else if (probes[2] == null) { + probes[2] = probe; + } else { + fail("Should only be three probes"); + } + } + } + }); + assertEquals(vm.eval(source).get(), 13); + assertNotNull("Add node should be probed", probes[0]); + assertNotNull("Value nodes should be probed", probes[1]); + assertNotNull("Value nodes should be probed", probes[2]); + // Check instrumentation with the simplest kind of counters. + // They should all be removed when the check is finished. + checkCounters(probes[0], vm, source, new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter)); - // Ensure there's a WrapperNode correctly inserted into the AST - iterator = rootNode.getChildren().iterator(); - Node wrapperNode = iterator.next(); - assertTrue(wrapperNode instanceof TestLanguageWrapperNode); - assertFalse(iterator.hasNext()); - assertEquals(rootNode, wrapperNode.getParent()); + // Now try with the more complex flavor of listener + checkCounters(probes[0], vm, source, new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter)); - // Check that the WrapperNode has both the probe and the wrapped node as children - iterator = wrapperNode.getChildren().iterator(); - assertEquals(addNode, iterator.next()); - ProbeNode probeNode = (ProbeNode) iterator.next(); - assertTrue(probeNode.getProbe() != null); - assertFalse(iterator.hasNext()); - - // Check that you can't probe the WrapperNodes - TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode; - try { - wrapper.probe(); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE); - } - - // Check that the "probed" AST still executes correctly - assertEquals(13, callTarget1.call()); } @Test - public void testListeners() { + public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { - // 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 PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = Source.fromText("testTagging text", "testTagging").withMimeType("text/x-instTest"); + + // Applies appropriate tags + final TestASTProber astProber = new TestASTProber(instrumenter); + instrumenter.registerASTProber(astProber); - // Creating a call target sets the parent pointers in this tree and is necessary prior to - // checking any parent/child relationships - final CallTarget callTarget = runtime.createCallTarget(rootNode); - // Probe the addition node - final Probe probe = addNode.probe(); + // Listens for probes and tags being added + final TestProbeListener probeListener = new TestProbeListener(); + instrumenter.addProbeListener(probeListener); + + assertEquals(13, vm.eval(source).get()); - // Check instrumentation with the simplest kind of counters. - // They should all be removed when the check is finished. - checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter()); + // Check that the prober added probes to the tree + assertEquals(probeListener.probeCount, 3); + assertEquals(probeListener.tagCount, 3); - // Now try with the more complex flavor of listener - checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter()); + assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.ADD_TAG).size(), 1); + assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.VALUE_TAG).size(), 2); } - private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) { + private static void checkCounters(Probe probe, PolyglotEngine vm, Source source, TestCounter counterA, TestCounter counterB, TestCounter counterC) throws IOException { // Attach a counting instrument to the probe counterA.attach(probe); @@ -205,7 +139,7 @@ counterB.attach(probe); // Run it again and check that the two instruments are working - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 1); @@ -215,141 +149,47 @@ counterA.dispose(); // Run it again and check that instrument B is still working but not A - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 2); assertEquals(counterB.leaveCount(), 2); - // Simulate a split by cloning the AST - final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy()); - // Run the clone and check that instrument B is still working but not A - assertEquals(13, callTarget2.call()); + // Attach a second instrument to the probe + counterC.attach(probe); + + // Run the original and check that instruments B,C working but not A + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 3); assertEquals(counterB.leaveCount(), 3); - - // Run the original and check that instrument B is still working but not A - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 4); - assertEquals(counterB.leaveCount(), 4); - - // Attach a second instrument to the probe - counterC.attach(probe); - - // Run the original and check that instruments B,C working but not A - assertEquals(13, callTarget.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 5); - assertEquals(counterB.leaveCount(), 5); assertEquals(counterC.enterCount(), 1); assertEquals(counterC.leaveCount(), 1); - // Run the clone and check that instruments B,C working but not A - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 6); - assertEquals(counterB.leaveCount(), 6); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - // Remove instrumentC counterC.dispose(); // Run the original and check that instrument B working but not A,C - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 7); - assertEquals(counterB.leaveCount(), 7); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - - // Run the clone and check that instrument B working but not A,C - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 8); - assertEquals(counterB.leaveCount(), 8); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); + assertEquals(counterB.enterCount(), 4); + assertEquals(counterB.leaveCount(), 4); + assertEquals(counterC.enterCount(), 1); + assertEquals(counterC.leaveCount(), 1); // Remove instrumentB counterB.dispose(); - // Run both the original and clone, check that no instruments working - assertEquals(13, callTarget.call()); - assertEquals(13, callTarget2.call()); + // Check that no instruments working + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 8); - assertEquals(counterB.leaveCount(), 8); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - } - - @Test - public void testTagging() { - // Applies appropriate tags - final TestASTProber astProber = new TestASTProber(); - Probe.registerASTProber(astProber); - - // Listens for probes and tags being added - final TestProbeListener probeListener = new TestProbeListener(); - Probe.addProbeListener(probeListener); - - // Counts all entries to all instances of addition nodes - final TestMultiCounter additionCounter = new TestMultiCounter(); - - // Counts all entries to all instances of value nodes - final TestMultiCounter valueCounter = new TestMultiCounter(); - - // 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 callTarget = runtime.createCallTarget(rootNode); - - // Check that the prober added probes to the tree - assertEquals(probeListener.probeCount, 3); - assertEquals(probeListener.tagCount, 3); - - assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1); - assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2); - - // Check that it executes correctly - assertEquals(13, callTarget.call()); - - // Dynamically attach a counter for all executions of all Addition nodes - for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) { - additionCounter.attachCounter(probe); - } - // Dynamically attach a counter for all executions of all Value nodes - for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) { - valueCounter.attachCounter(probe); - } - - // Counters initialized at 0 - assertEquals(additionCounter.count, 0); - assertEquals(valueCounter.count, 0); - - // Execute again - assertEquals(13, callTarget.call()); - - // There are two value nodes in the AST, but only one addition node - assertEquals(additionCounter.count, 1); - assertEquals(valueCounter.count, 2); - - Probe.unregisterASTProber(astProber); + assertEquals(counterB.enterCount(), 4); + assertEquals(counterB.leaveCount(), 4); + assertEquals(counterC.enterCount(), 1); + assertEquals(counterC.leaveCount(), 1); } private interface TestCounter { @@ -370,28 +210,11 @@ public int enterCount = 0; public int leaveCount = 0; - public final Instrument instrument; - - public TestSimpleInstrumentCounter() { - this.instrument = Instrument.create(new SimpleInstrumentListener() { - - public void enter(Probe probe) { - enterCount++; - } + public Instrumenter instrumenter; + private ProbeInstrument instrument; - public void returnVoid(Probe probe) { - leaveCount++; - } - - public void returnValue(Probe probe, Object result) { - leaveCount++; - } - - public void returnExceptional(Probe probe, Exception exception) { - leaveCount++; - } - - }, "Instrumentation Test Counter"); + public TestSimpleInstrumentCounter(Instrumenter instrumenter) { + this.instrumenter = instrumenter; } @Override @@ -406,7 +229,25 @@ @Override public void attach(Probe probe) { - probe.attach(instrument); + instrument = instrumenter.attach(probe, new SimpleInstrumentListener() { + + public void onEnter(Probe p) { + enterCount++; + } + + public void onReturnVoid(Probe p) { + leaveCount++; + } + + public void onReturnValue(Probe p, Object result) { + leaveCount++; + } + + public void onReturnExceptional(Probe p, Exception exception) { + leaveCount++; + } + + }, "Instrumentation Test Counter"); } @Override @@ -422,28 +263,11 @@ public int enterCount = 0; public int leaveCount = 0; - public final Instrument instrument; - - public TestStandardInstrumentCounter() { - this.instrument = Instrument.create(new StandardInstrumentListener() { - - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - enterCount++; - } + public final Instrumenter instrumenter; + public ProbeInstrument instrument; - public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { - leaveCount++; - } - - public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { - leaveCount++; - } - - public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { - leaveCount++; - } - - }, "Instrumentation Test Counter"); + public TestStandardInstrumentCounter(Instrumenter instrumenter) { + this.instrumenter = instrumenter; } @Override @@ -458,7 +282,25 @@ @Override public void attach(Probe probe) { - probe.attach(instrument); + instrument = instrumenter.attach(probe, new StandardInstrumentListener() { + + public void onEnter(Probe p, Node node, VirtualFrame vFrame) { + enterCount++; + } + + public void onReturnVoid(Probe p, Node node, VirtualFrame vFrame) { + leaveCount++; + } + + public void onReturnValue(Probe p, Node node, VirtualFrame vFrame, Object result) { + leaveCount++; + } + + public void onReturnExceptional(Probe p, Node node, VirtualFrame vFrame, Exception exception) { + leaveCount++; + } + + }, "Instrumentation Test Counter"); } @Override @@ -472,6 +314,12 @@ */ private static final class TestASTProber implements NodeVisitor, ASTProber { + private final Instrumenter instrumenter; + + TestASTProber(Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public boolean visit(Node node) { if (node instanceof TestLanguageNode) { @@ -479,10 +327,10 @@ final TestLanguageNode testNode = (TestLanguageNode) node; if (node instanceof TestValueNode) { - testNode.probe().tagAs(VALUE_TAG, null); + instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null); } else if (node instanceof TestAdditionNode) { - testNode.probe().tagAs(ADD_TAG, null); + instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null); } } @@ -490,8 +338,8 @@ } @Override - public void probeAST(Node node) { - node.accept(this); + public void probeAST(Instrumenter inst, RootNode rootNode) { + rootNode.accept(this); } } @@ -503,7 +351,7 @@ public int counter = 0; @Override - public void enter(Probe probe) { + public void onEnter(Probe probe) { counter++; } } @@ -516,35 +364,11 @@ public int counter = 0; @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { counter++; } } - /** - * A counter that can count executions at multiple nodes; it attaches a separate instrument at - * each Probe, but keeps a total count. - */ - private static final class TestMultiCounter { - - public int count = 0; - - public void attachCounter(Probe probe) { - - // Attach a new instrument for every Probe - // where we want to count executions. - // it will get copied when ASTs cloned, so - // keep the count in this outer class. - probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() { - - @Override - public void enter(Probe p) { - count++; - } - }, "Instrumentation Test MultiCounter")); - } - } - private static final class TestProbeListener extends DefaultProbeListener { public int probeCount = 0;
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Wed Sep 30 16:33:56 2015 -0700 @@ -25,15 +25,15 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.test.TestingLanguage; /** * Tests instrumentation where a client can attach a node that gets attached into the AST. @@ -43,21 +43,12 @@ 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; + @Child private EventHandlerNode eventHandlerNode; public TestLanguageWrapperNode(TestLanguageNode child) { assert !(child instanceof TestLanguageWrapperNode); @@ -70,18 +61,13 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } @Override public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } @Override @@ -91,15 +77,15 @@ @Override public Object execute(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); Object result; try { result = child.execute(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result; @@ -145,7 +131,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. */ - static class TestRootNode extends RootNode { + static class InstrumentationTestRootNode extends RootNode { @Child private TestLanguageNode body; /** @@ -153,8 +139,8 @@ * 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(TestingLanguage.class, null, null); + public InstrumentationTestRootNode(TestLanguageNode body) { + super(InstrumentationTestingLanguage.class, null, null); this.body = body; } @@ -167,10 +153,37 @@ public boolean isCloningAllowed() { return true; } + } + + /** + * 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; + + final Instrumenter instrumenter; + + /** + * 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, Instrumenter instrumenter) { + super(InstrumentationTestingLanguage.class, null, null); + this.instrumenter = instrumenter; + this.body = body; + } @Override - public void applyInstrumentation() { - Probe.applyASTProbers(body); + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; } } @@ -192,4 +205,5 @@ return null; } } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestingLanguage.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,164 @@ +/* + * 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 java.io.IOException; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleRuntime; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.InstrumentationTestRootNode; +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.TestValueNode; + +@TruffleLanguage.Registration(name = "instrumentationTestLanguage", version = "0", mimeType = "text/x-instTest") +public final class InstrumentationTestingLanguage extends TruffleLanguage<Object> { + + public static final InstrumentationTestingLanguage INSTANCE = new InstrumentationTestingLanguage(); + + static enum InstrumentTestTag implements SyntaxTag { + + ADD_TAG("addition", "test language addition node"), + + VALUE_TAG("value", "test language value node"); + + private final String name; + private final String description; + + private InstrumentTestTag(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + } + + private InstrumentationTestingLanguage() { + } + + @Override + protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException { + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode); + final TruffleRuntime runtime = Truffle.getRuntime(); + final CallTarget callTarget = runtime.createCallTarget(rootNode); + return callTarget; + } + + @Override + protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal(Object context) { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof TestAdditionNode || node instanceof TestValueNode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + if (isInstrumentable(node)) { + return new TestLanguageWrapperNode((TestLanguageNode) node); + } + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @Override + protected Object createContext(Env env) { + return null; + } + + static final class TestASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, RootNode startNode) { + startNode.accept(new NodeVisitor() { + + @Override + public boolean visit(Node node) { + if (node instanceof TestLanguageNode) { + + final TestLanguageNode testNode = (TestLanguageNode) node; + + if (node instanceof TestValueNode) { + instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null); + + } else if (node instanceof TestAdditionNode) { + instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null); + + } + } + return true; + } + }); + } + } + +}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -91,7 +91,8 @@ w.write(text); } - Source s1 = Source.fromFileName(file.getPath()); + // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632 + Source s1 = Source.fromFileName(file.getPath()).withMimeType("text/x-java"); assertEquals("Recognized as Java", "text/x-java", s1.getMimeType()); Source s2 = s1.withMimeType("text/x-c"); assertEquals("They have the same content", s1.getCode(), s2.getCode()); @@ -107,7 +108,8 @@ String text = "// Hello"; - Source s1 = Source.fromFileName(text, file.getPath()); + // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632 + Source s1 = Source.fromFileName(text, file.getPath()).withMimeType("text/x-java"); assertEquals("Recognized as Java", "text/x-java", s1.getMimeType()); Source s2 = s1.withMimeType("text/x-c"); assertEquals("They have the same content", s1.getCode(), s2.getCode());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/InstrumentationTestMode.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,53 @@ +/* + * 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.utilities; + +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.vm.PolyglotEngine; + +public class InstrumentationTestMode { + + public static void set(boolean enable) { + + try { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter"); + instrumenterField.setAccessible(true); + final Object instrumenter = instrumenterField.get(vm); + final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM"); + testVMField.setAccessible(true); + if (enable) { + testVMField.set(instrumenter, vm); + } else { + testVMField.set(instrumenter, null); + } + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) { + fail("Reflective access to Instrumenter for testing"); + } + + } +}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,18 +24,37 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.test.utilities.InstrumentationTestMode; + import static com.oracle.truffle.api.test.vm.ImplicitExplicitExportTest.L3; + import com.oracle.truffle.api.vm.PolyglotEngine; + import java.io.IOException; import java.util.List; import java.util.concurrent.Executors; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; public class GlobalSymbolTest { + + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void globalSymbolFoundByLanguage() throws IOException { PolyglotEngine vm = PolyglotEngine.buildNew().globalSymbol("ahoj", "42").executor(Executors.newSingleThreadExecutor()).build();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,16 +22,10 @@ */ package com.oracle.truffle.api.test.vm; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.RootCallTarget; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.PolyglotEngine; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.io.Reader; import java.util.Enumeration; @@ -41,13 +35,25 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.Executors; + import org.junit.After; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; + public class ImplicitExplicitExportTest { private static Thread mainThread; private PolyglotEngine vm; @@ -185,12 +191,27 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override - protected DebugSupportProvider getDebugSupport() { + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { return null; }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -22,13 +22,21 @@ */ package com.oracle.truffle.api.test.vm; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.DebugSupportException; -import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.debug.ExecutionEvent; import com.oracle.truffle.api.frame.MaterializedFrame; @@ -36,23 +44,19 @@ import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.vm.EventConsumer; import com.oracle.truffle.api.vm.PolyglotEngine; -import java.io.IOException; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import org.junit.Test; /** * Bug report validating test. @@ -65,8 +69,9 @@ * Debugger instance simulates. */ public class InitializationTest { + @Test - public void accessProbeForAbstractLanguage() throws IOException { + public void accessProbeForAbstractLanguage() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { final Debugger[] arr = {null}; PolyglotEngine vm = PolyglotEngine.buildNew().onEvent(new EventConsumer<ExecutionEvent>(ExecutionEvent.class) { @Override @@ -75,7 +80,26 @@ } }).build(); - Source source = Source.fromText("any text", "any text").withMimeType("application/x-abstrlang"); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ASTProber() { + + public void probeAST(final Instrumenter inst, RootNode startNode) { + startNode.accept(new NodeVisitor() { + + public boolean visit(Node node) { + + if (node instanceof ANode) { + inst.probe(node).tagAs(StandardSyntaxTag.STATEMENT, null); + } + return true; + } + }); + } + }); + + Source source = Source.fromText("accessProbeForAbstractLanguage text", "accessProbeForAbstractLanguage").withMimeType("application/x-abstrlang"); vm.eval(source); @@ -102,7 +126,6 @@ super(AbstractLanguage.class, ss, null); node = new ANode(42); adoptChildren(); - node.probe().tagAs(StandardSyntaxTag.STATEMENT, this); } @Override @@ -123,54 +146,46 @@ return getRootNode().getSourceSection(); } - @Override - public boolean isInstrumentable() { - return true; + Object constant() { + return constant; + } + } + + private static class ANodeWrapper extends ANode implements WrapperNode { + @Child ANode child; + @Child private EventHandlerNode eventHandlerNode; + + ANodeWrapper(ANode node) { + super(1); // dummy + this.child = node; } @Override - public ProbeNode.WrapperNode createWrapperNode() { - class WN extends ANode implements ProbeNode.WrapperNode { - private ProbeNode probeNode; - - public WN(int constant) { - super(constant); - } - - @Override - public Node getChild() { - return ANode.this; - } + public Node getChild() { + return child; + } - @Override - public Probe getProbe() { - return probeNode.getProbe(); - } - - @Override - public void insertProbe(ProbeNode pn) { - this.probeNode = pn; - } - - @Override - public String instrumentationInfo() { - throw new UnsupportedOperationException(); - } - } - return new WN(constant); + @Override + public Probe getProbe() { + return eventHandlerNode.getProbe(); } - Object constant() { - return constant; + @Override + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } + @Override + public String instrumentationInfo() { + throw new UnsupportedOperationException(); + } } private abstract static class AbstractLanguage extends TruffleLanguage<Object> { } @TruffleLanguage.Registration(mimeType = "application/x-abstrlang", name = "AbstrLang", version = "0.1") - public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider { + public static final class TestLanguage extends AbstractLanguage { public static final TestLanguage INSTANCE = new TestLanguage(); @Override @@ -200,22 +215,12 @@ } @Override - protected ToolSupportProvider getToolSupport() { + public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) { throw new UnsupportedOperationException(); } @Override - protected DebugSupportProvider getDebugSupport() { - return this; - } - - @Override - public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { - throw new UnsupportedOperationException(); - } - - @Override - public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException { + public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) { throw new InstrumentOKException(); } @@ -225,8 +230,13 @@ } @Override - public void enableASTProbing(ASTProber astProber) { - throw new UnsupportedOperationException(); + protected boolean isInstrumentable(Node node) { + return node instanceof ANode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return node instanceof ANode ? new ANodeWrapper((ANode) node) : null; } }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java Wed Sep 30 16:33:56 2015 -0700 @@ -54,14 +54,13 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLanguage.Registration; -import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.debug.ExecutionEvent; import com.oracle.truffle.api.debug.SuspendedEvent; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; @@ -114,7 +113,8 @@ private final OutputStream out; private final EventConsumer<?>[] handlers; private final Map<String, Object> globals; - private Debugger debugger; + private final Instrumenter instrumenter; + private final Debugger debugger; private boolean disposed; /** @@ -129,6 +129,8 @@ this.handlers = null; this.globals = null; this.executor = null; + this.instrumenter = null; + this.debugger = null; } /** @@ -142,6 +144,8 @@ this.handlers = handlers; this.initThread = Thread.currentThread(); this.globals = new HashMap<>(globals); + this.instrumenter = SPI.createInstrumenter(this); + this.debugger = SPI.createDebugger(this, this.instrumenter); Map<String, Language> map = new HashMap<>(); /* We want to create a language instance but per LanguageCache and not per mime type. */ Set<LanguageCache> uniqueCaches = new HashSet<>(LanguageCache.languages().values()); @@ -331,7 +335,6 @@ * * @return new, isolated virtual machine with pre-registered languages */ - @SuppressWarnings("deprecation") public PolyglotEngine build() { if (out == null) { out = System.out; @@ -348,7 +351,7 @@ command.run(); } }; - return new TruffleVM(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0])); + return new PolyglotEngine(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0])); } } @@ -492,14 +495,13 @@ } private Value eval(final Language l, final Source s) throws IOException { - final Debugger[] fillIn = {debugger}; final Object[] result = {null, null}; final CountDownLatch ready = new CountDownLatch(1); final TruffleLanguage[] lang = {null}; executor.execute(new Runnable() { @Override public void run() { - evalImpl(fillIn, lang, s, result, l, ready); + evalImpl(lang, s, result, l, ready); } }); exceptionCheck(result); @@ -515,14 +517,10 @@ } @SuppressWarnings("try") - private void evalImpl(Debugger[] fillIn, TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) { - try (Closeable d = SPI.executionStart(this, fillIn, s)) { + private void evalImpl(TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) { + try (Closeable d = SPI.executionStart(this, debugger, s)) { TruffleLanguage<?> langImpl = l.getImpl(true); fillLang[0] = langImpl; - PolyglotEngine.findDebuggerSupport(langImpl); - if (debugger == null) { - debugger = fillIn[0]; - } result[0] = SPI.eval(langImpl, s); } catch (IOException ex) { result[1] = ex; @@ -533,15 +531,11 @@ @SuppressWarnings("try") final Object invokeForeign(final Node foreignNode, final VirtualFrame frame, final TruffleObject receiver) throws IOException { - final Debugger[] fillIn = {debugger}; final Object[] res = {null, null}; executor.execute(new Runnable() { @Override public void run() { - try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) { - if (debugger == null) { - debugger = fillIn[0]; - } + try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) { res[0] = ForeignAccess.execute(foreignNode, frame, receiver, ForeignAccess.getArguments(frame).toArray()); } catch (IOException ex) { res[1] = ex; @@ -752,13 +746,12 @@ */ public Value invoke(final Object thiz, final Object... args) throws IOException { get(); - final Debugger[] fillIn = {debugger}; final CountDownLatch done = new CountDownLatch(1); final Object[] res = {null, null}; executor.execute(new Runnable() { @Override public void run() { - invokeImpl(fillIn, thiz, args, res, done); + invokeImpl(thiz, args, res, done); } }); exceptionCheck(res); @@ -766,11 +759,8 @@ } @SuppressWarnings("try") - private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) { - try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) { - if (debugger == null) { - debugger = fillIn[0]; - } + private void invokeImpl(Object thiz, Object[] args, Object[] res, CountDownLatch done) { + try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) { List<Object> arr = new ArrayList<>(); if (thiz == null) { if (language != null) { @@ -869,12 +859,13 @@ TruffleLanguage<?> getImpl(boolean create) { getEnv(create); - return info.getImpl(false); + TruffleLanguage<?> impl = info.getImpl(false); + return impl; } TruffleLanguage.Env getEnv(boolean create) { if (env == null && create) { - env = SPI.attachEnv(PolyglotEngine.this, info.getImpl(true), out, err, in); + env = SPI.attachEnv(PolyglotEngine.this, info.getImpl(true), out, err, in, instrumenter); } return env; } @@ -889,8 +880,7 @@ // Accessor helper methods // - TruffleLanguage<?> findLanguage(Probe probe) { - Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe); + TruffleLanguage<?> findLanguage(Class<? extends TruffleLanguage> languageClazz) { for (Map.Entry<String, Language> entrySet : langs.entrySet()) { Language languageDescription = entrySet.getValue(); final TruffleLanguage<?> impl = languageDescription.getImpl(false); @@ -901,6 +891,10 @@ throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); } + TruffleLanguage<?> findLanguage(Probe probe) { + return findLanguage(SPI.findLanguage(probe)); + } + Env findEnv(Class<? extends TruffleLanguage> languageClazz) { for (Map.Entry<String, Language> entrySet : langs.entrySet()) { Language languageDescription = entrySet.getValue(); @@ -912,10 +906,6 @@ 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(Object vmObj, TruffleLanguage<?> ownLang, String globalName) { @@ -951,9 +941,9 @@ } @Override - protected Env attachEnv(Object obj, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) { + protected Env attachEnv(Object obj, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) { PolyglotEngine vm = (PolyglotEngine) obj; - return super.attachEnv(vm, language, stdOut, stdErr, stdIn); + return super.attachEnv(vm, language, stdOut, stdErr, stdIn, instrumenter); } @Override @@ -972,13 +962,19 @@ } @Override - public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return super.getToolSupport(l); + protected Instrumenter createInstrumenter(Object vm) { + return super.createInstrumenter(vm); } @Override - public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return super.getDebugSupport(l); + protected Debugger createDebugger(Object vm, Instrumenter instrumenter) { + return super.createDebugger(vm, instrumenter); + } + + @Override + protected Instrumenter getInstrumenter(Object obj) { + final PolyglotEngine vm = (PolyglotEngine) obj; + return vm.instrumenter; } @Override @@ -993,9 +989,15 @@ } @Override - protected Closeable executionStart(Object obj, Debugger[] fillIn, Source s) { + protected TruffleLanguage findLanguageImpl(Object obj, Class<? extends TruffleLanguage> languageClazz) { + final PolyglotEngine vm = (PolyglotEngine) obj; + return vm.findLanguage(languageClazz); + } + + @Override + protected Closeable executionStart(Object obj, Debugger debugger, Source s) { PolyglotEngine vm = (PolyglotEngine) obj; - return super.executionStart(vm, fillIn, s); + return super.executionStart(vm, debugger, s); } @Override
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,10 +24,6 @@ */ 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; -import com.oracle.truffle.api.source.Source; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -37,6 +33,11 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; +import com.oracle.truffle.api.source.Source; + /** * @deprecated */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,12 +24,6 @@ */ package com.oracle.truffle.api; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.impl.FindContextNode; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -45,6 +39,21 @@ import java.util.Map; import java.util.WeakHashMap; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.impl.FindContextNode; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; + /** * An entry point for everyone who wants to implement a Truffle based language. By providing an * implementation of this type and registering it using {@link Registration} annotation, your @@ -52,6 +61,13 @@ * polyglot execution engine} - all they will need to do is to include your JAR into their * application and all the Truffle goodies (multi-language support, multitenant hosting, debugging, * etc.) will be made available to them. + * <p> + * The use of {@linkplain Instrumenter Instrument-based services} requires that the language + * {@linkplain Instrumenter#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) register} + * an instance of {@link ASTProber} suitable for the language implementation that can be applied to + * "mark up" each newly created AST with {@link SyntaxTag "tags"} that identify standard syntactic + * constructs in order to configure tool behavior. See also {@linkplain #createContext(Env) + * createContext(Env)}. * * @param <C> internal state of the language associated with every thread that is executing program * {@link #parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...) @@ -67,18 +83,18 @@ /** * The annotation to use to register your language to the - * {@link com.oracle.truffle.api.vm.TruffleVM Truffle} system. By annotating your implementation - * of {@link TruffleLanguage} by this annotation you are just a + * {@link com.oracle.truffle.api.vm.PolyglotEngine Truffle} system. By annotating your + * implementation of {@link TruffleLanguage} by this annotation you are just a * <em>one JAR drop to the class path</em> away from your users. Once they include your JAR in * their application, your language will be available to the - * {@link com.oracle.truffle.api.vm.TruffleVM Truffle virtual machine}. + * {@link com.oracle.truffle.api.vm.PolyglotEngine Truffle virtual machine}. */ @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface Registration { /** * Unique name of your language. This name will be exposed to users via the - * {@link com.oracle.truffle.api.vm.TruffleVM.Language#getName()} getter. + * {@link com.oracle.truffle.api.vm.PolyglotEngine.Language#getName()} getter. * * @return identifier of your language */ @@ -86,7 +102,7 @@ /** * Unique string identifying the language version. This name will be exposed to users via - * the {@link com.oracle.truffle.api.vm.TruffleVM.Language#getVersion()} getter. + * the {@link com.oracle.truffle.api.vm.PolyglotEngine.Language#getVersion()} getter. * * @return version of your language */ @@ -95,7 +111,7 @@ /** * List of MIME types associated with your language. Users will use them (directly or * indirectly) when - * {@link com.oracle.truffle.api.vm.TruffleVM#eval(com.oracle.truffle.api.source.Source) + * {@link com.oracle.truffle.api.vm.PolyglotEngine#eval(com.oracle.truffle.api.source.Source) * executing} their code snippets or their {@link Source files}. * * @return array of MIME types assigned to your language files @@ -117,6 +133,15 @@ * {@link Node findNode} and later {@link #findContext(com.oracle.truffle.api.nodes.Node) * findContext(findNode)} to get back your language context. * + * If it is expected that any {@linkplain Instrumenter Instrumentation Services} or tools that + * depend on those services (e.g. the {@link Debugger}, then part of the preparation in the new + * context is to + * {@linkplain Instrumenter#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) + * register} a "default" {@link ASTProber} for the language implementation. Instrumentation + * requires that this be available to "mark up" each newly created AST with + * {@linkplain SyntaxTag "tags"} that identify standard syntactic constructs in order to + * configure tool behavior. + * * @param env the environment the language is supposed to operate in * @return internal data of the language in given environment */ @@ -152,7 +177,7 @@ * just parsed <code>code</code> * @throws IOException thrown when I/O or parsing goes wrong. Here-in thrown exception is * propagate to the user who called one of <code>eval</code> methods of - * {@link com.oracle.truffle.api.vm.TruffleVM} + * {@link com.oracle.truffle.api.vm.PolyglotEngine} */ protected abstract CallTarget parse(Source code, Node context, String... argumentNames) throws IOException; @@ -201,9 +226,59 @@ */ protected abstract boolean isObjectOfLanguage(Object object); - protected abstract ToolSupportProvider getToolSupport(); + /** + * Gets visualization services for language-specific information. + */ + protected abstract Visualizer getVisualizer(); + + /** + * Returns {@code true} for a node can be "instrumented" by + * {@linkplain Instrumenter#probe(Node) probing}. + * <p> + * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode} + * + * @see WrapperNode + */ + protected abstract boolean isInstrumentable(Node node); - protected abstract DebugSupportProvider getDebugSupport(); + /** + * For nodes in this language that are <em>instrumentable</em>, this method returns an + * {@linkplain Node AST node} that: + * <ol> + * <li>implements {@link WrapperNode};</li> + * <li>has the node argument as it's child; and</li> + * <li>whose type is safe for replacement of the node in the parent.</li> + * </ol> + * + * @return an appropriately typed {@link WrapperNode} + */ + protected abstract WrapperNode createWrapperNode(Node node); + + /** + * Runs source code in a halted execution context, or at top level. + * + * @param source the code to run + * @param node node where execution halted, {@code null} if no execution context + * @param mFrame frame where execution halted, {@code null} if no execution context + * @return result of running the code in the context, or at top level if no execution context. + * @throws IOException if the evaluation cannot be performed + */ + protected abstract Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException; + + /** + * 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 Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)} + * . + * + * @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. + */ + protected abstract AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException; /** * Allows a language implementor to create a node that can effectively lookup up the context @@ -278,13 +353,15 @@ private final InputStream in; private final OutputStream err; private final OutputStream out; + private final Instrumenter instrumenter; - Env(Object vm, TruffleLanguage<?> lang, OutputStream out, OutputStream err, InputStream in) { + Env(Object vm, TruffleLanguage<?> lang, OutputStream out, OutputStream err, InputStream in, Instrumenter instrumenter) { this.vm = vm; this.in = in; this.err = err; this.out = out; this.lang = lang; + this.instrumenter = instrumenter; this.langCtx = new LangCtx<>(lang, this); } @@ -302,8 +379,8 @@ } /** - * Input associated with {@link com.oracle.truffle.api.vm.TruffleVM} this language is being - * executed in. + * Input associated with {@link com.oracle.truffle.api.vm.PolyglotEngine} this language is + * being executed in. * * @return reader, never <code>null</code> */ @@ -317,8 +394,8 @@ } /** - * Standard output writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is - * being executed in. + * Standard output writer for {@link com.oracle.truffle.api.vm.PolyglotEngine} this language + * is being executed in. * * @return writer, never <code>null</code> */ @@ -332,8 +409,8 @@ } /** - * Standard error writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is - * being executed in. + * Standard error writer for {@link com.oracle.truffle.api.vm.PolyglotEngine} this language + * is being executed in. * * @return writer, never <code>null</code> */ @@ -345,6 +422,10 @@ public Writer stdErr() { return new OutputStreamWriter(err); } + + public Instrumenter instrumenter() { + return instrumenter; + } } private static final AccessAPI API = new AccessAPI(); @@ -352,8 +433,8 @@ @SuppressWarnings("rawtypes") private static final class AccessAPI extends Accessor { @Override - protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) { - Env env = new Env(vm, language, stdOut, stdErr, stdIn); + protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) { + Env env = new Env(vm, language, stdOut, stdErr, stdIn, instrumenter); return env; } @@ -382,6 +463,14 @@ } @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, + AdvancedInstrumentResultListener resultListener) throws IOException { + + final TruffleLanguage language = findLanguageImpl(vm, languageClass); + return language.createAdvancedInstrumentRootFactory(expr, resultListener); + } + + @Override protected Object findExportedSymbol(TruffleLanguage.Env env, String globalName, boolean onlyExplicit) { return env.langCtx.findExportedSymbol(globalName, onlyExplicit); } @@ -407,13 +496,13 @@ } @Override - protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return l.getToolSupport(); + protected boolean isInstrumentable(Node node, TruffleLanguage language) { + return language.isInstrumentable(node); } @Override - protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return l.getDebugSupport(); + protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) { + return language.createWrapperNode(node); } @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,10 +24,13 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.instrument.Instrument; +import java.io.IOException; + +import javax.sound.midi.Instrument; + +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.source.Source; -import java.io.IOException; /** * Breakpoint in a {@link com.oracle.truffle.api.vm.PolyglotEngine} with @@ -115,8 +118,8 @@ } /** - * Enables or disables this breakpoint's AST instrumentation. The breakpoint is enabled by - * default. + * Enables or disables this breakpoint's AST {@linkplain Instrumenter instrumentation}. The + * breakpoint is enabled by default. * * @param enabled <code>true</code> to activate the instrumentation, <code>false</code> to * deactivate the instrumentation so that it has no effect.
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportException.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +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.api.debug; - -public class DebugSupportException extends Exception { - - private static final long serialVersionUID = 3039074861617372741L; - - public DebugSupportException(String string) { - super(string); - } - - public DebugSupportException(Exception ex) { - super(ex); - } - -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +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.api.debug; - -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; -import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; -import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; - -/** - * Access to language-specific information and execution services to enable debugging. - */ -public interface DebugSupportProvider extends ToolSupportProvider { - /** - * Runs source code in a halted execution context, or at top level. - * - * @param source the code to run - * @param node node where execution halted, {@code null} if no execution context - * @param mFrame frame where execution halted, {@code null} if no execution context - * @return result of running the code in the context, or at top level if no execution context. - * @throws DebugSupportException if the evaluation cannot be performed - */ - Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException; - - /** - * 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 DebugSupportException if the factory cannot be created, for example if the expression - * is badly formed. - */ - AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException; -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,9 +24,17 @@ */ package com.oracle.truffle.api.debug; +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.FrameInstanceVisitor; import com.oracle.truffle.api.frame.MaterializedFrame; @@ -34,21 +42,17 @@ import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.StandardAfterInstrumentListener; +import com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTag; -import com.oracle.truffle.api.instrument.SyntaxTagTrap; +import com.oracle.truffle.api.instrument.TagInstrument; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.Source; -import java.io.Closeable; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; /** * Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}. @@ -60,7 +64,7 @@ public final class Debugger { private static final boolean TRACE = false; - private static final String TRACE_PREFIX = "DEBUG ENGINE: "; + private static final String TRACE_PREFIX = "Debugger: "; private static final PrintStream OUT = System.out; @@ -73,6 +77,7 @@ } } + private final Instrumenter instrumenter; private final Object vm; private Source lastSource; @@ -92,6 +97,9 @@ void addWarning(String warning); } + private final BreakpointCallback breakpointCallback; + private final WarningLog warningLog; + /** * Implementation of line-oriented breakpoints. */ @@ -107,9 +115,9 @@ */ private DebugExecutionContext debugContext; - Debugger(Object vm) { + Debugger(Object vm, Instrumenter instrumenter) { this.vm = vm; - + this.instrumenter = instrumenter; Source.setFileCaching(true); // Initialize execution context stack @@ -117,7 +125,7 @@ prepareContinue(); debugContext.contextTrace("START EXEC DEFAULT"); - final BreakpointCallback breakpointCallback = new BreakpointCallback() { + breakpointCallback = new BreakpointCallback() { @TruffleBoundary public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) { @@ -125,7 +133,7 @@ } }; - final WarningLog warningLog = new WarningLog() { + warningLog = new WarningLog() { public void addWarning(String warning) { assert debugContext != null; @@ -137,10 +145,6 @@ this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); } - Object vm() { - return vm; - } - /** * Sets a breakpoint to halt at a source line. * @@ -269,11 +273,12 @@ debugContext.setStrategy(new StepOver(stepCount)); } + // TODO (mlvdv) used by the breakpoint factories; to be deprecated/replaced. /** * 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) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * * @param expr a guest language expression @@ -284,15 +289,12 @@ */ @SuppressWarnings("rawtypes") AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Probe probe, String expr, AdvancedInstrumentResultListener resultListener) throws IOException { - try { - Class<? extends TruffleLanguage> langugageClass = ACCESSOR.findLanguage(probe); - TruffleLanguage.Env env = ACCESSOR.findLanguage(vm, langugageClass); - TruffleLanguage<?> l = ACCESSOR.findLanguage(env); - DebugSupportProvider dsp = ACCESSOR.getDebugSupport(l); - return dsp.createAdvancedInstrumentRootFactory(expr, resultListener); - } catch (DebugSupportException ex) { - throw new IOException(ex); - } + Class<? extends TruffleLanguage> languageClass = ACCESSOR.findLanguage(probe); + return ACCESSOR.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); + } + + Instrumenter getInstrumenter() { + return instrumenter; } /** @@ -407,6 +409,8 @@ * @see Debugger#prepareStepInto(int) */ private final class StepInto extends StepStrategy { + private TagInstrument beforeTagInstrument; + private TagInstrument afterTagInstrument; private int unfinishedStepCount; StepInto(int stepCount) { @@ -416,26 +420,40 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + + beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() { @TruffleBoundary @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { // HALT: just before statement --unfinishedStepCount; - strategyTrace("TRAP BEFORE", "unfinished steps=%d", unfinishedStepCount); + strategyTrace("HALT BEFORE", "unfinished steps=%d", unfinishedStepCount); // Should run in fast path if (unfinishedStepCount <= 0) { - halt(node, mFrame, true); + halt(node, vFrame.materialize(), true); } strategyTrace("RESUME BEFORE", ""); } - }); - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + }, "Debugger StepInto"); + + afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() { + + public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) { + doHalt(node, vFrame.materialize()); + } + + public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + doHalt(node, vFrame.materialize()); + } + + public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + doHalt(node, vFrame.materialize()); + } + @TruffleBoundary - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + private void doHalt(Node node, MaterializedFrame mFrame) { --unfinishedStepCount; - strategyTrace(null, "TRAP AFTER unfinished steps=%d", unfinishedStepCount); + strategyTrace(null, "HALT AFTER unfinished steps=%d", unfinishedStepCount); if (currentStackDepth() < stackDepth) { // HALT: just "stepped out" if (unfinishedStepCount <= 0) { @@ -444,13 +462,13 @@ } strategyTrace("RESUME AFTER", ""); } - }); + }, "Debugger StepInto"); } @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + beforeTagInstrument.dispose(); + afterTagInstrument.dispose(); } } @@ -471,27 +489,41 @@ */ private final class StepOut extends StepStrategy { + private TagInstrument afterTagInstrument; + @Override protected void setStrategy(final int stackDepth) { - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + + afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() { + + public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) { + doHalt(node, vFrame.materialize()); + } + + public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + doHalt(node, vFrame.materialize()); + } + + public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + doHalt(node, vFrame.materialize()); + } @TruffleBoundary - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + private void doHalt(Node node, MaterializedFrame mFrame) { // HALT: final int currentStackDepth = currentStackDepth(); - strategyTrace("TRAP AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth); + strategyTrace("HALT AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth); if (currentStackDepth < stackDepth) { halt(node, mFrame, false); } strategyTrace("RESUME AFTER", ""); } - }); + }, "Debugger StepOut"); } @Override protected void unsetStrategy() { - Probe.setAfterTagTrap(null); + afterTagInstrument.dispose(); } } @@ -509,6 +541,8 @@ * </ul> */ private final class StepOver extends StepStrategy { + private TagInstrument beforeTagInstrument; + private TagInstrument afterTagInstrument; private int unfinishedStepCount; StepOver(int stepCount) { @@ -517,20 +551,21 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() { + @TruffleBoundary @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { 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); + strategyTrace("HALT 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); + halt(node, vFrame.materialize(), true); } } else { // CONTINUE: Stack depth increased; don't count as a step @@ -540,17 +575,29 @@ } strategyTrace("RESUME BEFORE", ""); } - }); + }, "Debugger StepOver"); + + afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() { + + public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) { + doHalt(node, vFrame.materialize()); + } - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + doHalt(node, vFrame.materialize()); + } + + public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + doHalt(node, vFrame.materialize()); + } + @TruffleBoundary - @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + private void doHalt(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); + strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); // Should run in fast path if (unfinishedStepCount <= 0) { halt(node, mFrame, false); @@ -558,13 +605,13 @@ strategyTrace("RESUME AFTER", ""); } } - }); + }, "Debugger StepOver"); } @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + beforeTagInstrument.dispose(); + afterTagInstrument.dispose(); } } @@ -582,6 +629,7 @@ * </ul> */ private final class StepOverNested extends StepStrategy { + private TagInstrument beforeTagInstrument; private int unfinishedStepCount; private final int startStackDepth; @@ -592,28 +640,28 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() { @TruffleBoundary @Override - public void tagTrappedAt(Node node, MaterializedFrame mFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { 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); + strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth); if (unfinishedStepCount <= 0) { - halt(node, mFrame, false); + halt(node, vFrame.materialize(), false); } // TODO (mlvdv) fixme for multiple steps strategyTrace("RESUME BEFORE", ""); } } - }); + }, "Debuger StepOverNested"); } @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); + beforeTagInstrument.dispose(); } } @@ -809,41 +857,32 @@ @SuppressWarnings("rawtypes") private static final class AccessorDebug extends Accessor { + @Override - protected Closeable executionStart(Object 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); + protected Closeable executionStart(Object vm, final Debugger debugger, Source s) { + debugger.executionStarted(s); return new Closeable() { @Override public void close() throws IOException { - d.executionEnded(); + debugger.executionEnded(); } }; } @Override + protected Debugger createDebugger(Object vm, Instrumenter instrumenter) { + return new Debugger(vm, instrumenter); + } + + @Override protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { return super.findLanguage(probe); } @Override - protected TruffleLanguage.Env findLanguage(Object vm, Class<? extends TruffleLanguage> languageClass) { - return super.findLanguage(vm, languageClass); - } - - @Override - protected TruffleLanguage<?> findLanguage(TruffleLanguage.Env env) { - return super.findLanguage(env); - } - - @Override - protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return super.getDebugSupport(l); + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, + AdvancedInstrumentResultListener resultListener) throws IOException { + return super.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,7 +26,6 @@ import com.oracle.truffle.api.source.LineLocation; -// TODO (mlvdv) generic? /** * A breakpoint associated with a {@linkplain LineLocation source line location}. *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,20 +24,32 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.Truffle; import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED; import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED_UNRESOLVED; import static com.oracle.truffle.api.debug.Breakpoint.State.DISPOSED; import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED; import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED_UNRESOLVED; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.debug.Debugger.BreakpointCallback; import com.oracle.truffle.api.debug.Debugger.WarningLog; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.ProbeInstrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTag; @@ -48,15 +60,6 @@ import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.utilities.CyclicAssumption; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; //TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport @@ -67,9 +70,9 @@ * <ol> * <li>Line breakpoints can only be set at nodes tagged as {@link StandardSyntaxTag#STATEMENT}.</li> * <li>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.</li> - * <li>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.</li> + * by installing an {@link ProbeInstrument} that calls back to the breakpoint.</li> + * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link ProbeInstrument} will + * be copied along with the rest of the AST and will call back to the same breakpoint.</li> * <li>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.</li> @@ -83,7 +86,7 @@ private static final boolean TRACE = false; private static final PrintStream OUT = System.out; - private static final String BREAKPOINT_NAME = "LINE BREAKPOINT"; + private static final String BREAKPOINT_NAME = "Line Breakpoints"; @TruffleBoundary private static void trace(String format, Object... args) { @@ -106,6 +109,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -127,17 +131,17 @@ */ @CompilationFinal private boolean breakpointsActive = true; private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - private final Debugger debugger; LineBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) { this.debugger = debugger; this.breakpointCallback = breakpointCallback; this.warningLog = warningLog; - lineToProbesMap = new LineToProbesMap(); - lineToProbesMap.install(); + final Instrumenter instrumenter = debugger.getInstrumenter(); + this.lineToProbesMap = new LineToProbesMap(); + instrumenter.install(lineToProbesMap); - Probe.addProbeListener(new DefaultProbeListener() { + instrumenter.addProbeListener(new DefaultProbeListener() { @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { @@ -288,7 +292,7 @@ * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: * {@code null} if not attached. */ - private List<Instrument> instruments = new ArrayList<>(); + private List<ProbeInstrument> instruments = new ArrayList<>(); public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) { super(ENABLED_UNRESOLVED, ignoreCount, oneShot); @@ -343,7 +347,7 @@ if (this.conditionExpr != null || expr != null) { // De-instrument the Probes instrumented by this breakpoint final ArrayList<Probe> probes = new ArrayList<>(); - for (Instrument instrument : instruments) { + for (ProbeInstrument instrument : instruments) { probes.add(instrument.getProbe()); instrument.dispose(); } @@ -365,7 +369,7 @@ @Override public void dispose() { if (getState() != DISPOSED) { - for (Instrument instrument : instruments) { + for (ProbeInstrument instrument : instruments) { instrument.dispose(); } changeState(DISPOSED); @@ -377,13 +381,13 @@ if (getState() == DISPOSED) { throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); } - Instrument newInstrument = null; + ProbeInstrument newInstrument = null; + final Instrumenter instrumenter = debugger.getInstrumenter(); if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); } else { - newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); } - newProbe.attach(newInstrument); instruments.add(newInstrument); changeState(isEnabled ? ENABLED : DISABLED); } @@ -442,7 +446,7 @@ } } - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + public void onExecution(Node node, VirtualFrame vFrame, Object result) { final boolean condition = (Boolean) result; if (TRACE) { trace("breakpoint condition = %b %s", condition, getShortDescription()); @@ -452,7 +456,7 @@ } } - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { addExceptionWarning(ex); if (TRACE) { trace("breakpoint failure = %s %s", ex, getShortDescription()); @@ -479,7 +483,7 @@ private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { LineBreakpointImpl.this.nodeEnter(node, vFrame); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,13 +24,6 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.instrument.InstrumentationTool; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeListener; -import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; @@ -38,11 +31,19 @@ import java.util.HashMap; import java.util.Map; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + /** - * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST, - * indexed by {@link Source} and line number. + * An {@linkplain Instrumenter.Tool Instrumentation-based Tool} that builds a map of every + * {@link Probe} attached to some AST, indexed by {@link Source} and line number. */ -final class LineToProbesMap extends InstrumentationTool { +final class LineToProbesMap extends Instrumenter.Tool { private static final boolean TRACE = false; private static final PrintStream OUT = System.out; @@ -68,7 +69,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(null)) { + addMapEntry(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -79,7 +84,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** @@ -115,21 +120,25 @@ @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<Probe> probes = lineToProbesMap.get(lineLocation); - if (probes == null) { - probes = new ArrayList<>(2); - lineToProbesMap.put(lineLocation, probes); - } else { - assert !probes.contains(probe); - } - probes.add(probe); + addMapEntry(probe); + } + } + + private void addMapEntry(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<Probe> probes = lineToProbesMap.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbesMap.put(lineLocation, probes); + } else { + assert !probes.contains(probe); + } + probes.add(probe); } } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,7 +26,6 @@ import com.oracle.truffle.api.instrument.SyntaxTag; -// TODO (mlvdv) generic? /** * A breakpoint associated with a {@link SyntaxTag}. *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Wed Sep 30 16:33:56 2015 -0700 @@ -46,10 +46,10 @@ import com.oracle.truffle.api.debug.Debugger.WarningLog; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeInstrument; import com.oracle.truffle.api.instrument.SyntaxTag; -import com.oracle.truffle.api.instrument.SyntaxTagTrap; import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener; import com.oracle.truffle.api.nodes.InvalidAssumptionException; @@ -62,18 +62,20 @@ * 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}. * <p> - * 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. + * The + * {@linkplain Instrumenter#attach(SyntaxTag, com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener, String) + * before TagInstrument}, does the same thing more efficiently, but there may only be one + * <em>before</em> TagInstrument active at a time. Any number of {@link TagBreakpoint}s may coexist + * with the <em>before</em> TagInstrument, but it would be confusing to have a {@link TagBreakpoint} + * set for the same Tag as the current <em>before</em> TagInstrument. * <p> * Notes: * <ol> * <li>Only one Tag Breakpoint can be active for a specific {@linkplain SyntaxTag Tag}.</li> * <li>A newly created breakpoint looks for probes matching the tag, attaches to them if found by - * installing an {@link Instrument}.</li> - * <li>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.</li> + * installing an {@link ProbeInstrument}.</li> + * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link ProbeInstrument} will + * be copied along with the rest of the AST and will call back to the same breakpoint.</li> * <li>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.</li> @@ -103,6 +105,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -117,14 +120,13 @@ */ @CompilationFinal private boolean breakpointsActive = true; private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - private final Debugger debugger; TagBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) { this.debugger = debugger; this.breakpointCallback = breakpointCallback; this.warningLog = warningLog; - Probe.addProbeListener(new DefaultProbeListener() { + debugger.getInstrumenter().addProbeListener(new DefaultProbeListener() { @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { @@ -195,7 +197,7 @@ tagToBreakpoint.put(tag, breakpoint); - for (Probe probe : Probe.findProbesTaggedAs(tag)) { + for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) { breakpoint.attach(probe); } } else { @@ -259,7 +261,7 @@ * The instrument(s) that this breakpoint currently has attached to a {@link Probe}: * {@code null} if not attached. */ - private List<Instrument> instruments = new ArrayList<>(); + private List<ProbeInstrument> instruments = new ArrayList<>(); private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) { super(ENABLED, ignoreCount, oneShot); @@ -306,7 +308,7 @@ if (this.conditionExpr != null || expr != null) { // De-instrument the Probes instrumented by this breakpoint final ArrayList<Probe> probes = new ArrayList<>(); - for (Instrument instrument : instruments) { + for (ProbeInstrument instrument : instruments) { probes.add(instrument.getProbe()); instrument.dispose(); } @@ -328,7 +330,7 @@ @Override public void dispose() { if (getState() != DISPOSED) { - for (Instrument instrument : instruments) { + for (ProbeInstrument instrument : instruments) { instrument.dispose(); } changeState(DISPOSED); @@ -340,13 +342,13 @@ if (getState() == DISPOSED) { throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); } - Instrument newInstrument = null; + ProbeInstrument newInstrument = null; + final Instrumenter instrumenter = debugger.getInstrumenter(); if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); } else { - newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); } - newProbe.attach(newInstrument); instruments.add(newInstrument); changeState(isEnabled ? ENABLED : DISABLED); } @@ -406,7 +408,7 @@ } - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + public void onExecution(Node node, VirtualFrame vFrame, Object result) { final boolean condition = (Boolean) result; if (TRACE) { trace("breakpoint condition = %b %s", condition, getShortDescription()); @@ -416,7 +418,7 @@ } } - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { addExceptionWarning(ex); if (TRACE) { trace("breakpoint failure = %s %s", ex, getShortDescription()); @@ -443,7 +445,7 @@ private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { TagBreakpointImpl.this.nodeEnter(node, vFrame); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,18 +24,6 @@ */ package com.oracle.truffle.api.impl; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; @@ -43,8 +31,26 @@ import java.lang.ref.Reference; import java.lang.ref.WeakReference; +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; + /** - * Communication between PolyglotEngine and TruffleLanguage API/SPI. + * Communication between PolyglotEngine, TruffleLanguage API/SPI, and other services. */ @SuppressWarnings("rawtypes") public abstract class Accessor { @@ -73,16 +79,6 @@ } @Override - protected ToolSupportProvider getToolSupport() { - return null; - } - - @Override - protected DebugSupportProvider getDebugSupport() { - return null; - } - - @Override protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException { throw new IOException(); } @@ -91,12 +87,38 @@ protected Object createContext(TruffleLanguage.Env env) { return null; } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } }; lng.hashCode(); new Node(null) { }.getRootNode(); try { + Class.forName(Instrumenter.class.getName(), true, Instrumenter.class.getClassLoader()); Class.forName(Debugger.class.getName(), true, Debugger.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex); @@ -132,8 +154,8 @@ } } - protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) { - return API.attachEnv(vm, language, stdOut, stdErr, stdIn); + protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) { + return API.attachEnv(vm, language, stdOut, stdErr, stdIn, instrumenter); } protected Object eval(TruffleLanguage<?> l, Source s) throws IOException { @@ -152,12 +174,37 @@ return API.languageGlobal(env); } - protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return API.getToolSupport(l); + /** + * Provided by each {@linkplain TruffleLanguage language implementation}. + */ + protected boolean isInstrumentable(Object vm, Node node) { + final RootNode rootNode = node.getRootNode(); + Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode); + TruffleLanguage language = findLanguageImpl(vm, languageClazz); + return isInstrumentable(node, language); + } + + protected boolean isInstrumentable(Node node, TruffleLanguage language) { + return API.isInstrumentable(node, language); } - protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return API.getDebugSupport(l); + /** + * Provided by each {@linkplain TruffleLanguage language implementation}. + */ + protected WrapperNode createWrapperNode(Object vm, Node node) { + final RootNode rootNode = node.getRootNode(); + Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode); + TruffleLanguage language = findLanguageImpl(vm, languageClazz); + return createWrapperNode(node, language); + } + + protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) { + return API.createWrapperNode(node, language); + } + + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, AdvancedInstrumentResultListener resultListener) + throws IOException { + return API.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { @@ -173,7 +220,7 @@ if (known == null) { vm = CURRENT_VM.get(); if (vm == null) { - throw new IllegalStateException(); + throw new IllegalStateException("Accessor.findLanguage access to vm"); } if (languageClass == null) { return null; @@ -184,12 +231,46 @@ return SPI.findLanguage(vm, languageClass); } + protected TruffleLanguage findLanguageImpl(Object known, Class<? extends TruffleLanguage> languageClass) { + Object vm; + if (known == null) { + vm = CURRENT_VM.get(); + if (vm == null) { + throw new IllegalStateException("Accessor.findLanguageImpl access to vm"); + } + } else { + vm = known; + } + return SPI.findLanguageImpl(vm, languageClass); + } + + protected Instrumenter getInstrumenter(Object known) { + Object vm; + if (known == null) { + vm = CURRENT_VM.get(); + if (vm == null) { + throw new IllegalStateException("Accessor.getInstrumenter access to vm"); + } + } else { + vm = known; + } + return SPI.getInstrumenter(vm); + } + + protected Instrumenter createInstrumenter(Object vm) { + return INSTRUMENT.createInstrumenter(vm); + } + + protected Debugger createDebugger(Object vm, Instrumenter instrumenter) { + return DEBUG.createDebugger(vm, instrumenter); + } + private static Reference<Object> previousVM = new WeakReference<>(null); private static Assumption oneVM = Truffle.getRuntime().createAssumption(); - protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) { + protected Closeable executionStart(Object vm, Debugger debugger, Source s) { vm.getClass(); - final Closeable debugClose = DEBUG.executionStart(vm, fillIn, s); + final Closeable debugClose = DEBUG.executionStart(vm, debugger, s); final Object prev = CURRENT_VM.get(); if (!(vm == previousVM.get())) { previousVM = new WeakReference<>(vm); @@ -216,7 +297,7 @@ return oneVM; } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({"unchecked"}) static <C> C findContext(Class<? extends TruffleLanguage> type) { Env env = SPI.findLanguage(CURRENT_VM.get(), type); return (C) API.findContext(env); @@ -239,6 +320,11 @@ return API.findLanguage(env); } + /** Applies all registered {@linkplain ASTProber probers} to the AST. */ + protected void probeAST(RootNode rootNode) { + INSTRUMENT.probeAST(rootNode); + } + protected void dispose(TruffleLanguage<?> impl, Env env) { API.dispose(impl, env); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,22 +24,20 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; /** - * Enables instrumentation by attaching {@linkplain Probe Probes} to some nodes in a (newly created, - * not yet executed) AST. + * Enables {@linkplain Instrumenter Instrumentation} by attaching {@linkplain Probe Probes} to some + * nodes in a (newly created, not yet executed) {@link RootNode}. * - * @see Probe - * @see Probe#addProbeListener(ProbeListener) + * @see Instrumenter#probe(com.oracle.truffle.api.nodes.Node) */ public interface ASTProber { /** - * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching - * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable() - * non-instrumentable} nodes. + * Walk the root's AST and enable instrumentation at selected nodes by attaching + * {@linkplain Probe Probes} to them. */ - void probeAST(Node node); + void probeAST(Instrumenter instrumenter, RootNode rootNode); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -31,10 +31,13 @@ /** * Listener for receiving the result a client-provided {@linkplain AdvancedInstrumentRoot AST * fragment}, when executed by a - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. * - * @see Instrument + * @see ProbeInstrument * @see AdvancedInstrumentRoot * @see AdvancedInstrumentRootFactory */ @@ -43,7 +46,7 @@ /** * Notifies listener that a client-provided {@linkplain AdvancedInstrumentRoot AST fragment} has * been executed by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument} with the specified result, possibly {@code null}. * <p> * <strong>Note: </strong> Truffle will attempt to optimize implementations through partial @@ -54,7 +57,7 @@ * @param vFrame execution frame at the guest-language AST node * @param result the result of this AST fragment's execution */ - void notifyResult(Node node, VirtualFrame vFrame, Object result); + void onExecution(Node node, VirtualFrame vFrame, Object result); /** * Notifies listener that execution of client-provided {@linkplain AdvancedInstrumentRoot AST @@ -69,6 +72,6 @@ * @param vFrame execution frame at the guest-language AST node * @param ex the exception */ - void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex); + void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java Wed Sep 30 16:33:56 2015 -0700 @@ -30,17 +30,17 @@ /** * Root of a client-provided AST fragment that can be executed efficiently, subject to full Truffle * optimization, by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * - * @see Instrument + * @see ProbeInstrument * @see AdvancedInstrumentRootFactory * @see AdvancedInstrumentResultListener */ public abstract class AdvancedInstrumentRoot extends Node implements InstrumentationNode { /** - * Executes this AST fragment on behalf of a client {@link Instrument}, just before the + * Executes this AST fragment on behalf of a client {@link ProbeInstrument}, just before the * guest-language AST node to which the {@link Probe} holding the Instrument is executed. * * @param node the guest-language AST node to which the host Instrument's Probe is attached
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java Wed Sep 30 16:33:56 2015 -0700 @@ -29,17 +29,18 @@ /** * Creator of {@linkplain AdvancedInstrumentRoot AST fragments} suitable for efficient execution, * subject to full Truffle optimization, by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * - * @see Instrument + * @see ProbeInstrument * @see AdvancedInstrumentRoot */ public interface AdvancedInstrumentRootFactory { /** * Provider of {@linkplain AdvancedInstrumentRoot AST fragment} instances to be executed by the - * Instrumentation Framework at a {@linkplain Probe Probed} site in a guest-language AST. + * {@linkplain Instrumenter Instrumentation Framework} at a {@linkplain Probe Probed} site in a + * guest-language AST. * <p> * <strong>Notes:</strong> * <ul>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/EventHandlerNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,64 @@ +/* + * 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.instrument; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; + +/** + * An Instrumentation-managed {@link Node} that synchronously propagates notification of AST + * Execution Events through the {@linkplain Instrumenter Instrumentation Framework} . + */ +public abstract class EventHandlerNode extends Node implements InstrumentationNode { + + protected EventHandlerNode() { + } + + /** + * An AST node's execute method is about to be called. + */ + public abstract void enter(Node node, VirtualFrame vFrame); + + /** + * An AST Node's {@code void}-valued execute method has just returned. + */ + public abstract void returnVoid(Node node, VirtualFrame vFrame); + + /** + * An AST Node's execute method has just returned a value (boxed if primitive). + */ + public abstract void returnValue(Node node, VirtualFrame vFrame, Object result); + + /** + * An AST Node's execute method has just thrown an exception. + */ + public abstract void returnExceptional(Node node, VirtualFrame vFrame, Exception exception); + + /** + * Gets the {@link Probe} that manages this chain of event handling. + */ + public abstract Probe getProbe(); + +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,627 +24,34 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeCost; -import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.source.SourceSection; - -// TODO (mlvdv) these statics should not be global. Move them to some kind of context. -// TODO (mlvdv) migrate factory (together with Probe)? break out nested classes? - /** * A <em>binding</em> between: * <ol> - * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in - * an executing Truffle AST, and</li> + * <li>Some source of <em>execution events</em> in an executing Truffle AST, and</li> * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client. * </ol> * <p> * Client-oriented documentation for the use of Instruments is available online at <a * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https:// * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a> - * <p> - * 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 - * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes. - * <p> - * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged - * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than - * the properties associated with a Probe: it's {@link SourceSection} and any associated instances - * of {@link SyntaxTag}. - * <p> - * AST cloning is <em>not transparent</em> to clients that use the - * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance - * (and thus the AST instance) where the event takes place. - * <p> - * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4> - * <p> - * <ul> - * <li>A new Instrument is created in permanent association with a client-provided - * <em>listener.</em></li> * - * <li>Multiple Instruments may share a single listener.</li> - * - * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a - * Probe, at which time the Instrument begins routing execution events from the Probe's AST location - * to the Instrument's listener.</li> - * - * <li>Neither Instruments nor Probes are {@link Node}s.</li> - * - * <li>A Probe has a single source-based location in an AST, but manages a separate - * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li> - * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned - * along with the rest of the AST.</li> - * - * <li>When a new Instrument (for example an instance created by - * {@link Instrument#create(com.oracle.truffle.api.instrument.SimpleInstrumentListener, java.lang.String)} - * is attached to a Probe, the Instrument inserts a new instance of its private Node type into - * <em>each of the instrument chains</em> managed by the Probe, i.e. one node instance per existing - * clone of the AST.</li> - * - * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the - * Instrument's private Node type will be cloned along with the rest of the the AST.</li> - * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the - * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener. - * </li> - * - * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the - * Instrument searches every instrument chain associated with the Probe and removes the instance of - * its private Node type.</li> - * - * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the - * AST.</li> - * - * </ul> - * - * @see Probe - * @see TruffleEvents + * @see Instrumenter */ public abstract class Instrument { - /** - * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a - * client-provided listener. - * - * @param listener a listener for execution events - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) { - return new SimpleInstrument(listener, instrumentInfo); - } - - /** - * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together - * with access to Truffle execution state, to a client-provided listener. - * - * @param standardListener a listener for execution events and execution state - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) { - return new StandardInstrument(standardListener, instrumentInfo); - } - - /** - * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to - * full Truffle optimization, a client-provided AST fragment every time the Probed node is - * entered. - * <p> - * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework - * and reported to the listener; there is no other notification. - * - * @param resultListener optional client callback for results/failure notification - * @param rootFactory provider of AST fragments on behalf of the client - * @param requiredResultType optional requirement, any non-assignable result is reported to the - * the listener, if any, as a failure - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { - return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo); - } - - // TODO (mlvdv) experimental - /** - * For implementation testing. - */ - public static Instrument create(TruffleOptListener listener) { - return new TruffleOptInstrument(listener, null); - } - - /** - * Has this instrument been disposed? stays true once set. - */ - private boolean isDisposed = false; - - protected Probe probe = null; - - /** - * Optional documentation, mainly for debugging. - */ - private final String instrumentInfo; - - private Instrument(String instrumentInfo) { - this.instrumentInfo = instrumentInfo; - } - - /** - * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not - * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}. - */ - public Probe getProbe() { - return probe; - } - - /** - * Removes this Instrument from the Probe to which it attached and renders this Instrument - * inert. - * - * @throws IllegalStateException if this instrument has already been disposed - */ - public void dispose() throws IllegalStateException { - if (isDisposed) { - throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt"); - } - if (probe != null) { - // It's attached - probe.disposeInstrument(this); - probe = null; - } - this.isDisposed = true; - } - - /** - * For internal implementation only. - */ - void setAttachedTo(Probe probe) { - this.probe = probe; - } - - /** - * Has this instrument been disposed and rendered unusable? - */ - boolean isDisposed() { - return isDisposed; - } - - abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode); - - /** - * Removes this instrument from an instrument chain. - */ - abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); - - /** - * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}. - */ - private static final class SimpleInstrument extends Instrument { - - /** - * Tool-supplied listener for events. - */ - private final SimpleInstrumentListener simpleListener; - - private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) { - super(instrumentInfo); - this.simpleListener = simpleListener; - } - - @Override - AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new SimpleInstrumentNode(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(SimpleInstrument.this); - } - if (!found) { - throw new IllegalStateException("Couldn't find instrument node to remove: " + this); - } - return instrumentNode; - } - - /** - * Node that implements a {@link SimpleInstrument} in a particular AST. - */ - @NodeInfo(cost = NodeCost.NONE) - private final class SimpleInstrumentNode extends AbstractInstrumentNode { - - private SimpleInstrumentNode(AbstractInstrumentNode nextNode) { - super(nextNode); - } - - public void enter(Node node, VirtualFrame vFrame) { - SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe); - if (nextInstrumentNode != null) { - nextInstrumentNode.enter(node, vFrame); - } - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnVoid(node, vFrame); - } - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnValue(node, vFrame, result); - } - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnExceptional(node, vFrame, exception); - } - } - - public String instrumentationInfo() { - final String info = getInstrumentInfo(); - return info != null ? info : simpleListener.getClass().getSimpleName(); - } - } + protected Instrument() { } /** - * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. + * Detaches this from its source of execution events and renders itself unusable. + * + * @throws IllegalStateException if this has already been disposed */ - private static final class StandardInstrument extends Instrument { - - /** - * Tool-supplied listener for AST events. - */ - private final StandardInstrumentListener standardListener; - - private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) { - super(instrumentInfo); - this.standardListener = standardListener; - } - - @Override - AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new StandardInstrumentNode(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(StandardInstrument.this); - } - if (!found) { - throw new IllegalStateException("Couldn't find instrument node to remove: " + this); - } - return instrumentNode; - } - - /** - * Node that implements a {@link StandardInstrument} in a particular AST. - */ - @NodeInfo(cost = NodeCost.NONE) - private final class StandardInstrumentNode extends AbstractInstrumentNode { - - private StandardInstrumentNode(AbstractInstrumentNode nextNode) { - super(nextNode); - } - - public void enter(Node node, VirtualFrame vFrame) { - standardListener.enter(StandardInstrument.this.probe, node, vFrame); - if (nextInstrumentNode != null) { - nextInstrumentNode.enter(node, vFrame); - } - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnVoid(node, vFrame); - } - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnValue(node, vFrame, result); - } - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception); - if (nextInstrumentNode != null) { - nextInstrumentNode.returnExceptional(node, vFrame, exception); - } - } - - public String instrumentationInfo() { - final String info = getInstrumentInfo(); - return info != null ? info : standardListener.getClass().getSimpleName(); - } - } - } + public abstract void dispose() throws IllegalStateException; /** - * An instrument that allows clients to provide an AST fragment to be executed directly from - * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle - * AST with potential for full optimization. + * Has this been detached from its source of execution events. */ - private static final class AdvancedInstrument extends Instrument { - - private final AdvancedInstrumentResultListener resultListener; - private final AdvancedInstrumentRootFactory rootFactory; - private final Class<?> requiredResultType; - - private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { - super(instrumentInfo); - this.resultListener = resultListener; - this.rootFactory = rootFactory; - this.requiredResultType = requiredResultType; - } - - @Override - AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new AdvancedInstrumentNode(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(AdvancedInstrument.this); - } - if (!found) { - throw new IllegalStateException("Couldn't find instrument node to remove: " + this); - } - return instrumentNode; - } - - /** - * Node that implements a {@link AdvancedInstrument} in a particular AST. - */ - @NodeInfo(cost = NodeCost.NONE) - private final class AdvancedInstrumentNode extends AbstractInstrumentNode { - - @Child private AdvancedInstrumentRoot instrumentRoot; - - private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) { - super(nextNode); - } - - public void enter(Node node, VirtualFrame vFrame) { - if (instrumentRoot == null) { - try { - final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node); - if (newInstrumentRoot != null) { - instrumentRoot = newInstrumentRoot; - adoptChildren(); - AdvancedInstrument.this.probe.invalidateProbeUnchanged(); - } - } catch (RuntimeException ex) { - if (resultListener != null) { - resultListener.notifyFailure(node, vFrame, ex); - } - } - } - if (instrumentRoot != null) { - try { - final Object result = instrumentRoot.executeRoot(node, vFrame); - if (resultListener != null) { - checkResultType(result); - resultListener.notifyResult(node, vFrame, result); - } - } catch (RuntimeException ex) { - if (resultListener != null) { - resultListener.notifyFailure(node, vFrame, ex); - } - } - } - if (nextInstrumentNode != null) { - nextInstrumentNode.enter(node, vFrame); - } - } - - private void checkResultType(Object result) { - if (requiredResultType == null) { - return; - } - if (result == null) { - throw instrumentResultNull(); - } - if (!(requiredResultType.isAssignableFrom(result.getClass()))) { - throw instrumentResultWrongType(result); - } - } - - @TruffleBoundary - private RuntimeException instrumentResultNull() { - return new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required"); - } - - @TruffleBoundary - private RuntimeException instrumentResultWrongType(Object result) { - return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName()); - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnVoid(node, vFrame); - } - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnValue(node, vFrame, result); - } - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnExceptional(node, vFrame, exception); - } - } - - public String instrumentationInfo() { - final String info = getInstrumentInfo(); - return info != null ? info : rootFactory.getClass().getSimpleName(); - } - } - } + public abstract boolean isDisposed(); - public interface TruffleOptListener { - void notifyIsCompiled(boolean isCompiled); - } - - private static final class TruffleOptInstrument extends Instrument { - - private final TruffleOptListener toolOptListener; - - private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) { - super(instrumentInfo); - this.toolOptListener = listener; - } - - @Override - AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new TruffleOptInstrumentNode(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(TruffleOptInstrument.this); - } - if (!found) { - throw new IllegalStateException("Couldn't find instrument node to remove: " + this); - } - return instrumentNode; - } - - @NodeInfo(cost = NodeCost.NONE) - private final class TruffleOptInstrumentNode extends AbstractInstrumentNode { - - private boolean isCompiled; - - private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) { - super(nextNode); - this.isCompiled = CompilerDirectives.inCompiledCode(); - } - - public void enter(Node node, VirtualFrame vFrame) { - if (this.isCompiled != CompilerDirectives.inCompiledCode()) { - this.isCompiled = CompilerDirectives.inCompiledCode(); - TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled); - } - if (nextInstrumentNode != null) { - nextInstrumentNode.enter(node, vFrame); - } - } - - public void returnVoid(Node node, VirtualFrame vFrame) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnVoid(node, vFrame); - } - } - - public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnValue(node, vFrame, result); - } - } - - public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (nextInstrumentNode != null) { - nextInstrumentNode.returnExceptional(node, vFrame, exception); - } - } - - public String instrumentationInfo() { - final String info = getInstrumentInfo(); - return info != null ? info : toolOptListener.getClass().getSimpleName(); - } - } - } - - @NodeInfo(cost = NodeCost.NONE) - abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode { - - @Child protected AbstractInstrumentNode nextInstrumentNode; - - protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) { - this.nextInstrumentNode = nextNode; - } - - @Override - public boolean isInstrumentable() { - return false; - } - - /** - * Gets the instrument that created this node. - */ - private Instrument getInstrument() { - return Instrument.this; - } - - /** - * Removes the node from this chain that was added by a particular instrument, assuming that - * the head of the chain is not the one to be replaced. This is awkward, but is required - * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work - * for the tail of the list, which would be replacing itself with null. So the replacement - * must be directed the parent of the node being removed. - */ - private boolean removeFromChain(Instrument instrument) { - assert getInstrument() != instrument; - if (nextInstrumentNode == null) { - return false; - } - if (nextInstrumentNode.getInstrument() == instrument) { - // Next is the one to remove - if (nextInstrumentNode.nextInstrumentNode == null) { - // Next is at the tail; just forget - nextInstrumentNode = null; - } else { - // Replace next with its successor - nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode); - } - return true; - } - return nextInstrumentNode.removeFromChain(instrument); - } - - protected String getInstrumentInfo() { - return Instrument.this.instrumentInfo; - } - } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +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.api.instrument; - -public class InstrumentationException extends Exception { - - public InstrumentationException(RuntimeException ex) { - super(ex); - } - - private static final long serialVersionUID = 447857066220935502L; - -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,13 +24,13 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; /** - * A marker interface for Truffle {@linkplain Node nodes} that internally implement the - * <em>Instrumentation Framework</em>. Such nodes should not be part of any Guest Language execution - * semantics, and should in general not be visible to ordinary Instrumentation clients. + * A marker interface for the Truffle {@linkplain Node Nodes} that internally implement the + * {@linkplain Instrumenter Instrumentation Framework} . Such nodes should not be part of any Guest + * Language execution semantics, and should in general not be visible to ordinary Instrumentation + * clients. */ public interface InstrumentationNode { @@ -39,30 +39,4 @@ */ String instrumentationInfo(); - /** - * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of - * the Instrumentation Framework, not visible in this form to Instrumentation clients. - */ - interface TruffleEvents { - - /** - * An AST node's execute method is about to be called. - */ - void enter(Node node, VirtualFrame vFrame); - - /** - * An AST Node's {@code void}-valued execute method has just returned. - */ - void returnVoid(Node node, VirtualFrame vFrame); - - /** - * An AST Node's execute method has just returned a value (boxed if primitive). - */ - void returnValue(Node node, VirtualFrame vFrame, Object result); - - /** - * An AST Node's execute method has just thrown an exception. - */ - void returnExceptional(Node node, VirtualFrame vFrame, Exception exception); - } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +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.api.instrument; - -/** - * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language - * program execution. - * <p> - * Tools share a common <em>life cycle</em>: - * <ul> - * <li>A newly created tool is inert until {@linkplain #install() installed}.</li> - * <li>An installed tool becomes <em>enabled</em> and immediately begins installing - * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from - * those instruments</li> - * <li>A tool may only be installed once.</li> - * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily) - * configured differently with respect to what data is being collected.</li> - * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled} - * arbitrarily.</li> - * <li>A disabled tool: - * <ul> - * <li>Collects no data;</li> - * <li>Retains existing AST instrumentation;</li> - * <li>Continues to instrument newly created ASTs; and</li> - * <li>Retains previously collected data.</li> - * </ul> - * </li> - * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool - * installed but with all previously collected data removed.</li> - * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not - * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li> - * </ul> - * <p> - * Tool-specific methods that access data collected by the tool should: - * <ul> - * <li>Return modification-safe representations of the data; and</li> - * <li>Not change the state of the data.</li> - * </ul> - * <b>Note:</b><br> - * Tool installation is currently <em>global</em> to the Truffle Execution environment. When - * language-agnostic management of individual execution environments is added to the platform, - * installation will be (optionally) specific to a single execution environment. - */ -public abstract class InstrumentationTool { - // TODO (mlvdv) still thinking about the most appropriate name for this class of tools - - private enum ToolState { - - /** Not yet installed, inert. */ - UNINSTALLED, - - /** Installed, collecting data. */ - ENABLED, - - /** Installed, not collecting data. */ - DISABLED, - - /** Was installed, but now removed, inactive, and no longer usable. */ - DISPOSED; - } - - private ToolState toolState = ToolState.UNINSTALLED; - - protected InstrumentationTool() { - } - - /** - * Connect the tool to some part of the Truffle runtime, and enable data collection to start. - * Instrumentation will only be added to subsequently created ASTs. - * - * @throws IllegalStateException if the tool has previously been installed. - */ - public final void install() { - checkUninstalled(); - if (internalInstall()) { - toolState = ToolState.ENABLED; - } - } - - /** - * @return whether the tool is currently collecting data. - */ - public final boolean isEnabled() { - return toolState == ToolState.ENABLED; - } - - /** - * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not - * collecting data, but keeping data already collected). - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void setEnabled(boolean isEnabled) { - checkInstalled(); - internalSetEnabled(isEnabled); - toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED; - } - - /** - * Clears any data already collected, but otherwise does not change the state of the tool. - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void reset() { - checkInstalled(); - internalReset(); - } - - /** - * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already - * collected. - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void dispose() { - checkInstalled(); - internalDispose(); - toolState = ToolState.DISPOSED; - } - - /** - * @return whether the installation succeeded. - */ - protected abstract boolean internalInstall(); - - /** - * No subclass action required. - * - * @param isEnabled - */ - protected void internalSetEnabled(boolean isEnabled) { - } - - protected abstract void internalReset(); - - protected abstract void internalDispose(); - - /** - * Ensure that the tool is currently installed. - * - * @throws IllegalStateException - */ - private void checkInstalled() throws IllegalStateException { - if (toolState == ToolState.UNINSTALLED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed"); - } - if (toolState == ToolState.DISPOSED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed"); - } - } - - /** - * Ensure that the tool has not yet been installed. - * - * @throws IllegalStateException - */ - private void checkUninstalled() { - if (toolState != ToolState.UNINSTALLED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed"); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,661 @@ +/* + * 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 java.io.PrintStream; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument; +import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + +/** + * Client access to instrumentation services in a Truffle execution environment. + * <p> + * Services include: + * <ul> + * <li>A collection of {@linkplain Probe Probes}, each of which is {@linkplain #probe(Node) created} + * by clients in permanent association with a particular {@linkplain SourceSection source code + * location} in an AST executing in this environment. The Probe keeps tracks of all <em>clones</em> + * of the AST and ensures that any instrumentation <em>attached</em> to the Probe is put into effect + * in each AST clone at the {@link Node} that corresponds to the Probe's source code location.</li> + * <p> + * <li>A collection of {@linkplain ProbeListener listeners} that have registered to be notified + * whenever a new {@link Probe} is created in this environment and whenever a new {@link SyntaxTag} + * (or simply "tag") is newly added to an existing {@link Probe} in this environment.</li> + * <p> + * <li>The ability to {@linkplain #findProbesTaggedAs(SyntaxTag) enumerate} all existing + * {@linkplain Probe Probes} in this environment, optionally filtered to include only those to which + * a specific {@linkplain SyntaxTag tag} has been added.</li> + * <p> + * <li>The ability to <em>attach</em> a client-provided <em>event listener</em> to an existing + * {@link Probe} in this environment. The listener subsequently receives notification of execution + * events at the {@linkplain Node Nodes} corresponding to the Probe's {@linkplain SourceSection + * source code location}. The <em>attachment</em> also produces a {@link ProbeInstrument} that + * represents the binding, and which can be used to {@linkplain Instrument#dispose() detach} the + * listener from the Probe and stop event notification. A listener can be attached to any number of + * Probes, each time producing a new {@linkplain ProbeInstrument} that represents the binding.</li> + * <p> + * <li>The ability to <em>attach</em> a client-provided <em>event listener</em> to a specific + * {@linkplain SyntaxTag tag} for all {@linkplain Probe Probes} in the environment. A maximum of + * <em>one</em> listener may be attached to receive notification of "<em>before</em>" execution + * events (i.e. the flow of execution is just about to enter a {@link Node}), and a maximum of + * <em>one</em> listener may be attached to receive notification of "<em>after</em>" execution + * events. The <em>attachment</em> also produces a {@link TagInstrument} that represents the + * binding, and which can be used to {@linkplain Instrument#dispose() detach} the listener from the + * Probes and stop event notification. This mechanism is designed for much lower runtime overhead + * than other ways to accomplish the same thing, e.g. by attaching one listener individually to + * every Probe with the desired tag.</li> + * <p> + * <li>A collection of {@linkplain Tool Tools}, possibly client-provided, that can be + * {@linkplain #install(Tool) installed} for data collection, possibly providing their own services + * with the resulting information.</li> + * </ul> + */ +public final class Instrumenter { + + private static final boolean TRACE = false; + private static final String TRACE_PREFIX = "Instrumenter: "; + private static final PrintStream OUT = System.out; + + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(TRACE_PREFIX + String.format(format, args)); + } + } + + private enum ToolState { + + /** Not yet installed, inert. */ + UNINSTALLED, + + /** Installed, collecting data. */ + ENABLED, + + /** Installed, not collecting data. */ + DISABLED, + + /** Was installed, but now removed, inactive, and no longer usable. */ + DISPOSED; + } + + /** + * {@linkplain Instrumenter Instrumentation}-based collectors of data during Guest Language + * program execution. + * <p> + * Tools share a common <em>life cycle</em>: + * <ul> + * <li>A newly created tool is "UNINSTALLED"; it does nothing until + * {@linkplain Instrumenter#install(Tool) installed} .</li> + * <li>An installed tool becomes "ENABLED" and immediately begins attaching + * {@linkplain ProbeInstrument instrumentation} to ASTs and collecting execution data.</li> + * <li>A tool may only be installed once.</li> + * <li>It is possible to install multiple instances of a tool, possibly (but not necessarily) + * configured differently with respect to what data is being collected.</li> + * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) "ENABLED" and "DISABLED"} + * arbitrarily.</li> + * <li>A disabled tool: + * <ul> + * <li>Collects no data;</li> + * <li>Retains existing AST instrumentation;</li> + * <li>Continues to instrument newly created ASTs; and</li> + * <li>Retains previously collected data.</li> + * </ul> + * </li> + * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool + * installed but with all previously collected data removed.</li> + * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not + * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data + * persists.</li> + * </ul> + * <p> + * Tool-specific methods that access data collected by the tool should: + * <ul> + * <li>Return modification-safe representations of the data; and</li> + * <li>Not change the state of the data.</li> + * </ul> + */ + public abstract static class Tool { + // TODO (mlvdv) still thinking about the most appropriate name for this class of tools + + private ToolState toolState = ToolState.UNINSTALLED; + + private Instrumenter instrumenter; + + protected Tool() { + } + + final void install(Instrumenter inst) { + checkUninstalled(); + this.instrumenter = inst; + + if (internalInstall()) { + toolState = ToolState.ENABLED; + } + instrumenter.tools.add(this); + } + + /** + * @return whether the tool is currently collecting data. + */ + public final boolean isEnabled() { + return toolState == ToolState.ENABLED; + } + + /** + * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not + * collecting data, but keeping data already collected). + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void setEnabled(boolean isEnabled) { + checkInstalled(); + internalSetEnabled(isEnabled); + toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED; + } + + /** + * Clears any data already collected, but otherwise does not change the state of the tool. + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void reset() { + checkInstalled(); + internalReset(); + } + + /** + * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data + * already collected. + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void dispose() { + checkInstalled(); + internalDispose(); + toolState = ToolState.DISPOSED; + instrumenter.tools.remove(this); + } + + /** + * @return whether the installation succeeded. + */ + protected abstract boolean internalInstall(); + + /** + * No subclass action required. + * + * @param isEnabled + */ + protected void internalSetEnabled(boolean isEnabled) { + } + + protected abstract void internalReset(); + + protected abstract void internalDispose(); + + protected final Instrumenter getInstrumenter() { + return instrumenter; + } + + /** + * Ensure that the tool is currently installed. + * + * @throws IllegalStateException + */ + private void checkInstalled() throws IllegalStateException { + if (toolState == ToolState.UNINSTALLED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed"); + } + if (toolState == ToolState.DISPOSED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed"); + } + } + + /** + * Ensure that the tool has not yet been installed. + * + * @throws IllegalStateException + */ + private void checkUninstalled() { + if (toolState != ToolState.UNINSTALLED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed"); + } + } + } + + private final Object vm; + + /** Tools that have been created, but not yet disposed. */ + Set<Tool> tools = new HashSet<>(); + + private final Set<ASTProber> astProbers = Collections.synchronizedSet(new LinkedHashSet<ASTProber>()); + + private final List<ProbeListener> probeListeners = new ArrayList<>(); + + /** + * All Probes that have been created. + */ + private final List<WeakReference<Probe>> probes = new ArrayList<>(); + + /** + * A global instrument that triggers notification just before executing any Node that is Probed + * with a matching tag. + */ + @CompilationFinal private BeforeTagInstrument beforeTagInstrument = null; + + /** + * A global instrument that triggers notification just after executing any Node that is Probed + * with a matching tag. + */ + @CompilationFinal private AfterTagInstrument afterTagInstrument = null; + + Instrumenter(Object vm) { + this.vm = vm; + } + + /** + * Prepares an AST node for {@linkplain ProbeInstrument instrumentation}, where the node is + * presumed to be part of a well-formed Truffle AST that has not yet been executed. + * <p> + * <em>Probing</em> a node is idempotent: + * <ul> + * <li>If the node has not been Probed, modifies the AST by first inserting a + * {@linkplain #createWrapperNode(Node) wrapper node} between the node and its parent and then + * returning the newly created Probe associated with the wrapper.</li> + * <li>If the node has been Probed, returns the Probe associated with its existing wrapper.</li> + * <li>No more than one {@link Probe} may be associated with a node, so a wrapper may not wrap + * another wrapper.</li> + * </ul> + * It is a runtime error to attempt Probing an AST node with no parent. + * + * @return a (possibly newly created) {@link Probe} associated with this node. + * @throws ProbeException (unchecked) when a Probe cannot be created, leaving the AST unchanged + */ + @SuppressWarnings("rawtypes") + public Probe probe(Node node) { + + final Node parent = node.getParent(); + + if (node instanceof WrapperNode) { + throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, node, null); + } + + if (parent == null) { + throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, node, null); + } + + if (parent instanceof WrapperNode) { + final WrapperNode wrapper = (WrapperNode) parent; + if (TRACE) { + final Probe probe = wrapper.getProbe(); + final SourceSection sourceSection = wrapper.getChild().getSourceSection(); + final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); + trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription()); + } + return wrapper.getProbe(); + } + + if (!ACCESSOR.isInstrumentable(vm, node)) { + throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null); + } + + // Create a new wrapper/Probe with this node as its child. + final WrapperNode wrapper = createWrapperNode(node); + + if (wrapper == null || !(wrapper instanceof Node)) { + throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper); + } + + final Node wrapperNode = (Node) wrapper; + + if (!node.isSafelyReplaceableBy(wrapperNode)) { + throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, node, wrapper); + } + + final SourceSection sourceSection = wrapper.getChild().getSourceSection(); + final ProbeNode probeNode = new ProbeNode(); + Class<? extends TruffleLanguage> l = ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); + final Probe probe = new Probe(this, l, probeNode, sourceSection); + probes.add(new WeakReference<>(probe)); + probeNode.probe = probe; // package private access + wrapper.insertEventHandlerNode(probeNode); + node.replace(wrapperNode); + if (TRACE) { + final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); + trace("PROBED %s %s %s", "Probe@", location, probe.getTagsDescription()); + } + for (ProbeListener listener : probeListeners) { + listener.newProbeInserted(probe); + } + return probe; + } + + /** + * Adds a {@link ProbeListener} to receive events. + */ + public void addProbeListener(ProbeListener listener) { + assert listener != null; + probeListeners.add(listener); + } + + /** + * Removes a {@link ProbeListener}. Ignored if listener not found. + */ + public void removeProbeListener(ProbeListener listener) { + probeListeners.remove(listener); + } + + /** + * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of + * probes if the specified tag is {@code null}. + * + * @return A collection of probes containing the given tag. + */ + public Collection<Probe> findProbesTaggedAs(SyntaxTag tag) { + final List<Probe> taggedProbes = new ArrayList<>(); + for (WeakReference<Probe> ref : probes) { + Probe probe = ref.get(); + if (probe != null) { + if (tag == null || probe.isTaggedAs(tag)) { + taggedProbes.add(ref.get()); + } + } + } + return taggedProbes; + } + + /** + * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if + * the argument is already registered, runtime error if argument is {@code null}. + */ + public void registerASTProber(ASTProber prober) { + if (prober == null) { + throw new IllegalArgumentException("Register non-null ASTProbers"); + } + astProbers.add(prober); + } + + public void unregisterASTProber(ASTProber prober) { + astProbers.remove(prober); + } + + /** + * <em>Attaches</em> a {@link SimpleInstrumentListener listener} to a {@link Probe}, creating a + * <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is + * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * + * @param probe source of AST execution events + * @param listener receiver of execution events + * @param instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public ProbeInstrument attach(Probe probe, SimpleInstrumentListener listener, String instrumentInfo) { + final ProbeInstrument instrument = new ProbeInstrument.SimpleInstrument(listener, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + /** + * <em>Attaches</em> a {@link StandardInstrumentListener listener} to a {@link Probe}, creating + * a <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is + * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * + * @param probe source of AST execution events + * @param listener receiver of execution events + * @param instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public ProbeInstrument attach(Probe probe, StandardInstrumentListener listener, String instrumentInfo) { + final ProbeInstrument instrument = new ProbeInstrument.StandardInstrument(listener, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + /** + * <em>Attaches</em> a {@link AdvancedInstrumentResultListener listener} to a {@link Probe}, + * creating a <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is + * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * <p> + * This Instrument executes efficiently, subject to full Truffle optimization, a client-provided + * AST fragment every time the Probed node is entered. + * <p> + * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework + * and reported to the listener; there is no other notification. + * + * @param probe source of AST execution events + * @param listener optional client callback for results/failure notification + * @param rootFactory provider of AST fragments on behalf of the client + * @param requiredResultType optional requirement, any non-assignable result is reported to the + * the listener, if any, as a failure + * @param instrumentInfo instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public ProbeInstrument attach(Probe probe, AdvancedInstrumentResultListener listener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { + final ProbeInstrument instrument = new ProbeInstrument.AdvancedInstrument(listener, rootFactory, requiredResultType, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + // TODO (mlvdv) allow multiple <em>before</em> instruments without performance hit? + /** + * Sets the current "<em>before</em>" TagInstrument; there can be no more than one in effect. + * <ul> + * <li>The Instrument triggers a callback just <strong><em>before</em></strong> execution + * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) + * with the specified {@link SyntaxTag}.</li> + * <li>Calling {@link TagInstrument#dispose()} removes the instrument.</li> + * </ul> + * + * @param tag identifies the nodes to be instrumented + * @param listener receiver of <em>before</em> execution events + * @param instrumentInfo optional, mainly for debugging. + * @return a newly created, active Instrument + * @throws IllegalStateException if called when a <em>before</em> Instrument is active. + */ + public TagInstrument attach(SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) { + if (beforeTagInstrument != null) { + throw new IllegalStateException("Only one 'before' TagInstrument at a time"); + } + this.beforeTagInstrument = new TagInstrument.BeforeTagInstrument(this, tag, listener, instrumentInfo); + notifyTagInstrumentChange(); + return beforeTagInstrument; + } + + // TODO (mlvdv) allow multiple <em>after</em> instruments without performance hit? + /** + * Sets the current "<em>after</em>" TagInstrument; there can be no more than one in effect. + * <ul> + * <li>The Instrument triggers a callback just <strong><em>after</em></strong> execution reaches + * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with + * the specified {@link SyntaxTag}.</li> + * <li>Calling {@link TagInstrument#dispose()} removes the instrument.</li> + * </ul> + * + * @param tag identifies the nodes to be instrumented + * @param listener receiver of <em>after</em> execution events + * @param instrumentInfo optional, mainly for debugging. + * @return a newly created, active Instrument + * @throws IllegalStateException if called when a <em>after</em> Instrument is active. + */ + public TagInstrument attach(SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) { + if (afterTagInstrument != null) { + throw new IllegalStateException("Only one 'afater' TagInstrument at a time"); + } + this.afterTagInstrument = new TagInstrument.AfterTagInstrument(this, tag, listener, instrumentInfo); + notifyTagInstrumentChange(); + return afterTagInstrument; + } + + /** + * Connects the tool to some part of the Truffle runtime, and enable data collection to start. + * + * @return the tool + * @throws IllegalStateException if the tool has previously been installed or has been disposed. + */ + public Tool install(Tool tool) { + tool.install(this); + return tool; + } + + @SuppressWarnings("unused") + void executionStarted(Source s) { + } + + void executionEnded() { + } + + WrapperNode createWrapperNode(Node node) { + return ACCESSOR.createWrapperNode(vm, node); + } + + void tagAdded(Probe probe, SyntaxTag tag, Object tagValue) { + for (ProbeListener listener : probeListeners) { + listener.probeTaggedAs(probe, tag, tagValue); + } + } + + BeforeTagInstrument getBeforeTagInstrument() { + return beforeTagInstrument; + } + + AfterTagInstrument getAfterTagInstrument() { + return afterTagInstrument; + } + + void disposeBeforeTagInstrument() { + beforeTagInstrument = null; + notifyTagInstrumentChange(); + } + + void disposeAfterTagInstrument() { + afterTagInstrument = null; + notifyTagInstrumentChange(); + } + + private void notifyTagInstrumentChange() { + for (WeakReference<Probe> ref : probes) { + final Probe probe = ref.get(); + if (probe != null) { + probe.notifyTagInstrumentsChanged(); + } + } + } + + /** + * Enables instrumentation in a newly created AST by applying all registered instances of + * {@link ASTProber}. + */ + private void probeAST(RootNode rootNode) { + if (!astProbers.isEmpty()) { + + String name = "<?>"; + final SourceSection sourceSection = rootNode.getSourceSection(); + if (sourceSection != null) { + final Source source = sourceSection.getSource(); + if (source != null) { + name = source.getShortName(); + } else { + name = sourceSection.getShortDescription(); + } + } + trace("START %s", name); + for (ProbeListener listener : probeListeners) { + listener.startASTProbing(rootNode); + } + for (ASTProber prober : astProbers) { + prober.probeAST(this, rootNode); + } + for (ProbeListener listener : probeListeners) { + listener.endASTProbing(rootNode); + } + trace("FINISHED %s", name); + } + } + + static final class AccessorInstrument extends Accessor { + + @Override + protected Instrumenter createInstrumenter(Object vm) { + return new Instrumenter(vm); + } + + @Override + protected boolean isInstrumentable(Object vm, Node node) { + return super.isInstrumentable(vm, node); + } + + @Override + public WrapperNode createWrapperNode(Object vm, Node node) { + return super.createWrapperNode(vm, node); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { + return super.findLanguage(n); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { + return probe.getLanguage(); + } + + @Override + protected void probeAST(RootNode rootNode) { + // Normally null vm argument; can be reflectively set for testing + super.getInstrumenter(testVM).probeAST(rootNode); + } + } + + static final AccessorInstrument ACCESSOR = new AccessorInstrument(); + + // Normally null; set for testing where the Accessor hasn't been fully initialized + private static Object testVM = null; + +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,18 +24,6 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeVisitor; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.utilities.CyclicAssumption; import java.io.PrintStream; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -43,66 +31,36 @@ import java.util.Collections; import java.util.List; -//TODO (mlvdv) these statics should not be global. Move them to some kind of context. +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument; +import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.utilities.CyclicAssumption; /** * A <em>binding</em> between: * <ol> - * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}), - * and</li> - * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that - * receive event notifications on behalf of external clients.</li> + * <li>a <em>guest language program location</em> in an executing Truffle AST (corresponding to a + * {@link SourceSection}), and</li> + * <li>a dynamically managed collection of <em>attached</em> {@linkplain Instrument Instruments} + * that receive event notifications on behalf of external clients.</li> * </ol> + * <strong>Note</strong>:The relationship for {@link ProbeInstrument} must be with an AST + * <em>location</em>, not a specific {@link Node}, because ASTs are routinely <em>cloned</em> at + * runtime. An AST <em>location</em> is best represented as the {@link SourceSection} from which the + * original AST Node was created. * <p> * Client-oriented documentation for the use of Probes is available online at <a * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes" >https://wiki.openjdk.java. * net/display/Graal/Finding+Probes</a> * <p> - * <h4>Implementation notes:</h4> - * <p> - * <ul> - * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a - * particular {@link SourceSection}, even though: - * <ul> - * <li>that location is represented in an AST as a {@link Node}, which might be replaced through - * optimizations such as specialization, and</li> - * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple - * Nodes in multiple ASTs.</li> - * </ul> - * </li> * - * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events} - * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is - * allowed to proceed to the child and again after execution completes.</li> - * - * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the - * same Probe.</li> - * - * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers - * correctly assigned), but before any cloning or executions. This is done by creating an instance - * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once - * registered, it will be applied automatically to every newly created AST.</li> - * - * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode} - * into the AST (as new parent of the Node being probed), together with an associated - * {@link ProbeNode} that routes execution events at the probed Node to all the - * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li> - * - * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well, - * together with their attached instrument chains. Each Probe instance intercepts cloning events and - * keeps track of all AST copies.</li> - * - * <li>All attached {@link InstrumentationNode}s effectively become part of the running program: - * <ul> - * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li> - * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any - * Truffle optimizations.</li> - * </ul> - * </li> - * - * </ul> - * - * @see Instrument + * @see Instrumenter + * @see ProbeInstrument * @see ASTProber * @see ProbeListener * @see SyntaxTag @@ -121,173 +79,7 @@ } } - private static final List<ASTProber> astProbers = new ArrayList<>(); - - private static final List<ProbeListener> probeListeners = new ArrayList<>(); - - /** - * All Probes that have been created. - */ - private static final List<WeakReference<Probe>> probes = new ArrayList<>(); - - /** - * A global trap that triggers notification just before executing any Node that is Probed with a - * matching tag. - */ - @CompilationFinal private static SyntaxTagTrap beforeTagTrap = null; - - /** - * A global trap that triggers notification just after executing any Node that is Probed with a - * matching tag. - */ - @CompilationFinal private static SyntaxTagTrap afterTagTrap = null; - - private static final class FindSourceVisitor implements NodeVisitor { - - Source source = null; - - public boolean visit(Node node) { - final SourceSection sourceSection = node.getSourceSection(); - if (sourceSection != null) { - source = sourceSection.getSource(); - return false; - } - return true; - } - } - - /** - * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning - * the {@link Source}. - */ - private static Source findSource(Node node) { - final FindSourceVisitor visitor = new FindSourceVisitor(); - node.accept(visitor); - return visitor.source; - } - - /** - * Enables instrumentation at selected nodes in all subsequently constructed ASTs. - */ - public static void registerASTProber(ASTProber prober) { - astProbers.add(prober); - } - - public static void unregisterASTProber(ASTProber prober) { - astProbers.remove(prober); - } - - /** - * Enables instrumentation in a newly created AST by applying all registered instances of - * {@link ASTProber}. - */ - public static void applyASTProbers(Node node) { - - String name = "<?>"; - final Source source = findSource(node); - if (source != null) { - name = source.getShortName(); - } else { - final SourceSection sourceSection = node.getEncapsulatingSourceSection(); - if (sourceSection != null) { - name = sourceSection.getShortDescription(); - } - } - trace("START %s", name); - for (ProbeListener listener : probeListeners) { - listener.startASTProbing(source); - } - for (ASTProber prober : astProbers) { - prober.probeAST(node); - } - for (ProbeListener listener : probeListeners) { - listener.endASTProbing(source); - } - trace("FINISHED %s", name); - } - - /** - * Adds a {@link ProbeListener} to receive events. - */ - public static void addProbeListener(ProbeListener listener) { - assert listener != null; - probeListeners.add(listener); - } - - /** - * Removes a {@link ProbeListener}. Ignored if listener not found. - */ - public static void removeProbeListener(ProbeListener listener) { - probeListeners.remove(listener); - } - - /** - * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of - * probes if the specified tag is {@code null}. - * - * @return A collection of probes containing the given tag. - */ - public static Collection<Probe> findProbesTaggedAs(SyntaxTag tag) { - final List<Probe> taggedProbes = new ArrayList<>(); - for (WeakReference<Probe> ref : probes) { - Probe probe = ref.get(); - if (probe != null) { - if (tag == null || probe.isTaggedAs(tag)) { - taggedProbes.add(ref.get()); - } - } - } - return taggedProbes; - } - - // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit? - /** - * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect. - * <ul> - * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution - * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) - * with the specified {@link SyntaxTag}.</li> - * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li> - * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set - * before-trap.</li> - * </ul> - * - * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. - */ - public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { - beforeTagTrap = newBeforeTagTrap; - for (WeakReference<Probe> ref : probes) { - final Probe probe = ref.get(); - if (probe != null) { - probe.notifyTrapsChanged(); - } - } - } - - // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit? - /** - * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect. - * <ul> - * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves - * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with - * the specified {@link SyntaxTag}.</li> - * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li> - * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set - * after-trap.</li> - * </ul> - * - * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. - */ - public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { - afterTagTrap = newAfterTagTrap; - for (WeakReference<Probe> ref : probes) { - final Probe probe = ref.get(); - if (probe != null) { - probe.notifyTrapsChanged(); - } - } - } - + private final Instrumenter instrumenter; private final SourceSection sourceSection; private final ArrayList<SyntaxTag> tags = new ArrayList<>(); private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>(); @@ -305,45 +97,67 @@ @CompilationFinal private Assumption probeStateUnchangedAssumption = probeStateUnchangedCyclic.getAssumption(); // Must invalidate whenever changed - @CompilationFinal private boolean isBeforeTrapActive = false; + @CompilationFinal private boolean isBeforeTagInstrumentActive = false; // Must invalidate whenever changed - @CompilationFinal private boolean isAfterTrapActive = false; + @CompilationFinal private boolean isAfterTagInstrumentActive = false; /** - * Intended for use only by {@link ProbeNode}. + * Constructor for use only by {@link ProbeNode}. + * <p> + * <h4>Probe Implementation notes:</h4> + * <p> + * <ul> + * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a + * particular {@link SourceSection}, even though: + * <ul> + * <li>that location is represented in an AST as a {@link Node}, which might be replaced through + * optimizations such as specialization, and</li> + * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by + * multiple Nodes in multiple ASTs.</li> + * </ul> + * </li> + * <li>The effect of the binding is to intercept {@linkplain EventHandlerNode execution events} + * arriving at the "probed" AST Node and notify each attached {@link ProbeInstrument} before + * execution is allowed to proceed to the child and again after execution completes.</li> + * + * <li>The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant + * calls return the same Probe.</li> + * + * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent + * pointers correctly assigned), but before any cloning or executions. This is done by applying + * instances of {@link ASTProber} provided by each language implementation, combined with any + * instances registered by tools via {@link Instrumenter#registerASTProber(ASTProber)}. Once + * registered, these will be applied automatically to every newly created AST.</li> + * + * <li>The "probing" of an AST Node is implemented by insertion of a + * {@link ProbeNode.WrapperNode} into the AST (as new parent of the Node being probed), together + * with an associated {@link ProbeNode} that routes execution events at the probed Node to all + * the {@linkplain ProbeInstrument Instruments} attached to the Probe's + * <em>instrument chain</em>.</li> + * + * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well, + * together with their attached instrument chains. Each Probe instance intercepts cloning events + * and keeps track of all AST copies.</li> + * + * <li>All attached {@link InstrumentationNode}s effectively become part of the running program: + * <ul> + * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle + * optimization.</li> + * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with + * any Truffle optimizations.</li> + * </ul> + * </li> + * </ul> */ - Probe(Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { + Probe(Instrumenter instrumenter, Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { + this.instrumenter = instrumenter; this.sourceSection = sourceSection; - probes.add(new WeakReference<>(this)); registerProbeNodeClone(probeNode); - if (TRACE) { - final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); - trace("ADDED %s %s %s", "Probe@", location, getTagsDescription()); - } - for (ProbeListener listener : probeListeners) { - listener.newProbeInserted(this); - } this.language = l; } /** - * Is this node tagged as belonging to a particular human-sensible category of language - * constructs? - */ - public boolean isTaggedAs(SyntaxTag tag) { - assert tag != null; - return tags.contains(tag); - } - - /** - * In which user-sensible categories has this node been tagged (<em>empty set</em> if none). - */ - public Collection<SyntaxTag> getSyntaxTags() { - return Collections.unmodifiableCollection(tags); - } - - /** * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe}; * {@code no-op} if already in the set. */ @@ -351,21 +165,21 @@ assert tag != null; if (!tags.contains(tag)) { tags.add(tag); - for (ProbeListener listener : probeListeners) { - listener.probeTaggedAs(this, tag, tagValue); - } + instrumenter.tagAdded(this, tag, tagValue); - // Update the status of this Probe with respect to global tag traps - boolean tagTrapsChanged = false; - if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) { - this.isBeforeTrapActive = true; - tagTrapsChanged = true; + // Update the status of this Probe with respect to global TagInstruments + boolean tagInstrumentsChanged = false; + final BeforeTagInstrument beforeTagInstrument = instrumenter.getBeforeTagInstrument(); + if (beforeTagInstrument != null && tag == beforeTagInstrument.getTag()) { + this.isBeforeTagInstrumentActive = true; + tagInstrumentsChanged = true; } - if (afterTagTrap != null && tag == afterTagTrap.getTag()) { - this.isAfterTrapActive = true; - tagTrapsChanged = true; + final AfterTagInstrument afterTagInstrument = instrumenter.getAfterTagInstrument(); + if (afterTagInstrument != null && tag == afterTagInstrument.getTag()) { + this.isAfterTagInstrumentActive = true; + tagInstrumentsChanged = true; } - if (tagTrapsChanged) { + if (tagInstrumentsChanged) { invalidateProbeUnchanged(); } if (TRACE) { @@ -375,12 +189,29 @@ } /** + * Is the <em>Probed node</em> tagged as belonging to a particular human-sensible category of + * language constructs? + */ + public boolean isTaggedAs(SyntaxTag tag) { + assert tag != null; + return tags.contains(tag); + } + + /** + * In which user-sensible categories has the <em>Probed node</em> been tagged ( + * <em>empty set</em> if none). + */ + public Collection<SyntaxTag> getSyntaxTags() { + return Collections.unmodifiableCollection(tags); + } + + /** * Adds instrumentation at this Probe. * * @param instrument an instrument not yet attached to a probe * @throws IllegalStateException if the instrument has ever been attached before */ - public void attach(Instrument instrument) throws IllegalStateException { + void attach(ProbeInstrument instrument) throws IllegalStateException { if (instrument.isDisposed()) { throw new IllegalStateException("Attempt to attach disposed instrument"); } @@ -398,8 +229,8 @@ } /** - * Gets the {@link SourceSection} associated with the Guest Language AST node being - * instrumented, possibly {@code null}. + * Gets the {@link SourceSection} associated with the <en>Probed AST node</em>, possibly + * {@code null}. */ public SourceSection getProbedSourceSection() { return sourceSection; @@ -416,9 +247,9 @@ * * @param instrument an instrument already attached * @throws IllegalStateException if instrument not attached at this Probe - * @see Instrument#dispose() + * @see ProbeInstrument#dispose() */ - void disposeInstrument(Instrument instrument) throws IllegalStateException { + void disposeInstrument(ProbeInstrument instrument) throws IllegalStateException { for (WeakReference<ProbeNode> ref : probeNodeClones) { final ProbeNode probeNode = ref.get(); if (probeNode != null) { @@ -437,25 +268,27 @@ } /** - * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag - * Trap} at this Probe. Non{@code -null} if the global - * {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this Probe - * holds the {@link SyntaxTag} specified in the trap. + * Gets the currently active {@linkplain BeforeTagInstrument} at this Probe. Non{@code -null} if + * the global {@linkplain BeforeTagInstrument} is set and if this Probe holds the + * {@link SyntaxTag} specified in the instrument. */ - SyntaxTagTrap getBeforeTrap() { + BeforeTagInstrument getBeforeTagInstrument() { checkProbeUnchanged(); - return isBeforeTrapActive ? beforeTagTrap : null; + return isBeforeTagInstrumentActive ? instrumenter.getBeforeTagInstrument() : null; } /** - * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap} - * at this Probe. Non{@code -null} if the global - * {@linkplain Probe#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this Probe - * holds the {@link SyntaxTag} specified in the trap. + * Gets the currently active {@linkplain AfterTagInstrument} at this Probe. Non{@code -null} if + * the global {@linkplain BeforeTagInstrument} is set and if this Probe holds the + * {@link SyntaxTag} specified in the instrument. */ - SyntaxTagTrap getAfterTrap() { + AfterTagInstrument getAfterTagInstrument() { checkProbeUnchanged(); - return isAfterTrapActive ? afterTagTrap : null; + return isAfterTagInstrumentActive ? instrumenter.getAfterTagInstrument() : null; + } + + Class<? extends TruffleLanguage> getLanguage() { + return language; } /** @@ -476,13 +309,15 @@ probeStateUnchangedCyclic.invalidate(); } - private void notifyTrapsChanged() { - this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag()); - this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag()); + void notifyTagInstrumentsChanged() { + final BeforeTagInstrument beforeTagInstrument = instrumenter.getBeforeTagInstrument(); + this.isBeforeTagInstrumentActive = beforeTagInstrument != null && this.isTaggedAs(beforeTagInstrument.getTag()); + final AfterTagInstrument afterTagInstrument = instrumenter.getAfterTagInstrument(); + this.isAfterTagInstrumentActive = afterTagInstrument != null && this.isTaggedAs(afterTagInstrument.getTag()); invalidateProbeUnchanged(); } - private String getTagsDescription() { + String getTagsDescription() { final StringBuilder sb = new StringBuilder(); sb.append("["); String prefix = ""; @@ -494,18 +329,4 @@ sb.append("]"); return sb.toString(); } - - static final class AccessorInstrument extends Accessor { - @Override - protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { - return super.findLanguage(n); - } - - @Override - protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { - return probe.language; - } - } - - static final AccessorInstrument ACCESSOR = new AccessorInstrument(); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Wed Sep 30 16:33:56 2015 -0700 @@ -28,7 +28,8 @@ import com.oracle.truffle.api.nodes.Node; /** - * An exception thrown when {@link Node#probe()} fails because of an implementation failure. + * An exception thrown when {@link Instrumenter#probe(Node)} fails because of an implementation + * failure. * <p> * Language and tool implementations should ensure that clients of tools never see this exception. */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,7 +24,6 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeFieldAccessor; import com.oracle.truffle.api.nodes.NodeUtil; @@ -47,7 +46,7 @@ WRAPPER_NODE("The node to be probed is a wrapper"), /** - * The node to be probed returned {@link Node#isInstrumentable()}{@code == false}. + * The node to be probed does not support {@linkplain Instrumenter Instrumentation} . */ NOT_INSTRUMENTABLE("The node to be project is \"not instrumentable\""), @@ -78,7 +77,7 @@ private final Object wrapper; /** - * Description of an internal failure of {@link Node#probe()}. + * Description of an internal failure of {@link Instrumenter#probe(Node)}. * * @param reason what caused the failure * @param parent the parent, if known, of the child being probed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeInstrument.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2013, 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.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.source.SourceSection; + +/** + * A <em>binding</em> between: + * <ol> + * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in + * an executing Truffle AST, and</li> + * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client. + * </ol> + * <p> + * Client-oriented documentation for the use of Instruments is available online at <a + * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https:// + * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a> + * + * @see Probe + * @see Instrumenter + */ +public abstract class ProbeInstrument extends Instrument { + + /** + * Optional documentation, mainly for debugging. + */ + private final String instrumentInfo; + + /** + * Has this instrument been disposed? stays true once set. + */ + private boolean isDisposed = false; + + protected Probe probe = null; + + /** + * <h4>Implementation Notes</h4> + * <p> + * 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 an AST must be cloned along with the AST: AST clones are not permitted to share + * Nodes. + * <p> + * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is + * encouraged by providing the {@link SimpleInstrumentListener} for clients that need know + * nothing more than the properties associated with a Probe: its {@link SourceSection} and any + * associated instances of {@link SyntaxTag}. + * <p> + * AST cloning is <em>not transparent</em> to clients that use the + * {@link StandardInstrumentListener}, since those event methods identify the concrete Node + * instance (and thus the AST instance) where the event takes place. + * <p> + * <h4>Implementation Notes: the Life Cycle of an {@link ProbeInstrument} at a {@link Probe}</h4> + * <p> + * <ul> + * <li>A new Instrument is created in permanent association with a client-provided + * <em>listener.</em></li> + * + * <li>Multiple Instruments may share a single listener.</li> + * + * <li>An Instrument does nothing until it is {@linkplain Probe#attach(ProbeInstrument) + * attached} to a Probe, at which time the Instrument begins routing execution events from the + * Probe's AST location to the Instrument's listener.</li> + * + * <li>Neither Instruments nor Probes are {@link Node}s.</li> + * + * <li>A Probe has a single source-based location in an AST, but manages a separate + * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li> + * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is + * cloned along with the rest of the AST.</li> + * + * <li>When a new Instrument is attached to a Probe, the Instrument inserts a new instance of + * its private Node type into <em>each of the instrument chains</em> managed by the Probe, i.e. + * one node instance per existing clone of the AST.</li> + * + * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the + * Instrument's private Node type will be cloned along with the rest of the the AST.</li> + * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the + * shared (outer) Instrument instance; that state includes a reference to the Instrument's + * listener.</li> + * + * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, + * the Instrument searches every instrument chain associated with the Probe and removes the + * instance of its private Node type.</li> + * + * <li>Attaching or disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of + * the AST.</li> + * + * </ul> + */ + private ProbeInstrument(String instrumentInfo) { + this.instrumentInfo = instrumentInfo; + } + + /** + * Removes this Instrument from the Probe to which it attached and renders this Instrument + * inert. + * + * @throws IllegalStateException if this instrument has already been disposed + */ + @Override + public void dispose() throws IllegalStateException { + if (isDisposed) { + throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt"); + } + if (probe != null) { + // It's attached + probe.disposeInstrument(this); + probe = null; + } + this.isDisposed = true; + } + + @Override + public boolean isDisposed() { + return isDisposed; + } + + /** + * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not + * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}. + */ + public Probe getProbe() { + return probe; + } + + /** + * For internal implementation only. + */ + void setAttachedTo(Probe probe) { + this.probe = probe; + } + + abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode); + + /** + * Removes this instrument from an instrument chain. + */ + abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); + + /** + * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}. + */ + static final class SimpleInstrument extends ProbeInstrument { + + /** + * Tool-supplied listener for events. + */ + private final SimpleInstrumentListener simpleListener; + + SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) { + super(instrumentInfo); + this.simpleListener = simpleListener; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new SimpleInstrumentNode(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(SimpleInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + /** + * Node that implements a {@link SimpleInstrument} in a particular AST. + */ + @NodeInfo(cost = NodeCost.NONE) + private final class SimpleInstrumentNode extends AbstractInstrumentNode { + + private SimpleInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + } + + @Override + public void enter(Node node, VirtualFrame vFrame) { + SimpleInstrument.this.simpleListener.onEnter(SimpleInstrument.this.probe); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + @Override + public void returnVoid(Node node, VirtualFrame vFrame) { + SimpleInstrument.this.simpleListener.onReturnVoid(SimpleInstrument.this.probe); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + @Override + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + SimpleInstrument.this.simpleListener.onReturnValue(SimpleInstrument.this.probe, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + @Override + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + SimpleInstrument.this.simpleListener.onReturnExceptional(SimpleInstrument.this.probe, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + @Override + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : simpleListener.getClass().getSimpleName(); + } + } + } + + /** + * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. + */ + static final class StandardInstrument extends ProbeInstrument { + + /** + * Tool-supplied listener for AST events. + */ + private final StandardInstrumentListener standardListener; + + StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) { + super(instrumentInfo); + this.standardListener = standardListener; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new StandardInstrumentNode(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(StandardInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + /** + * Node that implements a {@link StandardInstrument} in a particular AST. + */ + @NodeInfo(cost = NodeCost.NONE) + private final class StandardInstrumentNode extends AbstractInstrumentNode { + + private StandardInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + } + + @Override + public void enter(Node node, VirtualFrame vFrame) { + standardListener.onEnter(StandardInstrument.this.probe, node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + @Override + public void returnVoid(Node node, VirtualFrame vFrame) { + standardListener.onReturnVoid(StandardInstrument.this.probe, node, vFrame); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + @Override + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + standardListener.onReturnValue(StandardInstrument.this.probe, node, vFrame, result); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + @Override + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + standardListener.onReturnExceptional(StandardInstrument.this.probe, node, vFrame, exception); + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + @Override + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : standardListener.getClass().getSimpleName(); + } + } + } + + /** + * An instrument that allows clients to provide an AST fragment to be executed directly from + * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle + * AST with potential for full optimization. + */ + static final class AdvancedInstrument extends ProbeInstrument { + + private final AdvancedInstrumentResultListener resultListener; + private final AdvancedInstrumentRootFactory rootFactory; + private final Class<?> requiredResultType; + + AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { + super(instrumentInfo); + this.resultListener = resultListener; + this.rootFactory = rootFactory; + this.requiredResultType = requiredResultType; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new AdvancedInstrumentNode(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(AdvancedInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + /** + * Node that implements a {@link AdvancedInstrument} in a particular AST. + */ + @NodeInfo(cost = NodeCost.NONE) + private final class AdvancedInstrumentNode extends AbstractInstrumentNode { + + @Child private AdvancedInstrumentRoot instrumentRoot; + + private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + } + + @Override + public void enter(Node node, VirtualFrame vFrame) { + if (instrumentRoot == null) { + try { + final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node); + if (newInstrumentRoot != null) { + instrumentRoot = newInstrumentRoot; + adoptChildren(); + AdvancedInstrument.this.probe.invalidateProbeUnchanged(); + } + } catch (RuntimeException ex) { + if (resultListener != null) { + resultListener.onFailure(node, vFrame, ex); + } + } + } + if (instrumentRoot != null) { + try { + final Object result = instrumentRoot.executeRoot(node, vFrame); + if (resultListener != null) { + checkResultType(result); + resultListener.onExecution(node, vFrame, result); + } + } catch (RuntimeException ex) { + if (resultListener != null) { + resultListener.onFailure(node, vFrame, ex); + } + } + } + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + private void checkResultType(Object result) { + if (requiredResultType == null) { + return; + } + if (result == null) { + throw instrumentResultNull(); + } + if (!(requiredResultType.isAssignableFrom(result.getClass()))) { + throw instrumentResultWrongType(result); + } + } + + @TruffleBoundary + private RuntimeException instrumentResultNull() { + return new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required"); + } + + @TruffleBoundary + private RuntimeException instrumentResultWrongType(Object result) { + return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName()); + } + + @Override + public void returnVoid(Node node, VirtualFrame vFrame) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + @Override + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + @Override + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + @Override + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : rootFactory.getClass().getSimpleName(); + } + } + } + + // TODO (mlvdv) experimental + public interface TruffleOptListener { + void notifyIsCompiled(boolean isCompiled); + } + + @SuppressWarnings("unused") + private static final class TruffleOptInstrument extends ProbeInstrument { + + private final TruffleOptListener toolOptListener; + + private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) { + super(instrumentInfo); + this.toolOptListener = listener; + } + + @Override + AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { + return new TruffleOptInstrumentNode(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(TruffleOptInstrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; + } + + @NodeInfo(cost = NodeCost.NONE) + private final class TruffleOptInstrumentNode extends AbstractInstrumentNode { + + private boolean isCompiled; + + private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) { + super(nextNode); + this.isCompiled = CompilerDirectives.inCompiledCode(); + } + + @Override + public void enter(Node node, VirtualFrame vFrame) { + if (this.isCompiled != CompilerDirectives.inCompiledCode()) { + this.isCompiled = CompilerDirectives.inCompiledCode(); + TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled); + } + if (nextInstrumentNode != null) { + nextInstrumentNode.enter(node, vFrame); + } + } + + @Override + public void returnVoid(Node node, VirtualFrame vFrame) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnVoid(node, vFrame); + } + } + + @Override + public void returnValue(Node node, VirtualFrame vFrame, Object result) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnValue(node, vFrame, result); + } + } + + @Override + public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { + if (nextInstrumentNode != null) { + nextInstrumentNode.returnExceptional(node, vFrame, exception); + } + } + + @Override + public String instrumentationInfo() { + final String info = getInstrumentInfo(); + return info != null ? info : toolOptListener.getClass().getSimpleName(); + } + } + } + + @NodeInfo(cost = NodeCost.NONE) + abstract class AbstractInstrumentNode extends EventHandlerNode { + + @Child protected AbstractInstrumentNode nextInstrumentNode; + + protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) { + this.nextInstrumentNode = nextNode; + } + + @Override + public Probe getProbe() { + return probe; + } + + /** + * Gets the instrument that created this node. + */ + private ProbeInstrument getInstrument() { + return ProbeInstrument.this; + } + + /** + * Removes the node from this chain that was added by a particular instrument, assuming that + * the head of the chain is not the one to be replaced. This is awkward, but is required + * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work + * for the tail of the list, which would be replacing itself with null. So the replacement + * must be directed the parent of the node being removed. + */ + private boolean removeFromChain(ProbeInstrument instrument) { + assert getInstrument() != instrument; + if (nextInstrumentNode == null) { + return false; + } + if (nextInstrumentNode.getInstrument() == instrument) { + // Next is the one to remove + if (nextInstrumentNode.nextInstrumentNode == null) { + // Next is at the tail; just forget + nextInstrumentNode = null; + } else { + // Replace next with its successor + nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode); + } + return true; + } + return nextInstrumentNode.removeFromChain(instrument); + } + + protected String getInstrumentInfo() { + return ProbeInstrument.this.instrumentInfo; + } + } +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -25,10 +25,11 @@ package com.oracle.truffle.api.instrument; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.nodes.RootNode; /** - * An observer of events related to {@link Probe}s: creating and tagging. + * An observer of events related to {@link Probe}s: creation of new Probes and the addition of new + * {@linkplain SyntaxTag SyntaxTags} to existing Probes. */ public interface ProbeListener { @@ -36,15 +37,16 @@ * Notifies that all registered {@link ASTProber}s are about to be applied to a newly * constructed AST. * - * @param source source code from which the AST was constructed + * @param rootNode parent of the newly created AST */ - void startASTProbing(Source source); + void startASTProbing(RootNode rootNode); /** - * Notifies that a {@link Probe} has been newly attached to an AST via {@link Node#probe()}. + * Notifies that a {@link Probe} has been newly attached to an AST via + * {@link Instrumenter#probe(Node)}. * <p> * There can be no more than one {@link Probe} at a node; this notification will only be - * delivered the first time {@linkplain Node#probe() probe()} is called at a particular AST + * delivered the first time {@linkplain Instrumenter#probe(Node)} is called at a particular AST * node. There will also be no notification when the AST to which the Probe is attached is * cloned. */ @@ -73,8 +75,8 @@ * Notifies that the application of all registered {@link ASTProber}s to a newly constructed AST * has completed. * - * @param source source code from which the AST was constructed + * @param rootNode parent of the newly created AST */ - void endASTProbing(Source source); + void endASTProbing(RootNode rootNode); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,109 +26,30 @@ 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.VirtualFrame; -import com.oracle.truffle.api.instrument.Instrument.AbstractInstrumentNode; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; +import com.oracle.truffle.api.instrument.ProbeInstrument.AbstractInstrumentNode; +import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument; +import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.source.SourceSection; /** * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to - * Truffle ASTs. + * Truffle ASTs, part of the {@linkplain Instrumenter Instrumentation Framework}. * <p> - * A {@link ProbeNode} is the head of a chain of nodes acting on behalf of {@linkplain Instrument - * instruments}. It is attached to an AST as a child of a guest-language-specific - * {@link WrapperNode} node. + * A {@link ProbeNode} is the head of a chain of nodes acting on behalf of + * {@linkplain ProbeInstrument instruments}. It is attached to an AST as a child of a + * guest-language-specific {@link WrapperNode} node. * <p> - * When Truffle clones an AST, the chain, including all attached {@linkplain Instrument instruments} - * will be cloned along with the {@link WrapperNode} to which it is attached. An instance of - * {@link Probe} represents abstractly the instrumentation at a particular location in a Guest - * Language AST, tracks the clones of the chain, and keeps the instrumentation attached to the + * When Truffle clones an AST, the chain, including all attached {@linkplain ProbeInstrument + * instruments} will be cloned along with the {@link WrapperNode} to which it is attached. An + * instance of {@link Probe} represents abstractly the instrumentation at a particular location in a + * Guest Language AST, tracks the clones of the chain, and keeps the instrumentation attached to the * clones consistent. */ @NodeInfo(cost = NodeCost.NONE) -public final class ProbeNode extends Node implements TruffleEvents, InstrumentationNode { - - /** - * A node that can be inserted into a Truffle AST, and which enables {@linkplain Instrument - * instrumentation} at a particular Guest Language (GL) node. Implementations must extend - * {@link Node} and should override {@link Node#isInstrumentable()} to return {@code false}. - * <p> - * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the - * wrapper's <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's - * execution semantics. - * <p> - * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode} - * attached as a second child of the {@link WrapperNode}. - * <p> - * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at - * the wrapped AST node during program execution. - * <p> - * When a GL AST is cloned, the {@link WrapperNode}, its {@link ProbeNode} and any - * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL - * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular - * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, - * and acts as a single point of access for tools. - * <p> - * This interface is not intended to be visible as part of the API for tools (instrumentation - * clients). - * <p> - * Implementation guidelines: - * <ol> - * <li>Each GL implementation should include a WrapperNode implementation; usually only one is - * needed.</li> - * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li> - * <li>Must have a field: {@code @Child private <GL>Node child;}</li> - * <li>Must have a field: {@code @Child private ProbeNode probeNode;}</li> - * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every - * possible <em>execute-</em> method that gets called on guest language AST node types by their - * parents, and passing along each call to its child.</li> - * <li>Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();} - * <li>Method {@code insertProbe(ProbeNode)} should be implemented as - * {@code this.probeNode=insert(newProbeNode);}</li> - * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce - * their runtime overhead to zero when there are no attached {@link Instrument}s.</li> - * </ol> - * <p> - * - * @see Instrument - */ - public interface WrapperNode extends InstrumentationNode { - - /** - * Gets the node being "wrapped", i.e. the AST node for which - * {@linkplain InstrumentationNode.TruffleEvents execution events} will be reported through - * the Instrumentation Framework. - */ - Node getChild(); - - /** - * Gets the {@link Probe} responsible for installing this wrapper. - */ - Probe getProbe(); - - /** - * Implementation support for completing a newly created wrapper node. - */ - void insertProbe(ProbeNode probeNode); - } - - /** - * Create a new {@link Probe} associated with, and attached to, a Guest Language specific - * instance of {@link WrapperNode}. - */ - @SuppressWarnings("rawtypes") - public static Probe insertProbe(WrapperNode wrapper) { - final SourceSection sourceSection = wrapper.getChild().getSourceSection(); - final ProbeNode probeNode = new ProbeNode(); // private constructor - Class<? extends TruffleLanguage> l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); - probeNode.probe = new Probe(l, probeNode, sourceSection); // package private access - wrapper.insertProbe(probeNode); - return probeNode.probe; - } +final class ProbeNode extends EventHandlerNode { // Never changed once set. @CompilationFinal Probe probe = null; @@ -138,11 +59,6 @@ @Child protected AbstractInstrumentNode firstInstrumentNode; @Override - public boolean isInstrumentable() { - return false; - } - - @Override public Node copy() { Node node = super.copy(); probe.registerProbeNodeClone((ProbeNode) node); @@ -152,54 +68,60 @@ /** * @return the {@link Probe} permanently associated with this {@link ProbeNode}. */ + @Override public Probe getProbe() { return probe; } + @Override public void enter(Node node, VirtualFrame vFrame) { this.probe.checkProbeUnchanged(); - final SyntaxTagTrap beforeTagTrap = probe.getBeforeTrap(); - if (beforeTagTrap != null) { - beforeTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize()); + final BeforeTagInstrument beforeTagInstrument = probe.getBeforeTagInstrument(); + if (beforeTagInstrument != null) { + beforeTagInstrument.getListener().onEnter(probe, ((WrapperNode) this.getParent()).getChild(), vFrame); } if (firstInstrumentNode != null) { firstInstrumentNode.enter(node, vFrame); } } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { firstInstrumentNode.returnVoid(node, vFrame); } - final SyntaxTagTrap afterTagTrap = probe.getAfterTrap(); - if (afterTagTrap != null) { - afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize()); + final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument(); + if (afterTagInstrument != null) { + afterTagInstrument.getListener().onReturnVoid(probe, ((WrapperNode) this.getParent()).getChild(), vFrame); } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { firstInstrumentNode.returnValue(node, vFrame, result); } - final SyntaxTagTrap afterTagTrap = probe.getAfterTrap(); - if (afterTagTrap != null) { - afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize()); + final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument(); + if (afterTagInstrument != null) { + afterTagInstrument.getListener().onReturnValue(probe, ((WrapperNode) this.getParent()).getChild(), vFrame, result); } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { firstInstrumentNode.returnExceptional(node, vFrame, exception); } - final SyntaxTagTrap afterTagTrap = probe.getAfterTrap(); - if (afterTagTrap != null) { - afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize()); + final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument(); + if (afterTagInstrument != null) { + afterTagInstrument.getListener().onReturnExceptional(probe, ((WrapperNode) this.getParent()).getChild(), vFrame, exception); } } + @Override public String instrumentationInfo() { return "Standard probe"; } @@ -215,7 +137,7 @@ * Adds an {@link AbstractInstrumentNode} to this chain. */ @TruffleBoundary - void addInstrument(Instrument instrument) { + void addInstrument(ProbeInstrument instrument) { assert instrument.getProbe() == probe; // The existing chain of nodes may be empty // Attach the modified chain. @@ -228,7 +150,7 @@ * @throws RuntimeException if no matching instrument is found, */ @TruffleBoundary - void removeInstrument(Instrument instrument) { + void removeInstrument(ProbeInstrument instrument) { assert instrument.getProbe() == probe; final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrumentNode); if (modifiedChain == null) {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -27,7 +27,8 @@ import com.oracle.truffle.api.source.SourceSection; /** - * A receiver of Truffle execution events that can act on behalf of an external client. + * A receiver of Truffle AST execution events (containing minimal information) that can act on + * behalf of an external client. * <p> * The {@link Probe} instance provides access to the {@link SourceSection} associated with the * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. @@ -37,8 +38,11 @@ * execution state should use {@link StandardInstrumentListener}. * <p> * Clients are free, of course, to record additional information in the listener implementation that - * carries additional information about the context and reason for the particular {@link Instrument} - * that is to be created from the listener. + * carries additional information about the context and reason for the particular + * {@link ProbeInstrument} that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. */ public interface SimpleInstrumentListener { @@ -47,7 +51,7 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void enter(Probe probe); + void onEnter(Probe probe); /** * Receive notification that a program location's {@code void}-valued execution has just @@ -55,7 +59,7 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnVoid(Probe probe); + void onReturnVoid(Probe probe); /** * Receive notification that a program location's execution has just completed and returned a @@ -63,12 +67,12 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnValue(Probe probe, Object result); + void onReturnValue(Probe probe, Object result); /** * Receive notification that a program location's execution has just thrown an exception. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnExceptional(Probe probe, Exception exception); + void onReturnExceptional(Probe probe, Exception exception); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardAfterInstrumentListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,72 @@ +/* + * 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.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; + +/** + * A receiver of Truffle AST <em>after</em> execution events that can act on behalf of an external + * client. + * <p> + * The {@link Probe} argument provides access to the {@link SourceSection} associated with the + * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. + * <p> + * This listener is designed for clients that also require access to the AST execution state at the + * time of the event. Clients that do not require access to the AST execution state should use the + * {@link SimpleInstrumentListener}. + * <p> + * Clients are free, of course, to record additional information in the listener implementation that + * carries additional information about the context and reason for the particular + * {@link ProbeInstrument} that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. + */ +public interface StandardAfterInstrumentListener { + + /** + * Receive notification that an AST Node's {@code void}-valued execute method has just returned. + * <p> + * <strong>Synchronous</strong>: Truffle execution waits until the call returns. + */ + void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame); + + /** + * Receive notification that an AST Node's execute method has just returned a value (boxed if + * primitive). + * <p> + * <strong>Synchronous</strong>: Truffle execution waits until the call returns. + */ + void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); + + /** + * Receive notification that an AST Node's execute method has just thrown an exception. + * <p> + * <strong>Synchronous</strong>: Truffle execution waits until the call returns. + */ + void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardBeforeInstrumentListener.java Wed Sep 30 16:33:56 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; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; + +/** + * A receiver of Truffle AST <em>before</em> execution events that can act on behalf of an external + * client. + * <p> + * The {@link Probe} argument provides access to the {@link SourceSection} associated with the + * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. + * <p> + * This listener is designed for clients that also require access to the AST execution state at the + * time of the event. Clients that do not require access to the AST execution state should use the + * {@link SimpleInstrumentListener}. + * <p> + * Clients are free, of course, to record additional information in the listener implementation that + * carries additional information about the context and reason for the particular + * {@link ProbeInstrument} that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. + */ +public interface StandardBeforeInstrumentListener { + + /** + * Receive notification that an AST node's execute method is about to be called. + * <p> + * <strong>Synchronous</strong>: Truffle execution waits until the call returns. + */ + void onEnter(Probe probe, Node node, VirtualFrame vFrame); + +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -29,18 +29,21 @@ import com.oracle.truffle.api.source.SourceSection; /** - * A receiver of Truffle execution events that can act on behalf of an external client. + * A receiver of Truffle AST execution events that can act on behalf of an external client. * <p> * The {@link Probe} argument provides access to the {@link SourceSection} associated with the - * event, as well as any {@link SyntaxTag}s that have been applied at that AST node. + * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. * <p> * This listener is designed for clients that also require access to the AST execution state at the * time of the event. Clients that do not require access to the AST execution state should use the * {@link SimpleInstrumentListener}. * <p> * Clients are free, of course, to record additional information in the listener implementation that - * carries additional information about the context and reason for the particular {@link Instrument} - * that is to be created from the listener. + * carries additional information about the context and reason for the particular + * {@link ProbeInstrument} that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. */ public interface StandardInstrumentListener { @@ -49,14 +52,14 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void enter(Probe probe, Node node, VirtualFrame vFrame); + void onEnter(Probe probe, Node node, VirtualFrame vFrame); /** * Receive notification that an AST Node's {@code void}-valued execute method has just returned. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnVoid(Probe probe, Node node, VirtualFrame vFrame); + void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame); /** * Receive notification that an AST Node's execute method has just returned a value (boxed if @@ -64,12 +67,12 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); + void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); /** * Receive notification that an AST Node's execute method has just thrown an exception. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); + void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java Wed Sep 30 16:33:56 2015 -0700 @@ -25,8 +25,9 @@ package com.oracle.truffle.api.instrument; /** - * Program element "tags", presumed to be singletons (best implemented as enums) that define - * user-visible behavior for debugging and other simple tools. These categories should correspond to + * Program element "tags" such as <code>"STATEMENT"</code>, presumed to be singletons (best + * implemented as enums) that define user-visible behavior for debugging and other + * {@linkplain Instrumenter Instrumentation-based} tools. These categories should correspond to * program structures, for example "statement" and "assignment", that are meaningful * ("human-sensible") to guest language programmers. * <p>
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagTrap.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * 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.instrument; - -import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.nodes.Node; - -/** - * A trap that can be set to interrupt execution at probed nodes carrying a specific tag. - * - * @see Probe - */ -public abstract class SyntaxTagTrap { - - private final SyntaxTag tag; - - protected SyntaxTagTrap(SyntaxTag tag) { - this.tag = tag; - } - - public final SyntaxTag getTag() { - return tag; - } - - /** - * Notifies that execution is halted at a node with the specified tag. - */ - public abstract void tagTrappedAt(Node node, MaterializedFrame frame); -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TagInstrument.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,127 @@ +/* + * 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; + +/** + * A <em>binding</em> between: + * <ol> + * <li>A {@link SyntaxTag} that specifies nodes to act as source of <em>execution events</em> taking + * place at a program location in an executing Truffle AST, and</li> + * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client. + * </ol> + * <p> + * Client-oriented documentation for the use of Instruments is available online at <a + * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https:// + * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a> + * + * @see SyntaxTag + * @see Instrumenter + */ +public abstract class TagInstrument extends Instrument { + + /** + * Optional documentation, mainly for debugging. + */ + @SuppressWarnings("unused") private final String instrumentInfo; + + protected Instrumenter instrumenter; + + /** + * Has this instrument been disposed? stays true once set. + */ + protected boolean isDisposed = false; + + private SyntaxTag tag = null; + + protected TagInstrument(Instrumenter instrumenter, SyntaxTag tag, String instrumentInfo) { + this.instrumenter = instrumenter; + this.tag = tag; + this.instrumentInfo = instrumentInfo; + } + + @Override + public void dispose() throws IllegalStateException { + if (isDisposed) { + throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt"); + } + instrumenter.disposeAfterTagInstrument(); + this.isDisposed = true; + } + + @Override + public boolean isDisposed() { + return isDisposed; + } + + public SyntaxTag getTag() { + return tag; + } + + static final class BeforeTagInstrument extends TagInstrument { + + private final StandardBeforeInstrumentListener listener; + + BeforeTagInstrument(Instrumenter instrumenter, SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) { + super(instrumenter, tag, instrumentInfo); + this.listener = listener; + } + + StandardBeforeInstrumentListener getListener() { + return listener; + } + + @Override + public void dispose() throws IllegalStateException { + if (isDisposed) { + throw new IllegalStateException("Disposed Instrument can not be disposed again"); + } + instrumenter.disposeBeforeTagInstrument(); + this.isDisposed = true; + } + } + + static final class AfterTagInstrument extends TagInstrument { + + private final StandardAfterInstrumentListener listener; + + AfterTagInstrument(Instrumenter instrumenter, SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) { + super(instrumenter, tag, instrumentInfo); + this.listener = listener; + } + + StandardAfterInstrumentListener getListener() { + return listener; + } + + @Override + public void dispose() throws IllegalStateException { + if (isDisposed) { + throw new IllegalStateException("Disposed Instrument can not be disposed again"); + } + instrumenter.disposeAfterTagInstrument(); + this.isDisposed = true; + } + } +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolSupportProvider.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +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.api.instrument; - -/** - * Access to language-specific information and execution services for external tools. - */ -public interface ToolSupportProvider { - - /** - * Gets visualization services for language-specific information. - */ - Visualizer getVisualizer(); - - /** - * Enables AST probing on all subsequently created ASTs (sources parsed). - * - * @param astProber optional AST prober to enable; the default for the language used if - * {@code null} - */ - void enableASTProbing(ASTProber astProber); - -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java Wed Sep 30 16:33:56 2015 -0700 @@ -29,9 +29,9 @@ import com.oracle.truffle.api.nodes.Node; /** - * Visualization services for the benefit of instrumentation-based tools, possibly specialized for - * each guest language and possibly specialized for relevant information from the underlying Truffle - * implementation. + * Visualization services for the benefit of {@linkplain Instrumenter Instrumentation-based} tools, + * possibly specialized for each guest language and possibly specialized for relevant information + * from the underlying Truffle implementation. * <p> * <strong>Disclaimer:</strong> experimental interface under development. */ @@ -61,7 +61,7 @@ /** * Converts a value in the guest language to a display string. If - * + * * @param trim if {@code > 0}, them limit size of String to either the value of trim or the * number of characters in the first line, whichever is lower. */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/WrapperNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013, 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.nodes.Node; + +/** + * A specialized {@link Node} instance that must be inserted into a Truffle AST in order to enable + * {@linkplain Instrumenter instrumentation} at a particular Guest Language (GL) node. + * <p> + * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the wrapper's + * <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's execution + * semantics. + * <p> + * Instrumentation at the wrapped node is implemented by an instance of {@link EventHandlerNode} + * attached as a second child of the {@link WrapperNode}. + * <p> + * A wrapper is obliged to notify its attached {@link EventHandlerNode} when execution events occur + * at the wrapped AST node during program execution. + * <p> + * When a GL AST is cloned, the {@link WrapperNode}, its {@link EventHandlerNode} and any + * {@linkplain ProbeInstrument instrumentation} are also cloned; they are in effect part of the GL + * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular + * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, and + * acts as a single point of access for tools. + * <p> + * Implementation guidelines: + * <ol> + * <li>Each GL implementation must implement a WrapperNode implementation for each AST context in + * which Instrumentation is to be supported.</li> + * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li> + * <li>Must have a field: {@code @Child private <GL>Node child;}</li> + * <li>Must have a field: {@code @Child private EventHandlerNode eventHandlerNode;}</li> + * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every + * possible <em>execute-</em> method that gets called on guest language AST node types by their + * parents, and passing along each call to its child.</li> + * <li>Method {@code Probe getProbe()} should be implemented as {@code eventHandlerNode.getProbe();} + * <li>Method {@code insertProbe(EventHandlerNode)} should be implemented as + * {@code this.eventHandlerNode=insert(eventHandlerNode);}</li> + * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce their + * runtime overhead to zero when there are no attached {@link ProbeInstrument}s.</li> + * </ol> + * <p> + * + * @see ProbeInstrument + */ +public interface WrapperNode extends InstrumentationNode { + + /** + * Gets the node being "wrapped", i.e. the AST node for which {@linkplain EventHandlerNode + * execution events} will be reported through the Instrumentation Framework. + */ + Node getChild(); + + /** + * Gets the {@link Probe} responsible for installing this wrapper. + */ + Probe getProbe(); + + /** + * Implementation support for completing a newly created wrapper node. + */ + void insertEventHandlerNode(EventHandlerNode eventHandlerNode); +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Wed Sep 30 16:33:56 2015 -0700 @@ -26,7 +26,7 @@ import com.oracle.truffle.api.instrument.ASTPrinter; import com.oracle.truffle.api.instrument.InstrumentationNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeClass; import com.oracle.truffle.api.nodes.NodeFieldAccessor;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -27,11 +27,11 @@ import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.ProbeListener; import com.oracle.truffle.api.instrument.SyntaxTag; -import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.nodes.RootNode; public abstract class DefaultProbeListener implements ProbeListener { - public void startASTProbing(Source source) { + public void startASTProbing(RootNode rootNode) { } public void newProbeInserted(Probe probe) { @@ -40,7 +40,7 @@ public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { } - public void endASTProbing(Source source) { + public void endASTProbing(RootNode rootNode) { } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -32,15 +32,15 @@ */ public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener { - public void enter(Probe probe) { + public void onEnter(Probe probe) { } - public void returnVoid(Probe probe) { + public void onReturnVoid(Probe probe) { } - public void returnValue(Probe probe, Object result) { + public void onReturnValue(Probe probe, Object result) { } - public void returnExceptional(Probe probe, Exception exception) { + public void onReturnExceptional(Probe probe, Exception exception) { } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java Wed Sep 30 16:33:56 2015 -0700 @@ -35,16 +35,16 @@ */ public class DefaultStandardInstrumentListener implements StandardInstrumentListener { - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { } - public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { + public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) { } - public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { } - public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,7 +24,171 @@ */ /** - * Advanced node manipulation instruments. + * <h4>Truffle {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumentation}: access to execution events for Debuggers and other tools.</h4> + * <p> + * This framework permits client + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool tools}, + * either builtin or third-party, to observe with <em>very low overhead</em> the execution of a + * {@linkplain com.oracle.truffle.api.TruffleLanguage Truffle Language} program at the level of + * <em>AST execution events</em>: + * <ul> + * <li>a Truffle + * {@linkplain com.oracle.truffle.api.nodes.Node Node} is about to be executed, or</li> + * <li>a Truffle + * {@linkplain com.oracle.truffle.api.nodes.Node Node} execution has just completed.</li> + * </ul> + * The framework supports many kinds of tools, for example simple collectors of data such as the + * CoverageTracker. + * It also supports Truffle's built-in + * {@linkplain com.oracle.truffle.api.debug.Debugger debugging services}, as well as utilities + * that maintain {@linkplain com.oracle.truffle.api.debug.LineToProbesMap maps of source code locations} + * for other tools such as {@linkplain com.oracle.truffle.api.debug.Debugger debugging}. + * + * <h4>Instrumentation Services</h4> + * <p> + * API access to instrumentation services is provided by the + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumenter} that is part of + * the Truffle execution environment. These services fall into several categories: + * + * <ul> + * + * <li><strong>AST Markup: probing</strong> + * <ul> + * <li>Execution events can only be observed at AST locations that have been + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#probe(com.oracle.truffle.api.nodes.Node) probed}, + * which results in the creation of a + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} that is permanently associated with a + * particular segment of source code, e.g. a "statement", that corresponds to an AST location.</li> + * <li>Probing is only supported at + * {@linkplain com.oracle.truffle.api.nodes.Node Nodes} where supported by specific language implementations.</li> + * <li>The relationship between a + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} and + * a source code location persists across Truffle <em>cloning</em> of ASTs, which is to say, a single + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} corresponds to (and keeps track of) + * the equivalent location in every clone of the original AST.</li> + * <li>Probing is specified by + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#registerASTProber(ASTProber) registering} + * an instance of + * {@linkplain com.oracle.truffle.api.instrument.ASTProber ASTProber}. + * A default prober provided by each {@linkplain com.oracle.truffle.api.TruffleLanguage TruffelLanguage} + * will be registered automatically.</li> + * <li>All AST probing must be complete before any AST execution has started.</li> + * </ul></li> + * + * <li><strong>AST Markup: tagging</strong> + * <ul> + * <li>Additional information can be added at any time to a + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} by + * {@linkplain com.oracle.truffle.api.instrument.Probe#tagAs(SyntaxTag, Object) adding} to its set of + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags}.</li> + * + * <li>{@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag Standard tags} should + * be used for common, language-agnostic concepts such as + * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}, and this + * is usually done by each language's default + * {@linkplain com.oracle.truffle.api.instrument.ASTProber ASTProber} while + * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} are being created.</li> + * <li>{@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} serve to configure the behavior + * of clients during program execution. For example, knowing which + * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} are on + * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT nodes} + * informs both the CoverageTracker and the + * {@linkplain com.oracle.truffle.api.debug.Debugger debugger} while "stepping".</li> + * <li>{@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags} can also be added at any + * time by any client for any purpose, including private + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} about which other clients + * can learn nothing.</li> + * </ul></li> + * + * <li><strong>Markup Discovery: finding probes</strong> + * <ul> + * <li>Clients can be observe {@linkplain com.oracle.truffle.api.instrument.Probe Probes} + * being {@linkplain com.oracle.truffle.api.instrument.Instrumenter#probe(com.oracle.truffle.api.nodes.Node) created} and + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} being + * {@linkplain com.oracle.truffle.api.instrument.Probe#tagAs(SyntaxTag, Object) added} by + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#addProbeListener(ProbeListener) registering} a + * {@linkplain com.oracle.truffle.api.instrument.ProbeListener ProbeListener}.</li> + * <li>Clients can also + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#findProbesTaggedAs(SyntaxTag) find} all existing + * {@linkplain com.oracle.truffle.api.instrument.Probe Probes}, possibly filtering the search by + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag}.</li> + * </ul></li> + * + * <li><strong>Observing Execution: event listening:</strong> + * <ul> + * <li>Clients can be notified of <em>AST execution events</em> by creating one of several kinds of + * <em>event listener</em> and <em>attaching</em> it to a + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe}. This creates an + * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} that notifies the listener + * of every subsequent execution event at the AST location corresponding to the + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe}.</li> + * <li>An + * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} can be + * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument#dispose() disposed}, at which time + * it is removed from service at every clone of the AST, incurs no further overhead, and is + * permanently unusable.</li> + * <li>Many clients need only implement a + * {@linkplain com.oracle.truffle.api.instrument.SimpleInstrumentListener SimpleInstrumentListener}, + * whose notification methods provide only the instance of the + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} to which it was attached. This + * provides access to the corresponding + * {@linkplain com.oracle.truffle.api.source.SourceSection source code location} and any + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags} that had been applied there.</li> + * <li>Clients that require deeper access to execution state implement a + * {@linkplain com.oracle.truffle.api.instrument.StandardInstrumentListener StandardInstrumentListener} + * whose notification methods provide access to the concrete + * {@linkplain com.oracle.truffle.api.nodes.Node AST location} and current + * {@linkplain com.oracle.truffle.api.frame.Frame stack frame}.</li> + * <li>Clients can also create an + * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} (whose design is currently + * under revision) that supports (effectively) inserting a Truffle AST fragment into the AST + * location, where it will be executed subject to full Truffle optimization.</li> + * </ul></li> + * + * <li><strong>Wide-area Instrumentation: TagInstruments</strong> + * <ul> + * <li>A specialized form of Instrumentation is provided that efficiently attaches a single + * listener called a + * {@linkplain com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener StandardBeforeInstrumentListener} to every + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} containing a specified + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag}.</li> + * <li>One (but no more than one) + * {@linkplain com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener StandardBeforeInstrumentListener} + * may optionally be attached for notification <em>before</em> every <em>AST execution event</em> where the specified + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag} is present.</li> + * <li>One (but no more than one) + * {@linkplain com.oracle.truffle.api.instrument.StandardAfterInstrumentListener StandardAfterInstrumentListener} + * may optionally be attached for notification <em>after</em> every <em>AST execution event</em> where the specified + * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag} is present.</li> + * <li>The + * {@linkplain com.oracle.truffle.api.instrument.TagInstrument TagInstrument} mechanism is independent + * of listeners that may be attached to + * {@linkplain com.oracle.truffle.api.instrument.Probe Probes}. It is especially valuable for + * applications such as the debugger, where during "stepping" the program should be halted at + * any node tagged with + * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}.</li> + * </ul></li> + * + * <li><strong>Data Gathering Utilities: tools</strong> + * <ul> + * <li>The Instrumentation Framework supports extensible set of utilities that can be + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#install(com.oracle.truffle.api.instrument.Instrumenter.Tool) + * installed}, at which time they start collecting information that can be queried at any time.</li> + * <li>Once installed, these + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool tools} can be dynamically + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool#setEnabled(boolean) disabled and re-enabled} and eventually + * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool#dispose() disposed} when no longer needed.</li> + * <li>A useful example is the + * {@linkplain com.oracle.truffle.api.debug.LineToProbesMap LineToProbesMap}, which incrementally builds and maintains a map of + * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} indexed by + * {@linkplain com.oracle.truffle.api.source.LineLocation source code line}. Truffle + * {@linkplain com.oracle.truffle.api.debug.Debugger debugging services} depend heavily on this utility.</li> + * <li>The CoverageTracker maintains counts of execution events where a + * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} has been tagged with + * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}, indexed by source code line.</li> + * </ul></li> + * </ul> + * */ package com.oracle.truffle.api.instrument;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,6 +24,15 @@ */ package com.oracle.truffle.api.nodes; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.Callable; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -32,22 +41,8 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeException; -import com.oracle.truffle.api.instrument.ProbeFailure; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.utilities.JSONHelper; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.Callable; /** * Abstract base class for all Truffle nodes. @@ -424,86 +419,6 @@ } /** - * Any node for which this is {@code true} can be "instrumented" by installing a {@link Probe} - * that intercepts execution events at the node and routes them to any {@link Instrument}s that - * have been attached to the {@link Probe}. Only one {@link Probe} may be installed at each - * node; subsequent calls return the one already installed. - * <p> - * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}, which must be - * provided by {@link #createWrapperNode()}. - * - * @see Instrument - */ - public boolean isInstrumentable() { - return false; - } - - /** - * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that: - * <ol> - * <li>implements {@link WrapperNode}</li> - * <li>has {@code this} as it's child, and</li> - * <li>whose type is safe for replacement of {@code this} in the parent.</li> - * </ol> - * - * @return an appropriately typed {@link WrapperNode} if {@link #isInstrumentable()}. - */ - public WrapperNode createWrapperNode() { - return null; - } - - /** - * Enables {@linkplain Instrument instrumentation} of a node, where the node is presumed to be - * part of a well-formed Truffle AST that is not being executed. If this node has not already - * been probed, modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between - * the node and its parent; the wrapper node must be provided by implementations of - * {@link #createWrapperNode()}. No more than one {@link Probe} may be associated with a node, - * so a {@linkplain WrapperNode wrapper} may not wrap another {@linkplain WrapperNode wrapper}. - * - * @return a (possibly newly created) {@link Probe} associated with this node. - * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged - */ - public final Probe probe() { - - if (this instanceof WrapperNode) { - throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null); - } - - if (parent == null) { - throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null); - } - - if (parent instanceof WrapperNode) { - return ((WrapperNode) parent).getProbe(); - } - - if (!isInstrumentable()) { - throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null); - } - - // Create a new wrapper/probe with this node as its child. - final WrapperNode wrapper = createWrapperNode(); - - if (wrapper == null || !(wrapper instanceof Node)) { - throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper); - } - - final Node wrapperNode = (Node) wrapper; - - if (!this.isSafelyReplaceableBy(wrapperNode)) { - throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper); - } - - // Connect it to a Probe - final Probe probe = ProbeNode.insertProbe(wrapper); - - // Replace this node in the AST with the wrapper - this.replace(wrapperNode); - - return probe; - } - - /** * Converts this node to a textual representation useful for debugging. */ @Override @@ -603,14 +518,19 @@ return true; } - private static final class AccessorNodes extends Accessor { + static final class AccessorNodes extends Accessor { @SuppressWarnings("rawtypes") @Override protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { return n.language; } + + @Override + protected void probeAST(RootNode rootNode) { + super.probeAST(rootNode); + } } // registers into Accessor.NODES - @SuppressWarnings("unused") private static final AccessorNodes ACCESSOR = new AccessorNodes(); + static final AccessorNodes ACCESSOR = new AccessorNodes(); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Sep 30 16:33:56 2015 -0700 @@ -42,9 +42,9 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind; import com.oracle.truffle.api.source.SourceSection;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -35,8 +35,6 @@ import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.DefaultCompilerOptions; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.source.SourceSection; /** @@ -80,6 +78,11 @@ /** * 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. + * <p> + * <strong>Note:</strong> Although the {@link SourceSection} <em>can</em> be {@code null}, this + * is strongly discouraged for the purposes of testing/tracing/tooling. Please use + * {@link SourceSection#createUnavailable(String, String)} to create a descriptive instance with + * a language-specific <em>kind</em> such as "SL Builtin" and a <em>name</em> if possible. * * @param language the language of the node, <b>cannot be</b> <code>null</code> * @param sourceSection a part of source associated with this node, can be <code>null</code> @@ -187,25 +190,22 @@ } } + public final void applyInstrumentation() { + if (isInstrumentable()) { + Node.ACCESSOR.probeAST(this); + } + } + /** - * Apply all registered instances of {@link ASTProber} to the AST, if any, held by this root - * node. This can only be done once the AST is complete, notably once all parent pointers are - * correctly assigned. But it also must be done before any AST cloning or execution. - * <p> - * If this is not done, then the AST will not be subject to debugging or any other - * instrumentation-supported tooling. - * <p> - * Implementations should ensure that instrumentation is never applied more than once to an AST, - * as this is not guaranteed to be error-free. - * - * @see Probe#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) + * Does this contain AST content that it is possible to instrument. */ - public void applyInstrumentation() { + protected boolean isInstrumentable() { + return true; } /** * Helper method to create a root node that always returns the same value. Certain operations - * (expecially {@link com.oracle.truffle.api.interop inter-operability} API) require return of + * (especially {@link com.oracle.truffle.api.interop inter-operability} API) require return of * stable {@link RootNode root nodes}. To simplify creation of such nodes, here is a factory * method that can create {@link RootNode} that returns always the same value. *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,8 +24,6 @@ */ package com.oracle.truffle.api.source; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.TruffleLanguage.Registration; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; @@ -54,6 +52,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.TruffleLanguage.Registration; + /** * Representation of a guest language source code unit and its contents. Sources originate in * several ways: @@ -425,7 +426,7 @@ /** * The URL if the source is retrieved via URL. - * + * * @return URL or <code>null</code> */ public abstract URL getURL(); @@ -909,6 +910,8 @@ return "text/x-c"; } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { return "application/x-r"; + } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) { + return "application/javascript"; } else { try { return Files.probeContentType(file.toPath()); @@ -997,6 +1000,8 @@ return "text/x-c"; } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { return "application/x-r"; + } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) { + return "application/javascript"; } else { try { return Files.probeContentType(file.toPath());
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java Wed Sep 30 16:33:56 2015 -0700 @@ -98,7 +98,7 @@ /** * Gets a representation of the first line of the section, suitable for a hash key. - * + * * @return first line of the section */ public LineLocation getLineLocation() { @@ -175,7 +175,7 @@ * @return the code as a String object */ public String getCode() { - return getSource().getCode(charIndex, charLength); + return source == null ? "<unavailable>" : source.getCode(charIndex, charLength); } /** @@ -262,6 +262,6 @@ * @return source section which is mostly <em>empty</em> */ public static SourceSection createUnavailable(String kind, String name) { - return new SourceSection(kind, null, name, -1, -1, -1, -1); + return new SourceSection(kind, null, name == null ? "<unknown>" : name, -1, -1, -1, -1); } }
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -44,11 +44,28 @@ import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.sl.test.instrument.InstrumentationTestMode; + import java.io.ByteArrayOutputStream; + import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; public class SLJavaInteropTest { + + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void asFunction() throws Exception { String scriptText = "function main() {\n" + " println(\"Called!\");\n" + "}\n";
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -42,8 +42,13 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.sl.test.instrument.InstrumentationTestMode; import com.oracle.truffle.tck.TruffleTCK; + import static org.junit.Assert.assertTrue; + +import org.junit.After; +import org.junit.Before; import org.junit.Test; /** @@ -51,6 +56,17 @@ * */ public class SLTckTest extends TruffleTCK { + + @Before + public void before() { + InstrumentationTestMode.set(true); + } + + @After + public void after() { + InstrumentationTestMode.set(false); + } + @Test public void testVerifyPresence() { PolyglotEngine vm = PolyglotEngine.buildNew().build(); @@ -176,4 +192,5 @@ @Override public void testPrimitiveidentityFloat() throws Exception { } + }
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Wed Sep 30 16:33:56 2015 -0700 @@ -40,22 +40,12 @@ */ package com.oracle.truffle.sl.test.instrument; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.PolyglotEngine; -import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; -import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; -import com.oracle.truffle.sl.test.SLTestRunner; -import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Field; import java.nio.charset.Charset; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; @@ -65,6 +55,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; + import org.junit.Assert; import org.junit.internal.TextListener; import org.junit.runner.Description; @@ -77,6 +68,18 @@ import org.junit.runners.ParentRunner; import org.junit.runners.model.InitializationError; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.StandardSyntaxTag; +import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; +import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; +import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; +import com.oracle.truffle.sl.test.SLTestRunner; +import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; + /** * This class builds and executes the tests for instrumenting SL. Although much of this class is * written with future automation in mind, at the moment the tests that are created are hard-coded @@ -94,7 +97,7 @@ private static final String LF = System.getProperty("line.separator"); - static class InstrumentTestCase { + static final class InstrumentTestCase { protected final Description name; protected final Path path; protected final String baseName; @@ -115,16 +118,12 @@ private final List<InstrumentTestCase> testCases; - public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError { + public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError, SecurityException, IllegalArgumentException { super(testClass); - final SLStandardASTProber prober = new SLStandardASTProber(); - Probe.registerASTProber(prober); try { testCases = createTests(testClass); } catch (IOException e) { throw new InitializationError(e); - } finally { - Probe.unregisterASTProber(prober); } } @@ -236,20 +235,25 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(out); final ASTProber prober = new SLStandardASTProber(); - Probe.registerASTProber(prober); + try { // 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 PolyglotEngine vm = PolyglotEngine.buildNew().setIn(new ByteArrayInputStream(testCase.testInput.getBytes("UTF-8"))).setOut(out).build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(prober); + final String src = readAllLines(testCase.path); vm.eval(Source.fromText(src, testCase.path.toString()).withMimeType("application/x-sl")); // Attach an instrument to every probe tagged as an assignment - for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { + for (Probe probe : instrumenter.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { SLPrintAssigmentValueListener slPrintAssigmentValueListener = new SLPrintAssigmentValueListener(ps); - probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value")); + instrumenter.attach(probe, slPrintAssigmentValueListener, "SL print assignment value"); } PolyglotEngine.Value main = vm.findGlobalSymbol("main"); @@ -263,13 +267,12 @@ } catch (Throwable ex) { notifier.fireTestFailure(new Failure(testCase.name, ex)); } finally { - Probe.unregisterASTProber(prober); notifier.fireTestFinished(testCase.name); } } - public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException { + public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException, SecurityException, IllegalArgumentException { JUnitCore core = new JUnitCore(); core.addListener(new TextListener(System.out)); SLInstrumentTestRunner suite = new SLInstrumentTestRunner(testClass); @@ -314,7 +317,7 @@ } @Override - public void returnValue(Probe probe, Object result) { + public void onReturnValue(Probe probe, Object result) { output.println(result); } }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Wed Sep 30 16:33:56 2015 -0700 @@ -40,21 +40,26 @@ */ package com.oracle.truffle.sl; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.debug.DebugSupportException; -import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; import com.oracle.truffle.api.frame.MaterializedFrame; -import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.GraphPrintVisitor; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; @@ -68,7 +73,9 @@ import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltin; import com.oracle.truffle.sl.builtins.SLPrintlnBuiltin; import com.oracle.truffle.sl.builtins.SLReadlnBuiltin; +import com.oracle.truffle.sl.nodes.SLExpressionNode; import com.oracle.truffle.sl.nodes.SLRootNode; +import com.oracle.truffle.sl.nodes.SLStatementNode; import com.oracle.truffle.sl.nodes.SLTypes; import com.oracle.truffle.sl.nodes.call.SLDispatchNode; import com.oracle.truffle.sl.nodes.call.SLInvokeNode; @@ -92,7 +99,9 @@ import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode; import com.oracle.truffle.sl.nodes.expression.SLSubNode; import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; +import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode; import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; +import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode; import com.oracle.truffle.sl.nodes.local.SLReadLocalVariableNode; import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; import com.oracle.truffle.sl.parser.Parser; @@ -102,14 +111,6 @@ import com.oracle.truffle.sl.runtime.SLFunction; import com.oracle.truffle.sl.runtime.SLFunctionRegistry; import com.oracle.truffle.sl.runtime.SLNull; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.math.BigInteger; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; /** * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as @@ -186,20 +187,20 @@ */ /* - * + * * <p> <b>Tools:</b><br> The use of some of Truffle's support for developer tools (based on the - * Truffle Instrumentation Framework) are demonstrated in this file, for example: <ul> <li>a - * {@linkplain NodeExecCounter counter for node executions}, tabulated by node type; and</li> <li>a - * simple {@linkplain CoverageTracker code coverage engine}.</li> </ul> In each case, the tool is - * enabled if a corresponding local boolean variable in this file is set to {@code true}. Results - * are printed at the end of the execution using each tool's <em>default printer</em>. + * Truffle {@linkplain Instrumenter Instrumentation Framework}) are demonstrated in this file, for + * example: <ul> <li>a {@linkplain NodeExecCounter counter for node executions}, tabulated by node + * type; and</li> <li>a simple {@linkplain CoverageTracker code coverage engine}.</li> </ul> In each + * case, the tool is enabled if a corresponding local boolean variable in this file is set to {@code + * true}. Results are printed at the end of the execution using each tool's <em>default + * printer</em>. */ @TruffleLanguage.Registration(name = "SL", version = "0.5", mimeType = "application/x-sl") public final class SLLanguage extends TruffleLanguage<SLContext> { + public static final String builtinKind = "SL builtin"; private static List<NodeFactory<? extends SLBuiltinNode>> builtins = Collections.emptyList(); private static Visualizer visualizer = new SLDefaultVisualizer(); - private static ASTProber registeredASTProber; // non-null if prober already registered - private DebugSupportProvider debugSupport; private SLLanguage() { } @@ -214,6 +215,7 @@ for (NodeFactory<? extends SLBuiltinNode> builtin : builtins) { context.installBuiltin(builtin, true); } + env.instrumenter().registerASTProber(new SLStandardASTProber()); return context; } @@ -256,6 +258,7 @@ /** * Temporary method during API evolution, supports debugger integration. */ + @Deprecated public static void run(Source source) throws IOException { PolyglotEngine vm = PolyglotEngine.buildNew().build(); assert vm.getLanguages().containsKey("application/x-sl"); @@ -478,28 +481,49 @@ } @Override - protected ToolSupportProvider getToolSupport() { - return getDebugSupport(); + protected Visualizer getVisualizer() { + if (visualizer == null) { + visualizer = new SLDefaultVisualizer(); + } + return visualizer; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof SLStatementNode; } @Override - protected DebugSupportProvider getDebugSupport() { - if (debugSupport == null) { - debugSupport = new SLDebugProvider(); + protected WrapperNode createWrapperNode(Node node) { + if (node instanceof SLExpressionNode) { + return new SLExpressionWrapperNode((SLExpressionNode) node); + } + if (node instanceof SLStatementNode) { + return new SLStatementWrapperNode((SLStatementNode) node); } - return debugSupport; + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + throw new IllegalStateException("evalInContext not supported in this language"); + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + throw new IllegalStateException("createAdvancedInstrumentRootFactory not supported in this language"); + } + + public Node createFindContextNode0() { + return createFindContextNode(); + } + + public SLContext findContext0(Node contextNode) { + return findContext(contextNode); } // TODO (mlvdv) remove the static hack when we no longer have the static demo variables private static void setupToolDemos() { - // if (statementCounts || coverage) { - // if (registeredASTProber == null) { - // final ASTProber newProber = new SLStandardASTProber(); - // // This should be registered on the PolyglotEngine - // Probe.registerASTProber(newProber); - // registeredASTProber = newProber; - // } - // } // if (nodeExecCounts) { // nodeExecCounter = new NodeExecCounter(); // nodeExecCounter.install(); @@ -531,46 +555,4 @@ // } } - public Node createFindContextNode0() { - return createFindContextNode(); - } - - public SLContext findContext0(Node contextNode) { - return findContext(contextNode); - } - - private final class SLDebugProvider implements DebugSupportProvider { - - public SLDebugProvider() { - if (registeredASTProber == null) { - registeredASTProber = new SLStandardASTProber(); - // This should be registered on the PolyglotEngine - Probe.registerASTProber(registeredASTProber); - } - } - - public Visualizer getVisualizer() { - if (visualizer == null) { - visualizer = new SLDefaultVisualizer(); - } - return visualizer; - } - - public void enableASTProbing(ASTProber prober) { - if (prober != null) { - // This should be registered on the PolyglotEngine - Probe.registerASTProber(prober); - } - } - - public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { - throw new DebugSupportException("evalInContext not supported in this language"); - } - - public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException { - throw new DebugSupportException("createAdvancedInstrumentRootFactory not supported in this language"); - } - - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -45,6 +45,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.sl.SLAssertionError; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.runtime.SLNull; /** @@ -55,7 +56,7 @@ public abstract class SLAssertFalseBuiltin extends SLBuiltinNode { public SLAssertFalseBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "assertFalse")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "assertFalse")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -45,6 +45,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.sl.SLAssertionError; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.runtime.SLNull; /** @@ -55,7 +56,7 @@ public abstract class SLAssertTrueBuiltin extends SLBuiltinNode { public SLAssertTrueBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "assertTrue")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "assertTrue")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -45,6 +45,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.parser.Parser; import com.oracle.truffle.sl.runtime.SLContext; @@ -56,7 +57,7 @@ public abstract class SLDefineFunctionBuiltin extends SLBuiltinNode { public SLDefineFunctionBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "defineFunction")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "defineFunction")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -49,6 +49,7 @@ import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; /** * This builtin sets the variable named "hello" in the caller frame to the string "world". @@ -57,7 +58,7 @@ public abstract class SLHelloEqualsWorldBuiltin extends SLBuiltinNode { public SLHelloEqualsWorldBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "helloEqualsWorld")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "helloEqualsWorld")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -43,6 +43,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; /** * Builtin function that returns the value of a high-resolution time, in nanoseconds. @@ -51,7 +52,7 @@ public abstract class SLNanoTimeBuiltin extends SLBuiltinNode { public SLNanoTimeBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "nanoTime")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "nanoTime")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -43,6 +43,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; /** * Built-in function to create a new object. Objects in SL are simply made up of name/value pairs. @@ -51,7 +52,7 @@ public abstract class SLNewObjectBuiltin extends SLBuiltinNode { public SLNewObjectBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "new")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "new")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -44,7 +44,9 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.runtime.SLContext; + import java.io.PrintWriter; /** @@ -60,7 +62,7 @@ public abstract class SLPrintlnBuiltin extends SLBuiltinNode { public SLPrintlnBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "println")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "println")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -45,7 +45,9 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.sl.SLException; +import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.runtime.SLContext; + import java.io.BufferedReader; import java.io.IOException; @@ -56,7 +58,7 @@ public abstract class SLReadlnBuiltin extends SLBuiltinNode { public SLReadlnBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "readln")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "readln")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java Wed Sep 30 16:33:56 2015 -0700 @@ -54,6 +54,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.sl.SLLanguage; /** * Returns a string representation of the current stack. This includes the {@link CallTarget}s and @@ -64,7 +65,7 @@ public abstract class SLStackTraceBuiltin extends SLBuiltinNode { public SLStackTraceBuiltin() { - super(SourceSection.createUnavailable("SL builtin", "stacktrace")); + super(SourceSection.createUnavailable(SLLanguage.builtinKind, "stacktrace")); } @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -42,11 +42,9 @@ import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode; import com.oracle.truffle.sl.runtime.SLFunction; /** @@ -94,15 +92,4 @@ public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { return SLTypesGen.expectBoolean(executeGeneric(frame)); } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new SLExpressionWrapperNode(this); - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -43,9 +43,9 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.builtins.SLBuiltinNode; import com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode; @@ -69,8 +69,8 @@ @CompilationFinal private boolean isCloningAllowed; @SuppressWarnings("unused") - public SLRootNode(SLContext ignore, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, String name) { - super(SLLanguage.class, null, frameDescriptor); + public SLRootNode(SLContext ignore, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, SourceSection sourceSection, String name) { + super(SLLanguage.class, sourceSection, frameDescriptor); this.bodyNode = bodyNode; this.name = name; } @@ -89,21 +89,12 @@ this.isCloningAllowed = isCloningAllowed; } - public SLExpressionNode getBodyNode() { - return bodyNode; - } - @Override public boolean isCloningAllowed() { return isCloningAllowed; } @Override - public void applyInstrumentation() { - Probe.applyASTProbers(bodyNode); - } - - @Override public String toString() { return "root " + name; }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -40,13 +40,12 @@ */ package com.oracle.truffle.sl.nodes; +import java.io.File; + import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode; -import java.io.File; /** * The base class of all Truffle nodes for SL. All nodes (even expressions) can be used as @@ -101,15 +100,4 @@ return String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : ""); } } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new SLStatementWrapperNode(this); - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -41,11 +41,12 @@ package com.oracle.truffle.sl.nodes.instrument; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.ProbeInstrument; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; @@ -56,14 +57,14 @@ /** * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to - * enable "instrumentation" of an {@link SLExpressionNode}. Tools wishing to interact with AST - * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the - * wrapper, and to which this wrapper routes execution events. + * enable {@linkplain Instrumenter Instrumentation} of an {@link SLExpressionNode}. Tools wishing to + * interact with AST execution may attach {@link ProbeInstrument}s to the {@link Probe} uniquely + * associated with the wrapper, and to which this wrapper routes execution events. */ @NodeInfo(cost = NodeCost.NONE) public final class SLExpressionWrapperNode extends SLExpressionNode implements WrapperNode { @Child private SLExpressionNode child; - @Child private ProbeNode probeNode; + @Child private EventHandlerNode eventHandlerNode; /** * Constructor. @@ -81,21 +82,16 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override public SLExpressionNode getNonWrapperNode() { return child; } - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } public Node getChild() { @@ -105,16 +101,16 @@ @Override public Object executeGeneric(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); Object result; try { result = child.executeGeneric(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result; @@ -132,14 +128,14 @@ @Override public SLFunction executeFunction(VirtualFrame vFrame) throws UnexpectedResultException { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); SLFunction result; try { result = child.executeFunction(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result;
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Wed Sep 30 16:33:56 2015 -0700 @@ -40,14 +40,17 @@ */ package com.oracle.truffle.sl.nodes.instrument; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.InstrumentationNode; -import com.oracle.truffle.api.instrument.Probe; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.ASSIGNMENT; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.START_LOOP; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.STATEMENT; + +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.InstrumentationNode; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.sl.nodes.SLExpressionNode; import com.oracle.truffle.sl.nodes.SLStatementNode; import com.oracle.truffle.sl.nodes.controlflow.SLWhileNode; @@ -55,41 +58,41 @@ /** * A visitor which traverses a completely parsed Simple AST (presumed not yet executed) and enables - * instrumentation at a few standard kinds of nodes. + * {@linkplain Instrumenter Instrumentation} at a few standard kinds of nodes. */ -public class SLStandardASTProber implements NodeVisitor, ASTProber { +public class SLStandardASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, RootNode startNode) { + startNode.accept(new NodeVisitor() { - /** - * {@inheritDoc} - * <p> - * Instruments and tags all relevant {@link SLStatementNode}s and {@link SLExpressionNode}s. - * Currently, only SLStatementNodes that are not SLExpressionNodes are tagged as statements. - */ - public boolean visit(Node node) { + /** + * Instruments and tags all relevant {@link SLStatementNode}s and + * {@link SLExpressionNode}s. Currently, only SLStatementNodes that are not + * SLExpressionNodes are tagged as statements. + */ + public boolean visit(Node node) { - if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) { - // All SL nodes are instrumentable, but treat expressions specially + if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) { + // All SL nodes are instrumentable, but treat expressions specially - if (node instanceof SLExpressionNode) { - SLExpressionNode expressionNode = (SLExpressionNode) node; - Probe probe = expressionNode.probe(); - if (node instanceof SLWriteLocalVariableNode) { - probe.tagAs(STATEMENT, null); - probe.tagAs(ASSIGNMENT, null); + if (node instanceof SLExpressionNode) { + SLExpressionNode expressionNode = (SLExpressionNode) node; + final Probe probe = instrumenter.probe(expressionNode); + if (node instanceof SLWriteLocalVariableNode) { + probe.tagAs(STATEMENT, null); + probe.tagAs(ASSIGNMENT, null); + } + } else { + SLStatementNode statementNode = (SLStatementNode) node; + final Probe probe = instrumenter.probe(statementNode); + probe.tagAs(STATEMENT, null); + if (node instanceof SLWhileNode) { + probe.tagAs(START_LOOP, null); + } + } } - } else { - SLStatementNode statementNode = (SLStatementNode) node; - Probe probe = statementNode.probe(); - probe.tagAs(STATEMENT, null); - if (node instanceof SLWhileNode) { - probe.tagAs(START_LOOP, null); - } + return true; } - } - return true; - } - - public void probeAST(Node node) { - node.accept(this); + }); } }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java Wed Sep 30 16:33:56 2015 -0700 @@ -41,11 +41,12 @@ package com.oracle.truffle.sl.nodes.instrument; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.ProbeInstrument; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; @@ -53,15 +54,15 @@ /** * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to - * enable "instrumentation" of a {@link SLStatementNode}. Tools wishing to interact with AST - * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the - * wrapper, and to which this wrapper routes execution events. + * enable {@linkplain Instrumenter Instrumentation} of a {@link SLStatementNode}. Tools wishing to + * interact with AST execution may attach {@link ProbeInstrument}s to the {@link Probe} uniquely + * associated with the wrapper, and to which this wrapper routes execution events. */ @NodeInfo(cost = NodeCost.NONE) public final class SLStatementWrapperNode extends SLStatementNode implements WrapperNode { @Child private SLStatementNode child; - @Child private ProbeNode probeNode; + @Child private EventHandlerNode eventHandlerNode; public SLStatementWrapperNode(SLStatementNode child) { super(child.getSourceSection()); @@ -74,21 +75,17 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override public SLStatementNode getNonWrapperNode() { return child; } - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + @Override + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } @Override @@ -98,15 +95,15 @@ @Override public void executeVoid(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); try { child.executeVoid(vFrame); - probeNode.returnVoid(child, vFrame); + eventHandlerNode.returnVoid(child, vFrame); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Wed Sep 30 16:33:56 2015 -0700 @@ -40,8 +40,15 @@ */ package com.oracle.truffle.sl.parser; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; @@ -80,11 +87,6 @@ import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNodeGen; import com.oracle.truffle.sl.runtime.SLContext; -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * Helper class used by the SL {@link Parser} to create nodes. The code is factored out of the @@ -166,8 +168,7 @@ assert lexicalScope == null : "Wrong scoping of blocks in parser"; final SLFunctionBodyNode functionBodyNode = new SLFunctionBodyNode(functionSrc, methodBlock); - final SLRootNode rootNode = new SLRootNode(this.context, frameDescriptor, functionBodyNode, functionName); - rootNode.assignSourceSection(functionSrc); + final SLRootNode rootNode = new SLRootNode(this.context, frameDescriptor, functionBodyNode, functionSrc, functionName); context.getFunctionRegistry().register(functionName, rootNode); @@ -346,7 +347,8 @@ /** * Returns a {@link SLReadLocalVariableNode} if this read is a local variable or a * {@link SLFunctionLiteralNode} if this read is global. In Simple, the only global names are - * functions. </br> There is currently no instrumentation for this node. + * functions. </br> There is currently no instrumentation{@linkplain Instrumenter + * Instrumentation} for this node. * * @param nameToken The name of the variable/function being read * @return either:
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Wed Sep 30 16:33:56 2015 -0700 @@ -48,6 +48,7 @@ import com.oracle.truffle.api.object.Layout; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.sl.SLLanguage; import com.oracle.truffle.sl.builtins.SLAssertFalseBuiltinFactory; import com.oracle.truffle.sl.builtins.SLAssertTrueBuiltinFactory; @@ -66,6 +67,7 @@ import com.oracle.truffle.sl.nodes.local.SLReadArgumentNode; import com.oracle.truffle.sl.parser.Parser; import com.oracle.truffle.sl.parser.SLNodeFactory; + import java.io.BufferedReader; import java.io.PrintWriter; import java.math.BigInteger; @@ -170,8 +172,10 @@ SLBuiltinNode builtinBodyNode = factory.createNode(argumentNodes, this); /* The name of the builtin function is specified via an annotation on the node class. */ String name = lookupNodeInfo(builtinBodyNode.getClass()).shortName(); + + final SourceSection srcSection = SourceSection.createUnavailable(SLLanguage.builtinKind, name); /* Wrap the builtin in a RootNode. Truffle requires all AST to start with a RootNode. */ - SLRootNode rootNode = new SLRootNode(this, new FrameDescriptor(), builtinBodyNode, name); + SLRootNode rootNode = new SLRootNode(this, new FrameDescriptor(), builtinBodyNode, srcSection, name); if (registerRootNodes) { /* Register the builtin function in our function registry. */
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java Wed Sep 30 16:33:56 2015 -0700 @@ -84,10 +84,9 @@ /** * @deprecated use {@link #engine()}. */ - @SuppressWarnings("deprecation") @Deprecated - public com.oracle.truffle.api.vm.TruffleVM vm() { - return (com.oracle.truffle.api.vm.TruffleVM) engine(); + public com.oracle.truffle.api.vm.PolyglotEngine vm() { + return engine(); } protected abstract Debugger db();
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,25 +24,34 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Map; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.tools.CoverageTracker; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import org.junit.Test; +import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag; public class CoverageTrackerTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ToolTestUtil.TestASTProber()); final CoverageTracker tool = new CoverageTracker(); assertEquals(tool.getCounts().entrySet().size(), 0); - tool.install(); + instrumenter.install(tool); assertEquals(tool.getCounts().entrySet().size(), 0); tool.setEnabled(false); assertEquals(tool.getCounts().entrySet().size(), 0); @@ -54,66 +63,56 @@ assertEquals(tool.getCounts().entrySet().size(), 0); } - @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); - final CoverageTracker tool = new CoverageTracker(); - tool.install(); - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); - tool.dispose(); - } - - @Test - public void testToolInstalledcTooLate() { - final CoverageTracker tool = new CoverageTracker(); - final RootNode expr13rootNode = createExpr13TestRootNode(); - tool.install(); - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); - tool.dispose(); + void checkCounts(Source source, CoverageTracker coverage, Long[] expectedCounts) { + final Map<Source, Long[]> countMap = coverage.getCounts(); + assertEquals(countMap.size(), 1); + final Long[] resultCounts = countMap.get(source); + assertTrue(Arrays.equals(resultCounts, expectedCounts)); } @Test - public void testCountingCoverage() { - final CoverageTracker tool = new CoverageTracker(); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); + public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ToolTestUtil.TestASTProber()); + final Source source = ToolTestUtil.createTestSource("testCountingCoverage"); - // Not probed yet. - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); + final CoverageTracker valueCoverage = new CoverageTracker(ToolTestTag.VALUE_TAG); + final CoverageTracker addCoverage = new CoverageTracker(ToolTestTag.ADD_TAG); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + instrumenter.install(valueCoverage); + assertTrue(valueCoverage.getCounts().isEmpty()); + + assertEquals(vm.eval(source).get(), 13); - // Probed but not tagged yet. - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(1), null, Long.valueOf(1), null}); - probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing"); + instrumenter.install(addCoverage); - // Counting now; execute once - assertEquals(13, expr13rootNode.execute(null)); + assertEquals(vm.eval(source).get(), 13); - final Long[] longs1 = tool.getCounts().get(addNode.getSourceSection().getSource()); - assertNotNull(longs1); - assertEquals(longs1.length, 2); - assertNull(longs1[0]); // Line 1 is empty (text lines are 1-based) - assertEquals(1L, longs1[1].longValue()); // Expression is on line 2 + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(1), null, null}); + + valueCoverage.setEnabled(false); + assertEquals(vm.eval(source).get(), 13); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13rootNode.execute(null)); - } + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(2), null, null}); + + valueCoverage.setEnabled(true); + assertEquals(vm.eval(source).get(), 13); - final Long[] longs100 = tool.getCounts().get(addNode.getSourceSection().getSource()); - assertNotNull(longs100); - assertEquals(longs100.length, 2); - assertNull(longs100[0]); - assertEquals(100L, longs100[1].longValue()); + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(3), null, Long.valueOf(3), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(3), null, null}); + + valueCoverage.dispose(); + assertEquals(vm.eval(source).get(), 13); - tool.dispose(); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(4), null, null}); + + addCoverage.dispose(); } - }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,67 +24,100 @@ */ package com.oracle.truffle.tools.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.tools.LineToProbesMap; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static com.oracle.truffle.tools.test.TestNodes.expr13Line1; -import static com.oracle.truffle.tools.test.TestNodes.expr13Line2; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import org.junit.Test; +import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag; public class LineToProbesMapTest { @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); - - final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); - - assertNull(tool.findFirstProbe(expr13Line1)); - assertNull(tool.findFirstProbe(expr13Line2)); - tool.dispose(); + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ToolTestUtil.TestASTProber()); + final Source source = ToolTestUtil.createTestSource("testNoExecution"); + final LineToProbesMap probesMap = new LineToProbesMap(); + instrumenter.install(probesMap); + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); + probesMap.dispose(); } @Test - public void testToolInstalledTooLate() { - final LineToProbesMap tool = new LineToProbesMap(); + public void testMapping1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ToolTestUtil.TestASTProber()); + final Source source = ToolTestUtil.createTestSource("testMapping1"); + final LineToProbesMap probesMap = new LineToProbesMap(); + + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); + // Map installed before AST gets created + instrumenter.install(probesMap); + assertEquals(vm.eval(source).get(), 13); - tool.install(); + final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1)); + assertNotNull(probe1); + assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG)); + final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2)); + assertNotNull(probe2); + assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG)); + final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3)); + assertNotNull(probe3); + assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG)); - assertNull(tool.findFirstProbe(expr13Line1)); - assertNull(tool.findFirstProbe(expr13Line2)); - tool.dispose(); + probesMap.dispose(); } @Test - public void testMapping() { - final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); + public void testMapping2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(new ToolTestUtil.TestASTProber()); + final Source source = ToolTestUtil.createTestSource("testMapping2"); + final LineToProbesMap probesMap = new LineToProbesMap(); + + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); + // Map installed after AST gets created + assertEquals(vm.eval(source).get(), 13); + instrumenter.install(probesMap); - assertNull(tool.findFirstProbe(expr13Line1)); - assertEquals(tool.findFirstProbe(expr13Line2), probe); - tool.dispose(); + final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1)); + assertNotNull(probe1); + assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG)); + final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2)); + assertNotNull(probe2); + assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG)); + final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3)); + assertNotNull(probe3); + assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG)); + + probesMap.dispose(); } - }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,29 +24,31 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; import com.oracle.truffle.tools.NodeExecCounter; import com.oracle.truffle.tools.NodeExecCounter.NodeExecutionCount; -import com.oracle.truffle.tools.test.TestNodes.TestAddNode; -import com.oracle.truffle.tools.test.TestNodes.TestValueNode; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestCallTarget; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import org.junit.Test; public class NodeExecCounterTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); final NodeExecCounter tool = new NodeExecCounter(); assertEquals(tool.getCounts().length, 0); - tool.install(); + instrumenter.install(tool); assertEquals(tool.getCounts().length, 0); tool.setEnabled(false); assertEquals(tool.getCounts().length, 0); @@ -58,113 +60,45 @@ assertEquals(tool.getCounts().length, 0); } - @Test - public void testToolCreatedTooLate() { - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); - assertEquals(13, expr13callTarget.call()); - assertEquals(tool.getCounts().length, 0); - tool.dispose(); - } - - @Test - public void testToolInstalledcTooLate() { - final NodeExecCounter tool = new NodeExecCounter(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - tool.install(); - assertEquals(13, expr13callTarget.call()); - assertEquals(tool.getCounts().length, 0); - tool.dispose(); + void checkCounts(NodeExecCounter execTool, int addCount, int valueCount) { + NodeExecutionCount[] counts = execTool.getCounts(); + assertEquals(counts.length, 2); + for (NodeExecutionCount counter : counts) { + if (counter.nodeClass() == ToolTestUtil.TestAdditionNode.class) { + assertEquals(counter.executionCount(), addCount); + } else if (counter.nodeClass() == ToolTestUtil.TestValueNode.class) { + assertEquals(counter.executionCount(), valueCount); + } else { + fail("correct classes counted"); + } + } } @Test - public void testCountingAll() { - final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - - // execute once - assertEquals(13, expr13callTarget.call()); - final NodeExecutionCount[] count1 = tool.getCounts(); - assertNotNull(count1); - assertEquals(count1.length, 2); - for (NodeExecutionCount count : count1) { - final Class<?> class1 = count.nodeClass(); - final long executionCount = count.executionCount(); - if (class1 == TestAddNode.class) { - assertEquals(executionCount, 1); - } else if (class1 == TestValueNode.class) { - assertEquals(executionCount, 2); - } else { - fail(); - } - } + public void testCounting() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testCounting"); + final NodeExecCounter execTool = new NodeExecCounter(); + instrumenter.install(execTool); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13callTarget.call()); - } - final NodeExecutionCount[] counts100 = tool.getCounts(); - assertNotNull(counts100); - assertEquals(counts100.length, 2); - for (NodeExecutionCount count : counts100) { - final Class<?> class1 = count.nodeClass(); - final long executionCount = count.executionCount(); - if (class1 == TestAddNode.class) { - assertEquals(executionCount, 100); - } else if (class1 == TestValueNode.class) { - assertEquals(executionCount, 200); - } else { - fail(); - } - } + assertEquals(execTool.getCounts().length, 0); - tool.dispose(); - } + assertEquals(vm.eval(source).get(), 13); - @Test - public void testCountingTagged() { - final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); - - // Not probed yet. - assertEquals(13, expr13rootNode.execute(null)); - assertEquals(tool.getCounts().length, 0); - - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - - // Probed but not tagged yet. - assertEquals(13, expr13rootNode.execute(null)); - assertEquals(tool.getCounts().length, 0); - - probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing"); + checkCounts(execTool, 1, 2); - // Counting now; execute once - assertEquals(13, expr13rootNode.execute(null)); - final NodeExecutionCount[] counts1 = tool.getCounts(); - assertNotNull(counts1); - assertEquals(counts1.length, 1); - final NodeExecutionCount count1 = counts1[0]; - assertNotNull(count1); - assertEquals(count1.nodeClass(), addNode.getClass()); - assertEquals(count1.executionCount(), 1); + for (int i = 0; i < 99; i++) { + assertEquals(vm.eval(source).get(), 13); + } + checkCounts(execTool, 100, 200); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13rootNode.execute(null)); - } + execTool.setEnabled(false); + assertEquals(vm.eval(source).get(), 13); + checkCounts(execTool, 100, 200); - final NodeExecutionCount[] counts100 = tool.getCounts(); - assertNotNull(counts100); - assertEquals(counts100.length, 1); - final NodeExecutionCount count100 = counts100[0]; - assertNotNull(count100); - assertEquals(count100.nodeClass(), addNode.getClass()); - assertEquals(count100.executionCount(), 100); - - tool.dispose(); + execTool.dispose(); } }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Tue Sep 29 18:04:11 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * 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.tools.test; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.KillException; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeCost; -import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; - -/** - * Nodes and an {@linkplain CallTarget executable ASTs} for testing. - */ -class TestNodes { - - /** - * A fake source used for testing: empty line 1, expression on line 2. - */ - public static final Source expr13Source = Source.fromText("\n6+7\n", "Test Source: expression on line 2 that evaluates to 13"); - public static final LineLocation expr13Line1 = expr13Source.createLineLocation(1); - public static final LineLocation expr13Line2 = expr13Source.createLineLocation(2); - - /** - * An executable addition expression that evaluates to 13. - */ - static CallTarget createExpr13TestCallTarget() { - final RootNode rootNode = createExpr13TestRootNode(); - return Truffle.getRuntime().createCallTarget(rootNode); - } - - /** - * Root holding an addition expression that evaluates to 13. - */ - static RootNode createExpr13TestRootNode() { - final TestLanguageNode ast = createExpr13AST(); - final TestRootNode rootNode = new TestRootNode(ast); - rootNode.adoptChildren(); - return rootNode; - } - - /** - * Addition expression that evaluates to 13, with faked source attribution. - */ - 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); - final TestValueNode rightValueNode = new TestValueNode(7, rightSourceSection); - final SourceSection exprSourceSection = expr13Source.createSection("expr", 1, 3); - return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection); - } - - abstract static class TestLanguageNode extends Node { - public abstract Object execute(VirtualFrame frame); - - public TestLanguageNode() { - } - - public TestLanguageNode(SourceSection srcSection) { - super(srcSection); - } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new TestWrapperNode(this); - } - } - - @NodeInfo(cost = NodeCost.NONE) - static class TestWrapperNode extends TestLanguageNode implements WrapperNode { - @Child private TestLanguageNode child; - @Child private ProbeNode probeNode; - - public TestWrapperNode(TestLanguageNode child) { - assert !(child instanceof TestWrapperNode); - 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 frame) { - probeNode.enter(child, frame); - Object result; - - try { - result = child.execute(frame); - probeNode.returnValue(child, frame, result); - } catch (KillException e) { - throw (e); - } catch (Exception e) { - probeNode.returnExceptional(child, frame, e); - throw (e); - } - - return result; - } - } - - /** - * 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(TruffleLanguage.class, null, null); - this.body = body; - } - - @Override - public Object execute(VirtualFrame frame) { - return body.execute(frame); - } - - @Override - public boolean isCloningAllowed() { - return true; - } - - @Override - public void applyInstrumentation() { - Probe.applyASTProbers(body); - } - } - - static class TestValueNode extends TestLanguageNode { - private final int value; - - public TestValueNode(int value) { - this.value = value; - } - - public TestValueNode(int value, SourceSection srcSection) { - super(srcSection); - this.value = value; - } - - @Override - public Object execute(VirtualFrame frame) { - return new Integer(this.value); - } - } - - static class TestAddNode extends TestLanguageNode { - @Child private TestLanguageNode leftChild; - @Child private TestLanguageNode rightChild; - - public TestAddNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection sourceSection) { - super(sourceSection); - this.leftChild = insert(leftChild); - this.rightChild = insert(rightChild); - } - - @Override - public Object execute(VirtualFrame frame) { - return new Integer(((Integer) leftChild.execute(frame)).intValue() + ((Integer) rightChild.execute(frame)).intValue()); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java Wed Sep 30 16:33:56 2015 -0700 @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2015, 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.test; + +import java.io.IOException; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleRuntime; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.KillException; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + +public class ToolTestUtil { + + static final String MIME_TYPE = "text/x-toolTest"; + + static enum ToolTestTag implements SyntaxTag { + + ADD_TAG("addition", "test language addition node"), + + VALUE_TAG("value", "test language value node"); + + private final String name; + private final String description; + + private ToolTestTag(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + } + + static Source createTestSource(String description) { + return Source.fromText("6\n+\n7\n" + description, description).withMimeType(MIME_TYPE); + } + + @TruffleLanguage.Registration(name = "toolTestLanguage", version = "0", mimeType = MIME_TYPE) + public static final class ToolTestLang extends TruffleLanguage<Object> { + + public static final ToolTestLang INSTANCE = new ToolTestLang(); + + private ToolTestLang() { + } + + @Override + protected CallTarget parse(Source source, Node context, String... argumentNames) throws IOException { + final TestValueNode leftValueNode = new TestValueNode(6, source.createSection("6", 0, 1)); + final TestValueNode rightValueNode = new TestValueNode(7, source.createSection("7", 4, 1)); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode, source.createSection("+", 2, 1)); + final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode); + final TruffleRuntime runtime = Truffle.getRuntime(); + final CallTarget callTarget = runtime.createCallTarget(rootNode); + return callTarget; + } + + @Override + protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal(Object context) { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof TestAdditionNode || node instanceof TestValueNode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + if (isInstrumentable(node)) { + return new ToolTestWrapperNode((ToolTestLangNode) node); + } + return null; + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @Override + protected Object createContext(Env env) { + return null; + } + } + + static final class TestASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, RootNode startNode) { + startNode.accept(new NodeVisitor() { + + @Override + public boolean visit(Node node) { + if (node instanceof ToolTestLangNode) { + + final ToolTestLangNode testNode = (ToolTestLangNode) node; + + if (node instanceof TestValueNode) { + instrumenter.probe(testNode).tagAs(ToolTestTag.VALUE_TAG, null); + + } else if (node instanceof TestAdditionNode) { + instrumenter.probe(testNode).tagAs(ToolTestTag.ADD_TAG, null); + + } + } + return true; + } + }); + } + } + + abstract static class ToolTestLangNode extends Node { + public abstract Object execute(VirtualFrame vFrame); + + protected ToolTestLangNode(SourceSection ss) { + super(ss); + } + } + + @NodeInfo(cost = NodeCost.NONE) + static class ToolTestWrapperNode extends ToolTestLangNode implements WrapperNode { + @Child private ToolTestLangNode child; + @Child private EventHandlerNode eventHandlerNode; + + public ToolTestWrapperNode(ToolTestLangNode child) { + super(null); + assert !(child instanceof ToolTestWrapperNode); + this.child = child; + } + + @Override + public String instrumentationInfo() { + return "Wrapper node for testing"; + } + + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; + } + + @Override + public Probe getProbe() { + return eventHandlerNode.getProbe(); + } + + @Override + public Node getChild() { + return child; + } + + @Override + public Object execute(VirtualFrame vFrame) { + eventHandlerNode.enter(child, vFrame); + Object result; + try { + result = child.execute(vFrame); + eventHandlerNode.returnValue(child, vFrame, result); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + eventHandlerNode.returnExceptional(child, vFrame, e); + throw (e); + } + return result; + } + } + + /** + * A simple node for our test language to store a value. + */ + static class TestValueNode extends ToolTestLangNode { + private final int value; + + public TestValueNode(int value, SourceSection s) { + super(s); + 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 ToolTestLangNode { + @Child private ToolTestLangNode leftChild; + @Child private ToolTestLangNode rightChild; + + public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection s) { + super(s); + 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 InstrumentationTestRootNode extends RootNode { + @Child private ToolTestLangNode 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 InstrumentationTestRootNode(ToolTestLangNode body) { + super(ToolTestLang.class, null, null); + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + } + + /** + * 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 ToolTestLangNode body; + + final Instrumenter instrumenter; + + /** + * 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(ToolTestLangNode body, Instrumenter instrumenter) { + super(ToolTestLang.class, null, null); + this.instrumenter = instrumenter; + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + } +}
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,21 +24,27 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.instrument.InstrumentationTool; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; + import org.junit.Test; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.vm.PolyglotEngine; + /** * Test the basic life cycle properties shared by all instances of {@link InstrumentationTool}. */ public class TruffleToolTest { @Test - public void testEmptyLifeCycle() { + public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); assertFalse(tool.isEnabled()); - tool.install(); + instrumenter.install(tool); assertTrue(tool.isEnabled()); tool.reset(); assertTrue(tool.isEnabled()); @@ -73,45 +79,58 @@ } @Test(expected = IllegalStateException.class) - public void testAlreadyInstalled() { + public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); - tool.install(); + instrumenter.install(tool); + instrumenter.install(tool); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed1() { + public void testAlreadyDisposed1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + instrumenter.install(tool); tool.dispose(); - tool.install(); + instrumenter.install(tool); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed2() { + public void testAlreadyDisposed2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + instrumenter.install(tool); tool.dispose(); tool.reset(); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed3() { + public void testAlreadyDisposed3() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + instrumenter.install(tool); tool.dispose(); tool.setEnabled(true); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed4() { + public void testAlreadyDisposed4() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + instrumenter.install(tool); tool.dispose(); tool.dispose(); } - private static final class DummyTruffleTool extends InstrumentationTool { + private static Instrumenter getInstrumenter() throws NoSuchFieldException, IllegalAccessException { + final PolyglotEngine vm = PolyglotEngine.buildNew().build(); + final Field field = PolyglotEngine.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + return instrumenter; + } + + private static final class DummyTruffleTool extends Instrumenter.Tool { @Override protected boolean internalInstall() {
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,9 +24,18 @@ */ package com.oracle.truffle.tools; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.InstrumentationTool; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeSet; + +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeInstrument; import com.oracle.truffle.api.instrument.ProbeListener; import com.oracle.truffle.api.instrument.SimpleInstrumentListener; import com.oracle.truffle.api.instrument.StandardSyntaxTag; @@ -37,33 +46,26 @@ import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeSet; /** - * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that - * hold a specified {@linkplain SyntaxTag syntax tag}, tabulated by source and line number - * associated with each node. Syntax tags are presumed to be applied external to the tool. If no tag - * is specified, {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to - * conventional behavior for code coverage tools. + * An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter + * <em>execution calls</em> to AST nodes that hold a specified {@linkplain SyntaxTag syntax tag}, + * tabulated by source and line number associated with each node. Syntax tags are presumed to be + * applied external to the tool. If no tag is specified, {@linkplain StandardSyntaxTag#STATEMENT + * STATEMENT} is used, corresponding to conventional behavior for code coverage tools. * <p> * <b>Tool Life Cycle</b> * <p> - * See {@link InstrumentationTool} for the life cycle common to all such tools. + * See {@linkplain Instrumenter.Tool Instrumentation Tool} for the life cycle common to all such + * tools. * <p> * <b>Execution Counts</b> * <p> * <ul> * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link SimpleInstrumentListener#enter(Probe)};</li> - * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which - * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> + * to produce the event {@link SimpleInstrumentListener#onEnter(Probe)};</li> + * <li>Execution calls are tabulated only at nodes where the guest languages supports + * {@linkplain Instrumenter#probe(Node) probing}.</li> * <li>Execution calls are tabulated only at nodes present in the AST when originally created; * dynamically added nodes will not be instrumented.</li> * </ul> @@ -78,16 +80,16 @@ * any time in a simple textual format, with no other effect on the state of the tool. * </p> * - * @see Instrument + * @see ProbeInstrument * @see SyntaxTag */ -public final class CoverageTracker extends InstrumentationTool { +public final class CoverageTracker extends Instrumenter.Tool { /** Counting data. */ private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>(); /** Needed for disposal. */ - private final List<Instrument> instruments = new ArrayList<>(); + private final List<ProbeInstrument> instruments = new ArrayList<>(); /** * Coverage counting is restricted to nodes holding this tag. @@ -115,7 +117,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(countingTag)) { + addCoverageCounter(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -126,8 +132,8 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); - for (Instrument instrument : instruments) { + getInstrumenter().removeProbeListener(probeListener); + for (ProbeInstrument instrument : instruments) { instrument.dispose(); } } @@ -242,7 +248,7 @@ private final class CoverageRecord extends DefaultSimpleInstrumentListener { private final SourceSection srcSection; // The text of the code being counted - private Instrument instrument; // The attached Instrument, in case need to remove. + private ProbeInstrument instrument; // The attached Instrument, in case need to remove. private long count = 0; CoverageRecord(SourceSection srcSection) { @@ -250,7 +256,7 @@ } @Override - public void enter(Probe probe) { + public void onEnter(Probe probe) { if (isEnabled()) { count++; } @@ -273,34 +279,35 @@ @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { if (countingTag == tag) { - - final SourceSection srcSection = probe.getProbedSourceSection(); - if (srcSection == null) { - // TODO (mlvdv) report this? - return; - } - // Get the source line where the - final LineLocation lineLocation = srcSection.getLineLocation(); - CoverageRecord record = coverageMap.get(lineLocation); - if (record != null) { - // Another node starts on same line; count only the first (textually) - if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) { - // Existing record, corresponds to code earlier on line - return; - } else { - // Existing record, corresponds to code at a later position; replace it - record.instrument.dispose(); - } - } - - final CoverageRecord coverage = new CoverageRecord(srcSection); - final Instrument instrument = Instrument.create(coverage, CoverageTracker.class.getSimpleName()); - coverage.instrument = instrument; - instruments.add(instrument); - probe.attach(instrument); - coverageMap.put(lineLocation, coverage); + addCoverageCounter(probe); } } } + private void addCoverageCounter(Probe probe) { + final SourceSection srcSection = probe.getProbedSourceSection(); + if (srcSection == null) { + // TODO (mlvdv) report this? + return; + } + // Get the source line where the + final LineLocation lineLocation = srcSection.getLineLocation(); + CoverageRecord record = coverageMap.get(lineLocation); + if (record != null) { + // Another node starts on same line; count only the first (textually) + if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) { + // Existing record, corresponds to code earlier on line + return; + } else { + // Existing record, corresponds to code at a later position; replace it + record.instrument.dispose(); + } + } + + final CoverageRecord coverageRecord = new CoverageRecord(srcSection); + final ProbeInstrument instrument = getInstrumenter().attach(probe, coverageRecord, CoverageTracker.class.getSimpleName()); + coverageRecord.instrument = instrument; + instruments.add(instrument); + coverageMap.put(lineLocation, coverageRecord); + } }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,13 +24,6 @@ */ package com.oracle.truffle.tools; -import com.oracle.truffle.api.instrument.InstrumentationTool; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeListener; -import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; @@ -38,11 +31,19 @@ import java.util.HashMap; import java.util.Map; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + /** - * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST, - * indexed by {@link Source} and line number. + * An {@linkplain Instrumenter.Tool Instrumentation Tool} that builds a map of every {@link Probe} + * attached to some AST, indexed by {@link Source} and line number. */ -public final class LineToProbesMap extends InstrumentationTool { +public final class LineToProbesMap extends Instrumenter.Tool { private static final boolean TRACE = false; private static final PrintStream OUT = System.out; @@ -68,7 +69,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(null)) { + addMapEntry(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -79,7 +84,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** @@ -115,21 +120,25 @@ @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<Probe> probes = lineToProbesMap.get(lineLocation); - if (probes == null) { - probes = new ArrayList<>(2); - lineToProbesMap.put(lineLocation, probes); - } else { - assert !probes.contains(probe); - } - probes.add(probe); + addMapEntry(probe); + } + } + + private void addMapEntry(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<Probe> probes = lineToProbesMap.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbesMap.put(lineLocation, probes); + } else { + assert !probes.contains(probe); + } + probes.add(probe); } } }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Tue Sep 29 18:04:11 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Wed Sep 30 16:33:56 2015 -0700 @@ -24,22 +24,6 @@ */ package com.oracle.truffle.tools; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.InstrumentationTool; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeException; -import com.oracle.truffle.api.instrument.ProbeFailure; -import com.oracle.truffle.api.instrument.ProbeListener; -import com.oracle.truffle.api.instrument.StandardInstrumentListener; -import com.oracle.truffle.api.instrument.SyntaxTag; -import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; -import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.Node.Child; -import com.oracle.truffle.api.nodes.NodeVisitor; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; @@ -50,23 +34,42 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicLong; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeException; +import com.oracle.truffle.api.instrument.ProbeFailure; +import com.oracle.truffle.api.instrument.ProbeInstrument; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.StandardInstrumentListener; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.Node.Child; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; + /** - * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes, - * tabulated by the type of called nodes; counting can be enabled <em>all</em> nodes or restricted - * to nodes with a specified {@linkplain SyntaxTag tag} that is presumed to be applied external to - * the tool. + * An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter + * <em>execution calls</em> to AST nodes, tabulated by the type of called nodes; counting can be + * enabled <em>all</em> nodes or restricted to nodes with a specified {@linkplain SyntaxTag tag} + * that is presumed to be applied external to the tool. * <p> - * <b>Tool Life Cycle</b> + * s <b>Tool Life Cycle</b> * <p> - * See {@link InstrumentationTool} for the life cycle common to all such tools. + * See {@linkplain Instrumenter.Tool Instrumentation Tool} for the life cycle common to all such + * tools. * </p> * <b>Execution Counts</b> * <p> * <ul> * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)};</li> + * to produce the event {@link StandardInstrumentListener#onEnter(Probe, Node, VirtualFrame)};</li> * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which - * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> + * {@linkplain Instrumenter#probe(Node) probing} is supported;</li> * <li>Execution calls are tabulated only at nodes present in the AST when originally created; * dynamically added nodes will not be instrumented.</li> * </ul> @@ -91,11 +94,11 @@ * any time in a simple textual format, without effect on the state of the tool. * </p> * - * @see Instrument + * @see ProbeInstrument * @see SyntaxTag * @see ProbeFailure */ -public final class NodeExecCounter extends InstrumentationTool { +public final class NodeExecCounter extends Instrumenter.Tool { /** * Execution count for AST nodes of a particular type. @@ -108,17 +111,17 @@ /** * Listener for events at instrumented nodes. Counts are maintained in a shared table, so the - * listener is stateless and can be shared by every {@link Instrument}. + * listener is stateless and can be shared by every {@link ProbeInstrument}. */ private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { if (isEnabled()) { final Class<?> nodeClass = node.getClass(); /* * Everything up to here is inlined by Truffle compilation. Delegate the next part * to a method behind an inlining boundary. - * + * * Note that it is not permitted to pass a {@link VirtualFrame} across an inlining * boundary; they are truly virtual in inlined code. */ @@ -149,7 +152,7 @@ private final List<ProbeFailure> failures = new ArrayList<>(); /** For disposal. */ - private final List<Instrument> instruments = new ArrayList<>(); + private final List<ProbeInstrument> instruments = new ArrayList<>(); /** * If non-null, counting is restricted to nodes holding this tag. @@ -170,7 +173,7 @@ * Create a per node-type execution counting tool for all nodes in subsequently created ASTs. */ public NodeExecCounter() { - this.countingTag = null; + this(null); } /** @@ -185,10 +188,10 @@ protected boolean internalInstall() { if (countingTag == null) { astProber = new ExecCounterASTProber(); - Probe.registerASTProber(astProber); + getInstrumenter().registerASTProber(astProber); } else { probeListener = new NodeExecCounterProbeListener(); - Probe.addProbeListener(probeListener); + getInstrumenter().addProbeListener(probeListener); } return true; } @@ -202,12 +205,12 @@ @Override protected void internalDispose() { if (astProber != null) { - Probe.unregisterASTProber(astProber); + getInstrumenter().unregisterASTProber(astProber); } if (probeListener != null) { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } - for (Instrument instrument : instruments) { + for (ProbeInstrument instrument : instruments) { instrument.dispose(); } } @@ -289,24 +292,25 @@ /** * A prober that attempts to probe and instrument every node. */ - private class ExecCounterASTProber implements ASTProber, NodeVisitor { + private class ExecCounterASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, final RootNode startNode) { - public boolean visit(Node node) { + startNode.accept(new NodeVisitor() { + + public boolean visit(Node node) { + try { - if (node.isInstrumentable()) { - try { - final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter"); - instruments.add(instrument); - node.probe().attach(instrument); - } catch (ProbeException ex) { - failures.add(ex.getFailure()); + final Probe probe = instrumenter.probe(node); + final ProbeInstrument instrument = instrumenter.attach(probe, instrumentListener, "NodeExecCounter"); + instruments.add(instrument); + } catch (ProbeException ex) { + failures.add(ex.getFailure()); + } + return true; } - } - return true; - } - public void probeAST(Node node) { - node.accept(this); + }); } } @@ -319,9 +323,8 @@ @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { if (countingTag == tag) { - final Instrument instrument = Instrument.create(instrumentListener, NodeExecCounter.class.getSimpleName()); + final ProbeInstrument instrument = getInstrumenter().attach(probe, instrumentListener, NodeExecCounter.class.getSimpleName()); instruments.add(instrument); - probe.attach(instrument); } } }