Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.pkl.thirdparty.truffle.api.TruffleContext Maven / Gradle / Ivy
Go to download
Shaded fat Jar for pkl-config-java, a Java config library based on the Pkl config language.
/*
* Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.pkl.thirdparty.truffle.api;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.pkl.thirdparty.graalvm.polyglot.PolyglotException;
import org.pkl.thirdparty.truffle.api.CompilerDirectives.TruffleBoundary;
import org.pkl.thirdparty.truffle.api.TruffleLanguage.ContextReference;
import org.pkl.thirdparty.truffle.api.TruffleLanguage.Env;
import org.pkl.thirdparty.truffle.api.nodes.LanguageInfo;
import org.pkl.thirdparty.truffle.api.nodes.Node;
import org.pkl.thirdparty.truffle.api.source.Source;
/**
* A handle on a context of a set of Truffle languages. This context handle is designed to be used
* by Truffle guest language implementations. The Truffle context can be used to create inner
* contexts for isolated execution of guest language code.
*
* A {@link TruffleContext context} consists of a {@link TruffleLanguage#createContext(Env) language
* context} instance for each {@link Env#getInternalLanguages() installed language}. The current
* language context is {@link TruffleLanguage#createContext(Env) created} eagerly and can be
* accessed using a {@link ContextReference context reference} after the context was
* {@link TruffleContext#enter(Node) entered}.
*
* The configuration for each language context is inherited from its parent/creator context. In
* addition to that {@link Builder#config(String, Object) config} parameters can be passed to new
* language context instance of the current language. The configuration of other installed languages
* cannot be modified. To run guest language code in a context, the context needs to be first
* {@link #enter(Node) entered} and then {@link #leave(Node, Object) left}. The context should be
* {@link #close() closed} when it is no longer needed. If the context is not closed explicitly,
* then it is automatically closed together with the parent context.
*
* Example usage: {@link TruffleContextSnippets.MyNode#executeInContext}
*
* @since 0.27
*/
public final class TruffleContext implements AutoCloseable {
static final TruffleContext EMPTY = new TruffleContext();
private static final ThreadLocal> CONTEXT_ASSERT_STACK;
static {
boolean assertions = false;
assert (assertions = true) == true;
CONTEXT_ASSERT_STACK = assertions ? new ThreadLocal<>() {
@Override
protected List initialValue() {
return new ArrayList<>();
}
} : null;
}
final Object polyglotContext;
final boolean creator;
TruffleContext(Object polyglotContext, boolean creator) {
this.polyglotContext = polyglotContext;
this.creator = creator;
}
/*
* Constructor necessary for inner builder.
*/
private TruffleContext() {
this.polyglotContext = null;
this.creator = false;
}
/**
* {@inheritDoc}
*
* @since 20.3
*/
@Override
@TruffleBoundary
public boolean equals(Object obj) {
if (!(obj instanceof TruffleContext)) {
return false;
}
TruffleContext c = (TruffleContext) obj;
return polyglotContext.equals(c.polyglotContext);
}
/**
* {@inheritDoc}
*
* @since 20.3
*/
@Override
public int hashCode() {
return polyglotContext.hashCode();
}
/**
* Get a parent context of this context, if any. This provides the hierarchy of inner contexts.
*
* @return a parent context, or null
if there is no parent
* @since 0.30
*/
@TruffleBoundary
public TruffleContext getParent() {
try {
return LanguageAccessor.engineAccess().getParentContext(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Enters this context and returns an object representing the previous context. Calls to enter
* must be followed by a call to {@link #leave(Node, Object)} in a finally block and the
* previous context must be passed as an argument. It is allowed to enter a context multiple
* times from the same thread. If the context is currently not entered by any thread then it is
* allowed be entered by an arbitrary thread. Entering the context from two or more different
* threads at the same time is possible, unless one of the loaded languages denies access to the
* thread, in which case an {@link IllegalStateException} is thrown.
*
* If the current thread was not previously entered in any context, the enter function returns
* {@code null}. If the return value is not {@code null}, the result of the enter function is
* unspecified and must only be passed to {@link #leave(Node, Object)}. The result value must
* not be stored permanently.
*
* An adopted node may be passed to allow perform optimizations on the fast-path. If a
* null
node is passed then entering a context will result in a
* {@link TruffleBoundary boundary} call in compiled code. If the provided node is not adopted
* an {@link IllegalArgumentException} is thrown.
*
* Entering a language context is designed for compilation and is most efficient if the
* {@link TruffleContext context} instance is compilation final.
*
*
* Example usage: {@link TruffleContextSnippets.MyNode#executeInContext}
*
* @see #leave(Node, Object)
* @since 20.3
*/
public Object enter(Node node) {
try {
CompilerAsserts.partialEvaluationConstant(node);
Object prev = LanguageAccessor.engineAccess().enterInternalContext(node, polyglotContext);
if (CONTEXT_ASSERT_STACK != null) {
verifyEnter(prev);
}
return prev;
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Forces initialization of an internal or public language. If the context is not an inner
* context and e.g. accessed using {@link Env#getContext()} an {@link IllegalStateException} is
* thrown. In such a case {@link Env#initializeLanguage(LanguageInfo)} should be used instead to
* initialize contexts.
*
* No context or the parent creator context must be entered to initialize languages in a
* {@link TruffleContext} otherwise an {@link IllegalStateException} will be thrown.
*
* @throws IllegalStateException if an invalid context is entered or the context is already
* closed.
* @throws IllegalArgumentException if the given language of the source cannot be accessed.
* @param languageId the id of the language to initialize
* @param node a partial evaluation constant node context used to optimize this operation. Can
* be null
if not available.
* @return true
if the language was initialized, else false
, e.g. if
* the language as already initialized.
*
* @since 22.3
*/
public boolean initializeInternal(Node node, String languageId) {
Objects.requireNonNull(languageId);
CompilerAsserts.partialEvaluationConstant(node);
try {
return LanguageAccessor.engineAccess().initializeInnerContext(node, polyglotContext, languageId, true);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* The same as {@link #initializeInternal(Node, String)}, but only public languages are
* accessible.
*
* @throws IllegalStateException if an invalid context is entered or the context is already
* closed.
* @throws IllegalArgumentException if the given language of the source cannot be accessed.
* @param languageId the id of the language to initialize
* @param node a partial evaluation constant node context used to optimize this operation. Can
* be null
if not available.
* @return true
if the language was initialized, else false
, e.g. if
* the language as already initialized.
*
* @since 22.3
*/
public boolean initializePublic(Node node, String languageId) {
Objects.requireNonNull(languageId);
CompilerAsserts.partialEvaluationConstant(node);
try {
return LanguageAccessor.engineAccess().initializeInnerContext(node, polyglotContext, languageId, false);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Evaluates a source in an inner context and returns the result. If the context is not an inner
* context and e.g. accessed using {@link Env#getContext()} an {@link IllegalStateException} is
* thrown. In such a case {@link Env#parseInternal(Source, String...)} should be used instead to
* evaluate sources.
*
* No context or the parent creator context must be entered to evaluate sources in a
* {@link TruffleContext} otherwise an {@link IllegalStateException} will be thrown. In order to
* ensure that all values are accessed from their respective contexts only, any non-primitive
* value returned by the evaluation will be wrapped and enter this context for each interop
* message sent. Parameters to interop messages will enter and leave the parent context when
* they are accessed. If the result is a primitive value, then the value is directly returned.
*
* This method has access to all public and internal languages the creator context has access
* to. This corresponds to the set of languages returned by {@link Env#getInternalLanguages()}.
* If a language cannot be accessed then an {@link IllegalArgumentException} is thrown. If a
* language is not yet initialized in the inner context, it will get automatically initialized.
*
* This method is designed to be used in compiled code paths. This method may be used from
* multiple threads at the same time. The result of this method must not be cached, instead the
* {@link Source} object should be cached.
*
*
* @throws IllegalArgumentException if the given language of the source cannot be accessed.
* @throws IllegalStateException if an invalid context is entered or the context is already
* closed.
* @param node a partial evaluation constant node context used to optimize this operation. Can
* be null
if not available.
* @param source the source to evaluate
* @since 21.3
*/
public Object evalInternal(Node node, Source source) {
CompilerAsserts.partialEvaluationConstant(node);
try {
return LanguageAccessor.engineAccess().evalInternalContext(node, polyglotContext, source, true);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* The same as {@link #evalInternal(Node, Source)}, but only public languages are accessible.
*
* @throws IllegalArgumentException if the given language of the source cannot be accessed.
* @throws IllegalStateException if an invalid context is entered or the context is already
* closed.
* @param node a partial evaluation constant node context used to optimize this operation. Can
* be null
if not available.
* @param source the source to evaluate
* @since 21.3
*/
public Object evalPublic(Node node, Source source) {
CompilerAsserts.partialEvaluationConstant(node);
try {
return LanguageAccessor.engineAccess().evalInternalContext(node, polyglotContext, source, false);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Checks whether the context is entered on the current thread and the context is the currently
* active context on this thread. There can be multiple contexts {@link #isActive() active} on a
* single thread, but this method only returns true
if it is the top-most context.
* This method is thread-safe and may be used from multiple threads.
*
* @since 20.0
* @return {@code true} if the context is active, {@code false} otherwise
*/
public boolean isEntered() {
try {
return LanguageAccessor.engineAccess().isContextEntered(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Returns true
if the context is currently active on the current thread, else
* false
. Checks whether the context has been previously entered by this thread
* with {@link #enter(Node)} and hasn't been left yet with
* {@link #leave(Node, java.lang.Object)} methods. Multiple contexts can be active on a single
* thread. See {@link #isEntered()} for checking whether it is the top-most entered context.
* This method is thread-safe and may be used from multiple threads.
*
* @since 20.3
*/
public boolean isActive() {
try {
return LanguageAccessor.engineAccess().isContextActive(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Returns true
if the context was closed else false
. A context may be
* closed if {@link #close()}, {@link #closeCancelled(Node, String)}, or
* {@link #closeExited(Node, int)} was called previously.
*
* @since 20.3
*/
@TruffleBoundary
public boolean isClosed() {
try {
return LanguageAccessor.engineAccess().isContextClosed(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Returns true
if the context is being cancelled else false
. A
* context may be in the process of cancelling if {@link #closeCancelled(Node, String)} was
* called previously.
*
* @since 21.1
*/
@TruffleBoundary
public boolean isCancelling() {
try {
return LanguageAccessor.engineAccess().isContextCancelling(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Returns true
if the context is being hard-exited else false
. A
* context may be in the process of exit if {@link #closeExited(Node, int)} was called
* previously.
*
* @since 22.0
*/
@TruffleBoundary
public boolean isExiting() {
try {
return LanguageAccessor.engineAccess().isContextExiting(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Pause execution on all threads for this context. This call does not wait for the threads to
* be actually paused. Instead, a future is returned that can be used to wait for the execution
* to be paused. The future is completed when all active threads are paused. New threads entered
* after this point are paused immediately after entering until
* {@link TruffleContext#resume(Future)} is called.
*
* @return a future that can be used to wait for the execution to be paused. Also, the future is
* used to resume execution by passing it to the {@link TruffleContext#resume(Future)}
* method.
*
* @since 21.2
*/
@TruffleBoundary
public Future pause() {
try {
return LanguageAccessor.engineAccess().pause(polyglotContext);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Resume previously paused execution on all threads for this context. The execution will not
* resume if {@link TruffleContext#pause()} was called multiple times and for some of the other
* calls resume was not called yet.
*
* @param pauseFuture pause future returned by a previous call to
* {@link TruffleContext#pause()}.
*
* @throws IllegalArgumentException in case the passed pause future was not obtained by a
* previous call to {@link TruffleContext#pause()} on this context.
*
* @since 21.2
*/
@TruffleBoundary
public void resume(Future pauseFuture) {
try {
LanguageAccessor.engineAccess().resume(polyglotContext, pauseFuture);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Leaves this context and sets the previous context as the new current context.
*
* An adopted node may be passed to allow perform optimizations on the fast-path. If a
* null
node is passed then entering a context will result in a
* {@link TruffleBoundary boundary} call in compiled code. If the node is not adopted an
* {@link IllegalArgumentException} is thrown.
*
* Leaving a language context is designed for compilation and is most efficient if the
* {@link TruffleContext context} instance is compilation final.
*
* @param prev the previous context returned by {@link #enter(Node)}
* @see #enter(Node)
* @since 20.3
*/
public void leave(Node node, Object prev) {
try {
if (CONTEXT_ASSERT_STACK != null) {
verifyLeave(prev);
}
LanguageAccessor.engineAccess().leaveInternalContext(node, polyglotContext, prev);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Leaves this context, runs the passed supplier and reenters the context. This is useful when
* the current thread must wait for another thread (and does not need to access the context to
* do so) and triggering multithreading is not desired, for instance when implementing
* coroutines with threads. The supplier cannot access the context and must not run any guest
* language code or invoke interoperability messages.
*
* The supplier will typically notify another thread that it can now enter the context without
* triggering multithreading and then wait for some thread to leave the context before exiting
* the supplier and reentering the context (again to avoid triggering multithreading).
*
* An adopted node may be passed to allow perform optimizations on the fast-path. If a
* null
node is passed then entering a context will result in a
* {@link TruffleBoundary boundary} call in compiled code. If the provided node is not adopted
* an {@link IllegalArgumentException} is thrown.
*
* Entering a language context is designed for compilation and is most efficient if the
* {@link TruffleContext context} instance is compilation final.
*
* @param node an adopted node or {@code null}
* @param runWhileOutsideContext the supplier to run while having left this context
* @since 21.1
*/
public T leaveAndEnter(Node node, Supplier runWhileOutsideContext) {
CompilerAsserts.partialEvaluationConstant(node);
try {
LanguageAccessor.engineAccess().leaveInternalContext(node, polyglotContext, null);
try {
return callSupplier(runWhileOutsideContext);
} finally {
LanguageAccessor.engineAccess().enterInternalContext(node, polyglotContext);
}
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
@TruffleBoundary
private static T callSupplier(Supplier supplier) {
return supplier.get();
}
@TruffleBoundary
private static void verifyEnter(Object prev) {
assert CONTEXT_ASSERT_STACK != null;
CONTEXT_ASSERT_STACK.get().add(prev);
}
@TruffleBoundary
private static void verifyLeave(Object prev) {
assert CONTEXT_ASSERT_STACK != null;
List list = CONTEXT_ASSERT_STACK.get();
assert !list.isEmpty() : "Assert stack is empty.";
Object expectedPrev = list.get(list.size() - 1);
assert prev == expectedPrev : "Invalid prev argument provided in TruffleContext.leave(Object).";
list.remove(list.size() - 1); // pop
}
/**
* Closes this context and disposes its resources. Closes this context and disposes its
* resources. A context cannot be closed if it is currently {@link #enter(Node) entered} or
* active by any thread. If a closed context is attempted to be accessed or entered, then an
* {@link IllegalStateException} is thrown. If the context is not closed explicitly, then it is
* automatically closed together with the parent context. If an attempt to close a context was
* successful then consecutive calls to close have no effect.
*
* @throws UnsupportedOperationException if the close operation is not supported on this context
* @throws IllegalStateException if the context is {@link #isActive() active}.
* @since 0.27
* @see #closeCancelled(Node, String)
* @see #closeResourceExhausted(Node, String)
*/
@Override
@TruffleBoundary
public void close() {
if (!creator) {
throw new UnsupportedOperationException("This context instance has no permission to close. " +
"Only the original creator of the truffle context or instruments can close.");
}
try {
LanguageAccessor.engineAccess().closeContext(polyglotContext, false, null, false, null);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Force closes the context as cancelled and stops all the execution on all active threads using
* a special {@link ThreadDeath} cancel exception. If this context is not currently
* {@link #isEntered() entered} on the current thread then this method waits until the close
* operation is complete and {@link #isClosed()} returns true
, else it throws the
* cancelled exception upon its completion of this method. If an attempt to close a context was
* successful then consecutive calls to close have no effect.
*
* The throwing of the special {@link ThreadDeath} cancel exception also applies to any guest
* code run during {@link TruffleLanguage#finalizeContext(Object)} which means that
* {@link TruffleLanguage#finalizeContext(Object)} cannot run guest code during the cancel
* operation.
*
* If forced and this context currently {@link #isEntered() entered} on the current thread and
* no other context is entered on the current thread then this method directly throws a
* {@link ThreadDeath} error instead of completing to indicate that the current thread should be
* stopped. The thrown {@link ThreadDeath} must not be caught by the guest language and freely
* propagated to the guest application to cancel the execution on the current thread. Please
* note that this means that the guest language's finally blocks must not be executed.
*
* If a context is {@link #isActive() active} on the current thread, but not {@link #isEntered()
* entered}, then an {@link IllegalStateException} is thrown, as parent contexts that are active
* on the current thread cannot be cancelled.
*
* @param closeLocation the node where the close occurred. If the context is currently entered
* on the thread, then this node will be used as location, for the exception thrown.
* If the context is not entered, then the closeLocation parameter will be ignored.
* @param message exception text for humans provided to the embedder and tools that observe the
* cancellation.
* @throws UnsupportedOperationException if the close operation is not supported on this context
* @throws IllegalStateException if the context is {@link #isActive() active} but not
* {@link #isEntered() entered} on the current thread.
* @see #close()
* @see #closeResourceExhausted(Node, String)
* @since 20.3
*/
@TruffleBoundary
public void closeCancelled(Node closeLocation, String message) {
if (!creator) {
throw new UnsupportedOperationException("This context instance has no permission to close. " +
"Only the original creator of the truffle context or instruments can close.");
}
try {
LanguageAccessor.engineAccess().closeContext(polyglotContext, true, closeLocation, false, message);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Initiates force close of the context as exited -
* {@link org.pkl.thirdparty.truffle.api.TruffleLanguage.ExitMode#HARD hard exit}. Requires the context
* to be entered on the current thread. Languages are first notified by calling
* {@link TruffleLanguage#exitContext(Object, TruffleLanguage.ExitMode, int)} and then the
* closing of the context is initiated. Execution on all active threads including the current
* thread is stopped by throwing a special {@link ThreadDeath} exit exception. This method does
* not wait for the execution on other threads to be stopped, it throws the {@link ThreadDeath}
* exception as soon as possible. To exit threads reliably, guest languages need to ensure that
* the {@link ThreadDeath} is always immediately rethrown and guest language exception handlers
* and finally blocks are not run.
*
* The throwing of the special {@link ThreadDeath} exit exception also applies to any guest code
* run during {@link TruffleLanguage#finalizeContext(Object)} which means that
* {@link TruffleLanguage#finalizeContext(Object)} cannot run guest code during hard exit.
*
* The exit code can be specified only once and the first call to this method also executes the
* {@link TruffleLanguage#exitContext(Object, TruffleLanguage.ExitMode, int) exit notifications}
* on the same thread. Further calls to this method will ensure that the following guest code on
* the calling thread is not executed by throwing the {@link ThreadDeath} exit exception, and
* the exit location is used as the stopping point of the thread, but the passed exit code is
* ignored and exit notifications are not run.
*
* In case the context is in one of the following states
*
* the context is being closed and the finalization stage has already begun
* ({@link TruffleLanguage#finalizeContext(Object)} is being executed for all language
* contexts).
* the context is already closed
* the context threads are being unwound as a part of the cancelling process
* the context threads are being unwound as a part of the hard exit process that comes after
* exit notifications
*
* then calling this method has no effect.
*
* @param exitLocation the node where the exit occurred.
* @param exitCode exitCode provided to the embedder and tools that observe the exit.
* @throws IllegalStateException if the context is not {@link #isEntered() entered} on the
* current thread.
* @see Context
* Exit
*
* @since 22.0
*/
@TruffleBoundary
public void closeExited(Node exitLocation, int exitCode) {
if (!isEntered()) {
throw new IllegalStateException("Exit cannot be initiated for this context because it is not currently entered.");
}
try {
LanguageAccessor.engineAccess().exitContext(polyglotContext, exitLocation, exitCode);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Force closes the context due to resource exhaustion. This method is equivalent to calling
* {@link #closeCancelled(Node, String) closeCancelled(location, message)} except in addition
* the thrown {@link PolyglotException} returns true
for
* {@link PolyglotException#isResourceExhausted()}.
*
* @see PolyglotException#isResourceExhausted()
* @see #close()
* @see #closeCancelled(Node, String)
* @since 20.3
*/
@TruffleBoundary
public void closeResourceExhausted(Node location, String message) {
if (!creator) {
throw new UnsupportedOperationException("This context instance has no permission to cancel. " +
"Only the original creator of the truffle context or instruments can close.");
}
try {
LanguageAccessor.engineAccess().closeContext(polyglotContext, true, location, true, message);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
/**
* Builder class to create new {@link TruffleContext} instances.
*
* @since 0.27
*/
public final class Builder {
private final Env sourceEnvironment;
private Map config;
private Map arguments;
private boolean initializeCreatorContext;
private Runnable onCancelled;
private Consumer onExited;
private Runnable onClosed;
private Boolean sharingEnabled;
private Map options;
private Map environment;
private String[] permittedLanguages;
private OutputStream out;
private OutputStream err;
private InputStream in;
private boolean inheritAccess;
private Boolean allowCreateThread;
private Boolean allowNativeAccess;
private Boolean allowIO;
private Boolean allowHostLookup;
private Boolean allowHostClassLoading;
private Boolean allowCreateProcess;
private Boolean allowInnerContextOptions;
private Boolean allowPolyglotAccess;
private Boolean allowEnvironmentAccess;
private ZoneId timeZone;
Builder(Env env) {
this.sourceEnvironment = env;
}
@SuppressWarnings("hiding")
Builder permittedLanguages(String... permittedLanguages) {
this.permittedLanguages = permittedLanguages;
return this;
}
/**
* Sets a config parameter that the child context of this language can access using
* {@link Env#getConfig()}.
*
* @since 0.27
*/
@TruffleBoundary
public Builder config(String key, Object value) {
if (config == null) {
config = new HashMap<>();
}
config.put(key, value);
return this;
}
/**
* Specifies whether the creating language context should be initialized in the new context.
* By default the creating language won't get initialized.
*
* @since 21.3
*/
public Builder initializeCreatorContext(boolean enabled) {
this.initializeCreatorContext = enabled;
return this;
}
/**
* Sets the standard output stream to be used for the context. If not set or set to
* null
, then the standard output stream is inherited from the outer context.
*
* @since 22.3
*/
@SuppressWarnings("hiding")
public Builder out(OutputStream out) {
this.out = out;
return this;
}
/**
* Sets the error output stream to be used for the context. If not set or set to
* null
, then the standard error stream is inherited from the outer context. is
* used.
*
* @since 22.3
*/
@SuppressWarnings("hiding")
public Builder err(OutputStream err) {
this.err = err;
return this;
}
/**
* Sets the input stream to be used for the context. If not set or set to null
,
* then the standard input stream is inherited from the outer context.
*
* @since 22.3
*/
@SuppressWarnings("hiding")
public Builder in(InputStream in) {
this.in = in;
return this;
}
/**
* Force enables or disables code sharing for this inner context. By default this option is
* set to null
which instructs this context to inherit sharing configuration
* from the outer context.
*
* If sharing is explicitly set to true
then code may be shared with all
* contexts compatible with the configuration of this inner context. Code may also be shared
* with the outer context if the outer context supports code sharing and its sharing layer
* is compatible. This may be useful to force sharing for multiple created inner contexts if
* the outer context configuration disabled sharing.
*
* If sharing is explicitly set to false
then no code will be shared even if
* the outer context has code sharing enabled and supported by the language. This may be
* useful to avoid runtime profile pollution through code run in an inner context.
*
* If multiple languages are potentially used it is recommended but not required to use
* {@link Env#newInnerContextBuilder(String...) permitted languages} to specify all used
* languages ahead of time. This allows to validate sharing layer compatibility eagerly and
* avoids late failures when a language is initialized lazily and is not compatible with the
* sharing layer. Use --engine.TraceSharing
for details on whether the sharing
* configuration is compatible.
*
* @since 22.3
*/
public Builder forceSharing(Boolean enabled) {
this.sharingEnabled = enabled;
return this;
}
/**
* Overrides an option of the inner context. Only language options may be changed. Engine or
* instrument options cannot be modified after their initial configuration in the outer
* context. All options for inner contexts are inherited from the outer context, but options
* set for the inner context have precedence over options set in the outer context.
*
* This method is currently only supported if the outer context is configured with
* {@link org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowInnerContextOptions(boolean)}. If all
* privileges are denied then an {@link IllegalArgumentException} will be thrown when
* building the inner context. To find out whether the inner context option privilege has
* been granted languages may use
* {@link TruffleLanguage.Env#isInnerContextOptionsAllowed()}.
*
* @since 22.3
*/
@TruffleBoundary
public Builder option(String key, String value) {
Objects.requireNonNull(key);
Objects.requireNonNull(value);
if (this.options == null) {
this.options = new HashMap<>();
}
this.options.put(key, value);
return this;
}
/**
* If set to true
allows all access privileges that were also granted to the
* outer context. If set to false
then all privileges are denied in the created
* context independent of the outer context. By default all privileges are denied and not
* inherited.
*
* This privilege enables inheritance for the following privileges:
*
* {@link #allowCreateThread(boolean)}
* {@link #allowNativeAccess(boolean)}
* {@link #allowIO(boolean)}
* {@link #allowHostClassLookup(boolean)}
* {@link #allowHostClassLoading(boolean)}
* {@link #allowCreateProcess(boolean)}
* {@link #allowPolyglotAccess(boolean)}
* {@link #allowInheritEnvironmentAccess(boolean)}
* {@link #allowInnerContextOptions(boolean)}
*
*
* If an individual privilege was set explicitly then the value of
* {@link #inheritAllAccess(boolean)} is ignored for that privilege. For example it is
* possible to set {@link #inheritAllAccess(boolean)} to true
but set
* {@link #allowNativeAccess(boolean)} to false
to allow inheritance of all but
* native access rights from the outer to the inner context.
*
* In case new privileges are added in the future, then this method will allow or disallow
* inheritance for the new privileges as well. For security sensitive use-cases it is
* recommended to keep the default value of {@link #inheritAllAccess(boolean)} and
* individually specify allowed privileges for this context. For non security sensitive
* use-cases it is recommended to set this flag to true
. Following this
* recommendation is important in case new privileges are added to the platform.
*
* @since 22.3
*/
public Builder inheritAllAccess(boolean b) {
this.inheritAccess = b;
return this;
}
/**
* Allows or denies creating threads for this context. Set to true
to inherit
* access privilege from the outer context or false
to deny access for this
* context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowCreateThread(boolean)
* @since 22.3
*/
public Builder allowCreateThread(boolean b) {
this.allowCreateThread = b;
return this;
}
/**
* Allows or denies using native access in this context. Set to true
to inherit
* access privilege from the outer context or false
to deny access for this
* context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowNativeAccess(boolean)
* @since 22.3
*/
public Builder allowNativeAccess(boolean b) {
this.allowNativeAccess = b;
return this;
}
/**
* Allows or denies using IO in this context. Set to true
to inherit access
* privilege from the outer context or false
to deny access for this context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowIO(org.pkl.thirdparty.graalvm.polyglot.io.IOAccess)
* @since 22.3
*/
public Builder allowIO(boolean b) {
this.allowIO = b;
return this;
}
/**
* Allows or denies loading new host classes in this context. Set to true
to
* inherit access privilege from the outer context or false
to deny access for
* this context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowHostAccess(org.pkl.thirdparty.graalvm.polyglot.HostAccess)
* @since 22.3
*/
public Builder allowHostClassLoading(boolean b) {
this.allowHostClassLoading = b;
return this;
}
/**
* Allows or denies loading new host classes in this context. Set to true
to
* inherit access privilege from the outer context or false
to deny access for
* this context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowHostAccess(org.pkl.thirdparty.graalvm.polyglot.HostAccess)
* @since 22.3
*/
public Builder allowHostClassLookup(boolean b) {
this.allowHostLookup = b;
return this;
}
/**
* Allows or denies creating processes in this context. Set to true
to inherit
* access privilege from the outer context or false
to deny access for this
* context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowCreateProcess(boolean)
* @since 22.3
*/
public Builder allowCreateProcess(boolean b) {
this.allowCreateProcess = b;
return this;
}
/**
* Allows or denies options for inner contexts created by this context. Set to
* true
to inherit access privilege from the outer context or
* false
to deny access for this context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowCreateProcess(boolean)
* @since 22.3
*/
public Builder allowInnerContextOptions(boolean b) {
this.allowInnerContextOptions = b;
return this;
}
/**
* Allows or denies access to polyglot languages in this context. Set to true
* to inherit access privilege from the outer context or false
to deny access
* for this context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowPolyglotAccess(org.pkl.thirdparty.graalvm.polyglot.PolyglotAccess)
* @since 22.3
*/
public Builder allowPolyglotAccess(boolean b) {
this.allowPolyglotAccess = b;
return this;
}
/**
* Allows or denies access to the parent context's environment in this context. Set to
* true
to inherit variables from the outer context or false
to
* deny inheritance for this context. Environment variables can be set for the inner context
* with {@link #environment(String, String)} even if inheritance is disabled. If inheritance
* is enabled, environment variables specified for the inner context override environment
* variables of the outer context.
*
* @see #inheritAllAccess(boolean)
* @see org.pkl.thirdparty.graalvm.polyglot.Context.Builder#allowEnvironmentAccess(org.pkl.thirdparty.graalvm.polyglot.EnvironmentAccess)
* @since 22.3
*/
public Builder allowInheritEnvironmentAccess(boolean b) {
this.allowEnvironmentAccess = b;
return this;
}
/**
* Sets an environment variable.
*
* @param name the environment variable name
* @param value the environment variable value
* @since 22.3
*/
public Builder environment(String name, String value) {
Objects.requireNonNull(name, "Name must be non null.");
Objects.requireNonNull(value, "Value must be non null.");
if (this.environment == null) {
this.environment = new HashMap<>();
}
this.environment.put(name, value);
return this;
}
/**
* Shortcut for setting multiple {@link #environment(String, String) environment variables}
* using a map. All values of the provided map must be non-null.
*
* @param env environment variables
* @see #environment(String, String) To set a single environment variable.
* @since 22.3
*/
public Builder environment(Map env) {
Objects.requireNonNull(env, "Env must be non null.");
for (Map.Entry e : env.entrySet()) {
environment(e.getKey(), e.getValue());
}
return this;
}
/**
* Sets the default time zone to be used for this context. If not set, or explicitly set to
* null
then the time zone of the outer context will be used.
*
* @since 22.3
*/
public Builder timeZone(ZoneId zone) {
this.timeZone = zone;
return this;
}
/**
* Sets the guest language application arguments for a language context. Application
* arguments are typically made available to guest language implementations. Passing no
* arguments to a language is equivalent to providing an empty arguments array.
*
* @param language the language id of the primary language.
* @param args an array of arguments passed to the guest language program.
* @throws IllegalArgumentException if an invalid language id was specified.
* @since 22.3
*/
public Builder arguments(String language, String[] args) {
Objects.requireNonNull(language);
Objects.requireNonNull(args);
String[] newArgs = args;
if (args.length > 0) {
newArgs = new String[args.length];
for (int i = 0; i < args.length; i++) { // defensive copy
newArgs[i] = Objects.requireNonNull(args[i]);
}
}
if (arguments == null) {
arguments = new HashMap<>();
}
arguments.put(language, newArgs);
return this;
}
/**
* Sets multiple options using a map. See {@link #option(String, String)} for details.
*
* @since 22.3
*/
@TruffleBoundary
@SuppressWarnings("hiding")
public Builder options(Map options) {
for (var entry : options.entrySet()) {
option(entry.getKey(), entry.getValue());
}
return this;
}
/**
* Specifies a {@link Runnable} that will be executed when the new context is
* {@link TruffleContext#closeCancelled(Node, String) cancelled} and the cancel exception is
* about to reach the outer context, or when some operation on the new context is attempted
* while the context is already cancelled. However, the runnable will only be executed if
* the outer context is not cancelled.
*
* The purpose of the runnable is to allow throwing a custom guest exception before the
* cancel exception reaches the outer context, so that the outer context can properly handle
* the exception. In case the runnable does not throw any exception, an internal error is
* thrown (should not be caught).
*
* @since 22.1
*/
public Builder onCancelled(Runnable r) {
this.onCancelled = r;
return this;
}
/**
* Specifies a {@link Consumer} that will be executed when the new context is
* {@link TruffleContext#closeExited(Node, int) hard-exited} and the exit exception is about
* to reach the outer context, or when some operation on the new context is attempted while
* the context is already hard-exited. However, the consumer will only be executed if the
* outer context is not hard-exited.
*
* The purpose of the consumer is to allow throwing a custom guest exception before the exit
* exception reaches the outer context, so that the outer context can properly handle the
* exception. In case the consumer does not throw any exception, an internal error is thrown
* (should not be caught).
*
* The single input argument to the {@link Consumer} is the exit code.
*
* @since 22.1
* @see Context
* Exit
*/
public Builder onExited(Consumer r) {
this.onExited = r;
return this;
}
/**
* Specifies a {@link Runnable} that will be executed when some operation on the new context
* attenmpted while the context is already {@link TruffleContext#close()} closed}. However,
* the runnable will only be executed if the outer context is not closed.
*
* The purpose of the runnable is to allow throwing a custom guest exception before the
* close exception reaches the outer context, so that the outer context can properly handle
* the exception. In case the runnable does not throw any exception, an internal error is
* thrown (should not be caught).
*
* @since 22.1
*/
public Builder onClosed(Runnable r) {
this.onClosed = r;
return this;
}
/**
* Builds the new context instance.
*
* @since 0.27
*/
@TruffleBoundary
public TruffleContext build() {
try {
return LanguageAccessor.engineAccess().createInternalContext(
sourceEnvironment.getPolyglotLanguageContext(), this.out, this.err, this.in, this.timeZone,
this.permittedLanguages, this.config, this.options, this.arguments, this.sharingEnabled, this.initializeCreatorContext, this.onCancelled, this.onExited,
this.onClosed, this.inheritAccess, this.allowCreateThread, this.allowNativeAccess, this.allowIO, this.allowHostLookup, this.allowHostClassLoading,
this.allowCreateProcess, this.allowPolyglotAccess, this.allowEnvironmentAccess, this.environment, this.allowInnerContextOptions);
} catch (Throwable t) {
throw Env.engineToLanguageException(t);
}
}
}
}
class TruffleContextSnippets {
// @formatter:off
abstract class MyContext {
}
abstract class MyLanguage extends TruffleLanguage {
}
static
// BEGIN: TruffleContextSnippets.MyNode#executeInContext
final class MyNode extends Node {
void executeInContext(Env env) {
MyContext outerLangContext = getContext(this);
TruffleContext innerContext = env.newInnerContextBuilder().
initializeCreatorContext(true).build();
Object p = innerContext.enter(this);
try {
/*
* Here the this node cannot be passed otherwise an
* invalid sharing error would occur.
*/
MyContext innerLangContext = getContext(null);
assert outerLangContext != innerLangContext;
} finally {
innerContext.leave(this, p);
}
assert outerLangContext == getContext(this);
innerContext.close();
}
}
private static ContextReference REFERENCE
= ContextReference.create(MyLanguage.class);
private static MyContext getContext(Node node) {
return REFERENCE.get(node);
}
// END: TruffleContextSnippets.MyNode#executeInContext
// @formatter:on
}