com.oracle.truffle.api.TruffleLanguage Maven / Gradle / Ivy
Show all versions of truffle-api Show documentation
/*
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.truffle.api;
import static com.oracle.truffle.api.LanguageAccessor.ENGINE;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.logging.Level;
import org.graalvm.options.OptionCategory;
import org.graalvm.options.OptionDescriptor;
import org.graalvm.options.OptionDescriptors;
import org.graalvm.options.OptionKey;
import org.graalvm.options.OptionValues;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Context.Builder;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.EnvironmentAccess;
import org.graalvm.polyglot.Language;
import org.graalvm.polyglot.PolyglotException;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.FileSystem;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleFile.FileSystemContext;
import com.oracle.truffle.api.TruffleFile.FileTypeDetector;
import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.TruffleSafepoint.Interrupter;
import com.oracle.truffle.api.TruffleSafepoint.Interruptible;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.impl.ReadOnlyArrayList;
import com.oracle.truffle.api.io.TruffleProcessBuilder;
import com.oracle.truffle.api.nodes.ExecutableNode;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
/**
* A Truffle language implementation contains all the services a language should provide to make it
* composable with other languages. Implementation classes must be annotated with
* {@link Registration} in order to be discoverable by the {@linkplain org.graalvm.polyglot Polyglot
* API}.
*
* {@link TruffleLanguage} subclasses must provide a public default constructor.
*
* Lifecycle
*
* A language implementation becomes available for use by an engine when metadata is added using the
* {@link Registration} annotation and the implementation's JAR file placed on the host Java Virtual
* Machine's class path.
*
* A newly created engine locates all available language implementations and creates a
* {@linkplain org.graalvm.polyglot.Language descriptor} for each. The descriptor holds the
* language's registered metadata, but its execution environment is not initialized until the
* language is needed for code execution. That execution environment remains initialized for the
* lifetime of the engine and is isolated from the environment in any other engine instance.
*
* Language global state can be shared between multiple context instances by saving them in a custom
* field of the {@link TruffleLanguage} subclass. Languages may control sharing between multiple
* contexts using its {@link Registration#contextPolicy() context policy}. By default the context
* policy is {@link ContextPolicy#EXCLUSIVE exclusive}: each context has its own separate
* TruffleLanguage instance.
*
* If the context policy is more permissive then the implementation needs to manually ensure data
* isolation between the contexts. This means that state associated with a context must not be
* stored in a TruffleLanguage subclass. ASTs and assumptions can be shared across multiple contexts
* if modifying them does not affect language semantics. Languages are strongly discouraged from
* using static mutable state in their languages. Instead {@link TruffleLanguage} instances should
* be used instead to store global state and their sharing should be configured using
* {@link Registration#contextPolicy() context policy}.
*
* Whenever an engine is disposed then each initialized language context will be
* {@link #disposeContext(Object) disposed}.
*
*
*
Context Policy
*
* The number of {@link TruffleLanguage} instances per polyglot {@link org.graalvm.polyglot.Context
* context} is configured by the {@link Registration#contextPolicy() context policy}. By default an
* {@link ContextPolicy#EXCLUSIVE exclusive} {@link TruffleLanguage language} instance is created
* for every {@link org.graalvm.polyglot.Context polyglot context} or
* {@link TruffleLanguage.Env#newContextBuilder() inner context}. With policy
* {@link ContextPolicy#REUSE reuse}, language instances will be reused after a language context was
* {@link TruffleLanguage#disposeContext(Object) disposed}. With policy {@link ContextPolicy#SHARED
* shared}, a language will also be reused if active contexts are not yet disposed. Language
* instances will only be shared or reused if they are
* {@link TruffleLanguage#areOptionsCompatible(OptionValues, OptionValues) compatible}. Language
* implementations are encouraged to support the most permissive context policy possible. Please see
* the individual {@link ContextPolicy policies} for details on the implications on the language
* implementation.
*
* The following illustration shows the cardinalities of the individual components:
*
*
* N: unbounded
* P: N for exclusive, 1 for shared context policy
* L: number of installed languages
* I: number of installed instruments
*
* - 1 : Host VM Processs
* - N : {@linkplain org.graalvm.polyglot.Engine Engine}
* - N : {@linkplain org.graalvm.polyglot.Context Context}
* - L : Language Context
* - P * L : {@link TruffleLanguage TruffleLanguage}
* - I : {@linkplain org.graalvm.polyglot.Instrument Instrument}
* - 1 : {@link com.oracle.truffle.api.instrumentation.TruffleInstrument TruffleInstrument}
*
*
* Parse Caching
*
* The result of the {@link #parse(ParsingRequest) parsing request} is cached per language instance,
* {@link ParsingRequest#getSource() source}, {@link ParsingRequest#getArgumentNames() argument
* names} and environment {@link Env#getOptions() options}. The scope of the caching is influenced
* by the {@link Registration#contextPolicy() context policy}. Caching may be
* {@link Source#isCached() disabled} for certain sources. It is enabled for new sources by default.
*
* Language Configuration
*
* On {@link #createContext(Env) context creation} each language context is provided with
* information about the environment {@link Env environment }. Language can optionally declare
* {@link org.graalvm.polyglot.Context.Builder#option(String, String) configurable} options in
* {@link #getOptionDescriptors()}.
*
* Polyglot Bindings
*
* Language implementations communicate with one another (and with instrumentation-based tools such
* as debuggers) by reading/writing named values into the {@link Env#getPolyglotBindings() polyglot
* bindings}. This bindings object is used to implement guest language export/import statements used
* for language interoperation.
*
* A language implementation can also {@linkplain Env#importSymbol(String) import} or
* {@linkplain Env#exportSymbol(String, Object) export} a global symbol by name. The scope may be
* accessed from multiple threads at the same time. Existing keys are overwritten.
*
*
Configuration vs. Initialization
*
* To ensure that a Truffle language can be used in a language-agnostic way, the implementation
* should be designed to decouple its configuration and initialization from language specifics as
* much as possible. One aspect of this is the initialization and start of execution via the
* {@link org.graalvm.polyglot.Context}, which should be designed in a generic way.
* Language-specific entry points, for instance to emulate the command-line interface of an existing
* implementation, should be handled externally.
*
* Multi-threading
*
* There are two kinds of threads that access contexts of Truffle guest languages:
*
* - Internal threads are {@link Env#createThread(Runnable) created} and managed by a language for
* a context. All internally created threads need to be stopped when the context is
* {@link #disposeContext(Object) disposed}.
*
- External threads are created and managed by the host application / language launcher. The
* host application is allowed to use language contexts from changing threads, sequentially or at
* the same time if the language {@link #isThreadAccessAllowed(Thread, boolean) allows} it.
*
*
* By default every {@link #createContext(Env) context} only allows access from one thread at the
* same time. Therefore if the context is tried to be accessed from multiple threads at the same
* time the access will fail. Languages that want to allow multi-threaded access to a context may
* override {@link #isThreadAccessAllowed(Thread, boolean)} and return true
also for
* multi-threaded accesses. Initialization actions for multi-threaded access can be performed by
* overriding {@link #initializeMultiThreading(Object)}. Threads are
* {@link #initializeThread(Object, Thread) initialized} and {@link #disposeContext(Object)
* disposed} before and after use with a context. Languages may {@link Env#createThread(Runnable)
* create} new threads if the environment {@link Env#isCreateThreadAllowed() allows} it.
*
* @param internal state of the language associated with every thread that is executing program
* {@link #parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest) parsed} by the
* language
* @see org.graalvm.polyglot.Context for embedding of Truffle languages in Java host applications.
* @since 0.8 or earlier
*/
@SuppressWarnings({"javadoc"})
public abstract class TruffleLanguage {
// get and isFinal are frequent operations -> cache the engine access call
@CompilationFinal LanguageInfo languageInfo;
@CompilationFinal Object polyglotLanguageInstance;
List> contextThreadLocals;
List> contextLocals;
/**
* Constructor to be called by subclasses. Language should not create any {@link RootNode}s in
* its constructor. The RootNodes created in the language constructor are not associated with a
* Context and they don't respect Context's engine options. The needed RootNodes can be created
* in the {@link #createContext(Env)}.
*
* @since 0.8 or earlier
*/
protected TruffleLanguage() {
}
/**
* The annotation to use to register your language to the {@link org.graalvm.polyglot Polyglot
* API}. By annotating your implementation of {@link TruffleLanguage} by this annotation the
* language can be discovered on the class path.
*
* @since 0.8 or earlier
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Registration {
/**
* Unique id of your language. This id will be exposed to users via the getter. It is used
* as group identifier for options of the language.
*
* @return identifier of your language
* @since 0.8 or earlier
*/
String id() default "";
/**
* Unique name of your language. This name will be exposed to users via the
* {@link org.graalvm.polyglot.Language#getName()} getter.
*
* @return identifier of your language
* @since 0.8 or earlier
*/
String name();
/**
* Unique name of your language implementation.
*
* @return the implementation name of your language
* @since 0.8 or earlier
*/
String implementationName() default "";
/**
* Unique string identifying the language version. This name will be exposed to users via
* the {@link org.graalvm.polyglot.Language#getVersion()} getter. It inherits from
* {@link org.graalvm.polyglot.Engine#getVersion()} by default.
*
* @return version of your language
* @since 0.8 or earlier
*/
String version() default "inherit";
/**
* Returns the default MIME type of this language. The default MIME type allows embedders
* and other language or instruments to find out how content is interpreted if no MIME type
* was specified. The default MIME type must be specified in the list of supported
* {@link #characterMimeTypes() character} or {@link #byteMimeTypes() byte} based MIME
* types.
*
* The default MIME type is mandatory if more than one supported MIME type was specified. If
* no default MIME type and no supported MIME types were specified then all sources for this
* language will be interpreted as {@link Source#hasCharacters() character} based sources.
*
* @see LanguageInfo#getDefaultMimeType()
* @see Language#getDefaultMimeType()
* @see #characterMimeTypes()
* @see #byteMimeTypes()
* @since 19.0
*/
String defaultMimeType() default "";
/**
* List of MIME types supported by this language which sources should be interpreted as
* {@link Source#hasCharacters() character} based sources. Languages may use MIME types to
* differentiate supported source kinds. If a MIME type is declared as supported then the
* language needs to be able to {@link TruffleLanguage#parse(ParsingRequest) parse} sources
* of this kind. If only one supported MIME type was specified by a language then it will be
* used as {@link #defaultMimeType() default} MIME type. If no supported character and byte
* based MIME types are specified then all sources will be interpreted as
* {@link Source#hasCharacters() character} based.
*
* @return array of MIME types assigned to your language files
* @see #defaultMimeType()
* @see #byteMimeTypes()
* @since 19.0
*/
String[] characterMimeTypes() default {};
/**
* List of MIME types supported by this language which sources should be interpreted as
* {@link Source#hasBytes() byte} based sources. Languages may use MIME types to
* differentiate supported source kinds. If a MIME type is declared as supported then the
* language needs to be able to {@link TruffleLanguage#parse(ParsingRequest) parse} sources
* of this kind. If only one supported MIME type was specified by a language then it will be
* used as {@link #defaultMimeType() default} MIME type. If no supported character and byte
* based MIME types are specified then all sources will be interpreted as
* {@link Source#hasCharacters() character} based.
*
* @return array of MIME types assigned to your language files
* @see #defaultMimeType()
* @see #characterMimeTypes()
* @since 19.0
*/
String[] byteMimeTypes() default {};
/**
* Specifies if the language is suitable for interactive evaluation of {@link Source
* sources}. {@link #interactive() Interactive} languages should be displayed in interactive
* environments and presented to the user. The default value of this attribute is
* true
assuming majority of the languages is interactive. Change the value to
* false
to opt-out and turn your language into non-interactive one.
*
* @return true
if the language should be presented to end-user in an
* interactive environment
* @since 0.22
*/
boolean interactive() default true;
/**
* Returns true
if this language is intended for internal use only. Internal
* languages cannot be used in the host environment directly, they can only be used from
* other languages or from instruments.
*
* @since 0.27
*/
boolean internal() default false;
/**
* Specifies a list of languages that this language depends on. Languages are referenced
* using their {@link #id()}. This has the following effects:
*
* - This language always has access to dependent languages if this language is
* accessible. Languages may not be accessible if language access is
* {@link org.graalvm.polyglot.Context#create(String...) restricted}.
*
- This language is finalized before dependent language contexts are
* {@link TruffleLanguage#finalizeContext(Object) finalized}.
*
- This language is disposed before dependent language contexts are
* {@link TruffleLanguage#disposeContext(Object) disposed}.
*
*
* {@link #internal() Non-internal} languages implicitly depend on all internal languages.
* Therefore by default non-internal languages are disposed and finalized before internal
* languages.
*
* Dependent languages should be parsed with {@link Env#parseInternal(Source, String...)} as
* the embedder might choose to disable access to it for
* {@link Env#parsePublic(Source, String...)}.
*
* Dependent languages references are optional. If a dependent language is not installed and
* the language needs to fail in such a case then the language should fail on
* {@link TruffleLanguage#initializeContext(Object) context initialization}. Cycles in
* dependencies will cause an {@link IllegalStateException} when one of the cyclic languages
* is {@link org.graalvm.polyglot.Context#initialize(String) initialized}.
*
* @since 0.30
*/
String[] dependentLanguages() default {
};
/**
* Defines the supported policy for reusing {@link TruffleLanguage languages} per context.
* I.e. the policy specifies the degree of sharing that is allowed between multiple language
* contexts. The default policy is {@link ContextPolicy#EXCLUSIVE exclusive}. Every language
* is encouraged to try to support a context policy that is as permissive as possible, where
* {@link ContextPolicy#EXCLUSIVE exclusive} is the least and {@link ContextPolicy#SHARED
* shared} is the most permissive policy. {@link TruffleLanguage#parse(ParsingRequest) Parse
* caching} is scoped per {@link TruffleLanguage language} instance, therefore the context
* policy influences its behavior.
*
* The context policy applies to contexts that were created using the
* {@link org.graalvm.polyglot.Context polyglot API} as well as for {@link TruffleContext
* inner contexts}. The context policy does not apply to nodes that were created using the
* Truffle interop protocol. Therefore, interop message nodes always need to be prepared to
* be used with policy {@link ContextPolicy#SHARED}.
*
* @see TruffleLanguage#parse(ParsingRequest)
* @since 19.0
*/
ContextPolicy contextPolicy() default ContextPolicy.EXCLUSIVE;
/**
* Declarative list of classes this language is known to provide. The language is supposed
* to override its {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)
* createContext} method and instantiate and {@link Env#registerService(java.lang.Object)
* register} all here in defined services.
*
* Languages automatically get created but not yet initialized when their registered
* {@link Env#lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class) service is
* requested}.
*
* @since 19.0
* @return list of service types that this language can provide
*/
Class>[] services() default {};
/**
* Declarative list of {@link TruffleFile.FileTypeDetector} classes provided by this
* language.
*
* The language has to support all MIME types recognized by the registered
* {@link TruffleFile.FileTypeDetector file type detectors}.
*
* @return list of file type detectors
* @since 19.0
*/
Class extends TruffleFile.FileTypeDetector>[] fileTypeDetectors() default {};
}
/**
* Used to register a {@link TruffleLanguage} using a {@link ServiceLoader}. This interface is
* not intended to be implemented directly by a language developer, rather the implementation is
* generated by the Truffle DSL. The generated implementation has to inherit the
* {@link Registration} and {@code ProvidedTags} annotations from the {@link TruffleLanguage}.
*
* @since 19.3.0
*/
public interface Provider {
/**
* Returns the name of a class implementing the {@link TruffleLanguage}.
*
* @since 19.3.0
*/
String getLanguageClassName();
/**
* Creates a new instance of a {@link TruffleLanguage}.
*
* @since 19.3.0
*/
TruffleLanguage> create();
/**
* Creates file type detectors used by the {@link TruffleLanguage}.
*
* @since 19.3.0
*/
List createFileTypeDetectors();
/**
* Returns the class names of provided services.
*
* @since 19.3.0
*/
Collection getServicesClassNames();
}
/**
* Returns true
if the combination of two sets of options allow to
* {@link ContextPolicy#SHARED share} or {@link ContextPolicy#REUSE reuse} the same language
* instance, else false
. If options are incompatible then a new language instance
* will be created for a new context. The first language context {@link #createContext(Env)
* created} for a {@link TruffleLanguage} instance always has compatible options, therefore
* {@link #areOptionsCompatible(OptionValues, OptionValues)} will not be invoked for it. The
* default implementation returns true
.
*
* If the context policy of a language is set to {@link ContextPolicy#EXCLUSIVE exclusive}
* (default behavior) then {@link #areOptionsCompatible(OptionValues, OptionValues)} will never
* be invoked as {@link TruffleLanguage} instances will not be shared for multiple contexts. For
* the other context policies {@link ContextPolicy#REUSE reuse} and {@link ContextPolicy#SHARED
* shared} this method can be used to further restrict the reuse of language instances.
* Compatibility influences {@link #parse(ParsingRequest) parse caching} because it uses the
* {@link TruffleLanguage language} instance as a key.
*
* Example usage of areOptionsCompatible if sharing of the language instances and parse caching
* should be restricted by the script version option:
*
* {@link TruffleLanguageSnippets.CompatibleLanguage#areOptionsCompatible}
*
* @param firstOptions the options used to create the first context, never null
* @param newOptions the options that will be used for the new context, never null
* @see ContextPolicy
* @see #parse(ParsingRequest)
* @since 19.0
*/
protected boolean areOptionsCompatible(OptionValues firstOptions, OptionValues newOptions) {
return true;
}
/**
* Creates internal representation of the executing context suitable for given environment. Each
* time the {@link TruffleLanguage language} is used by a new
* {@link org.graalvm.polyglot.Context}, the system calls this method to let the
* {@link TruffleLanguage language} prepare for execution. The returned execution
* context is completely language specific; it is however expected it will contain reference to
* here-in provided env
and adjust itself according to parameters provided by the
* env
object.
*
* The context created by this method is accessible using {@link ContextReference context
* references}. An {@link IllegalStateException} is thrown if the context is tried to be
* accessed while the createContext method is executed.
*
* This method shouldn't perform any complex operations. The runtime system is just being
* initialized and for example making
* {@link Env#parsePublic(com.oracle.truffle.api.source.Source, java.lang.String...) calls into
* other languages} and assuming your language is already initialized and others can see it
* would be wrong - until you return from this method, the initialization isn't over. The same
* is true for instrumentation, the instruments cannot receive any meta data about code executed
* during context creation. Should there be a need to perform complex initialization, do it by
* overriding the {@link #initializeContext(java.lang.Object)} method.
*
* Additional services provided by the language must be
* {@link Env#registerService(java.lang.Object) registered} by this method otherwise
* {@link IllegalStateException} is thrown.
*
* May return {@code null} if the language does not need any per-{@linkplain Context context}
* state. Otherwise it should return a new object instance every time it is called.
*
* @param env the environment the language is supposed to operate in
* @return internal data of the language in given environment or {@code null}
* @since 0.8 or earlier
*/
protected abstract C createContext(Env env);
/**
* Perform any complex initialization. The
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env) } factory method shouldn't
* do any complex operations. Just create the instance of the context, let the runtime system
* register it properly. Should there be a need to perform complex initialization, override this
* method and let the runtime call it later to finish any post initialization
* actions. Example:
*
* {@link TruffleLanguageSnippets.PostInitLanguage#createContext}
*
* @param context the context created by
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)}
* @throws java.lang.Exception if something goes wrong
* @since 0.17
*/
protected void initializeContext(C context) throws Exception {
}
/**
* Performs language context finalization actions that are necessary before language contexts
* are {@link #disposeContext(Object) disposed}. However, in case the underlying polyglot
* context is being cancelled, {@link #disposeContext(Object)} is called even if
* {@link #finalizeContext(Object)} throws a {@link TruffleException} or a {@link ThreadDeath}
* exception. All installed languages must remain usable after finalization. The finalization
* order can be influenced by specifying {@link Registration#dependentLanguages() language
* dependencies}. By default internal languages are finalized last, otherwise the default order
* is unspecified but deterministic.
*
* While finalization code is run, other language contexts may become initialized. In such a
* case, the finalization order may be non-deterministic and/or not respect the order specified
* by language dependencies.
*
* All threads {@link Env#createThread(Runnable) created} by a language must be stopped and
* joined during finalizeContext. The languages are responsible for fulfilling that contract,
* otherwise an {@link AssertionError} is thrown. It's not safe to use the
* {@link ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit)} to detect
* Thread termination as the polyglot thread may be cancelled before executing the executor
* worker.
*
* Typical implementation looks like:
*
* {@link TruffleLanguageSnippets.AsyncThreadLanguage#finalizeContext}
*
* @see Registration#dependentLanguages() for specifying language dependencies.
* @param context the context created by
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)}
* @since 0.30
*/
protected void finalizeContext(C context) {
}
/**
* @since 19.0
* @deprecated in 1.0. Got renamed to {@link #initializeMultipleContexts()} instead. Instead of
* returning a boolean configure {@link Registration#contextPolicy() context policy}
* .
*/
@Deprecated
protected boolean initializeMultiContext() {
return false;
}
/**
* Initializes this language instance for use with multiple contexts. Whether a language
* instance supports being used for multiple contexts depends on its
* {@link Registration#contextPolicy() context policy}.
*
* With the default context policy {@link ContextPolicy#EXCLUSIVE exclusive}, this method will
* never be invoked. This method will be called prior or after the first context was created for
* this language. In case an {@link org.graalvm.polyglot.Context.Builder#engine(Engine) explicit
* engine} was used to create a context, then this method will be invoked prior to the
* {@link #createContext(Env) creation} of the first language context of a language. For inner
* contexts, this method may be invoked prior to the first
* {@link TruffleLanguage.Env#newContextBuilder() inner context} that is created, but after the
* the first outer context was created. No guest language code must be invoked in this method.
* This method is called at most once per language instance.
*
* A language may use this method to invalidate assumptions that assume a single context only.
* For example, assumptions that are dependent on the language context data. It is required to
* invalidate any such assumptions that are used in the AST when this method is invoked.
*
* @see #areOptionsCompatible(OptionValues, OptionValues)
* @see ContextPolicy
* @since 19.0
*/
protected void initializeMultipleContexts() {
}
/**
* Disposes the context created by
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)}. A dispose cleans up all
* resources associated with a context. The context may become unusable after it was disposed.
* It is not allowed to run guest language code while disposing a context. Finalization code
* should be run in {@link #finalizeContext(Object)} instead. Finalization will be performed
* prior to context {@link #disposeContext(Object) disposal}. However, in case the underlying
* polyglot context is being cancelled, {@link #disposeContext(Object)} is called even if
* {@link #finalizeContext(Object)} throws {@link TruffleException} or {@link ThreadDeath}
* exception..
*
* The disposal order can be influenced by specifying {@link Registration#dependentLanguages()
* language dependencies}. By default internal languages are disposed last, otherwise the
* default order is unspecified but deterministic. During disposal no other language must be
* accessed using the {@link Env language environment}.
*
* @param context the context created by
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)}
* @see #finalizeContext(Object) to run finalization code for a context.
* @see #disposeThread(Object, Thread) to perform disposal actions when a thread is no longer
* used.
*
* @since 0.8 or earlier
*/
protected void disposeContext(C context) {
}
/**
* Parses the {@link ParsingRequest#getSource() provided source} and generates its appropriate
* AST representation. The parsing should execute no user code, it should only create the
* {@link Node} tree to represent the source. If the {@link ParsingRequest#getSource() provided
* source} does not correspond naturally to a {@link CallTarget call target}, the returned call
* target should create and if necessary initialize the corresponding language entity and return
* it.
*
* The result of the parsing request is cached per language instance,
* {@link ParsingRequest#getSource() source} and {@link ParsingRequest#getArgumentNames()
* argument names}. It is safe to assume that current {@link TruffleLanguage language} instance
* and {@link ParsingRequest#getArgumentNames() argument names} will remain unchanged for a
* parsed {@link CallTarget}. The scope of the caching is influenced by the
* {@link Registration#contextPolicy() context policy} and option
* {@link TruffleLanguage#areOptionsCompatible(OptionValues, OptionValues) compatibility}.
* Caching may be {@link Source#isCached() disabled} for sources. It is enabled for new sources
* by default.
*
* The {@code argumentNames} may contain symbolic names for actual parameters of the call to the
* returned value. The result should be a call target with method
* {@link CallTarget#call(java.lang.Object...)} that accepts as many arguments as were provided
* via the {@link ParsingRequest#getArgumentNames()} method.
*
* Implement {@link #parse(com.oracle.truffle.api.TruffleLanguage.InlineParsingRequest)} to
* parse source in a specific context location.
*
* @see TruffleLanguage.Registration#contextPolicy()
* @param request request for parsing
* @return a call target to invoke which also keeps in memory the {@link Node} tree representing
* just parsed code
* @throws Exception exception can be thrown when parsing goes wrong. Here-in thrown exception
* is propagated to the user who called one of eval
methods of
* {@link org.graalvm.polyglot.Context}
* @since 0.22
*/
protected CallTarget parse(ParsingRequest request) throws Exception {
throw new UnsupportedOperationException(
String.format("Override parse method of %s, it will be made abstract in future version of Truffle API!", getClass().getName()));
}
/**
* Parses the {@link InlineParsingRequest#getSource() provided source snippet} at the
* {@link InlineParsingRequest#getLocation() provided location} and generates its appropriate
* AST representation. The parsing should execute no user code, it should only create the
* {@link Node} tree to represent the source.
*
* The parsing should be performed in a context (specified by
* {@link InlineParsingRequest#getLocation()}). The result should be an AST fragment with method
* {@link ExecutableNode#execute(com.oracle.truffle.api.frame.VirtualFrame)} that accepts frames
* valid at the {@link InlineParsingRequest#getLocation() provided location}.
*
* When not implemented, null
is returned by default.
*
* @param request request for parsing
* @return a fragment to invoke which also keeps in memory the {@link Node} tree representing
* just parsed {@link InlineParsingRequest#getSource() code}, or null
when
* inline parsing of code snippets is not implemented
* @throws Exception exception can be thrown when parsing goes wrong.
* @since 0.31
*/
protected ExecutableNode parse(InlineParsingRequest request) throws Exception {
return null;
}
/**
* Returns a set of option descriptors that are supported by this language. Option values are
* accessible using the {@link Env#getOptions() environment} when the context is
* {@link #createContext(Env) created}. To construct option descriptors from a list then
* {@link OptionDescriptors#create(List)} can be used. Languages must always return the same
* option descriptors independent of the language instance or side-effects.
*
* @see Option For an example of declaring the option descriptor using an annotation.
* @since 0.27
*/
protected OptionDescriptors getOptionDescriptors() {
return OptionDescriptors.EMPTY;
}
/**
* Notifies the language with pre-initialized context about {@link Env} change. See
* {@link org.graalvm.polyglot.Context} for information how to enable the Context
* pre-initialization.
*
* During the pre-initialization (in the native compilation time) the
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} and
* {@link #initializeContext(java.lang.Object)} methods are called. In the image execution time,
* the {@link #patchContext(java.lang.Object, com.oracle.truffle.api.TruffleLanguage.Env)} is
* called on all languages whose contexts were created during the pre-initialization a
* consequence of {@link org.graalvm.polyglot.Context#create(java.lang.String...)} invocation.
* The contexts are patched in a topological order starting from dependent languages. If the
* {@link #patchContext(java.lang.Object, com.oracle.truffle.api.TruffleLanguage.Env)} is
* successful for all pre-initialized languages the pre-initialized context is used, otherwise a
* new context is created.
*
* Typical implementation looks like:
*
* {@link TruffleLanguageSnippets.PreInitializedLanguage#patchContext}
*
* @param context the context created by
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} during
* pre-initialization
* @param newEnv the new environment replacing the environment used in pre-initialization phase
* @return true in case of successful environment update. When the context cannot be updated to
* a new environment return false to create a new context. By default it returns
* {@code false} to prevent an usage of pre-initialized context by a language which is
* not aware of context pre-initialization.
* @since 0.31
*/
protected boolean patchContext(C context, Env newEnv) {
return false;
}
/**
* Request for parsing. Contains information of what to parse and in which context.
*
* @since 0.22
*/
public static final class ParsingRequest {
private final Source source;
private final String[] argumentNames;
private boolean disposed;
ParsingRequest(Source source, String... argumentNames) {
Objects.requireNonNull(source);
this.source = source;
this.argumentNames = argumentNames;
}
/**
* The source code to parse.
*
* @return the source code, never null
* @since 0.22
*/
public Source getSource() {
if (disposed) {
throw new IllegalStateException();
}
return source;
}
/**
* Argument names. The result of
* {@link #parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest) parsing} is an
* instance of {@link CallTarget} that {@link CallTarget#call(java.lang.Object...) can be
* invoked} without or with some parameters. If the invocation requires some arguments, and
* the {@link #getSource()} references them, it is essential to name them. Example that uses
* the argument names:
*
* {@link TruffleLanguageSnippets#parseWithParams}
*
* @return symbolic names for parameters of {@link CallTarget#call(java.lang.Object...)}
* @since 0.22
*/
public List getArgumentNames() {
if (disposed) {
throw new IllegalStateException();
}
return argumentNames == null ? Collections. emptyList() : ReadOnlyArrayList.asList(argumentNames, 0, argumentNames.length);
}
void dispose() {
disposed = true;
}
CallTarget parse(TruffleLanguage> truffleLanguage) throws Exception {
return truffleLanguage.parse(this);
}
}
/**
* Request for inline parsing. Contains information of what to parse and in which context.
*
* @since 0.31
*/
public static final class InlineParsingRequest {
private final Node node;
private final MaterializedFrame frame;
private final Source source;
private boolean disposed;
InlineParsingRequest(Source source, Node node, MaterializedFrame frame) {
Objects.requireNonNull(source);
this.node = node;
this.frame = frame;
this.source = source;
}
/**
* The source code to parse.
*
* @return the source code, never null
* @since 0.31
*/
public Source getSource() {
if (disposed) {
throw new IllegalStateException();
}
return source;
}
/**
* Specifies the code location for parsing. The location is specified as an instance of a
* {@link Node} in the AST. The node can be
* {@link com.oracle.truffle.api.instrumentation.EventContext#getInstrumentedNode()}, for
* example.
*
* @return a {@link Node} defining AST context for the parsing, it's never null
* @since 0.31
*/
public Node getLocation() {
if (disposed) {
throw new IllegalStateException();
}
return node;
}
/**
* Specifies the execution context for parsing. If the parsing request is used for
* evaluation during halted execution, for example as in
* {@link com.oracle.truffle.api.debug.DebugStackFrame#eval(String)} method, this method
* provides access to current {@link MaterializedFrame frame} with local variables, etc.
*
* @return a {@link MaterializedFrame} exposing the current execution state or
* null
if there is none
* @since 0.31
*/
public MaterializedFrame getFrame() {
if (disposed) {
throw new IllegalStateException();
}
return frame;
}
void dispose() {
disposed = true;
}
ExecutableNode parse(TruffleLanguage> truffleLanguage) throws Exception {
return truffleLanguage.parse(this);
}
}
/**
* Called when some other language is seeking for a global symbol. This method is supposed to do
* lazy binding, e.g. there is no need to export symbols in advance, it is fine to wait until
* somebody asks for it (by calling this method).
*
* The exported object can either be TruffleObject
(e.g. a native object from the
* other language) to support interoperability between languages, {@link String} or one of the
* Java primitive wrappers ( {@link Integer}, {@link Double}, {@link Short}, {@link Boolean},
* etc.).
*
* The way a symbol becomes exported is language dependent. In general it is preferred
* to make the export explicit - e.g. call some function or method to register an object under
* specific name. Some languages may however decide to support implicit export of symbols (for
* example from global scope, if they have one). However explicit exports should always be
* preferred. Implicitly exported object of some name should only be used when there is no
* explicit export under such globalName
. To ensure so the infrastructure first
* asks all known languages for onlyExplicit
symbols and only when none is found,
* it does one more round with onlyExplicit
set to false
.
*
* @param context context to locate the global symbol in
* @param globalName the name of the global symbol to find
* @param onlyExplicit should the language seek for implicitly exported object or only consider
* the explicitly exported ones?
* @return an exported object or null
, if the symbol does not represent anything
* meaningful in this language
* @since 0.8 or earlier
* @deprecated write to the {@link Env#getPolyglotBindings() polyglot bindings} object instead
* when symbols need to be exported. Implicit exported values should be exposed
* using {@link TruffleLanguage#getScope(Object)} instead.
*/
@Deprecated
protected Object findExportedSymbol(C context, String globalName, boolean onlyExplicit) {
return null;
}
/**
* Returns true
if code of this language is allowed to be executed on this thread.
* The method returns false
to deny execution on this thread. The default
* implementation denies access to more than one thread at the same time. The
* {@link Thread#currentThread() current thread} may differ from the passed thread. If this
* method throws an {@link com.oracle.truffle.api.exception.AbstractTruffleException} the
* exception interop messages may be executed without a context being entered.
*
* Example multi-threaded language implementation:
* {@link TruffleLanguageSnippets.MultiThreadedLanguage#initializeThread}
*
* @param thread the thread that accesses the context for the first time.
* @param singleThreaded true
if the access is considered single-threaded,
* false
if more than one thread is active at the same time.
* @since 0.28
*/
protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) {
return singleThreaded;
}
/**
* Invoked before the context is accessed from multiple threads at the same time. This allows
* languages to perform actions that are required to support multi-threading. It will never be
* invoked if {@link #isThreadAccessAllowed(Thread, boolean)} is implemented to deny access from
* multiple threads at the same time. All initialized languages must allow multi-threading for
* this method to be invoked. If this method throws an
* {@link com.oracle.truffle.api.exception.AbstractTruffleException} the exception interop
* messages may be executed without a context being entered.
*
* Example multi-threaded language implementation:
* {@link TruffleLanguageSnippets.MultiThreadedLanguage#initializeThread}
*
* @param context the context that should be prepared for multi-threading.
* @since 0.28
*/
protected void initializeMultiThreading(C context) {
}
/**
* Invoked before a context is accessed from a new thread. This allows the language to perform
* initialization actions for each thread before guest language code is executed. Also for
* languages that deny access from multiple threads at the same time, multiple threads may be
* initialized if they are used sequentially. This method will be invoked before the context is
* {@link #initializeContext(Object) initialized} for the thread the context will be initialized
* with. If the thread is stored in the context it must be referenced using
* {@link WeakReference} to avoid leaking thread objects.
*
* The {@link Thread#currentThread() current thread} may differ from the initialized thread.
*
* If this method throws an {@link com.oracle.truffle.api.exception.AbstractTruffleException}
* the exception interop messages may be executed without a context being entered.
*
* Example multi-threaded language implementation:
* {@link TruffleLanguageSnippets.MultiThreadedLanguage#initializeThread}
*
* @param context the context that is entered
* @param thread the thread that accesses the context for the first time.
*
* @since 0.28
*/
protected void initializeThread(C context, Thread thread) {
}
/**
* Invoked the last time code will be executed for this thread and context. This allows the
* language to perform cleanup actions for each thread and context. Threads might be disposed
* before after or while a context is disposed. The {@link Thread#currentThread() current
* thread} may differ from the disposed thread. Disposal of threads is only guaranteed for
* threads that were created by guest languages, so called {@link Env#createThread(Runnable)
* polyglot threads}. Other threads, created by the embedder, may be collected by the garbage
* collector before they can be disposed and may therefore not be disposed.
*
* @see #initializeThread(Object, Thread) For usage details.
* @since 0.28
*/
@SuppressWarnings("unused")
protected void disposeThread(C context, Thread thread) {
}
/**
* Returns global object for the language.
*
* The object is expected to be TruffleObject
(e.g. a native object from the other
* language) but technically it can be one of the Java primitive wrappers ({@link Integer},
* {@link Double}, {@link Short}, etc.).
*
* @param context context to find the language global in
* @return the global object or null
if the language does not support such concept
* @since 0.8 or earlier
* @deprecated in 0.33 implement {@link #getScope(Object)} instead.
*/
@Deprecated
protected Object getLanguageGlobal(C context) {
return null;
}
/**
* Checks whether the object is provided by this language.
*
* @param object the object to check
* @return true
if this language can deal with such object in native way
* @since 0.8 or earlier
* @deprecated implement {@link #getLanguageView(Object, Object)} and export
* {@link com.oracle.truffle.api.interop.InteropLibrary#getLanguage(Object)}
* instead.
*/
@Deprecated
protected boolean isObjectOfLanguage(Object object) {
return false;
}
/**
* Find a hierarchy of local scopes enclosing the given {@link Node node}. Unless the node is in
* a global scope, it is expected that there is at least one scope provided, that corresponds to
* the enclosing function. The language might provide additional block scopes, closure scopes,
* etc. Global top scopes are provided by {@link #getScope(Object)}. The scope hierarchy should
* correspond with the scope nesting, from the inner-most to the outer-most. The scopes are
* expected to contain variables valid at the given node.
*
* Scopes may depend on the information provided by the frame.
* Lexical scopes are returned when frame
argument is null
.
*
* When not overridden, the enclosing {@link RootNode}'s scope with variables read from its
* {@link FrameDescriptor}'s {@link FrameSlot}s is provided by default.
*
* The
* {@link com.oracle.truffle.api.instrumentation.TruffleInstrument.Env#findLocalScopes(com.oracle.truffle.api.nodes.Node, com.oracle.truffle.api.frame.Frame)}
* provides result of this method to instruments.
*
* @param context the current context of the language
* @param node a node to find the enclosing scopes for. The node, is inside a {@link RootNode}
* associated with this language.
* @param frame The current frame the node is in, or null
for lexical access when
* the program is not running, or is not suspended at the node's location.
* @return an iterable with scopes in their nesting order from the inner-most to the outer-most.
* @since 0.30
* @deprecated implement {@link com.oracle.truffle.api.interop.NodeLibrary} instead.
*/
@Deprecated
@SuppressWarnings({"unchecked", "deprecation"})
protected Iterable findLocalScopes(C context, Node node, Frame frame) {
assert node != null;
return LanguageAccessor.engineAccess().createDefaultLexicalScope(node, frame, (Class extends TruffleLanguage>>) getClass());
}
/**
* Find a hierarchy of top-most scopes of the language, if any. The scopes should be returned
* from the inner-most to the outer-most scope order. The language may return an empty iterable
* to indicate no scopes. The returned scope objects may be cached by the caller per language
* context. Therefore the method should always return equivalent top-scopes and variables
* objects for a given language context. Changes to the top scope by executing guest language
* code should be reflected by cached scope instances. It is recommended to store the top-scopes
* iterable directly in the language context for efficient access.
*
*
Interpretation
In most languages, just evaluating an expression like
* Math
is equivalent of a lookup with the identifier 'Math' in the top-most scopes
* of the language. Looking up the identifier 'Math' should have equivalent semantics as reading
* with the key 'Math' from the variables object of one of the top-most scopes of the language.
* In addition languages may optionally allow modification and insertion with the variables
* object of the returned top-scopes.
*
* Languages may want to specify multiple top-scopes. It is recommended to stay as close as
* possible to the set of top-scopes that as is described in the guest language specification,
* if available. For example, in JavaScript, there is a 'global environment' and a 'global
* object' scope. While the global environment scope contains class declarations and is not
* insertable, the global object scope is used to insert new global variable values and is
* therefore insertable.
*
*
Use Cases
*
* - Top scopes are accessible to instruments with
* {@link com.oracle.truffle.api.instrumentation.TruffleInstrument.Env#findTopScopes(java.lang.String)}
* . They are used by debuggers to access the top-most scopes of the language.
*
- Top scopes available in the {@link org.graalvm.polyglot polyglot API} as context
* {@link Context#getBindings(String) bindings} object. When members of the bindings object are
* {@link Value#getMember(String) read} then the first scope where the key exists is read. If a
* member is {@link Value#putMember(String, Object) modified} in the bindings object, then the
* value will be written to the first scope where the key exists. If a new member is added to
* the bindings object then it is added to the first variables object where the key is
* insertable. If a member is removed, it is only tried to be removed from the first scope of
* where such a key exists. If {@link Value#getMemberKeys() member keys} are requested from the
* bindings object, then the variable object keys are returned sorted from first to last.
*
*
* When not overridden then a single read-only scope named 'global' without any keys will be
* returned.
*
* @param context the current context of the language
* @return an iterable with scopes in their nesting order from the inner-most to the outer-most.
* @since 0.30
* @deprecated implement {@link #getScope(Object)} instead.
*/
@SuppressWarnings("deprecation")
@Deprecated
protected Iterable findTopScopes(C context) {
Object global = getLanguageGlobal(context);
return LanguageAccessor.engineAccess().createDefaultTopScope(global);
}
/**
* Get a top scope of the language, if any. The returned object must be an
* {@link com.oracle.truffle.api.interop.InteropLibrary#isScope(Object) interop scope object}
* and may have {@link com.oracle.truffle.api.interop.InteropLibrary#hasScopeParent(Object)
* parent scopes}. The scope object exposes all top scopes variables as flattened
* {@link com.oracle.truffle.api.interop.InteropLibrary#getMembers(Object) members}. Top scopes
* are independent of a {@link Frame}. See
* {@link com.oracle.truffle.api.interop.InteropLibrary#isScope(Object)} for details.
*
* The returned scope objects may be cached by the caller per language context. Therefore the
* method should always return equivalent top-scopes and variables objects for a given language
* context. Changes to the top scope by executing guest language code should be reflected by
* cached scope instances. It is recommended to store the top-scope directly in the language
* context for efficient access.
*
*
Interpretation
In most languages, just evaluating an expression like
* Math
is equivalent of a lookup with the identifier 'Math' in the top-most scopes
* of the language. Looking up the identifier 'Math' should have equivalent semantics as reading
* with the key 'Math' from the variables object of one of the top-most scopes of the language.
* In addition languages may optionally allow modification and insertion with the variables
* object of the returned top-scopes.
*
* Languages may want to specify multiple parent top-scopes. It is recommended to stay as close
* as possible to the set of top-scopes that as is described in the guest language
* specification, if available. For example, in JavaScript, there is a 'global environment' and
* a 'global object' scope. While the global environment scope contains class declarations and
* is not insertable, the global object scope is used to insert new global variable values and
* is therefore insertable.
*
*
Use Cases
*
* - Top scopes are accessible to instruments with
* {@link com.oracle.truffle.api.instrumentation.TruffleInstrument.Env#getScope(LanguageInfo)}.
* They are used by debuggers to access the top-most scopes of the language.
*
- Top scopes available in the {@link org.graalvm.polyglot polyglot API} as context
* {@link Context#getBindings(String) bindings} object. Access to members of the bindings object
* is applied to the returned scope object via interop.
*
*
*
* @param context the context to find the language top scope in
* @return the scope object or null
if the language does not support such concept
* @since 20.3
*/
@SuppressWarnings({"deprecation", "unchecked"})
protected Object getScope(C context) {
Iterable legacyScopes = findTopScopes(context);
return LanguageAccessor.engineAccess().legacyScopes2ScopeObject(null, legacyScopes.iterator(), (Class extends TruffleLanguage>>) getClass());
}
/**
* Generates language specific textual representation of a value. Each language may have special
* formating conventions - even primitive values may not follow the traditional Java formating
* rules. As such when {@link org.graalvm.polyglot.Value#toString()} is requested, it consults
* the language that produced the value by calling this method. By default this method calls
* {@link Objects#toString(java.lang.Object)}.
*
* @param context the execution context for doing the conversion
* @param value the value to convert. Either primitive type or
* {@link com.oracle.truffle.api.interop.TruffleObject}
* @return textual representation of the value in this language
* @since 0.8 or earlier
* @deprecated implement {@link #getLanguageView(Object, Object)} and
* {@link com.oracle.truffle.api.interop.InteropLibrary#toDisplayString(Object)}
* instead.
*/
@Deprecated
protected String toString(C context, Object value) {
return Objects.toString(value);
}
/**
* Decides whether the result of evaluating an interactive source should be printed to stdout.
* By default this methods returns true
claiming all values are visible.
*
* This method affects behavior of
* {@link org.graalvm.polyglot.Context#eval(org.graalvm.polyglot.Source)} - when evaluating an
* {@link Source#isInteractive() interactive source} the result of the evaluation is tested for
* {@link #isVisible(java.lang.Object, java.lang.Object) visibility} and if the value is found
* visible, it gets converted to string and printed to
* {@link org.graalvm.polyglot.Context.Builder#out(OutputStream) standard output}.
*
* A language can control whether a value is or isn't printed by overriding this method and
* returning false
for some or all values. In such case it is up to the language
* itself to use the {@link Env#out()}, {@link Env#err()} and {@link Env#in()} streams of the
* environment.
*
* When evaluation is called with an {@link Source#isInteractive() interactive source} of a
* language that controls its interactive behavior, it is the responsibility of the language
* itself to print the result to use the {@link Env#out()}, {@link Env#err()} and
* {@link Env#in()} streams of the environment.
*
* @param context the execution context for doing the conversion
* @param value the value to check. Either primitive type or
* {@link com.oracle.truffle.api.interop.TruffleObject}
* @return true
if the language implements an interactive response to evaluation of
* interactive sources.
* @since 0.22
*/
protected boolean isVisible(C context, Object value) {
return true;
}
/**
* Wraps the value to provide language-specific information for primitive and foreign values.
* Foreign values should be enhanced to look like the most generic object type of the language.
* The wrapper needs to introduce any "virtual" methods and properties that are commonly used in
* language constructs and in algorithms that are written to work on this generic object type.
* The wrapper may add or remove existing interop traits, but it is not allowed to change the
* {@link com.oracle.truffle.api.interop.InteropLibrary interop type}. For example, it is not
* allowed to change the type from number to string. If the behavior of an existing trait is
* modified then all writes on the mapper need to be forwarded to the underlying object, apart
* from the virtual members. Writes to the virtual members should be persisted in the wrapper if
* this is the behavior of the object type that is being mapped to.
*
* Every language view wrapper must return the current language as their associated
* {@link com.oracle.truffle.api.interop.InteropLibrary#getLanguage(Object) language}. An
* {@link AssertionError} is thrown when a language view is requested if this contract is
* violated.
*
* Example modifications language view wrappers may perform:
*
* - Provide a language specific
* {@link com.oracle.truffle.api.interop.InteropLibrary#toDisplayString(Object) display string}
* for primitive and foreign values.
*
- Return a language specific
* {@link com.oracle.truffle.api.interop.InteropLibrary#getMetaObject(Object) metaobject} for
* primitive or foreign values.
*
- Add virtual members to the object for the view. For example, any JavaScript object is
* expected to have an implicit __proto__ member. Foreign objects, even if they do not have such
* a member, are interpreted as if they have.
*
- There are languages where all scalar values are also vectors. In such a case the array
* element trait may be added using the language wrapper to such values.
*
*
* The default implementation returns null
. If null
is returned then
* the default language view will be used. The default language view wraps the value and returns
* the current language as their associated language. With the default view wrapper all interop
* library messages will be forwarded to the delegate value, except the messages for
* {@link com.oracle.truffle.api.interop.InteropLibrary#getMetaObject(Object) metaobjects} and
* {@link com.oracle.truffle.api.interop.InteropLibrary#toDisplayString(Object) display strings}
* .
*
* This following example shows a simplified language view. For a full implementation including
* an example of metaobjects can be found in the Truffle examples language "SimpleLanguage".
*
*
* @ExportLibrary(value = InteropLibrary.class, delegateTo = "delegate")
* final class ExampleLanguageView implements TruffleObject {
*
* protected final Object delegate;
*
* ExampleLanguageView(Object delegate) {
* this.delegate = delegate;
* }
*
* @ExportMessage
* boolean hasLanguage() {
* return true;
* }
*
* @ExportMessage
* Class<? extends TruffleLanguage<?>> getLanguage() {
* return MyLanguage.class;
* }
*
* @ExportMessage
* Object toDisplayString(boolean allowSideEffects,
* @CachedLibrary("this.delegate") InteropLibrary dLib) {
* try {
* if (dLib.isString(this.delegate)) {
* return dLib.asString(this.delegate);
* } else if (dLib.isBoolean(this.delegate)) {
* return dLib.asBoolean(this.delegate) ? "TRUE" : "FALSE";
* } else if (dLib.fitsInLong(this.delegate)) {
* return longToString(dLib.asLong(this.delegate));
* } else {
* // full list truncated for this language
* return "Unsupported value";
* }
* } catch (UnsupportedMessageException e) {
* CompilerDirectives.transferToInterpreter();
* throw new AssertionError(e);
* }
* }
*
* @TruffleBoundary
* private static String longToString(long value) {
* return String.valueOf(value);
* }
*
* @ExportMessage
* boolean hasMetaObject(@CachedLibrary("this.delegate") InteropLibrary dLib) {
* return dLib.isString(this.delegate)//
* || dLib.fitsInLong(this.delegate)//
* || dLib.isBoolean(this.delegate);
* }
*
* @ExportMessage
* Object getMetaObject(@CachedLibrary("this.delegate") InteropLibrary dLib)
* throws UnsupportedMessageException {
* if (dLib.isString(this.delegate)) {
* return MyMetaObject.PRIMITIVE_STRING;
* } else if (dLib.isBoolean(this.delegate)) {
* return MyMetaObject.PRIMITIVE_LONG;
* } else if (dLib.fitsInLong(this.delegate)) {
* return MyMetaObject.PRIMITIVE_BOOLEAN;
* } else {
* // no associable metaobject
* throw UnsupportedMessageException.create();
* }
* }
* }
*
*
* @param context the current context.
* @param value the value
* @since 20.1
*/
protected Object getLanguageView(C context, Object value) {
return null;
}
/**
* Wraps the value to filter or add scoping specific information for values associated with the
* current language and location in the code. Allows the language to augment the perspective
* tools have on values depending on location and frame. This may be useful to apply local
* specific visibility rules. By default this method does return the passed value, not applying
* any scope information to the value. If a language does not implement any scoping and/or has
* not concept of visibility then this method typically can stay without an implementation. A
* typical implementation of this method may do the following:
*
* - Apply visiblity and scoping rules to the value hiding or removing members from the
* object.
*
- Add or remove implicit members that are only available within this source location.
*
*
* This method is only invoked with values that are associated with the current
* {@link com.oracle.truffle.api.interop.InteropLibrary#getLanguage(Object) language}. For
* values without language the {@link #getLanguageView(Object, Object) language view} is
* requested first before the scoped view is requested. If this method needs an implementation
* then {@link #getLanguageView(Object, Object)} should be implemented as well.
*
* Scoped views may be implemented in a very similar way to
* {@link #getLanguageView(Object, Object) language views}. Please refer to the examples from
* this method.
*
* @param context the current context.
* @param location the current source location. Guaranteed to be a node from a {@link RootNode}
* associated with this language. Never null
.
* @param frame the current active frame. Guaranteed to be a frame from a {@link RootNode}
* associated with this language. Never null
.
* @param value the value to provide scope information for. Never null
. Always
* associated with this language.
* @since 20.1
* @deprecated in 20.3, implement
* {@link com.oracle.truffle.api.interop.NodeLibrary#getView(Object, Frame, Object)}
* instead.
*/
@Deprecated
@SuppressWarnings("unused")
protected Object getScopedView(C context, Node location, Frame frame, Object value) {
return value;
}
/**
* Find a metaobject of a value, if any. The metaobject represents a description of the object,
* reveals it's kind and it's features. Some information that a metaobject might define includes
* the base object's type, interface, class, methods, attributes, etc.
*
* A programmatic {@link #toString(java.lang.Object, java.lang.Object) textual representation}
* should be provided for metaobjects, when possible. The metaobject may have properties
* describing their structure.
*
* NOTE: Allocating the meta object must not be treated as or cause any
* {@link com.oracle.truffle.api.instrumentation.AllocationListener reported guest language
* value allocations}
*
* When no metaobject is known, return null
. The default implementation returns
* null
. The metaobject should be an interop value. An interop value can be either
* a TruffleObject
(e.g. a native object from the other language) to support
* interoperability between languages or a {@link String}.
*
* It can be beneficial for performance to return the same value for each guest type (i.e. cache
* the metaobjects per context).
*
* @param context the execution context
* @param value a value to find the metaobject of
* @return the metaobject, or null
* @since 0.22
* @deprecated implement {@link #getLanguageView(Object, Object)} and export
* {@link com.oracle.truffle.api.interop.InteropLibrary#getMetaObject(Object)}
* instead.
*/
@Deprecated
protected Object findMetaObject(C context, Object value) {
return null;
}
/**
* Find a source location where a value is declared, if any. This is often useful especially for
* retrieval of source locations of {@link #findMetaObject metaobjects}. The default
* implementation returns null
.
*
* @param context the execution context
* @param value a value to get the source location for
* @return a source location of the object, or null
* @since 0.22
* @deprecated implement {@link #getLanguageView(Object, Object)} and export
* {@link com.oracle.truffle.api.interop.InteropLibrary#getSourceLocation(Object)}
* instead.
*/
@Deprecated
protected SourceSection findSourceLocation(C context, Object value) {
return null;
}
CallTarget parse(Source source, String... argumentNames) {
ParsingRequest request = new ParsingRequest(source, argumentNames);
CallTarget target;
try {
target = request.parse(this);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
request.dispose();
}
return target;
}
ExecutableNode parseInline(Source source, Node context, MaterializedFrame frame) {
assert context != null;
InlineParsingRequest request = new InlineParsingRequest(source, context, frame);
ExecutableNode snippet;
try {
snippet = request.parse(this);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
request.dispose();
}
return snippet;
}
/**
* @since 0.27
* @deprecated in 21.3, use static final context references instead. See
* {@link ContextReference} for the new intended usage.
*/
@Deprecated
protected static > T getCurrentLanguage(Class languageClass) {
try {
return LanguageAccessor.engineAccess().getCurrentLanguage(languageClass);
} catch (Throwable t) {
CompilerDirectives.transferToInterpreter();
throw Env.engineToLanguageException(t);
}
}
/**
* @since 0.27
* @deprecated in 21.3, use static final context references instead. See
* {@link LanguageReference} for the new intended usage.
*/
@Deprecated
protected static > C getCurrentContext(Class languageClass) {
try {
return ENGINE.getCurrentContext(languageClass);
} catch (Throwable t) {
CompilerDirectives.transferToInterpreter();
throw Env.engineToLanguageException(t);
}
}
/**
* Creates a new context local reference for this Truffle language. Context locals for languages
* allow to store additional top-level values for each context besides the language context. The
* advantage of context locals compared to storing the value in a field of the language context
* is that reading a context local requires one indirection less. It is recommended to use
* context locals for languages only if the read is critical for performance.
*
* Context local references must be created during the invocation in the {@link TruffleLanguage}
* constructor. Calling this method at a later point in time will throw an
* {@link IllegalStateException}. For each registered {@link TruffleLanguage} subclass it is
* required to always produce the same number of context local references. The values produced
* by the factory must not be null
and use a stable exact value type for each
* instance of a registered language class. If the return value of the factory is not stable or
* null
then an {@link IllegalStateException} is thrown. These restrictions allow
* the Truffle runtime to read the value more efficiently.
*
* Usage example:
*
*
* @TruffleLanguage.Registration(id = "example", name = "ExampleLanguage")
* public final class ExampleLanguage extends TruffleLanguage {
*
* final ContextLocal contextLocal = createContextLocal(ExampleLocal::new);
*
* @Override
* protected Env createContext(Env env) {
* return env;
* }
*
* @Override
* protected CallTarget parse(ParsingRequest request) throws Exception {
* return Truffle.getRuntime().createCallTarget(new RootNode(this) {
* @Override
* public Object execute(VirtualFrame frame) {
* // fast read
* ExampleLocal local = contextLocal.get();
* // access local
* return "";
* }
* });
* }
*
* static final class ExampleLocal {
*
* final Env env;
*
* ExampleLocal(Env env) {
* this.env = env;
* }
*
* }
* }
*
*
* @since 20.3
*/
protected final ContextLocal createContextLocal(ContextLocalFactory factory) {
ContextLocal local = ENGINE.createLanguageContextLocal(factory);
if (contextLocals == null) {
contextLocals = new ArrayList<>();
}
try {
contextLocals.add(local);
} catch (UnsupportedOperationException e) {
throw new IllegalStateException("The set of context locals is frozen. Context locals can only be created during construction of the TruffleLanguage subclass.");
}
return local;
}
/**
* Creates a new context thread local reference for this Truffle language. Context thread locals
* for languages allow storing additional top-level values for each context and thread. The
* factory may be invoked on any thread other than the thread of the context thread local value.
*
* Context thread local references must be created during the invocation in the
* {@link TruffleLanguage} constructor. Calling this method at a later point in time will throw
* an {@link IllegalStateException}. For each registered {@link TruffleLanguage} subclass it is
* required to always produce the same number of context thread local references. The values
* produced by the factory must not be null
and use a stable exact value type for
* each instance of a registered language class. If the return value of the factory is not
* stable or null
then an {@link IllegalStateException} is thrown. These
* restrictions allow the Truffle runtime to read the value more efficiently.
*
* Context thread locals should not contain a strong reference to the provided thread. Use a
* weak reference instance for that purpose.
*
* Usage example:
*
*
* @TruffleLanguage.Registration(id = "example", name = "ExampleLanguage")
* public static class ExampleLanguage extends TruffleLanguage {
*
* final ContextThreadLocal threadLocal = createContextThreadLocal(ExampleLocal::new);
*
* @Override
* protected Env createContext(Env env) {
* return env;
* }
*
* @Override
* protected CallTarget parse(ParsingRequest request) throws Exception {
* return Truffle.getRuntime().createCallTarget(new RootNode(this) {
* @Override
* public Object execute(VirtualFrame frame) {
* // fast read
* ExampleLocal local = threadLocal.get();
* // access local
* return "";
* }
* });
* }
*
* static final class ExampleLocal {
*
* final Env env;
* final WeakReference thread;
*
* ExampleLocal(Env env, Thread thread) {
* this.env = env;
* this.thread = new WeakReference<>(thread);
* }
*
* }
* }
*
*
* @since 20.3
*/
protected final ContextThreadLocal createContextThreadLocal(ContextThreadLocalFactory factory) {
ContextThreadLocal local = ENGINE.createLanguageContextThreadLocal(factory);
if (contextThreadLocals == null) {
contextThreadLocals = new ArrayList<>();
}
try {
contextThreadLocals.add(local);
} catch (UnsupportedOperationException e) {
throw new IllegalStateException("The set of context thread locals is frozen. Context thread locals can only be created during construction of the TruffleLanguage subclass.");
}
return local;
}
/**
* Returns the home location for this language. This corresponds to the directory in which the
* Jar file is located, if run from a Jar file. For an AOT compiled binary, this corresponds to
* the location of the language files in the default GraalVM distribution layout. executable or
* shared library.
*
* @since 19.0
*/
protected final String getLanguageHome() {
try {
return LanguageAccessor.engineAccess().getLanguageHome(LanguageAccessor.nodesAccess().getPolyglotLanguage(languageInfo));
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Get the depth of asynchronous stack. When zero, the language should not sacrifice performance
* to be able to provide asynchronous stack. When the depth is non-zero, the language should
* provide asynchronous stack up to that depth. The language may provide more asynchronous
* frames than this depth if it's of no performance penalty, or if requested by other (e.g.
* language-specific) options. The returned depth may change at any time.
*
* Override {@link RootNode#findAsynchronousFrames(Frame)} to provide the asynchronous stack
* frames.
*
* @see RootNode#findAsynchronousFrames(Frame)
* @since 20.1.0
*/
protected final int getAsynchronousStackDepth() {
return LanguageAccessor.engineAccess().getAsynchronousStackDepth(LanguageAccessor.nodesAccess().getPolyglotLanguage(languageInfo));
}
/**
* Context local factory for Truffle languages. Creates a new value per context.
*
* @since 20.3
*/
@FunctionalInterface
protected interface ContextLocalFactory {
/**
* Returns a new value for a context local of a language. The returned value must not be
* null
and must return a stable and exact type per registered language. A
* thread local must always return the same {@link Object#getClass() class}, even for
* multiple instances of the same {@link TruffleLanguage}. If this method throws an
* {@link com.oracle.truffle.api.exception.AbstractTruffleException} the exception interop
* messages may be executed without a context being entered.
*
* @see TruffleLanguage#createContextLocal(ContextLocalFactory)
* @since 20.3
*/
T create(C context);
}
/**
* Context thread local factory for Truffle languages. Creates a new value per context and
* thread.
*
* @since 20.3
*/
@FunctionalInterface
protected interface ContextThreadLocalFactory {
/**
* Returns a new value for a context thread local for a language context and thread. The
* returned value must not be null
and must return a stable and exact type per
* TruffleLanguage subclass. A thread local must always return the same
* {@link Object#getClass() class}, even for multiple instances of the same
* {@link TruffleLanguage}. If this method throws an
* {@link com.oracle.truffle.api.exception.AbstractTruffleException} the exception interop
* messages may be executed without a context being entered.
*
* @see TruffleLanguage#createContextThreadLocal(ContextThreadLocalFactory)
* @since 20.3
*/
T create(C context, Thread thread);
}
/**
* Represents execution environment of the {@link TruffleLanguage}. Each active
* {@link TruffleLanguage} receives instance of the environment before any code is executed upon
* it. The environment has knowledge of all active languages and can exchange symbols between
* them.
*
* @since 0.8 or earlier
*/
public static final class Env {
static final Object UNSET_CONTEXT = new Object();
final Object polyglotLanguageContext; // PolylgotLanguageContext
final TruffleLanguage