# HG changeset patch # User Michael Van De Vanter # Date 1422419094 28800 # Node ID 8670585759797e702604be0279b8d8b989a7112f # Parent 0f462015296f0d63a7b5913d213869a120417b78 Truffle: Improved support for "probing" AST nodes: - Node.isSafelyReplacaeableBy(Node) checks in advance if Node.replace(Node) would be unsafe (crash the VM). - Hoist Probe() from language imlementations into Node; now completely language agnostic. - Language implementations support probing by implementing Node.isInstrumentable() and Node.createWrapperNode() - Node.Probe() throws ProbeException (without side effects) if the probe fails. -- ProbeException contains an instance of ProbeFailure that diagnoses the failure in detail - Additional measures to prevent instrumentation from being applied to internal InstrumentationNodes. - Promote ProbeListener to top level interface and add a default implementation diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/SafeReplaceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/SafeReplaceTest.java Tue Jan 27 20:24:54 2015 -0800 @@ -0,0 +1,95 @@ +/* + * 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.nodes; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * Tests optional method for ensuring that a node replacement is type safe. Ordinary node + * replacement is performed by unsafe assignment of a parent node's child field. + */ +public class SafeReplaceTest { + + @Test + public void testCorrectReplacement() { + TestRootNode root = new TestRootNode(); + final TestNode oldChild = new TestNode(); + root.child = oldChild; + assertFalse(oldChild.isReplaceable()); // No parent node + root.adoptChildren(); + assertTrue(oldChild.isReplaceable()); // Now adopted by parent + final TestNode newChild = new TestNode(); + assertTrue(oldChild.isSafelyReplaceableBy(newChild)); // Parent field type is assignable by + // new node + oldChild.replace(newChild); + root.execute(null); + assertEquals(root.executed, 1); + assertEquals(oldChild.executed, 0); + assertEquals(newChild.executed, 1); + } + + @Test + public void testIncorrectReplacement() { + TestRootNode root = new TestRootNode(); + final TestNode oldChild = new TestNode(); + root.child = oldChild; + root.adoptChildren(); + final WrongTestNode newChild = new WrongTestNode(); + assertFalse(oldChild.isSafelyReplaceableBy(newChild)); + // Can't test: oldChild.replace(newChild); + // Fails if assertions checked; else unsafe assignment will eventually crash the VM + } + + private static class TestNode extends Node { + + private int executed; + + public Object execute() { + executed++; + return null; + } + } + + private static class TestRootNode extends RootNode { + + @Child TestNode child; + + private int executed; + + @Override + public Object execute(VirtualFrame frame) { + executed++; + child.execute(); + return null; + } + } + + private static class WrongTestNode extends Node { + } + +} diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Tue Jan 27 20:23:13 2015 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Tue Jan 27 20:24:54 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,13 +31,14 @@ * not yet executed) AST. * * @see Probe - * @see Probe#addProbeListener(com.oracle.truffle.api.instrument.Probe.ProbeListener) + * @see Probe#addProbeListener(ProbeListener) */ public interface ASTProber { /** * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching - * {@linkplain Probe Probes} to them. + * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable() + * non-instrumentable} nodes. */ void probeAST(Node node); diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Jan 27 20:23:13 2015 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue Jan 27 20:24:54 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -248,6 +248,11 @@ this.nextInstrument = nextNode; } + @Override + public boolean isInstrumentable() { + return false; + } + /** * Gets the instrument that created this node. */ diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue Jan 27 20:23:13 2015 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue Jan 27 20:24:54 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,31 +28,32 @@ import java.util.*; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.instrument.ProbeNode.Instrumentable; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.utilities.*; //TODO (mlvdv) migrate some of this to external documentation. /** - * A binding between a particular location in the Truffle AST representation of a running Guest - * Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of "attached" - * {@linkplain Instrument instrumentation} for use by external tools. + * A binding between a particular location in the Truffle AST representation of a running + * Guest Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of + * "attached" {@linkplain Instrument instrumentation} for use by external tools. The instrumentation + * is intended to persist at the location, even if the specific node instance is + * {@linkplain Node#replace(Node) replaced}. *

- * The effect of a binding is to intercept {@linkplain TruffleEventReceiver execution events} at the - * node and notify each attached {@link Instrument} before execution is allowed to resume. + * The effect of a binding is to intercept {@linkplain TruffleEventReceiver execution events} + * arriving at the node and notify each attached {@link Instrument} before execution is allowed to + * proceed to the child. *

- * A Probe is "inserted" into a GL node via a call to {@link Instrumentable#probe()}; a GL node must - * implement {@link Instrumentable} in order to support instrumentation. No more than one Probe can - * be inserted at a node. + * A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one Probe + * can be inserted at a node. *

* The "probing" of a Truffle AST must be done after it is complete (i.e. with parent pointers * correctly assigned), but before any executions. This is done by creating an instance of * {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it * will be automatically applied to newly created ASTs. *

- * Each Probe may also have assigned to it one or more {@link SyntaxTag}s, for example identifying a - * node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to + * Each Probe may also have assigned to it any number of {@link SyntaxTag}s, for example identifying + * a node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to * configure behavior relevant to each probed node. *

* Instrumentation is implemented by modifying ASTs, both by inserting nodes into each AST at probed @@ -60,69 +61,16 @@ * Attached instrumentation code become, in effect, part of the GL program, and is subject to the * same levels of optimization as other GL code. This implementation accounts properly for the fact * that Truffle frequently clones ASTs, along with any attached instrumentation nodes. A - * Probe, along with attached Instruments, represents a logical binding with a source code - * location, producing event notifications that are (mostly) independent of which AST clone is - * executing. + * {@link Probe}, along with attached {@link Instrument}s, represents a logical binding + * with a source code location, producing event notifications that are (mostly) independent of which + * AST clone is executing. * * @see Instrument - * @see Instrumentable * @see ASTProber + * @see ProbeListener */ public final class Probe implements SyntaxTagged { - /** - * An observer of events related to {@link Probe}s: creating and tagging. - */ - public interface ProbeListener { - - /** - * 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 - */ - void startASTProbing(Source source); - - /** - * Notifies that a {@link Probe} has been newly attached to an AST via - * {@link Instrumentable#probe()}. - *

- * There can be no more than one {@link Probe} at a node; this notification will only be - * delivered the first time {@linkplain Instrumentable#probe() probe()} is called at a - * particular AST node. There will also be no notification when the AST to which the Probe - * is attached is cloned. - */ - void newProbeInserted(Probe probe); - - /** - * Notifies that a {@link SyntaxTag} has been newly added to the set of tags associated with - * a {@link Probe} via {@link Probe#tagAs(SyntaxTag, Object)}. - *

- * The {@linkplain SyntaxTag tags} at a {@link Probe} are a set; this notification - * will only be delivered the first time a particular {@linkplain SyntaxTag tag} is added at - * a {@link Probe}. - *

- * An optional value supplied with {@linkplain Probe#tagAs(SyntaxTag, Object) - * tagAs(SyntaxTag, Object)} is reported to all listeners, but not stored. As a consequence, - * the optional value will have no effect at all if the tag had already been added. - * - * @param probe where a tag has been added - * @param tag the tag that has been newly added (subsequent additions of the tag are - * unreported). - * @param tagValue an optional value associated with the tag for the purposes of reporting. - */ - void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue); - - /** - * 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 - */ - void endASTProbing(Source source); - - } - private static final List astProbers = new ArrayList<>(); private static final List probeListeners = new ArrayList<>(); @@ -282,7 +230,7 @@ private boolean trapActive = false; /** - * @see Instrumentable#probe() + * Intended for use only by {@link ProbeNode}. */ Probe(ProbeNode probeNode, SourceSection sourceSection) { this.sourceSection = sourceSection; diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Tue Jan 27 20:24:54 2015 -0800 @@ -0,0 +1,52 @@ +/* + * 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.instrument.ProbeFailure.Reason; +import com.oracle.truffle.api.nodes.*; + +/** + * An exception thrown when {@link Node#probe()} fails because of an implementation failure. + *

+ * Language and tool implementations should ensure that clients of tools never see this exception. + */ +public class ProbeException extends RuntimeException { + static final long serialVersionUID = 1L; + private final ProbeFailure failure; + + public ProbeException(Reason reason, Node parent, Node child, Object wrapper) { + this.failure = new ProbeFailure(reason, parent, child, wrapper); + } + + public ProbeFailure getFailure() { + return failure; + } + + @Override + public String toString() { + return failure.getMessage(); + } + +} diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Tue Jan 27 20:24:54 2015 -0800 @@ -0,0 +1,145 @@ +/* + * 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.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.NodeUtil.NodeField; + +/** + * Description of a failed attempt to instrument an AST node. + */ +public final class ProbeFailure { + + public enum Reason { + + /** + * Node to be probed has no parent. + */ + NO_PARENT("Node to be probed has no parent"), + + /** + * The node to be probed is a wrapper. + */ + WRAPPER_NODE("The node to be probed is a wrapper"), + + /** + * The node to be probed returned {@link Node#isInstrumentable()}{@code == false}. + */ + NOT_INSTRUMENTABLE("The node to be project is \"not instrumentable\""), + + /** + * No wrapper could be created that is also a {@link Node}. + */ + NO_WRAPPER("No wrapper could be created"), + + /** + * Wrapper not assignable to the parent's child field. + */ + WRAPPER_TYPE("Wrapper not assignable to parent's child field"), + + /** + * Attempt to \"probe lite\" an already probed node. + */ + LITE_VIOLATION("Attempt to \"probe lite\" an already probed node"); + + final String message; + + private Reason(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } + + private final Reason reason; + private final Node parent; + private final Node child; + private final Object wrapper; + + /** + * Description of an internal failure of {@link Node#probe()}. + * + * @param reason what caused the failure + * @param parent the parent, if known, of the child being probed + * @param child this child being probed + * @param wrapper the {@link WrapperNode} created to implement the probe + */ + public ProbeFailure(Reason reason, Node parent, Node child, Object wrapper) { + this.reason = reason; + this.parent = parent; + this.child = child; + this.wrapper = wrapper; + } + + /** + * @return a short explanation of the failure + */ + public Reason getReason() { + return reason; + } + + /** + * @return the parent, if any, of the node being probed + */ + public Node getParent() { + return parent; + } + + /** + * @return the node being probed + */ + public Node getChild() { + return child; + } + + /** + * @return the {@link WrapperNode} created for the probe attempt + */ + public Object getWrapper() { + return wrapper; + } + + public String getMessage() { + final StringBuilder sb = new StringBuilder(reason.message + ": "); + if (parent != null) { + sb.append("parent=" + parent.getClass().getSimpleName() + " "); + if (child != null) { + sb.append("child=" + child.getClass().getSimpleName() + " "); + final NodeField field = NodeUtil.findChildField(parent, child); + if (field != null) { + sb.append("field=" + field.getName() + " "); + } + } + } + if (wrapper != null) { + sb.append("wrapper=" + wrapper.getClass().getSimpleName()); + } + return sb.toString(); + } + +} diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Tue Jan 27 20:24:54 2015 -0800 @@ -0,0 +1,80 @@ +/* + * 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.nodes.*; +import com.oracle.truffle.api.source.*; + +/** + * An observer of events related to {@link Probe}s: creating and tagging. + */ +public interface ProbeListener { + + /** + * 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 + */ + void startASTProbing(Source source); + + /** + * Notifies that a {@link Probe} has been newly attached to an AST via {@link Node#probe()}. + *

+ * 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 + * node. There will also be no notification when the AST to which the Probe is attached is + * cloned. + */ + void newProbeInserted(Probe probe); + + /** + * Notifies that a {@link SyntaxTag} has been newly added to the set of tags associated with a + * {@link Probe} via {@link Probe#tagAs(SyntaxTag, Object)}. + *

+ * The {@linkplain SyntaxTag tags} at a {@link Probe} are a set; this notification will + * only be delivered the first time a particular {@linkplain SyntaxTag tag} is added at a + * {@link Probe}. + *

+ * An optional value supplied with {@linkplain Probe#tagAs(SyntaxTag, Object) tagAs(SyntaxTag, + * Object)} is reported to all listeners, but not stored. As a consequence, the optional value + * will have no effect at all if the tag had already been added. + * + * @param probe where a tag has been added + * @param tag the tag that has been newly added (subsequent additions of the tag are + * unreported). + * @param tagValue an optional value associated with the tag for the purposes of reporting. + */ + void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue); + + /** + * 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 + */ + void endASTProbing(Source source); + +} diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Tue Jan 27 20:23:13 2015 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Tue Jan 27 20:24:54 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -38,46 +38,9 @@ public abstract class ProbeNode extends Node implements TruffleEventReceiver, InstrumentationNode { /** - * Any Truffle node implementing this interface 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. - * - * @see Instrument - */ - public interface Instrumentable { - - /** - * Enables "instrumentation" of a Guest Language Truffle node, where the node is presumed to - * be part of a well-formed Truffle AST that is not being executed. The AST may be modified - * as a side effect. - *

- * This interface is not intended to be visible as part of the API for tools - * (instrumentation clients). - * - * @return a (possibly newly created) {@link Probe} associated with this node. - */ - Probe probe(); - - /** - * Enables a one-time, unchangeable "instrumentation" of a Guest Language Truffle node, - * where the node is presumed to be part of a well-formed Truffle AST that is not being - * executed. The AST may be modified as a side-effect. Unlike {@link #probe()}, once - * {@link #probeLite(TruffleEventReceiver)} is called at a node, no additional probing can - * be added and no additional instrumentation can be attached. - *

- * This interface is not intended to be visible as part of the API for tools - * (instrumentation clients). - * - * @param eventReceiver The {@link TruffleEventReceiver} for the single "instrument" being - * attached to this node. - */ - void probeLite(TruffleEventReceiver eventReceiver); - } - - /** - * A node that can be inserted into a Truffle AST, and which enables instrumentation at - * a particular Guest Language (GL) node. + * 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}. *

* The implementation must be GL-specific. A wrapper decorates a GL AST node (the * wrapper's child) by acting as a transparent proxy with respect to the GL's @@ -115,7 +78,6 @@ * their runtime overhead to zero when there are no attached {@link Instrument}s. * *

- * Disclaimer: experimental interface under development. * * @see Instrument */ @@ -130,7 +92,7 @@ /** * Gets the {@link Probe} responsible for installing this wrapper; none if the wrapper - * installed via {@linkplain Instrumentable#probeLite(TruffleEventReceiver) "lite-Probing"}. + * installed via {@linkplain Node#probeLite(TruffleEventReceiver) "lite-Probing"}. */ Probe getProbe(); @@ -163,6 +125,11 @@ wrapper.insertProbe(probeLiteNode); } + @Override + public boolean isInstrumentable() { + return false; + } + /** * @return the {@link Probe} permanently associated with this {@link ProbeNode}. * diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java Tue Jan 27 20:24:54 2015 -0800 @@ -0,0 +1,44 @@ +/* + * 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.impl; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.source.*; + +public abstract class DefaultProbeListener implements ProbeListener { + + public void startASTProbing(Source source) { + } + + public void newProbeInserted(Probe probe) { + } + + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + } + + public void endASTProbing(Source source) { + } + +} diff -r 0f462015296f -r 867058575979 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Tue Jan 27 20:23:13 2015 -0800 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Tue Jan 27 20:24:54 2015 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -30,6 +30,8 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.utilities.*; @@ -288,9 +290,9 @@ } /** - * Checks if this node is properly adopted by a parent and can be replaced. + * Checks if this node is properly adopted by its parent. * - * @return {@code true} if it is safe to replace this node. + * @return {@code true} if it is structurally safe to replace this node. */ public final boolean isReplaceable() { if (getParent() != null) { @@ -303,6 +305,13 @@ return false; } + /** + * Checks if this node can be replaced by another node, both structurally and with type safety. + */ + public final boolean isSafelyReplaceableBy(Node newNode) { + return isReplaceable() && NodeUtil.isReplacementSafe(getParent(), this, newNode); + } + private void reportReplace(Node oldNode, Node newNode, CharSequence reason) { Node node = this; while (node != null) { @@ -424,6 +433,138 @@ } /** + * 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. + * + * @see Instrument + */ + public boolean isInstrumentable() { + return false; + } + + /** + * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that: + *

    + *
  1. implements {@link WrapperNode}
  2. + *
  3. has {@code this} as it's child, and
  4. + *
  5. whose type is suitable for (unsafe) replacement of {@code this} in the parent.
  6. + *
+ * + * @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; + } + + /** + * Enables "one-shot", unmodifiable {@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. + *

+ * 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()}. + *

+ * Unlike {@link #probe()}, once {@link #probeLite(TruffleEventReceiver)} is called at a node, + * no additional probing can be added and no additional instrumentation can be attached. + *

+ * This restricted form of instrumentation is intended for special cases where only one kind of + * instrumentation is desired, and for which performance is a concern + * + * @param eventReceiver + * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged + */ + public final void probeLite(TruffleEventReceiver eventReceiver) { + + 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) { + throw new ProbeException(ProbeFailure.Reason.LITE_VIOLATION, null, this, null); + } + + 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 + ProbeNode.insertProbeLite(wrapper, eventReceiver); + + // Replace this node in the AST with the wrapper + this.replace(wrapperNode); + } + + /** * Converts this node to a textual representation useful for debugging. */ @Override