Class BlockNode<T extends Node>

java.lang.Object
com.oracle.truffle.api.nodes.Node
com.oracle.truffle.api.nodes.BlockNode<T>
Type Parameters:
T - the type of the block element node
All Implemented Interfaces:
NodeInterface, Cloneable

public abstract class BlockNode<T extends Node> extends Node
Represents a standard node for guest language blocks. Using standard blocks in a guest language is not strictly necessary, but recommended as it allows the optimizing runtime to split compilation of very big methods into multiple compilation units. Block nodes may be executed with a customizable argument to resume the execution at a particular location.

Elements are executed using the BlockNode.ElementExecutor provided when creating the block node. When a block is executed then all elements are executed using executeVoid except the last element which will be executed using the typed execute method also used to execute the block node. This allows to implement boxing elimination of the return value of blocks in the interpreter. For example, if executeInt is invoked on the block , then all elements except the last one is executed using executeVoid, but the last one with executeInt.

The optimizing runtime may decide to group elements of a block into multiple block compilation units. This may happen if the block is too big to be compiled with a single compilation unit. If the compilation final state of an element is changed, or a node is replaced only the compilation unit of the current block is invalidated and not all compilations units of a block. Therefore, no compilation final assumptions must be taken between elements of a block.

Simple Usage:

The following example shows how a language with untyped execute methods, but with blocks that return values would use the block node.
// language base node
abstract class LanguageNode extends Node {

    public abstract Object execute(VirtualFrame frame);

}

final class LanguageBlockNode extends LanguageNode
                implements ElementExecutor<LanguageNode> {

    @Child private BlockNode<LanguageNode> block;

    LanguageBlockNode(LanguageNode[] elements) {
        this.block = BlockNode.create(elements, this);
    }

    @Override
    public void executeVoid(VirtualFrame frame, LanguageNode node,
                    int index, int arg) {
        node.execute(frame);
    }

    @Override
    public Object executeGeneric(VirtualFrame frame, LanguageNode node,
                    int index, int arg) {
        return node.execute(frame);
    }

    @Override
    public Object execute(VirtualFrame frame) {
        return block.executeGeneric(frame, 0);
    }
}

Resumable Usage:

The following example shows how the block node can be used to implement resumable blocks, e.g. for generator implementations:
final class YieldException extends ControlFlowException {

    final Object result;

    YieldException(Object result) {
        this.result = result;
    }

}

final class ResumableBlockNode extends LanguageNode
                implements ElementExecutor<LanguageNode> {

    @CompilationFinal private Integer indexSlot;
    @Child private BlockNode<LanguageNode> block;

    ResumableBlockNode(LanguageNode[] elements) {
        this.block = BlockNode.create(elements, this);
    }

    @Override
    public Object execute(VirtualFrame frame) {
        frame.setAuxiliarySlot(getIndexSlot(), 0);
        return block.executeGeneric(frame, 0);
    }

    // Called if the resumable block needs to be
    // resumed later on after a yield.
    public void resume(VirtualFrame frame) {
        getIndexSlot();
        int startIndex = frame.getInt(getIndexSlot());
        block.executeGeneric(frame, startIndex);
    }

    @Override
    public void executeVoid(VirtualFrame frame, LanguageNode node,
                    int elementIndex, int startIndex) {
        executeGeneric(frame, node, elementIndex, startIndex);
    }

    @Override
    public Object executeGeneric(VirtualFrame frame, LanguageNode node,
                    int elementIndex, int startIndex) {
        if (elementIndex >= startIndex) {
            try {
                return node.execute(frame);
            } catch (YieldException e) {
                // store index to be able to resume later
                frame.setInt(getIndexSlot(), elementIndex);
                return e.result;
            }
        } else {
            CompilerDirectives.transferToInterpreterAndInvalidate();
            throw new AssertionError("Invalid start index");
        }
    }

    private int getIndexSlot() {
        Integer slot = this.indexSlot;
        if (slot == null) {
            CompilerDirectives.transferToInterpreterAndInvalidate();
            FrameDescriptor fd = getRootNode().getFrameDescriptor();
            this.indexSlot = slot = fd.findOrAddAuxiliarySlot(this);
        }
        return slot;
    }

}
Since:
19.3