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 #getCurrentContext(Class)}. 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}. 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}.
*
* 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.
*
* 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.
*
* 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.
*
* 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;
}
/**
* @deprecated in 19.3 as this method is inefficient in many situations. The most efficient
* context lookup can be achieved knowing the current AST in which it is used. As
* this method does not know the current {@link Node node} it must be unnecessarily
* conservative about the lookup and therefore inefficient. More efficient context
* reference versions are available for fast-paths by calling
* {@link Node#lookupContextReference(Class)} or for slow-paths
* {@link TruffleLanguage#getCurrentContext(Class)}. Truffle DSL has support for
* context lookup with {@link com.oracle.truffle.api.dsl.CachedContext
* CachedContext} that uses the most efficient lookup automatically.
*
* @since 0.25
*/
@SuppressWarnings("unchecked")
@Deprecated
public final ContextReference getContextReference() {
if (reference == null) {
throw new IllegalStateException("TruffleLanguage instance is not initialized. Cannot get the current context reference.");
}
return (ContextReference) reference;
}
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;
}
/**
* Returns the current language instance for the current {@link Thread thread}. If a {@link Node
* node} is accessible then {@link Node#lookupLanguageReference(Class)} should be used instead.
* Throws an {@link IllegalStateException} if the language is not yet initialized or not
* executing on this thread. If invoked on the fast-path then languageClass
must be
* a compilation final value.
*
* @param the language type
* @param languageClass the exact language class needs to be provided for the lookup.
* @see Node#lookupLanguageReference(Class)
* @see com.oracle.truffle.api.dsl.CachedLanguage
* @since 0.27
*/
protected static > T getCurrentLanguage(Class languageClass) {
try {
return LanguageAccessor.engineAccess().getCurrentLanguage(languageClass);
} catch (Throwable t) {
CompilerDirectives.transferToInterpreter();
throw Env.engineToLanguageException(t);
}
}
/**
* Returns the current language context entered on the current thread. If a {@link Node node} is
* accessible then {@link Node#lookupContextReference(Class)} should be used instead. An
* {@link IllegalStateException} is thrown if the language is not yet initialized or not
* executing on this thread. If invoked on the fast-path then languageClass
must be
* a compilation final value.
*
* @param the context type
* @param the language type
* @param languageClass the exact language class needs to be provided for the lookup.
* @see Node#lookupContextReference(Class)
* @see com.oracle.truffle.api.dsl.CachedContext
* @since 0.27
*/
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 to store 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
* produces 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}.
*
* @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}.
*
* @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 spi;
private final InputStream in;
private final OutputStream err;
private final OutputStream out;
private final Map config;
private final OptionValues options;
private final String[] applicationArguments;
@CompilationFinal volatile List services;
@CompilationFinal volatile Object context = UNSET_CONTEXT;
@CompilationFinal volatile Assumption contextUnchangedAssumption = Truffle.getRuntime().createAssumption("Language context unchanged");
@CompilationFinal volatile boolean initialized = false;
@CompilationFinal private volatile Assumption initializedUnchangedAssumption = Truffle.getRuntime().createAssumption("Language context initialized unchanged");
@CompilationFinal volatile boolean valid;
volatile List languageServicesCollector;
@SuppressWarnings("unchecked")
Env(Object polyglotLanguageContext, TruffleLanguage> language, OutputStream out, OutputStream err, InputStream in, Map config, OptionValues options,
String[] applicationArguments) {
this.polyglotLanguageContext = polyglotLanguageContext;
this.spi = (TruffleLanguage) language;
this.in = in;
this.err = err;
this.out = out;
this.config = config;
this.options = options;
this.applicationArguments = applicationArguments == null ? new String[0] : applicationArguments;
this.valid = true;
}
TruffleFile.FileSystemContext getPublicFileSystemContext() {
return (TruffleFile.FileSystemContext) LanguageAccessor.engineAccess().getPublicFileSystemContext(polyglotLanguageContext);
}
TruffleFile.FileSystemContext getInternalFileSystemContext() {
return (TruffleFile.FileSystemContext) LanguageAccessor.engineAccess().getInternalFileSystemContext(polyglotLanguageContext);
}
Object getPolyglotLanguageContext() {
return polyglotLanguageContext;
}
TruffleLanguage getSpi() {
return spi;
}
void checkDisposed() {
if (LanguageAccessor.engineAccess().isDisposed(polyglotLanguageContext)) {
throw new IllegalStateException("Language environment is already disposed.");
}
if (!valid) {
throw new IllegalStateException("Language environment is already invalidated.");
}
}
/**
* Returns option values for the options described in
* {@link TruffleLanguage#getOptionDescriptors()}. The returned options are never
* null
.
*
* @since 0.27
*/
public OptionValues getOptions() {
return options;
}
/**
* Returns the application arguments that were provided for this context. The arguments
* array and its elements are never null
. It is up to the language
* implementation whether and how they are accessible within the guest language scripts.
*
* @since 0.27
*/
public String[] getApplicationArguments() {
return applicationArguments;
}
/**
* Returns true
if the creation of new threads is allowed in the current
* environment.
*
* @see #createThread(Runnable)
* @since 0.28
*/
public boolean isCreateThreadAllowed() {
try {
return LanguageAccessor.engineAccess().isCreateThreadAllowed(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Creates a new thread that has access to the current language context. See
* {@link #createThread(Runnable, TruffleContext, ThreadGroup, long)} for a detailed
* description of the parameters. The group
is null and stackSize
* set to 0.
*
* @since 0.28
*/
@TruffleBoundary
public Thread createThread(Runnable runnable) {
return createThread(runnable, null);
}
/**
* Creates a new thread that has access to the given context. See
* {@link #createThread(Runnable, TruffleContext, ThreadGroup, long)} for a detailed
* description of the parameters. The group
is null and stackSize
* set to 0.
*
* @see #getContext()
* @see #newContextBuilder()
* @since 0.28
*/
@TruffleBoundary
public Thread createThread(Runnable runnable, @SuppressWarnings("hiding") TruffleContext context) {
return createThread(runnable, context, null, 0);
}
/**
* Creates a new thread that has access to the given context. See
* {@link #createThread(Runnable, TruffleContext, ThreadGroup, long)} for a detailed
* description of the parameters. The stackSize
set to 0.
*
* @see #getContext()
* @see #newContextBuilder()
* @since 0.28
*/
@TruffleBoundary
public Thread createThread(Runnable runnable, @SuppressWarnings("hiding") TruffleContext context, ThreadGroup group) {
return createThread(runnable, context, group, 0);
}
/**
* Creates a new thread that has access to the given context. A thread is
* {@link TruffleLanguage#initializeThread(Object, Thread) initialized} when it is
* {@link Thread#start() started} and {@link TruffleLanguage#disposeThread(Object, Thread)
* disposed} as soon as the thread finished the execution.
*
* It is recommended to set an
* {@link Thread#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)
* uncaught exception handler} for the created thread. For example the thread can throw an
* uncaught exception if one of the initialized language contexts don't support execution on
* this thread.
*
* The language that created and started the thread is responsible to stop and join it
* during the {@link TruffleLanguage#finalizeContext(Object) finalizeContext}, 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.
* A typical implementation looks like:
* {@link TruffleLanguageSnippets.AsyncThreadLanguage#finalizeContext}
*
* The {@link TruffleContext} can be either an inner context created by
* {@link #newContextBuilder()}.{@link TruffleContext.Builder#build() build()}, or the
* context associated with this environment obtained from {@link #getContext()}.
*
* @param runnable the runnable to run on this thread.
* @param context the context to enter and leave when the thread is started.
* @param group the thread group, passed on to the underlying {@link Thread}.
* @param stackSize the desired stack size for the new thread, or zero if this parameter is
* to be ignored.
* @throws IllegalStateException if thread creation is not {@link #isCreateThreadAllowed()
* allowed}.
* @see #getContext()
* @see #newContextBuilder()
* @since 0.28
*/
@TruffleBoundary
public Thread createThread(Runnable runnable, @SuppressWarnings("hiding") TruffleContext context, ThreadGroup group, long stackSize) {
try {
return LanguageAccessor.engineAccess().createThread(polyglotLanguageContext, runnable, context != null ? context.polyglotContext : null, group, stackSize);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns a new context builder useful to create inner context instances.
*
* @see TruffleContext for details on language inner contexts.
* @since 0.27
*/
public TruffleContext.Builder newContextBuilder() {
return TruffleContext.EMPTY.new Builder(this);
}
/**
* Returns a TruffleObject that represents the polyglot bindings. The polyglot bindings
* consists of a set of symbols that have been exported explicitly by the languages or the
* embedder. This set of symbols allows for data exchange between polyglot languages. The
* polyglot bindings is separate from language bindings. The symbols can by read using
* string identifiers, a list of symbols may be requested with the keys message. Existing
* identifiers are removable, modifiable, readable and any new identifiers are insertable.
*
* @throws SecurityException if polyglot access is not enabled
* @see #isPolyglotBindingsAccessAllowed()
* @since 0.32
*/
@TruffleBoundary
public Object getPolyglotBindings() {
try {
if (!isPolyglotBindingsAccessAllowed()) {
throw new SecurityException("Polyglot bindings are not accessible for this language. Use --polyglot or allowPolyglotAccess when building the context.");
}
return LanguageAccessor.engineAccess().getPolyglotBindingsForLanguage(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Explicitly imports a symbol from the polyglot bindings. The behavior of this method is
* equivalent to sending a READ message to the {@link #getPolyglotBindings() polyglot
* bindings} object. Reading a symbol that does not exist will return null
.
*
* The returned symbol value can either be a 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 Byte}, {@link Boolean}, etc.).
*
* Polyglot symbols can only be imported if the {@link #isPolyglotBindingsAccessAllowed()
* polyglot bindings access} is allowed.
*
* @param symbolName the name of the symbol to search for
* @return object representing the symbol or null
if it does not exist
* @throws SecurityException if importing polyglot symbols is not allowed
* @since 0.8 or earlier
*/
@TruffleBoundary
public Object importSymbol(String symbolName) {
try {
if (!isPolyglotBindingsAccessAllowed()) {
throw new SecurityException("Polyglot bindings are not accessible for this language. Use --polyglot or allowPolyglotAccess when building the context.");
}
return LanguageAccessor.engineAccess().importSymbol(polyglotLanguageContext, this, symbolName);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Explicitly exports a symbol to the polyglot bindings object. The behavior of this method
* is equivalent to sending a WRITE message to the {@link #getPolyglotBindings() polyglot
* bindings} object. Exporting a symbol with a null
value will remove the
* symbol from the polyglot object.
*
* The exported symbol value can either be a 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 Byte}, {@link Boolean}, etc.).
*
* Polyglot symbols can only be export if the {@link #isPolyglotBindingsAccessAllowed()
* polyglot bindings access} is allowed.
*
* @param symbolName the name with which the symbol should be exported into the polyglot
* scope
* @param value the value to export for
* @throws SecurityException if exporting polyglot symbols is not allowed
* @since 0.27
*/
@TruffleBoundary
public void exportSymbol(String symbolName, Object value) {
try {
if (!isPolyglotBindingsAccessAllowed()) {
throw new SecurityException("Polyglot bindings are not accessible for this language. Use --polyglot or allowPolyglotAccess when building the context.");
}
LanguageAccessor.engineAccess().exportSymbol(polyglotLanguageContext, symbolName, value);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if host access is generally allowed. If this method returns
* false
then {@link #lookupHostSymbol(String)} will always fail. Host lookup
* is generally disallowed if the embedder provided a null
* {@link org.graalvm.polyglot.Context.Builder#allowHostClassLookup(java.util.function.Predicate)
* host class filter}.
*
* @since 0.27
*/
@TruffleBoundary
public boolean isHostLookupAllowed() {
try {
return LanguageAccessor.engineAccess().isHostAccessAllowed(polyglotLanguageContext, this);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Adds an entry to the Java host class loader. All classes looked up with
* {@link #lookupHostSymbol(String)} will lookup classes with this new entry. If the entry
* was already added then calling this method again for the same entry has no effect. Given
* entry must not be null
.
*
* @throws SecurityException if the file is not {@link TruffleFile#isReadable() readable}.
* @since 19.0
*/
@TruffleBoundary
public void addToHostClassPath(TruffleFile entry) {
try {
Objects.requireNonNull(entry);
LanguageAccessor.engineAccess().addToHostClassPath(polyglotLanguageContext, entry);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Looks up a Java class in the top-most scope the host environment. Throws an error if no
* symbol was found or the symbol was not accessible. Symbols might not be accessible if a
* {@link org.graalvm.polyglot.Context.Builder#allowHostClassLookup(java.util.function.Predicate)
* host class filter} prevents access. The returned object is always a
* TruffleObject
that represents the class symbol.
*
* @param symbolName the qualified class name in the host language.
* @since 0.27
*/
@TruffleBoundary
public Object lookupHostSymbol(String symbolName) {
try {
return LanguageAccessor.engineAccess().lookupHostSymbol(polyglotLanguageContext, this, symbolName);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if the argument is Java host language object wrapped using
* Truffle interop.
*
* @see #asHostObject(Object)
* @since 19.0
*/
@SuppressWarnings("static-method")
public boolean isHostObject(Object value) {
try {
return LanguageAccessor.engineAccess().isHostObject(value);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns the java host representation of a Truffle guest object if it represents a Java
* host language object. Throws {@link ClassCastException} if the provided argument is not a
* {@link #isHostObject(Object) host object}.
*
* @since 19.0
*/
public Object asHostObject(Object value) {
if (!isHostObject(value)) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new ClassCastException();
}
try {
return LanguageAccessor.engineAccess().asHostObject(value);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Converts a existing Java host object to a guest language value. If the value is already
* an interop value, then no conversion will be performed. Otherwise, the returned wraps the
* host object and provides support for the interop contract to access the java members. The
* interpretation of converted objects is described in {@link Context#asValue(Object)}.
*
* This method should be used exclusively to convert already allocated Java objects to a
* guest language representation. To allocate new host objects users should use
* {@link #lookupHostSymbol(String)} to lookup the class and then send a NEW interop message
* to that object to instantiate it. This method does not respect configured
* {@link org.graalvm.polyglot.Context.Builder#allowHostClassLookup(java.util.function.Predicate)
* class filters}.
*
* @param hostObject the host object to convert
* @since 19.0
*/
public Object asGuestValue(Object hostObject) {
try {
return LanguageAccessor.engineAccess().toGuestValue(hostObject, polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Wraps primitive interop values in a TruffleObject exposing their methods as members. By
* default primitive host values are not wrapped in TruffleObjects to expose their members.
* This method is intended for compatibility with existing Java interop APIs that expect
* such behavior. This method boxes the following primitive interop values: {@link Boolean},
* {@link Byte}, {@link Short}, {@link Integer}, {@link Long}, {@link Float}, {@link Double}
* , {@link Character}, {@link String}. If the provided value is already boxed then the
* current value will be returned. If the provided value is not an interop value then an
* {@link IllegalArgumentException} will be thrown.
*
* @throws IllegalArgumentException if value is an invalid interop value.
* @param guestObject the primitive guest value to box
* @see #asGuestValue(Object)
* @since 19.0
*/
public Object asBoxedGuestValue(Object guestObject) {
try {
return LanguageAccessor.engineAccess().asBoxedGuestValue(guestObject, polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if the argument is a Java host language function wrapped using
* Truffle interop.
*
* @since 19.0
*/
@SuppressWarnings("static-method")
public boolean isHostFunction(Object value) {
try {
return LanguageAccessor.engineAccess().isHostFunction(value);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* 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.
*
* When no metaobject is known, returns null
. The metaobject is 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}.
*
* @param value the value to find the meta object for.
* @since 19.0
*/
public Object findMetaObject(Object value) {
try {
return LanguageAccessor.engineAccess().findMetaObjectForLanguage(polyglotLanguageContext, value);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Tests whether an exception is a host exception thrown by a Java Interop method
* invocation.
*
* Host exceptions may be thrown by interoperability messages. The host exception may be
* unwrapped using {@link #asHostException(Throwable)}.
*
* @param exception the {@link Throwable} to test
* @return {@code true} if the {@code exception} is a host exception, {@code false}
* otherwise
* @see #asHostException(Throwable)
* @since 19.0
*/
@SuppressWarnings("static-method")
public boolean isHostException(Throwable exception) {
try {
return LanguageAccessor.engineAccess().isHostException(exception);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Unwraps a host exception thrown by a Java method invocation.
*
* Host exceptions may be thrown by interoperability messages. The host exception may be
* unwrapped using {@link #asHostException(Throwable)}.
*
* @param exception the host exception to unwrap
* @return the original Java exception
* @throws IllegalArgumentException if the {@code exception} is not a host exception
* @see #isHostException(Throwable)
* @since 19.0
*/
@SuppressWarnings("static-method")
public Throwable asHostException(Throwable exception) {
try {
return LanguageAccessor.engineAccess().asHostException(exception);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns {@code true} if the argument is a host symbol, representing the constructor and
* static members of a Java class, as obtained by e.g. {@link #lookupHostSymbol}.
*
* @see #lookupHostSymbol(String)
* @since 19.0
*/
@SuppressWarnings("static-method")
public boolean isHostSymbol(Object guestObject) {
try {
return LanguageAccessor.engineAccess().isHostSymbol(guestObject);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Converts a Java class to a host symbol as if by
* {@code lookupHostSymbol(symbolClass.getName())} but without an actual lookup. Must not be
* used with Truffle or guest language classes.
*
* @see #lookupHostSymbol(String)
* @since 19.0
*/
@TruffleBoundary
public Object asHostSymbol(Class> symbolClass) {
try {
return LanguageAccessor.engineAccess().asHostSymbol(polyglotLanguageContext, symbolClass);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if access to native code is generally allowed. If this method
* returns false
then loading native libraries with the Truffle NFI will fail.
*
* @since 19.0
*/
@TruffleBoundary
public boolean isNativeAccessAllowed() {
try {
return LanguageAccessor.engineAccess().isNativeAccessAllowed(polyglotLanguageContext, this);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* @since 19.0
* @deprecated use either {@link #isPolyglotEvalAllowed()} or
* {@link #isPolyglotBindingsAccessAllowed()} instead
*/
@Deprecated
public boolean isPolyglotAccessAllowed() {
return isPolyglotEvalAllowed() || isPolyglotBindingsAccessAllowed();
}
/**
* Returns true
if polyglot evaluation is allowed, else false
.
* Guest languages should hide or disable all polyglot evaluation builtins if this flag is
* set to false
. Note that if polyglot evaluation access is disabled, then the
* {@link #getInternalLanguages() available languages list} only shows the current language,
* {@link Registration#dependentLanguages() dependent languages} and
* {@link Registration#internal() internal languages}.
*
* @see org.graalvm.polyglot.Context.Builder#allowPolyglotAccess(org.graalvm.polyglot.PolyglotAccess)
* @since 19.2
*/
@TruffleBoundary
public boolean isPolyglotEvalAllowed() {
try {
return LanguageAccessor.engineAccess().isPolyglotEvalAllowed(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if polyglot bindings access is allowed, else false
* . Guest languages should hide or disable all polyglot bindings builtins if this flag is
* set to false
. If polyglot bindings access is disabled then
* {@link #getPolyglotBindings()}, {@link #importSymbol(String)} or
* {@link #exportSymbol(String, Object)} fails with a SecurityException.
*
* @see org.graalvm.polyglot.Context.Builder#allowPolyglotAccess(org.graalvm.polyglot.PolyglotAccess)
* @since 19.2
*/
@TruffleBoundary
public boolean isPolyglotBindingsAccessAllowed() {
try {
return LanguageAccessor.engineAccess().isPolyglotBindingsAccessAllowed(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Allows it to be determined if this {@link org.graalvm.polyglot.Context} can execute code
* written in a language with a given MIME type.
*
* @see Source#getMimeType()
* @see #parsePublic(Source, String...)
*
* @return a boolean that indicates if the MIME type is supported
* @since 0.11
*/
@TruffleBoundary
public boolean isMimeTypeSupported(String mimeType) {
checkDisposed();
try {
return LanguageAccessor.engineAccess().isMimeTypeSupported(polyglotLanguageContext, mimeType);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* @throws IllegalStateException if polyglot context associated with this environment is not
* entered
* @since 0.8 or earlier
* @deprecated use {@link #parseInternal(Source, String...)} or
* {@link #parsePublic(Source, String...)} instead.
*/
@TruffleBoundary
@Deprecated
public CallTarget parse(Source source, String... argumentNames) {
CompilerAsserts.neverPartOfCompilation();
checkDisposed();
try {
return LanguageAccessor.engineAccess().parseForLanguage(polyglotLanguageContext, source, argumentNames, true);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Parses the source of a public or internal language and returns the parse result as
* {@link CallTarget}. The {@link Source#getLanguage() language id} is used to identify the
* {@link TruffleLanguage} to use to perform the
* {@link #parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest)}. The names of
* arguments are parameters for the resulting {#link CallTarget} that allow the
* source
to reference the actual parameters passed to
* {@link CallTarget#call(java.lang.Object...)}.
*
* Compared to {@link #parsePublic(Source, String...)} this method provides also access to
* {@link TruffleLanguage.Registration#internal() internal} and dependent languages in
* addition to public languages. For example, in JavaScript, a call to the eval builtin
* should forward to {@link #parsePublic(Source, String...)} as it contains code provided by
* the guest language user. Parsing regular expressions with the internal regular expression
* engine should call {@link #parseInternal(Source, String...)} instead, as this is
* considered an implementation detail of the language.
*
* It is recommended that the language uses {@link Env#parsePublic(Source, String...)} or
* {@link Env#parseInternal(Source, String...)} instead of directly passing the Source to
* the parser, in order to support code caching with {@link ContextPolicy#SHARED} and
* {@link ContextPolicy#REUSE}.
*
* @param source the source to evaluate
* @param argumentNames the names of {@link CallTarget#call(java.lang.Object...)} arguments
* that can be referenced from the source
* @return the call target representing the parsed result
* @throws IllegalStateException if polyglot context associated with this environment is not
* entered
* @see #parsePublic(Source, String...)
* @since 19.2
*/
@TruffleBoundary
public CallTarget parseInternal(Source source, String... argumentNames) {
CompilerAsserts.neverPartOfCompilation();
checkDisposed();
try {
return LanguageAccessor.engineAccess().parseForLanguage(polyglotLanguageContext, source, argumentNames, true);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Parses the source of a public language and returns the parse result as {@link CallTarget}
* . The {@link Source#getLanguage() language id} is used to identify the
* {@link TruffleLanguage} to use to perform the
* {@link #parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest)}. The names of
* arguments are parameters for the resulting {#link CallTarget} that allow the
* source
to reference the actual parameters passed to
* {@link CallTarget#call(java.lang.Object...)}.
*
* Compared to {@link #parseInternal(Source, String...)} this method does only provide
* access to non internal, non dependent, public languages. Public languages are configured
* by the embedder to be accessible to the guest language program. For example, in
* JavaScript, a call to the eval builtin should forward to
* {@link #parsePublic(Source, String...)} as it contains code provided by the guest
* language user. Parsing regular expressions with the internal regular expression engine
* should call {@link #parseInternal(Source, String...)} instead, as this is considered an
* implementation detail of the language.
*
* It is recommended that the language uses {@link Env#parsePublic(Source, String...)} or
* {@link Env#parseInternal(Source, String...)} instead of directly passing the Source to
* the parser, in order to support code caching with {@link ContextPolicy#SHARED} and
* {@link ContextPolicy#REUSE}.
*
* @param source the source to evaluate
* @param argumentNames the names of {@link CallTarget#call(java.lang.Object...)} arguments
* that can be referenced from the source
* @return the call target representing the parsed result
* @throws IllegalStateException if polyglot context associated with this environment is not
* entered
* @see #parseInternal(Source, String...)
* @since 19.2
*/
@TruffleBoundary
public CallTarget parsePublic(Source source, String... argumentNames) {
CompilerAsserts.neverPartOfCompilation();
checkDisposed();
try {
return LanguageAccessor.engineAccess().parseForLanguage(polyglotLanguageContext, source, argumentNames, false);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Input stream provided by {@link org.graalvm.polyglot.Context.Builder#in(InputStream)}
* this language is being executed in.
*
* @return reader, never null
* @since 0.8 or earlier
*/
@TruffleBoundary
public InputStream in() {
checkDisposed();
return in;
}
/**
* Standard output writer provided by
* {@link org.graalvm.polyglot.Context.Builder#out(OutputStream)} this language is being
* executed in.
*
* @return writer, never null
* @since 0.8 or earlier
*/
@TruffleBoundary
public OutputStream out() {
checkDisposed();
return out;
}
/**
* Standard error writer provided by
* {@link org.graalvm.polyglot.Context.Builder#err(OutputStream)} this language is being
* executed in.
*
* @return writer, never null
* @since 0.8 or earlier
*/
@TruffleBoundary
public OutputStream err() {
checkDisposed();
return err;
}
/**
* Looks additional service up. An environment for a particular {@link TruffleLanguage
* language} may also be associated with additional services. One can request
* implementations of such services by calling this method with the type identifying the
* requested service and its API.
*
* Services that can be obtained via this method include
* {@link com.oracle.truffle.api.instrumentation.Instrumenter} and others.
*
* @param type of requested service
* @param type class of requested service
* @return instance of T or null
if there is no such service available
* @since 0.12
*/
@TruffleBoundary
public T lookup(Class type) {
checkDisposed();
for (Object obj : services) {
if (type.isInstance(obj)) {
return type.cast(obj);
}
}
return null;
}
/**
* Returns an additional service provided by this instrument, specified by type. If an
* instrument is not enabled, it will be enabled automatically by requesting a supported
* service. If the instrument does not provide a service for a given type it will not be
* enabled automatically.
*
* @param the requested type
* @param instrument identification of the instrument to query
* @param type the class of the requested type
* @return the registered service or null
if none is found
* @since 0.26
*/
@SuppressWarnings("static-method")
@TruffleBoundary
public S lookup(InstrumentInfo instrument, Class type) {
if (isPreInitialization()) {
throw new IllegalStateException("Instrument lookup is not allowed during context pre-initialization.");
}
try {
return LanguageAccessor.engineAccess().lookup(instrument, type);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns an additional service provided by the given language, specified by type. For
* services registered by {@link Registration#services()} the service lookup will ensure
* that the language is loaded and services are registered. The provided langauge and type
* must not be null
.
*
* @param the requested type
* @param language the language to query
* @param type the class of the requested type
* @return the registered service or null
if none is found
* @since 0.26
* @since 19.0 supports services registered by {@link Env#registerService(java.lang.Object)
* registerService}
*/
@TruffleBoundary
public S lookup(LanguageInfo language, Class type) {
if (this.getSpi().languageInfo == language) {
throw new IllegalArgumentException("Cannot request services from the current language.");
}
try {
Objects.requireNonNull(language);
return LanguageAccessor.engineAccess().lookupService(polyglotLanguageContext, language, this.getSpi().languageInfo, type);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Ensures that the target language is initialized. This method firstly verifies that the
* target language is accessible from this language. If not the {@link SecurityException} is
* thrown. Then the target language initialization is performed if not already done.
*
* @param targetLanguage the language to initialize
* @throws SecurityException if an access to {@code targetLanguage} is not permitted
* @since 20.1.0
*/
@TruffleBoundary
public boolean initializeLanguage(LanguageInfo targetLanguage) {
Objects.requireNonNull(targetLanguage, "TargetLanguage must be non null.");
try {
return LanguageAccessor.engineAccess().initializeLanguage(polyglotLanguageContext, targetLanguage);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* @since 0.26
* @deprecated
*/
@Deprecated
@TruffleBoundary
public Map getLanguages() {
try {
return LanguageAccessor.engineAccess().getInternalLanguages(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns all languages that are installed and internally accessible in the environment.
* Using the language instance additional services can be
* {@link #lookup(LanguageInfo, Class) looked up}. {@link #parseInternal(Source, String...)}
* is allowed for all languages returned by this method. This list of languages should not
* be exposed to guest language programs, as it lists internal languages.
*
* @see #lookup(LanguageInfo, Class)
* @see #parseInternal(Source, String...)
* @since 19.2
*/
@TruffleBoundary
public Map getInternalLanguages() {
try {
return LanguageAccessor.engineAccess().getInternalLanguages(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns all languages that are installed and publicly accessible in the environment.
* Using the language instance additional services can be
* {@link #lookup(LanguageInfo, Class) looked up}. {@link #parsePublic(Source, String...)}
* is allowed for all languages returned by this method. This list of languages may be
* exposed ot the guest language program.
*
* @see #parsePublic(Source, String...)
* @since 19.2
*/
@TruffleBoundary
public Map getPublicLanguages() {
try {
return LanguageAccessor.engineAccess().getPublicLanguages(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns a map instrument-id to instrument instance of all instruments that are installed
* in the environment. Using the instrument instance additional services can be
* {@link #lookup(InstrumentInfo, Class) looked up} .
*
* @since 0.26
*/
@TruffleBoundary
public Map getInstruments() {
try {
return LanguageAccessor.engineAccess().getInstruments(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns the default time zone of this environment. If the time-zone was not explicitly
* set by the embedder then the {@link ZoneId#systemDefault() system default} time-zone will
* be returned.
*
* @see ZoneId#systemDefault()
* @since 19.2
*/
public ZoneId getTimeZone() {
checkDisposed();
try {
return LanguageAccessor.engineAccess().getTimeZone(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Configuration arguments passed from an outer language context to an inner language
* context. Inner language contexts can be created using {@link #newContextBuilder()}.
*
* @see TruffleContext to create inner contexts.
* @see TruffleContext.Builder#config(String, Object) to pass configuration objects to the
* inner context.
* @since 0.11
*/
@TruffleBoundary
public Map getConfig() {
checkDisposed();
return config;
}
/**
* Returns the polyglot context associated with this environment.
*
* @return the polyglot context
* @since 0.30
*/
public TruffleContext getContext() {
try {
return LanguageAccessor.engineAccess().getTruffleContext(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns true
if this {@link org.graalvm.polyglot.Context} is being
* pre-initialized. For a given {@link Env environment}, the return value of this method
* never changes.
*
* @see #initializeContext(Object)
* @see #patchContext(Object, Env)
* @since 19.0
*/
@TruffleBoundary
public boolean isPreInitialization() {
try {
return LanguageAccessor.engineAccess().inContextPreInitialization(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Returns a {@link TruffleFile} for given path.
*
* @param path the absolute or relative path to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.0
* @deprecated use {@link #getInternalTruffleFile(java.lang.String) getInternalTruffleFile}
* for language standard library files or
* {@link #getPublicTruffleFile(java.lang.String) getPublicTruffleFile} for user
* files.
*/
@TruffleBoundary
@Deprecated
public TruffleFile getTruffleFile(String path) {
return getInternalTruffleFile(path);
}
/**
* Returns a {@link TruffleFile} for given {@link URI}.
*
* @param uri the {@link URI} to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.0
* @deprecated use {@link #getInternalTruffleFile(java.lang.String) getInternalTruffleFile}
* for language standard library files or
* {@link #getPublicTruffleFile(java.lang.String) getPublicTruffleFile} for user
* files.
*/
@TruffleBoundary
@Deprecated
public TruffleFile getTruffleFile(URI uri) {
return getInternalTruffleFile(uri);
}
/**
* Returns a public {@link TruffleFile} for given path. The access to public files depends
* on {@link Builder#allowIO(boolean) Context's IO policy}. When IO is not enabled by the
* {@code Context} the {@link TruffleFile} operations throw {@link SecurityException}. The
* {@code getPublicTruffleFile} method should be used to access user files or to implement
* language IO builtins.
*
* @param path the absolute or relative path to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.3.0
*/
@TruffleBoundary
public TruffleFile getPublicTruffleFile(String path) {
checkDisposed();
FileSystemContext fs = getPublicFileSystemContext();
try {
return new TruffleFile(fs, fs.fileSystem.parsePath(path));
} catch (UnsupportedOperationException e) {
throw e;
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Returns a public {@link TruffleFile} for given {@link URI}. The access to public files
* depends on {@link Builder#allowIO(boolean) Context's IO policy}. When IO is not enabled
* by the {@code Context} the {@link TruffleFile} operations throw {@link SecurityException}
* . The {@code getPublicTruffleFile} method should be used to access user files or to
* implement language IO builtins.
*
* @param uri the {@link URI} to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.3.0
*/
@TruffleBoundary
public TruffleFile getPublicTruffleFile(URI uri) {
checkDisposed();
FileSystemContext fs = getPublicFileSystemContext();
try {
return new TruffleFile(fs, fs.fileSystem.parsePath(uri));
} catch (UnsupportedOperationException e) {
throw new FileSystemNotFoundException("FileSystem for: " + uri.getScheme() + " scheme is not supported.");
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Returns a public or internal {@link TruffleFile} for given path. Unlike
* {@link #getPublicTruffleFile(java.lang.String) getPublicTruffleFile} the
* {@link TruffleFile} returned by this method for a file in a language home is readable
* even when IO is not enabled by the {@code Context}. The {@code getInternalTruffleFile}
* method should be used to read language standard libraries in a language home. For
* security reasons the language should check that the file is a language source file in
* language standard libraries folder before using this method for a file in a language
* home.
*
* @param path the absolute or relative path to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.3.0
* @see #getPublicTruffleFile(java.lang.String)
*/
@TruffleBoundary
public TruffleFile getInternalTruffleFile(String path) {
checkDisposed();
FileSystemContext fs = getInternalFileSystemContext();
try {
return new TruffleFile(fs, fs.fileSystem.parsePath(path));
} catch (UnsupportedOperationException e) {
throw e;
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Returns a public or internal {@link TruffleFile} for given {@link URI}. Unlike
* {@link #getPublicTruffleFile(java.lang.String) getPublicTruffleFile} the
* {@link TruffleFile} returned by this method for a file in a language home is readable
* even when IO is not enabled by the {@code Context}. The {@code getInternalTruffleFile}
* method should be used to read language standard libraries in a language home. For
* security reasons the language should check that the file is a language source file in
* language standard libraries folder before using this method for a file in a language
* home.
*
* @param uri the {@link URI} to create {@link TruffleFile} for
* @return {@link TruffleFile}
* @since 19.3.0
* @see #getPublicTruffleFile(java.net.URI)
*/
@TruffleBoundary
public TruffleFile getInternalTruffleFile(URI uri) {
checkDisposed();
FileSystemContext fs = getInternalFileSystemContext();
try {
return new TruffleFile(fs, fs.fileSystem.parsePath(uri));
} catch (UnsupportedOperationException e) {
throw new FileSystemNotFoundException("FileSystem for: " + uri.getScheme() + " scheme is not supported.");
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Gets the current working directory. The current working directory is used to resolve non
* absolute paths in {@link TruffleFile} methods.
*
* @return the current working directory
* @throws SecurityException if the {@link FileSystem filesystem} denies reading of the
* current working directory
* @since 19.0
*/
@TruffleBoundary
public TruffleFile getCurrentWorkingDirectory() {
return getPublicTruffleFile("").getAbsoluteFile();
}
/**
* Sets the current working directory. The current working directory is used to resolve non
* absolute paths in {@link TruffleFile} methods.
*
* @param currentWorkingDirectory the new current working directory
* @throws UnsupportedOperationException if setting of the current working directory is not
* supported
* @throws IllegalArgumentException if the {@code currentWorkingDirectory} is not a valid
* current working directory
* @throws SecurityException if {@code currentWorkingDirectory} is not readable
* @since 19.0
*/
@TruffleBoundary
public void setCurrentWorkingDirectory(TruffleFile currentWorkingDirectory) {
checkDisposed();
Objects.requireNonNull(currentWorkingDirectory, "Current working directory must be non null.");
if (!currentWorkingDirectory.isAbsolute()) {
throw new IllegalArgumentException("Current working directory must be absolute.");
}
if (!currentWorkingDirectory.isDirectory()) {
throw new IllegalArgumentException("Current working directory must be directory.");
}
FileSystemContext fileSystemContext = getPublicFileSystemContext();
FileSystemContext internalFileSystemContext = getInternalFileSystemContext();
try {
fileSystemContext.fileSystem.setCurrentWorkingDirectory(currentWorkingDirectory.getSPIPath());
if (fileSystemContext.fileSystem != internalFileSystemContext.fileSystem) {
internalFileSystemContext.fileSystem.setCurrentWorkingDirectory(currentWorkingDirectory.getSPIPath());
}
} catch (UnsupportedOperationException | IllegalArgumentException | SecurityException e) {
throw e;
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fileSystemContext.fileSystem);
}
}
/**
* Returns the name separator used to separate names in {@link TruffleFile}'s path string.
*
* @return the name separator
* @since 19.0
*/
@TruffleBoundary
public String getFileNameSeparator() {
checkDisposed();
FileSystemContext fs = getPublicFileSystemContext();
try {
return fs.fileSystem.getSeparator();
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Returns the path separator used to separate filenames in a path list. On UNIX the path
* separator is {@code ':'}. On Windows it's {@code ';'}.
*
* @return the path separator
* @since 19.1.0
*/
@TruffleBoundary
public String getPathSeparator() {
checkDisposed();
FileSystemContext fs = getPublicFileSystemContext();
try {
return fs.fileSystem.getPathSeparator();
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Registers additional services provided by the language. The registered services are made
* available to users via
* {@link #lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class)} query method.
*
* For each service interface enumerated in {@link Registration#services() language
* registration} the language has to register a single service implementation.
*
* This method can be called only during the execution of the
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env) createContext method},
* then the services are collected and cannot be changed anymore.
*
* @param service a service to be returned from associated
* {@link Env#lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class)
* lookup method}
* @throws IllegalStateException if the method is called outside of
* {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} method
* @since 19.0
*/
public void registerService(Object service) {
if (languageServicesCollector == null) {
throw new IllegalStateException("The registerService method can only be called during the execution of the Env.createContext method.");
}
languageServicesCollector.add(service);
}
/**
* Returns {@code true} if the creation of a sub-process is allowed in the current
* environment.
*
* @see #newProcessBuilder(java.lang.String...)
* @since 19.1.0
*/
public boolean isCreateProcessAllowed() {
try {
return LanguageAccessor.engineAccess().isCreateProcessAllowed(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Creates a new process builder with the specified operating program and arguments.
*
* @param command the executable and its arguments
* @throws SecurityException when process creation is not allowed
* @since 19.1.0
*/
@TruffleBoundary
public TruffleProcessBuilder newProcessBuilder(String... command) {
if (!isCreateProcessAllowed()) {
throw new SecurityException("Process creation is not allowed, to enable it set Context.Builder.allowCreateProcess(true).");
}
FileSystemContext fs = getPublicFileSystemContext();
List cmd = new ArrayList<>(command.length);
Collections.addAll(cmd, command);
return LanguageAccessor.ioAccess().createProcessBuilder(polyglotLanguageContext, fs.fileSystem, cmd);
}
/**
* Returns an unmodifiable map of the process environment. When the {@code Context} is
* configured with {@link EnvironmentAccess#INHERIT} it returns the {@link System#getenv()}
* and the environment variables configured on the {@code Context}. For the
* {@link EnvironmentAccess#NONE} only the environment variables configured on the
* {@code Context} are returned.
*
* @return the process environment as a map of variable names to values
* @since 19.1.0
*/
@TruffleBoundary
public Map getEnvironment() {
try {
return LanguageAccessor.engineAccess().getProcessEnvironment(polyglotLanguageContext);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
/**
* Creates a new empty file in the specified or default temporary directory, using the given
* prefix and suffix to generate its name.
*
* This method provides only part of a temporary file facility. To arrange for a file
* created by this method to be deleted automatically the resulting file must be opened
* using the {@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} option. In this case
* the file is deleted when the appropriate {@code close} method is invoked. Alternatively,
* a {@link Runtime#addShutdownHook shutdown hook} may be used to delete the file
* automatically.
*
* @param dir the directory in which the file should be created or {@code null} for a
* default temporary directory
* @param prefix the prefix to generate the file's name or {@code null}
* @param suffix the suffix to generate the file's name or {@code null} in which case
* "{@code .tmp}" is used
* @param attrs the optional attributes to set atomically when creating the file
* @return the {@link TruffleFile} representing the newly created file that did not exist
* before this method was invoked
* @throws IOException in case of IO error
* @throws IllegalArgumentException if the prefix or suffix cannot be used to generate a
* valid file name
* @throws UnsupportedOperationException if the attributes contain an attribute which cannot
* be set atomically or {@link FileSystem} does not support default temporary
* directory
* @throws SecurityException if the {@link FileSystem} denied the operation
* @since 19.3.0
*/
@TruffleBoundary
public TruffleFile createTempFile(TruffleFile dir, String prefix, String suffix, FileAttribute>... attrs) throws IOException {
FileSystemContext fs = getPublicFileSystemContext();
try {
TruffleFile useDir = dir == null ? new TruffleFile(fs, fs.fileSystem.getTempDirectory()) : dir;
return TruffleFile.createTempFile(useDir, prefix, suffix, false, attrs);
} catch (UnsupportedOperationException | IllegalArgumentException | IOException | SecurityException e) {
throw e;
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Creates a new directory in the specified or default temporary directory, using the given
* prefix to generate its name.
*
* This method provides only part of a temporary file facility. A
* {@link Runtime#addShutdownHook shutdown hook} may be used to delete the directory
* automatically.
*
* @param dir the directory in which the directory should be created or {@code null} for a
* default temporary directory
* @param prefix the prefix to generate the directory's name or {@code null}
* @param attrs the optional attributes to set atomically when creating the directory
* @return the {@link TruffleFile} representing the newly created directory that did not
* exist before this method was invoked
* @throws IOException in case of IO error
* @throws IllegalArgumentException if the prefix cannot be used to generate a valid file
* name
* @throws UnsupportedOperationException if the attributes contain an attribute which cannot
* be set atomically or {@link FileSystem} does not support default temporary
* directory
* @throws SecurityException if the {@link FileSystem} denied the operation
* @since 19.3.0
*/
@TruffleBoundary
public TruffleFile createTempDirectory(TruffleFile dir, String prefix, FileAttribute>... attrs) throws IOException {
FileSystemContext fs = getPublicFileSystemContext();
try {
TruffleFile useDir = dir == null ? new TruffleFile(fs, fs.fileSystem.getTempDirectory()) : dir;
return TruffleFile.createTempFile(useDir, prefix, null, true, attrs);
} catch (UnsupportedOperationException | IllegalArgumentException | IOException | SecurityException e) {
throw e;
} catch (Throwable t) {
throw TruffleFile.wrapHostException(t, fs.fileSystem);
}
}
/**
* Creates a Java host adapter class that can be
* {@linkplain com.oracle.truffle.api.interop.InteropLibrary#instantiate instantiated} with
* a guest object (as the last argument) in order to create adapter instances of the
* provided host types, (non-final) methods of which delegate to the guest object's
* {@linkplain com.oracle.truffle.api.interop.InteropLibrary#isMemberInvocable invocable}
* members. Implementations must be
* {@linkplain org.graalvm.polyglot.HostAccess.Builder#allowImplementations(Class) allowed}
* for these types. The returned adapter class is also a
* {@linkplain com.oracle.truffle.api.interop.InteropLibrary#isMetaObject meta object}, so
* {@link com.oracle.truffle.api.interop.InteropLibrary#isMetaInstance isMetaInstance} can
* be used to check if an object is an instance of this adapter class. See usage example
* below.
*
* A host class is generated as follows:
*
* For every protected or public constructor in the extended class, the adapter class will
* have one public constructor (visibility of protected constructors in the extended class
* is promoted to public).
*
* For every super constructor, a constructor taking a trailing {@link Value} argument
* preceded by original constructor's arguments is generated. When such a constructor is
* invoked, the passed {@link Value}'s member functions are used to implement and/or
* override methods on the original class, dispatched by name. A single invocable member
* will act as the implementation for all overloaded methods of the same name. When methods
* on an adapter instance are invoked, the functions are invoked having the {@link Value}
* passed in the instance constructor as their receiver. Subsequent changes to the members
* of that {@link Value} (reassignment or removal of its functions) are reflected in the
* adapter instance; the method implementations are not bound to functions at constructor
* invocation time.
*
* The generated host class extends all non-final public or protected methods, and forwards
* their invocations to the guest object provided to the constructor. If the guest object
* does not contain an invocable member with that name, the super/default method is invoked
* instead. If the super method is abstract, an {@link AbstractMethodError} is thrown.
*
* If the original types collectively have only one abstract method, or have several of
* them, but all share the same name, the constructor(s) will check if the {@link Value} is
* executable, and if so, will use the passed function as the implementation for all
* abstract methods. For consistency, any concrete methods sharing the single abstract
* method name will also be overridden by the function.
*
* For non-void methods, all the conversions supported by {@link Value#as} will be in effect
* to coerce the guest methods' return value to the expected Java return type.
*
* Instances of the host class have the following additional special members:
*
* {@code super}: provides access to the super methods of the host class via a wrapper
* object. Can be used to call super methods from guest method overrides.
* {@code this}: returns the original guest object.
*
*
* Example:
*
*
*
* Object hostClass = env.createHostAdapterClass(new Class>[]{Superclass.class, Interface.class});
* // generates a class along the lines of:
*
* public class Adapter extends Superclass implements Interface {
* private Value delegate;
*
* public Adapter(Value delegate) { this.delegate = delegate; }
*
* public method(Object... args) {
* if (delegate.canInvokeMember("method") {
* return delegate.invokeMember("method", args);
* } else {
* return super.method(args);
* }
* }
* }
*
* // and can be instantiated as follows:
* Object instance = InteropLibrary.getUncached().instantiate(hostClass, guestObject);
* assert InteropLibrary.getUncached().isMetaInstance(hostClass, instance);
*
*
*
* @param types the types to extend. Must be non-null and contain at least one extensible
* superclass or interface, and at most one superclass. All types must be public,
* accessible, and allow implementation.
* @return a host class that can be instantiated to create instances of a host class that
* extends all the provided host types. The host class may be cached.
* @throws IllegalArgumentException if the types are not extensible or more than one
* superclass is given.
* @throws SecurityException if host access does not allow creating adapter classes for
* these types.
* @throws UnsupportedOperationException if creating adapter classes is not supported on
* this runtime at all, which is currently the case for native images.
* @throws NullPointerException if {@code types} is null
*
* @see #createHostAdapterClassWithStaticOverrides(Class[], Object)
* @since 20.3.0
*/
@TruffleBoundary
public Object createHostAdapterClass(Class>[] types) {
Objects.requireNonNull(types, "types");
return createHostAdapterClassImpl(types, null);
}
/**
* Like {@link #createHostAdapterClass(Class[])} but creates a Java host adapter class with
* class-level overrides, i.e., the guest object provided as {@code classOverrides} is
* statically bound to the class rather than instances of the class. Returns a host class
* that can be {@linkplain com.oracle.truffle.api.interop.InteropLibrary#instantiate
* instantiated} to create instances of the provided host {@code types}, (non-final) methods
* of which delegate to the guest object provided as {@code classOverrides}.
*
* Allows creating host adapter class hierarchies, i.e., the returned class can be used as a
* superclass of other host adapter classes. Note that classes created with method cannot be
* cached. Therefore, this feature should be used sparingly.
*
* See {@link #createHostAdapterClass(Class[])} for more details.
*
* @param types the types to extend. Must be non-null and contain at least one extensible
* superclass or interface, and at most one superclass. All types must be public,
* accessible, and allow implementation.
* @param classOverrides a guest object with class-level overrides. If not null, the object
* is bound to the class, not to any instance. Consequently, the generated
* constructors are changed to not take an object; all instances will share the
* same overrides object. Note that since a new class has to be generated for
* every overrides object instance and cannot be shared, use of this feature is
* discouraged; it is provided only for compatibility reasons.
* @return a host class symbol that can be instantiated to create instances of a host class
* that extends all the provided host types.
* @throws IllegalArgumentException if the types are not extensible or more than one
* superclass is given.
* @throws SecurityException if host access does not allow creating adapter classes for
* these types.
* @throws UnsupportedOperationException if creating adapter classes is not supported on
* this runtime at all, which is currently the case for native images.
* @throws NullPointerException if either {@code types} or {@code classOverrides} is null.
*
* @see #createHostAdapterClass(Class[])
* @since 20.3.0
*/
@TruffleBoundary
public Object createHostAdapterClassWithStaticOverrides(Class>[] types, Object classOverrides) {
Objects.requireNonNull(types, "types");
Objects.requireNonNull(classOverrides, "classOverrides");
return createHostAdapterClassImpl(types, classOverrides);
}
private Object createHostAdapterClassImpl(Class>[] types, Object classOverrides) {
checkDisposed();
try {
if (types.length == 0) {
throw new IllegalArgumentException("Expected at least one type.");
}
return LanguageAccessor.engineAccess().createHostAdapterClass(polyglotLanguageContext, types, classOverrides);
} catch (Throwable t) {
throw engineToLanguageException(t);
}
}
@SuppressWarnings("rawtypes")
@TruffleBoundary
E getLanguage(Class languageClass) {
checkDisposed();
if (languageClass != getSpi().getClass()) {
throw new IllegalArgumentException("Invalid access to language " + languageClass + ".");
}
return languageClass.cast(getSpi());
}
Object findExportedSymbol(String globalName, boolean onlyExplicit) {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
return getSpi().findExportedSymbol(c, globalName, onlyExplicit);
} else {
return null;
}
}
Object getLanguageGlobal() {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
return getSpi().getLanguageGlobal(c);
} else {
return null;
}
}
Object findMetaObjectImpl(Object obj) {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
return getSpi().findMetaObject(c, obj);
} else {
return null;
}
}
SourceSection findSourceLocation(Object obj) {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
return getSpi().findSourceLocation(c, obj);
} else {
return null;
}
}
boolean isObjectOfLanguage(Object obj) {
return getSpi().isObjectOfLanguage(obj);
}
@SuppressWarnings("deprecation")
Iterable findLocalScopes(Node node, Frame frame) {
assert node != null;
return getSpi().findLocalScopes(context, node, frame);
}
@SuppressWarnings("deprecation")
Iterable findTopScopes() {
return getSpi().findTopScopes(context);
}
void dispose() {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
getSpi().disposeContext(c);
} else {
throw new IllegalStateException("Disposing while context has not been set yet.");
}
}
@TruffleBoundary
void postInit() {
try {
getSpi().initializeContext(context);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
} finally {
initialized = true;
Assumption old = initializedUnchangedAssumption;
initializedUnchangedAssumption = Truffle.getRuntime().createAssumption("Language context initialized unchanged");
old.invalidate();
}
}
boolean isInitialized() {
if (CompilerDirectives.isPartialEvaluationConstant(this)) {
boolean localInitialized = initialized;
if (initializedUnchangedAssumption.isValid()) {
return localInitialized;
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
return initialized;
}
} else {
return initialized;
}
}
boolean isVisible(Object value) {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
return getSpi().isVisible(c, value);
} else {
return false;
}
}
String toStringIfVisible(Object value, boolean checkVisibility) {
Object c = getLanguageContext();
if (c != UNSET_CONTEXT) {
if (checkVisibility) {
if (!getSpi().isVisible(c, value)) {
return null;
}
}
return getSpi().toString(c, value);
} else {
return null;
}
}
Object getLanguageContext() {
if (CompilerDirectives.isPartialEvaluationConstant(this)) {
Object languageContext = this.context;
if (contextUnchangedAssumption.isValid()) {
return languageContext;
} else {
CompilerDirectives.transferToInterpreterAndInvalidate();
return context;
}
} else {
return this.context;
}
}
@SuppressWarnings("unchecked")
@TruffleBoundary
static RuntimeException engineToLanguageException(Throwable t) {
return LanguageAccessor.engineAccess().engineToLanguageException(t);
}
}
/**
* Represents a reference to the language to be stored in an AST. A reference can be accessed
* using {@link Node#lookupLanguageReference(Class)} and the current language can be accessed
* using the {@link LanguageReference#get()} method of the returned reference.
*
* The current language might vary between {@link RootNode#execute(VirtualFrame) executions} if
* the reference is used with interoperability APIs in the AST of a foreign language.
*
* @since 19.0
*/
@SuppressWarnings("rawtypes")
public abstract static class LanguageReference {
/**
* Constructors for subclasses.
*
* @since 19.0
*/
protected LanguageReference() {
}
/**
* Returns the current language of the current execution context. If a context is accessed
* during {@link TruffleLanguage#createContext(Env) context creation} or in the language
* class constructor an {@link IllegalStateException} is thrown. This methods is designed to
* be called safely from compiled code paths.
*
* The current language might vary between {@link RootNode#execute(VirtualFrame) executions}
* if the reference is used with interoperability APIs in the AST of a foreign language.
*
* @since 19.0
*/
public abstract L get();
}
/**
* Represents a reference to the current context to be stored in an AST. A reference can be
* accessed using {@link Node#lookupContextReference(Class)} and the current context can be
* accessed using the {@link ContextReference#get()} method of the returned reference.
*
* The current context might vary between {@link RootNode#execute(VirtualFrame) executions} if
* resources or code is shared between multiple contexts.
*
* @since 0.25
*/
public abstract static class ContextReference {
/**
* Constructors for subclasses.
*
* @since 19.0
*/
protected ContextReference() {
}
/**
* Returns the current language context of the current execution context. If a context is
* accessed during {@link TruffleLanguage#createContext(Env) context creation} or in the
* language class constructor an {@link IllegalStateException} is thrown. This methods is
* designed to be called safely from compiled code paths.
*
* The current context might vary between {@link RootNode#execute(VirtualFrame) executions}
* if resources or code is shared between multiple contexts.
*
* @since 0.25
*/
@SuppressWarnings("unchecked")
public abstract C get();
}
/**
* 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 #EXCLUSIVE exclusive}. Every language is encouraged to
* try to support a context policy that is as permissive as possible, where {@link #EXCLUSIVE
* exclusive} is the least and {@link #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 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 Registration#contextPolicy() To configure context policy for a language.
* @see TruffleLanguage#parse(ParsingRequest)
* @since 19.0
*/
public enum ContextPolicy {
/**
* Use one exclusive {@link TruffleLanguage} instance per language context instance.
*
* Using this policy has the following implications:
*
* {@link TruffleLanguage#parse(ParsingRequest) Parse caching} is scoped per
* {@link TruffleLanguage language} instance. This means the language context instance may
* be directly stored in instance fields of AST nodes without the use of
* {@link ContextReference}. The use of {@link ContextReference} is still recommended to
* simplify migration to more permissive policies.
* If the language does not allow
* {@link TruffleLanguage#isThreadAccessAllowed(Thread, boolean) multi-threading} (default
* behavior) then the language instance is guaranteed to be used from one thread at a time
* only. Cached ASTs will not be used from multiple threads at the same time.
* {@link TruffleLanguage#initializeMultipleContexts()} is guaranteed to never be
* invoked.
*
*
* @since 19.0
*/
EXCLUSIVE,
/**
* Use a single {@link TruffleLanguage} instance per context instance, but allow the reuse
* of a language instance when a context was {@link TruffleLanguage#disposeContext(Object)
* disposed}. This policy is useful when parsed ASTs should be shared, but multiple thread
* execution of the AST is not yet supported by the language.
*
* Using this policy has the following implications:
*
* {@link TruffleLanguage#parse(ParsingRequest) Parse caching} is scoped per
* {@link TruffleLanguage language} instance. This means language context instances must NOT
* be directly stored in instance fields of AST nodes and the {@link ContextReference} must
* be used instead.
* If the language does not
* {@link TruffleLanguage#isThreadAccessAllowed(Thread, boolean) allow} access from multiple
* threads (default behavior) then the language instance is guaranteed to be used from one
* thread at a time only. In this case also cached ASTs will not be used from multiple
* threads at the same time. If the language allows access from multiple threads then also
* ASTs may be used from multiple threads at the same time.
* {@link TruffleLanguage#initializeMultipleContexts()} will be invoked to notify the
* language that multiple contexts will be used with one language instance.
* {@link TruffleLanguage Language} instance fields must only be used for data that can
* be shared across multiple contexts.
*
*
* @since 19.0
*/
REUSE,
/**
* Use one {@link TruffleLanguage} instance for many language context instances.
*
* Using this policy has the following implications:
*
* {@link TruffleLanguage#parse(ParsingRequest) Parse caching} is scoped per
* {@link TruffleLanguage language} instance. With multiple language instances per context,
* context instances must not be directly stored in instance fields of AST nodes and
* the {@link ContextReference} must be used instead.
* All methods of the {@link TruffleLanguage language} instance and parsed ASTs may be
* called from multiple threads at the same time independent of whether multiple thread
* access is {@link TruffleLanguage#isThreadAccessAllowed(Thread, boolean) allowed} for the
* language.
* {@link TruffleLanguage#initializeMultipleContexts()} will be invoked to notify the
* language that multiple contexts will be used with one language instance.
* {@link TruffleLanguage Language} instance fields must only be used for data that can
* be shared across multiple contexts and mutable data held by the language instance must be
* synchronized to support concurrent access.
*
*
* @since 19.0
*/
SHARED;
}
}
class TruffleLanguageSnippets {
class Context {
String[] args;
Env env;
CallTarget mul;
InputStream in;
OutputStream out;
OutputStream err;
String languageVersion;
Context(String[] args) {
this.args = args;
this.env = null;
this.languageVersion = null;
}
Context(Env env) {
this.env = env;
this.args = null;
this.languageVersion = null;
}
Context(Env env, String languageVersion) {
this.env = env;
this.args = null;
this.languageVersion = languageVersion;
}
Context fork() {
return null;
}
final Assumption singleThreaded = Truffle.getRuntime().createAssumption();
final List startedThreads = new ArrayList<>();
}
// @formatter:off
abstract
class MyLanguage extends TruffleLanguage {
@Override
protected Context createContext(Env env) {
String[] args = env.getApplicationArguments();
return new Context(args);
}
}
abstract
// BEGIN: TruffleLanguageSnippets.PostInitLanguage#createContext
class PostInitLanguage extends TruffleLanguage {
@Override
protected Context createContext(Env env) {
// "quickly" create the context
return new Context(env);
}
@Override
protected void initializeContext(Context context) throws IOException {
// called "later" to finish the initialization
// for example call into another language
Source source = Source.newBuilder("js",
"function(x, y) x * y",
"mul.js").build();
context.mul = context.env.parsePublic(source);
}
}
// END: TruffleLanguageSnippets.PostInitLanguage#createContext
abstract static
// BEGIN: TruffleLanguageSnippets.CompatibleLanguage#areOptionsCompatible
class CompatibleLanguage extends TruffleLanguage {
@Option(help = "", category = OptionCategory.USER)
static final OptionKey ScriptVersion
= new OptionKey<>("ECMA2017");
@Override
protected boolean areOptionsCompatible(OptionValues firstOptions,
OptionValues newOptions) {
return firstOptions.get(ScriptVersion).
equals(newOptions.get(ScriptVersion));
}
@Override
protected OptionDescriptors getOptionDescriptors() {
return new CompatibleLanguageOptionDescriptors();
}
}
// END: TruffleLanguageSnippets.CompatibleLanguage#areOptionsCompatible
static class CompatibleLanguageOptionDescriptors implements OptionDescriptors{
public OptionDescriptor get(String optionName) {
return null;
}
public Iterator iterator() {
return null;
}
}
abstract
class PreInitializedLanguage extends TruffleLanguage {
private final OptionKey version = new OptionKey<>("2.0");
// BEGIN: TruffleLanguageSnippets.PreInitializedLanguage#patchContext
@Override
protected boolean patchContext(Context context, Env newEnv) {
if (!optionsAllowPreInitializedContext(context, newEnv)) {
// Incompatible options - cannot use pre-initialized context
return false;
}
context.env = newEnv;
context.args = newEnv.getApplicationArguments();
context.in = newEnv.in();
context.out = newEnv.out();
context.err = newEnv.err();
return true;
}
private boolean optionsAllowPreInitializedContext(Context context, Env newEnv) {
// Verify that values of important options in the new Env do not differ
// from values in the pre-initialized context
final String newVersionValue = newEnv.getOptions().get(version);
return Objects.equals(context.languageVersion, newVersionValue);
}
// END: TruffleLanguageSnippets.PreInitializedLanguage#patchContext
}
// BEGIN: TruffleLanguageSnippets#parseWithParams
public void parseWithParams(Env env) {
Source multiply = Source.newBuilder("js",
"a * b",
"mul.js").build();
CallTarget method = env.parsePublic(multiply, "a", "b");
Number fortyTwo = (Number) method.call(6, 7);
assert 42 == fortyTwo.intValue();
Number ten = (Number) method.call(2, 5);
assert 10 == ten.intValue();
}
// END: TruffleLanguageSnippets#parseWithParams
abstract
// BEGIN: TruffleLanguageSnippets.MultiThreadedLanguage#initializeThread
class MultiThreadedLanguage extends TruffleLanguage {
@Override
protected Context createContext(Env env) {
return new Context(env);
}
@Override
protected boolean isThreadAccessAllowed(Thread thread,
boolean singleThreaded) {
// allow access from any thread instead of just one
return true;
}
@Override
protected void initializeMultiThreading(Context context) {
// perform actions when the context is switched to multi-threading
context.singleThreaded.invalidate();
}
@Override
protected void initializeThread(Context context, Thread thread) {
// perform initialization actions for threads
}
@Override
protected void disposeThread(Context context, Thread thread) {
// perform disposal actions for threads
}
}
// END: TruffleLanguageSnippets.MultiThreadedLanguage#initializeThread
abstract
// BEGIN: TruffleLanguageSnippets.AsyncThreadLanguage#finalizeContext
class AsyncThreadLanguage extends TruffleLanguage {
@Override
protected Context createContext(Env env) {
return new Context(env);
}
@Override
protected boolean isThreadAccessAllowed(Thread thread,
boolean singleThreaded) {
// allow access from any thread instead of just one
return true;
}
@Override
protected void initializeContext(Context context) throws Exception {
// create and start a Thread for the asynchronous task
// remeber the Thread reference to stop and join it in
// the finalizeContext
Thread t = context.env.createThread(new Runnable() {
@Override
public void run() {
// asynchronous task
}
});
context.startedThreads.add(t);
t.start();
}
@Override
protected void finalizeContext(Context context) {
// stop and join all the created Threads
boolean interrupted = false;
for (int i = 0; i < context.startedThreads.size();) {
Thread threadToJoin = context.startedThreads.get(i);
try {
if (threadToJoin != Thread.currentThread()) {
threadToJoin.interrupt();
threadToJoin.join();
}
i++;
} catch (InterruptedException ie) {
interrupted = true;
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
// END: TruffleLanguageSnippets.AsyncThreadLanguage#finalizeContext
}