diff graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java @ 20106:2e3cc2a27711

Truffle/Instrumentation: a new flavor of Instrument that lazily provides an AST fragment to be attached/adopted directly into a running AST, and to which execution event notifications will be routed. Important use cases so far include conditional breakpoints (with optimizeable conditions) and Ruby set_trace_func.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 31 Mar 2015 19:01:07 -0700
parents 907128d02b31
children e7ece52e1ff3
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue Mar 31 18:58:36 2015 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue Mar 31 19:01:07 2015 -0700
@@ -194,6 +194,19 @@
         return new ASTInstrument(astListener, instrumentInfo);
     }
 
+    /**
+     * Creates an instrument that, when executed the first time in any particular AST location,
+     * invites the tool to provide an AST fragment for attachment/adoption into the running AST.
+     *
+     * @param toolNodeListener a listener for the tool that can request an AST fragment
+     * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful
+     *            for debugging.
+     * @return a new instrument, ready for attachment at a probe.
+     */
+    public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) {
+        return new ToolNodeInstrument(toolNodeListener, instrumentInfo);
+    }
+
     // TODO (mlvdv) experimental
     /**
      * For implementation testing.
@@ -278,7 +291,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(BasicInstrument.this);
@@ -298,29 +311,29 @@
 
             public void enter(Node node, VirtualFrame vFrame) {
                 BasicInstrument.this.instrumentListener.enter(BasicInstrument.this.probe);
-                if (nextInstrument != null) {
-                    nextInstrument.enter(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
                 BasicInstrument.this.instrumentListener.returnVoid(BasicInstrument.this.probe);
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
                 BasicInstrument.this.instrumentListener.returnValue(BasicInstrument.this.probe, result);
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
                 BasicInstrument.this.instrumentListener.returnExceptional(BasicInstrument.this.probe, exception);
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -362,7 +375,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(ASTInstrument.this);
@@ -382,29 +395,29 @@
 
             public void enter(Node node, VirtualFrame vFrame) {
                 ASTInstrument.this.astListener.enter(ASTInstrument.this.probe, node, vFrame);
-                if (nextInstrument != null) {
-                    nextInstrument.enter(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
                 ASTInstrument.this.astListener.returnVoid(ASTInstrument.this.probe, node, vFrame);
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
                 ASTInstrument.this.astListener.returnValue(ASTInstrument.this.probe, node, vFrame, result);
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
                 ASTInstrument.this.astListener.returnExceptional(ASTInstrument.this.probe, node, vFrame, exception);
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -416,6 +429,104 @@
 
     }
 
+    /**
+     * An instrument that propagates events to an instance of {@link ASTInstrumentListener}.
+     */
+    private static final class ToolNodeInstrument extends Instrument {
+
+        /**
+         * Tool-supplied listener for AST events.
+         */
+        private final ToolNodeInstrumentListener toolNodeListener;
+
+        private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolNodeListener = toolNodeListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new ToolInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(ToolNodeInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class ToolInstrumentNode extends AbstractInstrumentNode {
+
+            @Child ToolNode toolNode;
+
+            private ToolInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (toolNode == null) {
+                    final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe);
+                    if (newToolNode != null) {
+                        toolNode = newToolNode;
+                        adoptChildren();
+                        ToolNodeInstrument.this.probe.invalidateProbeUnchanged();
+                    }
+                }
+                if (toolNode != null) {
+                    toolNode.enter(node, vFrame);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (toolNode != null) {
+                    toolNode.returnVoid(node, vFrame);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (toolNode != null) {
+                    toolNode.returnValue(node, vFrame, result);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (toolNode != null) {
+                    toolNode.returnExceptional(node, vFrame, exception);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolNodeListener.getClass().getSimpleName();
+            }
+        }
+
+    }
+
     public interface TruffleOptListener {
         void notifyIsCompiled(boolean isCompiled);
     }
@@ -440,7 +551,7 @@
             if (instrumentNode != null) {
                 if (instrumentNode.getInstrument() == this) {
                     // Found the match at the head of the chain
-                    return instrumentNode.nextInstrument;
+                    return instrumentNode.nextInstrumentNode;
                 }
                 // Match not at the head of the chain; remove it.
                 found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
@@ -466,26 +577,26 @@
                     this.isCompiled = CompilerDirectives.inCompiledCode();
                     TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
                 }
-                if (nextInstrument != null) {
-                    nextInstrument.enter(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnVoid(node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnValue(node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (nextInstrument != null) {
-                    nextInstrument.returnExceptional(node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
@@ -500,10 +611,10 @@
     @NodeInfo(cost = NodeCost.NONE)
     abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
 
-        @Child protected AbstractInstrumentNode nextInstrument;
+        @Child protected AbstractInstrumentNode nextInstrumentNode;
 
         protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
-            this.nextInstrument = nextNode;
+            this.nextInstrumentNode = nextNode;
         }
 
         @Override
@@ -527,21 +638,21 @@
          */
         private boolean removeFromChain(Instrument instrument) {
             assert getInstrument() != instrument;
-            if (nextInstrument == null) {
+            if (nextInstrumentNode == null) {
                 return false;
             }
-            if (nextInstrument.getInstrument() == instrument) {
+            if (nextInstrumentNode.getInstrument() == instrument) {
                 // Next is the one to remove
-                if (nextInstrument.nextInstrument == null) {
+                if (nextInstrumentNode.nextInstrumentNode == null) {
                     // Next is at the tail; just forget
-                    nextInstrument = null;
+                    nextInstrumentNode = null;
                 } else {
                     // Replace next with its successor
-                    nextInstrument.replace(nextInstrument.nextInstrument);
+                    nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
                 }
                 return true;
             }
-            return nextInstrument.removeFromChain(instrument);
+            return nextInstrumentNode.removeFromChain(instrument);
         }
 
         protected String getInstrumentInfo() {