com.oracle.truffle.api.nodes.RootNode Maven / Gradle / Ivy
Show all versions of truffle-api Show documentation
/*
* Copyright (c) 2012, 2013, 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.nodes;
import java.util.concurrent.locks.ReentrantLock;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerOptions;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleLanguage.ParsingRequest;
import com.oracle.truffle.api.TruffleRuntime;
import com.oracle.truffle.api.frame.FrameDescriptor;
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.source.Source;
import com.oracle.truffle.api.source.SourceSection;
/**
* Represents the root node in a Truffle AST. The root node is a {@link Node node} that allows to be
* {@link #execute(VirtualFrame) executed} using a {@link VirtualFrame frame} instance created by
* the framework. Please note that the {@link RootNode} should not be executed directly but using
* {@link CallTarget#call(Object...)}. The structure of the frame is provided by the
* {@link FrameDescriptor frame descriptor} passed in the constructor. A root node has always a
* null
{@link #getParent() parent} and cannot be {@link #replace(Node) replaced}.
*
* Construction
*
* The root node can be constructed with a {@link TruffleLanguage language implementation} if it is
* available. The language implementation instance is obtainable while
* {@link TruffleLanguage#createContext(Env)} or {@link TruffleLanguage#parse(ParsingRequest)} is
* executed. If no language environment is available, then null
can be passed. Please
* note that root nodes with null
language are considered not instrumentable and have
* no access to the {@link #getLanguage(Class) language} or its public {@link #getLanguageInfo()
* information}.
*
* Execution
*
* In order to execute a root node, a call target needs to be created using
* {@link TruffleRuntime#createCallTarget(RootNode)}. This allows the runtime system to optimize the
* execution of the AST. The {@link CallTarget} can either be {@link CallTarget#call(Object...)
* called} directly from runtime code or {@link DirectCallNode direct} and {@link IndirectCallNode
* indirect} call nodes can be created, inserted in a child field and
* {@link DirectCallNode#call(Object[]) called}. The use of direct call nodes allows the framework
* to automatically inline and further optimize call sites based on heuristics.
*
* After several calls to a call target or call node, the root node might get compiled using partial
* evaluation. The details of the compilation heuristic are unspecified, therefore the Truffle
* runtime system might decide to not compile at all.
*
*
Cardinalities
*
* One root node instance refers to other classes using the following cardinalities:
*
* - one {@link TruffleLanguage language}
*
- one {@link CallTarget call target}
*
- many {@link TruffleLanguage#createContext(Env) created} language contexts
*
*
* Instrumentation
*
* A root node can be {@linkplain com.oracle.truffle.api.instrumentation instrumented} if the
* following conditions apply:
*
* - A non-null {@link TruffleLanguage language} is passed in the root node constructor.
*
- {@link #isInstrumentable()} is overridden and returns
true
.
* - {@link #getSourceSection()} is overridden and returns a non-null value.
*
- The AST contains at least one node that is annotated with
* {@link com.oracle.truffle.api.instrumentation.Instrumentable} .
*
*
* Note: It is recommended to override {@link #getSourceSection()} and provide a
* source section if available. This allows for better testing/tracing/tooling. If no concrete
* source section is available please consider using {@link Source#createUnavailableSection()}.
*
* @since 0.8 or earlier
*/
@SuppressWarnings("rawtypes")
public abstract class RootNode extends Node {
/*
* Since languages were singletons in the past, we cannot use the Env instance stored in
* TruffleLanguage for languages that are not yet migrated. We use this env reference instead
* for compatibility.
*/
private final LanguageInfo languageInfo;
private RootCallTarget callTarget;
@CompilationFinal private FrameDescriptor frameDescriptor;
private final SourceSection sourceSection;
final ReentrantLock lock = new ReentrantLock();
/**
* @deprecated use {@link RootNode(TruffleLanguage, FrameDescriptor)} instead. Root nodes do not
* support source sections by default any longer. Please override
* {@link #getSourceSection()} instead if a source section is available.
* @since 0.8 or earlier
*/
@Deprecated
protected RootNode(Class extends TruffleLanguage> language, SourceSection sourceSection, FrameDescriptor frameDescriptor) {
CompilerAsserts.neverPartOfCompilation();
if (!TruffleLanguage.class.isAssignableFrom(language)) {
throw new IllegalStateException();
}
this.languageInfo = Node.ACCESSOR.languageSupport().getLegacyLanguageInfo(language);
this.sourceSection = sourceSection;
if (frameDescriptor == null) {
this.frameDescriptor = new FrameDescriptor();
} else {
this.frameDescriptor = frameDescriptor;
}
}
/**
* Creates new root node with a given language instance. The language instance is obtainable
* while {@link TruffleLanguage#createContext(Env)} or
* {@link TruffleLanguage#parse(ParsingRequest)} is executed. If no language environment is
* available, then null
can be passed. Please note that root nodes with
* null
language are considered not instrumentable and have no access to the
* {@link #getLanguage(Class) language} or its public {@link #getLanguageInfo() information}.
*
* @param language the language this root node is associated with
* @since 0.25
*/
protected RootNode(TruffleLanguage> language) {
this(language, null);
}
/**
* Creates new root node given an language environment and frame descriptor. The language
* instance is obtainable while {@link TruffleLanguage#createContext(Env)} or
* {@link TruffleLanguage#parse(ParsingRequest)} is executed. If no language environment is
* available, then null
can be passed. Please note that root nodes with
* null
language are considered not instrumentable and have no access to the
* {@link #getLanguage(Class) language} or its public {@link #getLanguageInfo() information}.
*
* @param language the language this root node is associated with
* @since 0.25
*/
protected RootNode(TruffleLanguage> language, FrameDescriptor frameDescriptor) {
CompilerAsserts.neverPartOfCompilation();
if (language != null) {
this.languageInfo = Node.ACCESSOR.languageSupport().getLanguageInfo(language);
if (languageInfo == null) {
throw new IllegalArgumentException("Truffle language instance is not initialized.");
}
} else {
this.languageInfo = null;
}
this.frameDescriptor = frameDescriptor == null ? new FrameDescriptor() : frameDescriptor;
this.sourceSection = null;
}
/**
* Returns the language instance associated with this root node. The language instance is
* intended for internal use in languages and is only accessible if the concrete type of the
* language is known. Public information about the language can be accessed using
* {@link #getLanguageInfo()}. The language is null
if the root node is not
* associated with a null
language.
*
* @see #getLanguageInfo()
* @since 0.25
*/
@SuppressWarnings("unchecked")
public final C getLanguage(Class languageClass) {
if (languageInfo == null) {
return null;
}
TruffleLanguage> language = languageInfo.getSpi();
if (language.getClass() != languageClass) {
if (!languageClass.isInstance(language) || languageClass == TruffleLanguage.class || !TruffleLanguage.class.isAssignableFrom(languageClass)) {
CompilerDirectives.transferToInterpreter();
throw new ClassCastException("Illegal language class specified. Expected " + language.getClass().getName() + ".");
}
}
return (C) language;
}
/**
* Returns public information about the language. The language can be assumed equal if the
* instances of the language info instance are the same. To access internal details of the
* language within the language implementation use {@link #getLanguage(Class)}.
*
* @since 0.25
*/
public final LanguageInfo getLanguageInfo() {
return languageInfo;
}
/** @since 0.8 or earlier */
@Override
public Node copy() {
RootNode root = (RootNode) super.copy();
root.frameDescriptor = frameDescriptor;
return root;
}
/**
* Returns the source section associated with this {@link RootNode}. Returns null
* if by default.
*
* @since 0.13
*/
@Override
public SourceSection getSourceSection() {
return sourceSection;
}
/**
* A description of the AST (expected to be a method or procedure name in most languages) that
* identifies the AST for the benefit of guest language programmers using tools; it might
* appear, for example in the context of a stack dump or trace and is not expected to be called
* often.
*
* In some languages AST "compilation units" may have no intrinsic names. When no information is
* available, language implementations might simply use the first few characters of the code,
* followed by "{@code ...}". Language implementations should assign a more helpful name
* whenever it becomes possible, for example when a functional value is assigned. This means
* that the name might not be stable over time.
*
* Language execution semantics should not depend on either this name or the way that it is
* formatted. The name should be presented in the way expected to be most useful for
* programmers.
*
* @return a name that helps guest language programmers identify code corresponding to the AST,
* possibly {@code null} if the language implementation is unable to provide any useful
* information.
* @since 0.15
*/
public String getName() {
return null;
}
/**
* Returns true
if this {@link RootNode} is allowed to be cloned. The runtime
* system might decide to create deep copies of the {@link RootNode} in order to gather context
* sensitive profiling feedback. The default implementation returns false
. Guest
* language specific implementations may want to return true
here to indicate that
* gathering call site specific profiling information might make sense for this {@link RootNode}
* .
*
* @return true
if cloning is allowed else false
.
* @since 0.8 or earlier
*/
public boolean isCloningAllowed() {
return false;
}
/**
* Returns true
if {@link #cloneUninitialized()} can be used to create
* uninitialized copies of an already initialized / executed root node. By default, or if this
* method returns false
, an optimizing Truffle runtime might need to copy the AST
* before it is executed for the first time to ensure it is able to create new uninitialized
* copies when needed. By returning true
and therefore supporting uninitialized
* copies an optimizing runtime does not need to keep a reference to an uninitialized copy on
* its own and might therefore be able to save memory. The returned boolean needs to be
* immutable for a {@link RootNode} instance.
*
* @return true
if calls to {@link #cloneUninitialized() uninitialized copies} are
* supported.
* @see #cloneUninitialized()
* @since 0.24
*/
protected boolean isCloneUninitializedSupported() {
return false;
}
/**
* Creates an uninitialized copy of an already initialized/executed root node if it is
* {@link #isCloneUninitializedSupported() supported}. Throws an
* {@link UnsupportedOperationException} exception by default. By default, or if
* {@link #isCloneUninitializedSupported()} returns false
, an optimizing Truffle
* runtime might need to copy the root node before it is executed for the first time to ensure
* it is able to create new uninitialized copies when needed. By supporting uninitialized copies
* an optimizing runtime does not need to keep a reference to an uninitialized copy on its own
* and might therefore be able to save memory.
*
*
* Two common strategies to implement {@link #cloneUninitialized()} are:
*
* - Reparsing: Support it by keeping a reference to the original source code including
* the lexical scope and create the uninitialized copy of the root node by reparsing the source.
*
- Resetting: Support it by traversing the {@link Node} tree and derive an
* uninitialized copy from each initialized node.
*
*
* @return an uninitialized copy of this root node if supported.
* @throws UnsupportedOperationException if not supported
* @see #isCloneUninitializedSupported()
* @since 0.24
*/
protected RootNode cloneUninitialized() {
throw new UnsupportedOperationException();
}
/**
* @since 0.8 or earlier
* @deprecated use {@link LoopNode#reportLoopCount(Node,int)} instead
*/
@Deprecated
public final void reportLoopCount(int iterations) {
LoopNode.reportLoopCount(this, iterations);
}
/**
* Executes this function using the specified frame and returns the result value.
*
* @param frame the frame of the currently executing guest language method
* @return the value of the execution
* @since 0.8 or earlier
*/
public abstract Object execute(VirtualFrame frame);
/** @since 0.8 or earlier */
public final RootCallTarget getCallTarget() {
return callTarget;
}
/** @since 0.8 or earlier */
public final FrameDescriptor getFrameDescriptor() {
return frameDescriptor;
}
/**
* @since 0.8 or earlier
* @deprecated No replacement. Changing {@link CallTarget} of an existing {@link RootNode} isn't
* a supported operation
*/
@Deprecated
public final void setCallTarget(RootCallTarget callTarget) {
this.callTarget = callTarget;
}
/**
* Returns the {@link ExecutionContext} associated with this RootNode
. This allows
* the correct ExecutionContext
to be determined for a RootNode
(and
* so also for a {@link RootCallTarget} and a {@link FrameInstance} obtained from the call
* stack) without prior knowledge of the language it has come from.
*
* Returns null
by default.
*
* @since 0.8 or earlier
* @deprecated in 0.25 use {@link #getLanguage(Class) getLanguage(Language.class)}.
* {@link TruffleLanguage#getCurrentContext(Class) getCurrentContext()} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
public com.oracle.truffle.api.ExecutionContext getExecutionContext() {
return null;
}
/**
* Get compiler options specific to this RootNode
.
*
* @since 0.8 or earlier
*/
@SuppressWarnings("deprecation")
public CompilerOptions getCompilerOptions() {
final com.oracle.truffle.api.ExecutionContext context = getExecutionContext();
if (context == null) {
return DefaultCompilerOptions.INSTANCE;
} else {
return context.getCompilerOptions();
}
}
/**
* Does this contain AST content that it is possible to instrument.
*
* @since 0.8 or earlier
*/
protected boolean isInstrumentable() {
return true;
}
/**
* Helper method to create a root node that always returns the same value. Certain operations
* (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.
*
* @param constant the constant to return
* @return root node returning the constant
* @since 0.8 or earlier
*/
public static RootNode createConstantNode(Object constant) {
return new Constant(constant);
}
private static final class Constant extends RootNode {
private final Object value;
Constant(Object value) {
super(null);
this.value = value;
}
@Override
public Object execute(VirtualFrame frame) {
return value;
}
}
}