comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java @ 18985:867058575979

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
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 27 Jan 2015 20:24:54 -0800
parents 8a758dce7d80
children c7e57dffc5ad
comparison
equal deleted inserted replaced
18984:0f462015296f 18985:867058575979
1 /* 1 /*
2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 * 4 *
5 * This code is free software; you can redistribute it and/or modify it 5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as 6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this 7 * published by the Free Software Foundation. Oracle designates this
28 import java.util.*; 28 import java.util.*;
29 import java.util.concurrent.*; 29 import java.util.concurrent.*;
30 30
31 import com.oracle.truffle.api.*; 31 import com.oracle.truffle.api.*;
32 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; 32 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
33 import com.oracle.truffle.api.instrument.*;
34 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
33 import com.oracle.truffle.api.source.*; 35 import com.oracle.truffle.api.source.*;
34 import com.oracle.truffle.api.utilities.*; 36 import com.oracle.truffle.api.utilities.*;
35 37
36 /** 38 /**
37 * Abstract base class for all Truffle nodes. 39 * Abstract base class for all Truffle nodes.
286 reportReplace(this, newNode, reason); 288 reportReplace(this, newNode, reason);
287 onReplace(newNode, reason); 289 onReplace(newNode, reason);
288 } 290 }
289 291
290 /** 292 /**
291 * Checks if this node is properly adopted by a parent and can be replaced. 293 * Checks if this node is properly adopted by its parent.
292 * 294 *
293 * @return {@code true} if it is safe to replace this node. 295 * @return {@code true} if it is structurally safe to replace this node.
294 */ 296 */
295 public final boolean isReplaceable() { 297 public final boolean isReplaceable() {
296 if (getParent() != null) { 298 if (getParent() != null) {
297 for (Node sibling : getParent().getChildren()) { 299 for (Node sibling : getParent().getChildren()) {
298 if (sibling == this) { 300 if (sibling == this) {
299 return true; 301 return true;
300 } 302 }
301 } 303 }
302 } 304 }
303 return false; 305 return false;
306 }
307
308 /**
309 * Checks if this node can be replaced by another node, both structurally and with type safety.
310 */
311 public final boolean isSafelyReplaceableBy(Node newNode) {
312 return isReplaceable() && NodeUtil.isReplacementSafe(getParent(), this, newNode);
304 } 313 }
305 314
306 private void reportReplace(Node oldNode, Node newNode, CharSequence reason) { 315 private void reportReplace(Node oldNode, Node newNode, CharSequence reason) {
307 Node node = this; 316 Node node = this;
308 while (node != null) { 317 while (node != null) {
422 } 431 }
423 return null; 432 return null;
424 } 433 }
425 434
426 /** 435 /**
436 * Any node for which this is {@code true} can be "instrumented" by installing a {@link Probe}
437 * that intercepts execution events at the node and routes them to any {@link Instrument}s that
438 * have been attached to the {@link Probe}. Only one {@link Probe} may be installed at each
439 * node; subsequent calls return the one already installed.
440 *
441 * @see Instrument
442 */
443 public boolean isInstrumentable() {
444 return false;
445 }
446
447 /**
448 * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that:
449 * <ol>
450 * <li>implements {@link WrapperNode}</li>
451 * <li>has {@code this} as it's child, and</li>
452 * <li>whose type is suitable for (unsafe) replacement of {@code this} in the parent.</li>
453 * </ol>
454 *
455 * @return an appropriately typed {@link WrapperNode} if {@link #isInstrumentable()}.
456 */
457 public WrapperNode createWrapperNode() {
458 return null;
459 }
460
461 /**
462 * Enables {@linkplain Instrument instrumentation} of a node, where the node is presumed to be
463 * part of a well-formed Truffle AST that is not being executed. If this node has not already
464 * been probed, modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between
465 * the node and its parent; the wrapper node must be provided by implementations of
466 * {@link #createWrapperNode()}. No more than one {@link Probe} may be associated with a node,
467 * so a {@linkplain WrapperNode wrapper} may not wrap another {@linkplain WrapperNode wrapper}.
468 *
469 * @return a (possibly newly created) {@link Probe} associated with this node.
470 * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged
471 */
472 public final Probe probe() {
473
474 if (this instanceof WrapperNode) {
475 throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null);
476 }
477
478 if (parent == null) {
479 throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null);
480 }
481
482 if (parent instanceof WrapperNode) {
483 return ((WrapperNode) parent).getProbe();
484 }
485
486 if (!isInstrumentable()) {
487 throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null);
488 }
489
490 // Create a new wrapper/probe with this node as its child.
491 final WrapperNode wrapper = createWrapperNode();
492
493 if (wrapper == null || !(wrapper instanceof Node)) {
494 throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper);
495 }
496
497 final Node wrapperNode = (Node) wrapper;
498
499 if (!this.isSafelyReplaceableBy(wrapperNode)) {
500 throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper);
501 }
502
503 // Connect it to a Probe
504 final Probe probe = ProbeNode.insertProbe(wrapper);
505
506 // Replace this node in the AST with the wrapper
507 this.replace(wrapperNode);
508
509 return probe;
510 }
511
512 /**
513 * Enables "one-shot", unmodifiable {@linkplain Instrument instrumentation} of a node, where the
514 * node is presumed to be part of a well-formed Truffle AST that is not being executed.
515 * <p>
516 * Modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between the node and
517 * its parent; the wrapper node must be provided by implementations of
518 * {@link #createWrapperNode()}.
519 * <p>
520 * Unlike {@link #probe()}, once {@link #probeLite(TruffleEventReceiver)} is called at a node,
521 * no additional probing can be added and no additional instrumentation can be attached.
522 * <p>
523 * This restricted form of instrumentation is intended for special cases where only one kind of
524 * instrumentation is desired, and for which performance is a concern
525 *
526 * @param eventReceiver
527 * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged
528 */
529 public final void probeLite(TruffleEventReceiver eventReceiver) {
530
531 if (this instanceof WrapperNode) {
532 throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null);
533 }
534
535 if (parent == null) {
536 throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null);
537 }
538
539 if (parent instanceof WrapperNode) {
540 throw new ProbeException(ProbeFailure.Reason.LITE_VIOLATION, null, this, null);
541 }
542
543 if (!isInstrumentable()) {
544 throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null);
545 }
546
547 // Create a new wrapper/probe with this node as its child.
548 final WrapperNode wrapper = createWrapperNode();
549
550 if (wrapper == null || !(wrapper instanceof Node)) {
551 throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper);
552 }
553
554 final Node wrapperNode = (Node) wrapper;
555
556 if (!this.isSafelyReplaceableBy(wrapperNode)) {
557 throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper);
558 }
559
560 // Connect it to a Probe
561 ProbeNode.insertProbeLite(wrapper, eventReceiver);
562
563 // Replace this node in the AST with the wrapper
564 this.replace(wrapperNode);
565 }
566
567 /**
427 * Converts this node to a textual representation useful for debugging. 568 * Converts this node to a textual representation useful for debugging.
428 */ 569 */
429 @Override 570 @Override
430 public String toString() { 571 public String toString() {
431 StringBuilder sb = new StringBuilder(getClass().getSimpleName()); 572 StringBuilder sb = new StringBuilder(getClass().getSimpleName());