All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.graalvm.polyglot.Context Maven / Gradle / Ivy

Go to download

GraalVM is an ecosystem for compiling and running applications written in multiple languages. GraalVM removes the isolation between programming languages and enables interoperability in a shared runtime.

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.graalvm.polyglot;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.logging.Handler;
import java.util.logging.Level;

import org.graalvm.polyglot.impl.AbstractPolyglotImpl.AbstractContextImpl;
import org.graalvm.polyglot.proxy.Proxy;
import org.graalvm.polyglot.io.FileSystem;

/**
 * A polyglot context for Graal guest languages that allows to {@link #eval(Source) evaluate} code.
 * A polyglot context represents the global runtime state of all {@link Engine#getLanguages()
 * installed} and {@link #newBuilder(String...) permitted} languages. Permitted languages are
 * {@link #initialize(String) initialized} lazily, when they are used for the first time. For many
 * operations of the context, a language identifier needs to be specified. A language
 * identifier is unique for each language.
 *
 * 

Evaluation

* * A context allows to evaluate Guest language source code using {@link #eval(Source)}. This is * possible by evaluating {@link Source} objects or given a language identifier and code * {@link String}. The {@link #eval(Source) evaluation} returns either the result value or throws a * {@link PolyglotException} if a guest language error occurs. *

* Example for evaluation of a fragment of JavaScript code with a new context: * *

 * Context context = Context.create();
 * Value result = context.eval("js", "42");
 * assert result.asInt() == 42;
 * context.close();
 * 
* * In this example: *
    *
  • We first first create a new context with all installed languages as permitted languages. *
  • Next, we evaluate the expression "42" with language "js", which is the language identifier * for JavaScript. Since this is the first time we access JavaScript, it automatically gets * {@link #initialize(String) initialized} first. *
  • Then, we assert the result value by converting the result value as primitive int * . *
  • Finally, if the context is no longer needed, it is necessary to close it to ensure that all * resources are freed. Contexts are also {@link AutoCloseable} for use with the Java * {@code try-with-resources} statement. *
* *

Configuration

*

* Contexts may be created either with default configuration using the {@link #create(String...) * create method} or with custom configuration using the {@link #newBuilder(String...) builder}. * Both methods allow to specify a subset of the installed languages as permitted languages. If no * language is specified then all installed languages are permitted. Using the builder method * {@link Builder#in(InputStream) input}, {@link Builder#err(OutputStream) error} and * {@link Builder#out(OutputStream) output} streams, {@link Builder#option(String, String) options}, * and {@link Builder#arguments(String, String[]) application arguments} may be configured. *

* Options may be specified for {@link Engine#getLanguages() languages}, * {@link Engine#getInstruments() instruments}, the {@link Engine#getOptions() engine} and the * {@link Engine#getOptions() compiler}. For {@link Language#getOptions() language options} the * option key consists of the {@link Language#getId() language id} plus a dot followed by the option * name (e.g. "js.Strict"). For most languages the option names start with an upper-case letter by * convention. A list of available options may be received using {@link Language#getOptions()}. * {@link Instrument#getOptions() Instrument options} are structured in the same way as language * options but start with the {@link Instrument#getId() instrument id} instead. *

* If system properties are {@link Engine.Builder#useSystemProperties(boolean) enabled}, which they * are by default, then all polyglot options maybe specified with the prefix "polyglot." (e.g. * "-Dpolyglot.js.Strict=true"). The system properties are read only once when the context or engine * instance is created. After that changes to the system properties have no affect. *

* Each Graal language performs an initialization step before it can be used to execute code, after * which it remains initialized for the lifetime of the context. Initialization is by default lazy * and automatic, but initialization can be forced {@link Context#initialize(String) manually} if * needed. *

* Example for custom configuration using the context builder: * *

 * OutputStream myOut = new BufferedOutputStream()
 * Context context = Context.newBuilder("js", "R")
 *                          .out(myOut)
 *                          .option("js.Strict", "true")
 *                          .allowAllAccess(true)
 *                          .build();
 * context.eval("js", "42");
 * context.eval("R", "42");
 * context.close();
 * 
* * In this example: *
    *
  • We first first create a new context with all installed languages as permitted languages. *
  • Next, we evaluate the expression "42" with language "js", which is the language identifier * for JavaScript. Since this is the first time we access JavaScript, it first gets * {@link #initialize(String) initialized} as well. *
  • Then, we assert the result value by converting the result value as primitive int * . *
  • Finally, if the context is no longer needed, it is necessary to close it to ensure that all * resources are freed. Contexts are also {@link AutoCloseable} for use with the Java * {@code try-with-resources} statement. *
* *

Bindings

* * The symbols of the top-most scope of a language can be accessed using the * {@link #getBindings(String) language bindings}. Each language provides its own bindings object * for a context. The bindings object may be used to read, modify, insert and delete members in the * top-most scope of the language. Certain languages may not allow write access to the bindings * object. See {@link #getBindings(String)} for details. *

* A context instance also provides access to the {@link #getPolyglotBindings() polyglot} bindings. * The polyglot bindings are shared between languages and may be used to exchange values. See * {@link #getPolyglotBindings()} for details. *

* Examples using language bindings from JavaScript: * *

 * Context context = Context.create("js");
 * Value jsBindings = context.getBindings("js")
 *
 * jsBindings.putMember("foo", 42);
 * assert context.eval("js", "foo").asInt() == 42;
 *
 * context.eval("js", "var bar = 42");
 * assert jsBindings.getMember("bar").asInt() == 42;
 *
 * assert jsBindings.getMember("Math")
 *                  .getMember("abs")
 *                  .execute(-42)
 *                  .asInt() == 42;
 * context.close();
 * 
* * In this example: *
    *
  • We create a new context with JavaScript as the only permitted language. *
  • Next, we load the JavaScript bindings object and assign it to a local variable * jsBindings. *
  • Then, we insert a new member foo into to the bindings object and verify that the * object is accessible within the language by reading from a global symbol with the same name. *
  • After that, we declare a new global variable in JavaScript and verify that it is accessible * as member of the language bindings object. *
  • Next, we access we access a JavaScript builtin named Math.abs symbol and execute * it with -42. This result is asserted to be 42. *
  • Finally, we close the context to free all allocated resources. *
* *

Host Interoperability

* * It is often necessary to interact with values of the host runtime within Graal guest languages. * Such objects are referred to as host objects. Every Java value that is passed to a Graal * language is interpreted according to the specification described in {@link #asValue(Object)}. * Also see {@link Value#as(Class)} for further details. * *

* Example using a Java object from JavaScript: * *

 * public class JavaRecord {
 *     public int x;
 *
 *     public String name() {
 *         return "foo";
 *     }
 * }
 * Context context = Context.create();
 *
 * JavaRecord record = new JavaRecord();
 * context.getBindings("js").putMember("javaRecord", record);
 *
 * context.eval("js", "record.x = 42");
 * assert record.x == 42;
 *
 * context.eval("js", "record.name()").asString().equals("foo");
 * 
* *

Error Handling

* * Guest languages code may fail when executing guest language code or when accessing guest language * object. So almost all methods in the {@link Context} and {@link Value} API throw a * {@link PolyglotException} in case an error occurs. See {@link PolyglotException} for further * details on error handling. * *

Isolation

* * Each context is by default isolated from all other instances with respect to both language * evaluation semantics and resource consumption. By default a new context instance has no access to * host resources, like threads, files or loading new host classes. To allow access to such * resources either the individual access right must be granted or * {@link Builder#allowAllAccess(boolean) all access} must be set to true. * *

* Contexts can be {@linkplain Builder#engine(Engine) configured} to share certain system resources * like ASTs, optimized code by specifying a single underlying engine; see {@link Engine} for more * details about code sharing. * *

Proxies

* * The {@link Proxy proxy interfaces} allow to mimic guest language objects, arrays, executables, * primitives and native objects in Graal languages. Every Graal language will treat instances of * proxies like an object of that particular language. Multiple proxy interfaces can be implemented * at the same time. For example, it is useful to provide proxy values that are objects with members * and arrays at the same time. * *

Thread-Safety

* * It is safe to use a context instance from a single thread. It is also safe to use it with * multiple threads if they do not access the context at the same time. Whether a single context * instance may be used from multiple threads at the same time depends on if all initialized * languages support it. If only languages are initialized that support multi-threading then the * context instance may be used from multiple threads at the same time. If a context is used from * multiple threads and the language does not fit then an {@link IllegalStateException} is thrown by * the accessing method. *

* Meta-data from the context's underlying {@link #getEngine() engine} can be retrieved safely by * any thread at any time. *

* A context may be {@linkplain #close() closed} from any thread, but only if the context is not * currently executing code. If a context is currently executing code, a different thread may kill * the currently runnign execution and close the context using {@link #close(boolean)}. * *

Pre-Initialization

* * The Context pre-initialization can be used to perform expensive builtin creation in the time of * native compilation. *

* The context pre-initialization is enabled by setting the system property * {@code polyglot.engine.PreinitializeContexts} to a comma separated list of language ids which * should be pre-initialized, for example: {@code -Dpolyglot.engine.PreinitializeContexts=js,python} *

* See * {@code com.oracle.truffle.api.TruffleLanguage.patchContext(java.lang.Object, com.oracle.truffle.api.TruffleLanguage.Env)} * for details about pre-initialization for language implementers. * * @since 1.0 */ public final class Context implements AutoCloseable { final AbstractContextImpl impl; Context(AbstractContextImpl impl) { this.impl = impl; } /** * Provides access to meta-data about the underlying Graal {@linkplain Engine engine}. * * @return the Graal {@link Engine} being used by this context * @since 1.0 */ public Engine getEngine() { return impl.getEngineImpl(this); } /** * Evaluates a source by using the {@linkplain Source#getLanguage() language} specified in the * source. The result is accessible as {@link Value value} and never null. The * first time a source is evaluated it will be parsed, consecutive invocations of eval with the * same source will only execute the already parsed code. *

* Basic Example: * *

     * Context context = Context.create();
     * Source source = Source.newBuilder("js", "42").name("mysource.js").build();
     * Value result = context.eval(source);
     * assert result.asInt() == 42;
     * context.close();
     * 
* * @param source a source object to evaluate * @throws PolyglotException in case parsing or evaluation of the guest language code failed. * @throws IllegalStateException if the context is already closed, the current thread is not * allowed to access this context * @throws IllegalArgumentException if the language of the given source is not installed or the * {@link Source#getMimeType() MIME type} is not supported with the language. * @return result of the evaluation. The returned instance is is never null, but * the result might represent a {@link Value#isNull() null} value. * @since 1.0 */ public Value eval(Source source) { return impl.eval(source.getLanguage(), source.impl); } /** * Evaluates a guest language code literal, using a provided {@link Language#getId() language * id}. The result is accessible as {@link Value value} and never null. The * provided {@link CharSequence} must represent an immutable String. *

* Basic Example: * *

     * Context context = Context.create();
     * Value result = context.eval("js", "42");
     * assert result.asInt() == 42;
     * context.close();
     * 
* * @throws PolyglotException in case parsing or evaluation of the guest language code failed. * @throws IllegalArgumentException if the language does not exist or is not accessible. * @throws IllegalStateException if the context is already closed, the current thread is not * allowed to access this context or if the given language is not installed. * @return result of the evaluation. The returned instance is is never null, but * the result might represent a {@link Value#isNull() null} value. * @since 1.0 */ public Value eval(String languageId, CharSequence source) { return eval(Source.create(languageId, source)); } /** * Returns polyglot bindings that may be used to exchange symbols between the host and guest * languages. All languages have unrestricted access to the polyglot bindings. The returned * bindings object always has {@link Value#hasMembers() members} and its members are * {@link Value#getMember(String) readable}, {@link Value#putMember(String, Object) writable} * and {@link Value#removeMember(String) removable}. *

* Guest languages may put and get members through language specific APIs. For example, in * JavaScript symbols of the polyglot bindings can be accessed using * Polyglot.import("name") and set using * Polyglot.export("name", value). Please see the individual language reference on * how to access these symbols. * * @throws IllegalStateException if context is already closed. * @since 1.0 */ public Value getPolyglotBindings() { return impl.getPolyglotBindings(); } /** * Returns a value that represents the top-most bindings of a language. The top most bindings of * the language returns a {@link Value#getMember(String) member} for symbol in the scope. * Languages may allow modifications of members of the returned bindings object at the * language's discretion. If the language was not yet {@link #initialize(String) initialized} it * will be initialized when the bindings are requested. * * @throws IllegalArgumentException if the language does not exist or is not accessible. * @throws IllegalStateException if the context is already closed. * @throws PolyglotException in case the lazy initialization failed due to a guest language * error. * @since 1.0 */ public Value getBindings(String languageId) { return impl.getBindings(languageId); } /** * Forces the initialization of a language. It is not necessary to explicitly initialize a * language, it will be initialized the first time it is used. * * @param languageId the identifier of the language to initialize. * @return true if the language was initialized. Returns false if it * was already initialized. * @throws PolyglotException in case the initialization failed due to a guest language error. * @throws IllegalArgumentException if the language does not exist or is not accessible. * @throws IllegalStateException if the context is already closed. * @since 1.0 */ public boolean initialize(String languageId) { return impl.initializeLanguage(languageId); } /** * Converts a host value to a polyglot {@link Value value} representation. This conversion is * applied implicitly whenever {@link Value#execute(Object...) execution} or * {@link Value#newInstance(Object...) instantiation} arguments are provided, * {@link Value#putMember(String, Object) members} and * {@link Value#setArrayElement(long, Object) array elements} are set or when a value is * returned by a {@link Proxy polyglot proxy}. It is not required nor efficient to explicitly * convert to polyglot values before performing these operations. This method is useful to * convert a {@link Value#as(Class) mapped} host value back to a polyglot value while preserving * the identity. *

* When a host value is converted to a polyglot value the following rules apply: *

    *
  1. If the hostValue is null then it will be interpreted as * polyglot {@link Value#isNull() null}. *
  2. If the hostValue is already a {@link Value polyglot value} then it will be * cast to {@link Value}. *
  3. If the hostValue is an instance of {@link Byte}, {@link Short}, * {@link Integer}, {@link Long}, {@link Float} or {@link Double} then it will be interpreted as * polyglot {@link Value#isNumber() number}. Other subclasses of {@link Number} will be * interpreted as {@link Value#isHostObject() host object} (see later). *
  4. If the hostValue is an instance of {@link Character} or {@link String} then * it will be interpreted as polyglot {@link Value#isString() string}. *
  5. If the hostValue is an instance of {@link Boolean} then it will be * interpreted as polyglot {@link Value#isBoolean() boolean}. *
  6. If the hostValue is a {@link Proxy polyglot proxy} then it will be * interpreted according to the behavior specified by the proxy. See the javadoc of the proxy * subclass for further details. *
  7. If the hostValue is a non-primitive {@link Value#as(Class) mapped Java * value} then the original value will be restored. For example if a guest language object was * mapped to {@link Map} then the original object identity will be preserved when converting * back to a polyglot value. *
  8. Any other hostValue will be interpreted as {@link Value#isHostObject() host * object}. Host objects expose all their public java fields and methods as * {@link Value#getMember(String) members}. In addition, Java arrays and subtypes of * {@link List} will be interpreted as value with {@link Value#hasArrayElements() array * elements} and single method interfaces annotated with {@link FunctionalInterface} are * {@link Value#execute(Object...) executable} directly. Java {@link Class} instances are * interpreted as {@link Value#canInstantiate() instantiable}, but they do not expose Class * methods as members. *
*

* Basic Examples: * * The following assertion statements always hold: * *

     * Context context = Context.create();
     * assert context.asValue(null).isNull();
     * assert context.asValue(42).isNumber();
     * assert context.asValue("42").isString();
     * assert context.asValue('c').isString();
     * assert context.asValue(new String[0]).hasArrayElements();
     * assert context.asValue(new ArrayList<>()).isHostObject();
     * assert context.asValue(new ArrayList<>()).hasArrayElements();
     * assert context.asValue((Supplier) () -> 42).execute().asInt() == 42;
     * 
* *

Mapping to Java methods and fields

* * When Java host objects are passed to guest languages, their public methods and fields are * provided as {@link Value#getMember(String) members}. Methods and fields are grouped by name, * so only one member is exposed for each name. *

* When an argument value needs to be mapped to match a required Java method parameter type then * the semantics of {@link Value#as(Class) host value mapping} is used. The result of the * mapping is equivalent of calling {@link Value#as(Class)} with the parameter type. Therefore a * {@link ClassCastException} or {@link NullPointerException} is thrown if a parameter value * cannot be cast to the required parameter type. *

* Overloaded java methods are selected based on the arguments that are provided. In case * multiple mapped Java methods with the same name are applicable for * {@link Value#execute(Object...) executions} or {@link Value#newInstance(Object...) * instantiations} then the method with the most concrete method with applicable arguments will * be used used. *

* The following parameter type hierarchy is used for method resolution. Left-most parameter * types are prioritized over types to their right. *

    *
  • {@link Value#isBoolean() Boolean} values: boolean, Boolean, Object *
  • String values: char, Character, String, CharSequence, Object *
  • Number values: byte, Byte, short, Short, int, Integer, long, Long, float, Float, double, * Double, Number, Object *
  • Other values are resolved based on their Java type hierarchy. *
* If there are multiple most concrete methods or too many arguments were provided then an * illegal argument type error will be raised. *

* Advanced Example: * * This example first creates a new instance of the Java class Record and inspects * it first using the polyglot value API and later using the JavaScript guest language. *

* In the following examples all assertions hold. * *

     * class JavaRecord {
     *   public int x = 42;
     *   public double y = 42.0;
     *   public String name() {
     *     return "foo";
     *   }
     * }
     * Context context = Context.create();
     * Value record = context.asValue(new JavaRecord());
     * assert record.getMember("x").asInt() == 42;
     * assert record.getMember("y").asDouble() == 42.0d;
     * assert record.getMember("name").execute().asString().equals("foo");
     *
     * assert context.eval("js", "(function(record) record.x)")
     *               .execute(record).asInt() == 42;
     * assert context.eval("js", "(function(record) record.y)")
     *               .execute(record).asDouble() == 42.0d;
     * assert context.eval("js", "(function(record) record.name())")
     *               .execute(record).asString().equals("foo");
     * 
* * @see Value#as(Class) * @throws IllegalStateException if the context is already closed * @param hostValue the host value to convert to a polyglot value. * @return the polyglot value. * @since 1.0 */ public Value asValue(Object hostValue) { return impl.asValue(hostValue); } /** * Explicitly enters this context on the current thread. A context needs to be entered for any * operation to be performed. The context implicitly enters and leaves the context for every * operation. For example, before and after invoking the {@link Value#execute(Object...) * execute} method. This can be inefficient if a very high number of simple operations needs to * be performed. By {@link #enter() entering} and {@link #leave() leaving} once explicitly, the * overhead for entering/leaving contexts for each operation can be eliminated. Contexts can be * entered multiple times on the same thread. * * @throws IllegalStateException if the context is already {@link #close() closed}. * @throws PolyglotException if a language has denied execution on the current thread. * @see #leave() to leave a context. * @since 1.0 */ public void enter() { impl.explicitEnter(this); } /** * Explicitly leaves this context on the current thread. The context must be {@link #enter() * entered} before calling this method. * * @throws IllegalStateException if the context is already closed or if the context was not * {@link #enter() entered} on the current thread. * @see #enter() to enter a context. * @since 1.0 */ public void leave() { impl.explicitLeave(this); } /** * Closes this context and frees up potentially allocated native resources. A context cannot * free all native resources allocated automatically. For this reason it is necessary to close * contexts after use. If a context is cancelled then the currently executing thread will throw * a {@link PolyglotException}. The exception indicates that it was * {@link PolyglotException#isCancelled() cancelled}. Please note that canceling a single * context can negatively affect the performance of other executing contexts constructed with * the same engine. *

* If internal errors occur during closing of the language then they are printed to the * configured {@link Builder#err(OutputStream) error output stream}. If a context was closed * then all its methods will throw an {@link IllegalStateException} when invoked. If an attempt * to close a context was successful then consecutive calls to close have no effect. * * @param cancelIfExecuting if true then currently executing contexts will be * {@link PolyglotException#isCancelled() cancelled}, else an * {@link IllegalStateException} is thrown. * @see Engine#close() To close an engine. * @throws PolyglotException in case the close failed due to a guest language error. * @throws IllegalStateException if the context is still running and cancelIfExecuting is * false * @since 1.0 */ public void close(boolean cancelIfExecuting) { impl.close(this, cancelIfExecuting); } /** * Closes this context and frees up potentially allocated native resources. Languages might not * be able to free all native resources allocated by a context automatically. For this reason it * is recommended to close contexts after use. If the context is currently being executed on * another thread then an {@link IllegalStateException} is thrown. To close concurrently * executing contexts see {@link #close(boolean)}. *

* If internal errors occur during closing of the language then they are printed to the * configured {@link Builder#err(OutputStream) error output stream}. If a context was closed * then all its methods will throw an {@link IllegalStateException} when invoked. If an attempt * to close a context was successful then consecutive calls to close have no effect. * * @throws PolyglotException in case the close failed due to a guest language error. * @throws IllegalStateException if the context is currently executing on another thread. * @see Engine#close() To close an engine. * @since 1.0 */ public void close() { close(false); } /** * Returns the currently entered polyglot context. A context is entered if the currently * executing Java method was called by a Graal guest language or if a context was entered * explicitly using {@link Context#enter()} on the current thread. The returned context may be * used to: *

    *
  • Evaluate guest language code from {@link #eval(String, CharSequence) string literals} or * {@link #eval(Source) file} sources. *
  • {@link #asValue(Object) Convert} Java values to {@link Value polyglot values}. *
  • Access top-level {@link #getBindings(String) bindings} of other languages. *
  • Access {@link #getPolyglotBindings() polyglot bindings}. *
  • Access meta-data like available {@link Engine#getLanguages() languages} or * {@link Engine#getOptions() options} of the {@link #getEngine() engine}. *
*

* The returned context may not be used to {@link #enter() enter} , {@link #leave() * leave} or {@link #close() close} the context or {@link #getEngine() engine}. Invoking such * methods will cause an {@link IllegalStateException} to be thrown. This ensures that only the * {@link #create(String...) creator} of a context is allowed to enter, leave or close a * context. *

* The currently entered context may change. It is therefore required to call * {@link #getCurrent() getCurrent} every time a context is needed. The currently entered * context should not be cached in static fields. * * @throws IllegalStateException if no context is currently entered. * @since 1.0 */ public static Context getCurrent() { return Engine.getImpl().getCurrentContext(); } /** * Creates a context with default configuration. * * @param permittedLanguages names of languages permitted in this context, if no languages are * provided then all the use of languages will be permitted. * @return a new context * @since 1.0 */ public static Context create(String... permittedLanguages) { return newBuilder(permittedLanguages).build(); } /** * Creates a builder for constructing a context with custom configuration. * * @param permittedLanguages names of languages permitted in this context, if no languages are * provided then the use of all languages will be permitted. * @return a builder that can create a context * @since 1.0 */ public static Builder newBuilder(String... permittedLanguages) { return EMPTY.new Builder(permittedLanguages); } private static final Context EMPTY = new Context(null); /** * Builder class to construct {@link Context} instances. A builder instance is not thread-safe * and must not be used from multiple threads at the same time. * * @see Context * @since 1.0 */ @SuppressWarnings("hiding") public final class Builder { private Engine sharedEngine; private String[] onlyLanguages; private OutputStream out; private OutputStream err; private InputStream in; private Map options; private Map arguments; private Predicate hostClassFilter; private Boolean allowHostAccess; private Boolean allowNativeAccess; private Boolean allowCreateThread; private boolean allowAllAccess; private Boolean allowIO; private Boolean allowHostClassLoading; private FileSystem customFileSystem; private Handler customLogHandler; Builder(String... onlyLanguages) { Objects.requireNonNull(onlyLanguages); for (String onlyLanguage : onlyLanguages) { Objects.requireNonNull(onlyLanguage); } this.onlyLanguages = onlyLanguages; } /** * Explicitly sets the underlying engine to use. By default every context has its own * isolated engine. If multiple contexts are created from one engine, then they may * share/cache certain system resources like ASTs, optimized code by specifying a single * underlying engine; see {@link Engine} for more details about system resource sharing. * * @since 1.0 */ public Builder engine(Engine engine) { Objects.requireNonNull(engine); this.sharedEngine = engine; return this; } /** * Sets the standard output stream to be used for this context. If not set then the standard * output stream configured for the {@link #engine(Engine) engine} is used or standard error * stream. * * @since 1.0 */ public Builder out(OutputStream out) { Objects.requireNonNull(out); this.out = out; return this; } /** * Sets the error output stream to be used for this context. If not set then either the * error stream configured for the {@link #engine(Engine) engine} is used or standard error * stream. * * @since 1.0 */ public Builder err(OutputStream err) { Objects.requireNonNull(err); this.err = err; return this; } /** * Sets the input stream to be used for this context. If not set then either the input * stream configured for the {@link #engine(Engine) engine} is used or standard in stream. * * @since 1.0 */ public Builder in(InputStream in) { Objects.requireNonNull(in); this.in = in; return this; } /** * Allows guest languages to access the host language by loading new classes. Default is * false. If {@link #allowAllAccess(boolean) all access} is set to * true then then host access is enabled if not allowed explicitly. * * @since 1.0 */ public Builder allowHostAccess(boolean enabled) { this.allowHostAccess = enabled; return this; } /** * Allows guest languages to access the native interface. * * @since 1.0 */ public Builder allowNativeAccess(boolean enabled) { this.allowNativeAccess = enabled; return this; } /** * If true allows guest languages to create new threads. Default is * false. If {@link #allowAllAccess(boolean) all access} is set to * true then the creation of threads is enabled if not allowed explicitly. * Threads created by guest languages are closed when the context is {@link Context#close() * closed}. * * @since 1.0 */ public Builder allowCreateThread(boolean enabled) { this.allowCreateThread = enabled; return this; } /** * If true grants the context the same access privileges as the host virtual * machine. If not explicitly specified then all access is false. If the host * VM runs without a {@link SecurityManager security manager} enabled, then enabling all * access gives the guest languages full control over the host process. Otherwise, the Java * {@link SecurityManager security manager} is in control of restricting the privileges of * the polyglot context. If new privilege restrictions are added to the polyglot API then * they will default to full access if all access is set to true. If all access * is enabled then certain privileges may still be disabled by configuring it explicitly * using this builder. *

* Grants full access to the following privileges by default: *

    *
  • The {@link #allowCreateThread(boolean) creation} and use of new threads. *
  • The access to public {@link #allowHostAccess(boolean) host classes}. *
  • The loading of new {@link #allowHostClassLoading(boolean) host classes} by adding * entries to the class path. *
  • Exporting new members into the polyglot {@link Context#getPolyglotBindings() * bindings}. *
  • Unrestricted {@link #allowIO(boolean) IO operations} on host system. *
* * @param enabled true for all access by default. * @since 1.0 */ public Builder allowAllAccess(boolean enabled) { this.allowAllAccess = enabled; return this; } /** * If host class loading is enabled then the guest language is allowed to load new host * classes via jar or class files. If {@link #allowAllAccess(boolean) all access} is set to * true then the host class loading is enabled if it is not disallowed * explicitly. For host class loading to be useful {@link #allowIO(boolean) IO} operations * and {@link #allowHostAccess(boolean) host access} need to be allowed as well. * * @since 1.0 */ public Builder allowHostClassLoading(boolean enabled) { this.allowHostClassLoading = enabled; return this; } /** * Sets a class filter that allows to limit the classes that are allowed to be loaded by * guest languages. If the filter returns true then the class is accessible, * else it is not accessible and throws an guest language error when accessed. In order to * have an effect {@link #allowHostAccess(boolean)} or {@link #allowAllAccess(boolean)} * needs to be set to true. * * @param classFilter a predicate that returns true or false for a * java qualified class name. * @since 1.0 */ public Builder hostClassFilter(Predicate classFilter) { Objects.requireNonNull(classFilter); this.hostClassFilter = classFilter; return this; } /** * Set an option for this {@link Context context}. By default any options for the * {@link Engine#getOptions() engine}, {@link Language#getOptions() language} or * {@link Instrument#getOptions() instrument} can be set for a context. If an * {@link #engine(Engine) explicit engine} is set for this context then only language * options can be set. Instrument and engine options can be set exclusively on the explicit * engine instance. If a language option was set for the context and the engine then the * option of the context is going to take precedence. *

* If one of the set option keys or values is invalid then an * {@link IllegalArgumentException} is thrown when the context is {@link #build() built}. * The given key and value must not be null. * * @see Engine.Builder#option(String, String) To specify an option for the engine. * @since 1.0 */ 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; } /** * Shortcut for setting multiple {@link #option(String, String) options} using a map. All * values of the provided map must be non-null. * * @param options a map options. * @see #option(String, String) To set a single option. * @since 1.0 */ public Builder options(Map options) { for (String key : options.keySet()) { option(key, options.get(key)); } return this; } /** * Sets the guest language application arguments for a language {@link Context context}. * Application arguments are typically made available to guest language implementations. It * depends on the language if and how they are accessible within the * {@link Context#eval(Source) evaluated} guest language scripts. 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 1.0 */ 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; } /** * If true allows guest language to perform unrestricted IO operations on host * system. Default is false. If {@link #allowAllAccess(boolean) all access} is * set to true then IO is enabled if not allowed explicitly. * * @param enabled {@code true} to enable Input/Output * @return the {@link Builder} * @since 1.0 */ public Builder allowIO(final boolean enabled) { allowIO = enabled; return this; } /** * Installs a new {@link FileSystem}. * * @param fileSystem the file system to be installed * @return the {@link Builder} * @since 1.0 */ public Builder fileSystem(final FileSystem fileSystem) { Objects.requireNonNull(fileSystem, "FileSystem must be non null."); this.customFileSystem = fileSystem; return this; } /** * Installs a new logging {@link Handler}. The logger's {@link Level} configuration is done * using the {@link #options(java.util.Map) Context's options}. The level option key has the * following format: {@code log.languageId.loggerName.level} or * {@code log.instrumentId.loggerName.level}. The value is either the name of pre-defined * {@link Level} constant or a numeric {@link Level} value. If not explicitly set in options * the level is inherited from the parent logger. *

* Examples of setting log level options:
* {@code builder.option("log.level","FINE");} sets the {@link Level#FINE FINE level} to all * {@code TruffleLogger}s.
* {@code builder.option("log.js.level","FINE");} sets the {@link Level#FINE FINE level} to * JavaScript {@code TruffleLogger}s.
* {@code builder.option("log.js.com.oracle.truffle.js.parser.JavaScriptLanguage.level","FINE");} * sets the {@link Level#FINE FINE level} to {@code TruffleLogger} for the * {@code JavaScriptLanguage} class.
*

* If the {@code logHandler} is not set on {@link Engine} nor on {@link Context} the log * messages are printed to {@link #out(java.io.OutputStream) Context's standard output * stream}. * * @param logHandler the {@link Handler} to use for logging in built {@link Context}. * @return the {@link Builder} * @since 1.0 */ public Builder logHandler(final Handler logHandler) { Objects.requireNonNull(logHandler, "Hanlder must be non null."); this.customLogHandler = logHandler; return this; } /** * Creates a new context instance from the configuration provided in the builder. The same * context builder can be used to create multiple context instances. * * @since 1.0 */ public Context build() { if (allowHostAccess == null) { allowHostAccess = allowAllAccess; } if (allowNativeAccess == null) { allowNativeAccess = allowAllAccess; } if (allowCreateThread == null) { allowCreateThread = allowAllAccess; } if (allowIO == null) { allowIO = allowAllAccess; } if (allowHostClassLoading == null) { allowHostClassLoading = allowAllAccess; } if (!allowIO && customFileSystem != null) { throw new IllegalStateException("Cannot install custom FileSystem when IO is disabled."); } Engine engine = this.sharedEngine; if (engine == null) { org.graalvm.polyglot.Engine.Builder engineBuilder = Engine.newBuilder().options(options == null ? Collections.emptyMap() : options); if (out != null) { engineBuilder.out(out); } if (err != null) { engineBuilder.err(err); } if (in != null) { engineBuilder.in(in); } if (customLogHandler != null) { engineBuilder.logHandler(customLogHandler); } engineBuilder.setBoundEngine(true); engine = engineBuilder.build(); return engine.impl.createContext(null, null, null, allowHostAccess, allowNativeAccess, allowCreateThread, allowIO, allowHostClassLoading, hostClassFilter, Collections.emptyMap(), arguments == null ? Collections.emptyMap() : arguments, onlyLanguages, customFileSystem, customLogHandler); } else { return engine.impl.createContext(out, err, in, allowHostAccess, allowNativeAccess, allowCreateThread, allowIO, allowHostClassLoading, hostClassFilter, options == null ? Collections.emptyMap() : options, arguments == null ? Collections.emptyMap() : arguments, onlyLanguages, customFileSystem, customLogHandler); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy