CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-graalvm-js--js-scriptengine

GraalJS ScriptEngine implementation based on JSR-223 providing JavaScript execution capabilities through the javax.script.ScriptEngine API.

Pending
Overview
Eval results
Files

configuration.mddocs/

Configuration & Context Management

Advanced configuration capabilities for GraalJS ScriptEngine including security settings, performance options, and polyglot context management. Provides multiple configuration approaches from simple binding-based options to full polyglot context builders.

Capabilities

Direct Engine Creation

Static factory methods for creating GraalJSScriptEngine instances with various levels of customization.

/**
 * Creates a new GraalJSScriptEngine with default configuration
 * Default includes syntax extensions, load/print/arguments support enabled
 * @return New GraalJSScriptEngine with standard settings
 */
static GraalJSScriptEngine create();

/**
 * Creates a new GraalJSScriptEngine with custom polyglot engine and context configuration
 * @param engine Polyglot engine to use (null for default)
 * @param newContextConfig Base configuration for new contexts (null for default)
 * @return New GraalJSScriptEngine with custom configuration
 */
static GraalJSScriptEngine create(Engine engine, Context.Builder newContextConfig);

Usage Examples:

// Simple default creation
GraalJSScriptEngine defaultEngine = GraalJSScriptEngine.create();
defaultEngine.eval("console.log('Hello, World!');");

// Custom engine with specific options
Engine customEngine = Engine.newBuilder()
    .allowExperimentalOptions(true)
    .option("engine.MaxCompilationDelay", "5000")
    .build();

Context.Builder contextConfig = Context.newBuilder("js")
    .allowHostAccess(HostAccess.ALL)
    .allowIO(IOAccess.ALL)
    .option("js.ecmascript-version", "2022");

GraalJSScriptEngine customEngine = GraalJSScriptEngine.create(customEngine, contextConfig);

// Custom configuration without custom engine
GraalJSScriptEngine configuredEngine = GraalJSScriptEngine.create(null, contextConfig);

Polyglot Context Access

Direct access to underlying GraalVM polyglot contexts for advanced operations and fine-grained control.

/**
 * Returns the polyglot context associated with the default ScriptContext
 * @return GraalVM polyglot Context instance
 */
Context getPolyglotContext();

/**
 * Returns the polyglot context associated with a specific ScriptContext
 * If context not initialized, creates it using default context builder
 * @param scriptContext The ScriptContext to get polyglot context for
 * @return GraalVM polyglot Context instance for the ScriptContext
 */
Context getPolyglotContext(ScriptContext scriptContext);

/**
 * Returns the polyglot engine associated with this script engine
 * @return GraalVM polyglot Engine instance
 */
Engine getPolyglotEngine();

Usage Examples:

GraalJSScriptEngine engine = GraalJSScriptEngine.create();

// Access default polyglot context
Context defaultContext = engine.getPolyglotContext();
Value jsBindings = defaultContext.getBindings("js");
jsBindings.putMember("javaObject", new Object());

// Work with multiple script contexts
SimpleScriptContext context1 = new SimpleScriptContext();
SimpleScriptContext context2 = new SimpleScriptContext();

Context polyContext1 = engine.getPolyglotContext(context1);
Context polyContext2 = engine.getPolyglotContext(context2);

// Each ScriptContext has its own polyglot context
assert polyContext1 != polyContext2;
assert polyContext1.getEngine() == polyContext2.getEngine();

// Direct polyglot operations
polyContext1.eval("js", "var ctx1Variable = 'context1'");
polyContext2.eval("js", "var ctx2Variable = 'context2'");

// Access polyglot engine
Engine polyEngine = engine.getPolyglotEngine();
System.out.println("Engine version: " + polyEngine.getVersion());

Magic Binding Configuration

Configure GraalJS context options through special binding keys that are processed during context initialization.

// Magic binding option prefix constant
static final String MAGIC_OPTION_PREFIX = "polyglot.js.";

// Magic binding option keys (set via Bindings.put() before context initialization)
"polyglot.js.allowHostAccess" // Boolean - Enable/disable host object access
"polyglot.js.allowNativeAccess" // Boolean - Enable/disable native library access  
"polyglot.js.allowCreateThread" // Boolean - Enable/disable thread creation
"polyglot.js.allowIO" // Boolean - Enable/disable I/O operations
"polyglot.js.allowHostClassLookup" // Boolean or Predicate<String> - Control class lookup
"polyglot.js.allowHostClassLoading" // Boolean - Enable/disable class loading
"polyglot.js.allowAllAccess" // Boolean - Enable/disable all access permissions
"polyglot.js.nashorn-compat" // Boolean - Enable/disable Nashorn compatibility mode
"polyglot.js.ecmascript-version" // String - Set ECMAScript version
"polyglot.js.intl-402" // Boolean - Enable/disable Intl API support

Usage Examples:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);

// Configure security options BEFORE first evaluation
bindings.put("polyglot.js.allowHostAccess", true);
bindings.put("polyglot.js.allowHostClassLookup", (Predicate<String>) s -> s.startsWith("java."));
bindings.put("polyglot.js.allowIO", false); // Disable I/O for security

// Configure language options
bindings.put("polyglot.js.ecmascript-version", "2022");
bindings.put("polyglot.js.nashorn-compat", false);

// Set Java objects accessible to JavaScript
bindings.put("javaList", Arrays.asList(1, 2, 3));
bindings.put("javaMap", Map.of("key", "value"));

// Now evaluate - context will be initialized with configured options
engine.eval("console.log(javaList.length);"); // Works due to allowHostAccess
engine.eval("console.log(Java.type('java.lang.String'));"); // Works due to allowHostClassLookup

// Attempting to set magic options after context initialization fails
try {
    engine.eval("var dummy = true;"); // Forces context initialization
    bindings.put("polyglot.js.allowIO", true); // This will throw IllegalStateException
} catch (IllegalStateException e) {
    System.out.println("Cannot set options after context initialization");
}

System Property Configuration

Configure GraalJS options globally through JVM system properties.

System Property Format:

# Engine options (affect all ScriptEngine instances)
-Dpolyglot.js.ecmascript-version=2022
-Dpolyglot.js.intl-402=true

# Global compatibility mode
-Dpolyglot.js.nashorn-compat=true

# Insecure access mode (development only)
-Dgraaljs.insecure-scriptengine-access=true

Usage Examples:

// Set system properties programmatically (before creating engines)
System.setProperty("polyglot.js.ecmascript-version", "2022");
System.setProperty("polyglot.js.intl-402", "true");

// Create engines - they inherit system property settings
ScriptEngine engine1 = new ScriptEngineManager().getEngineByName("js");
ScriptEngine engine2 = new ScriptEngineManager().getEngineByName("js");

// Both engines use ES2022 with Intl support
engine1.eval("console.log(Object.hasOwn({}, 'prop'));"); // ES2022 feature
engine2.eval("console.log(new Intl.DateTimeFormat('en-US'));"); // Intl API

// Command line usage:
// java -Dpolyglot.js.ecmascript-version=2022 -Dpolyglot.js.nashorn-compat=true MyApp

Custom Context Builder Configuration

Full control over polyglot context configuration through direct Context.Builder usage.

/**
 * Example Context.Builder configuration options
 */
Context.Builder contextBuilder = Context.newBuilder("js")
    // Security settings
    .allowHostAccess(HostAccess.ALL)  // Or HostAccess.NONE, custom HostAccess
    .allowHostClassLookup(s -> true)  // Or specific predicate
    .allowHostClassLoading(true)
    .allowNativeAccess(true)
    .allowCreateThread(true)
    .allowIO(IOAccess.ALL)           // Or IOAccess.NONE
    .allowAllAccess(true)            // Enables all permissions
    
    // Language options
    .option("js.ecmascript-version", "2022")
    .option("js.intl-402", "true")
    .option("js.nashorn-compat", "true")
    .option("js.syntax-extensions", "true")
    
    // I/O configuration
    .in(System.in)
    .out(System.out)
    .err(System.err)
    
    // Engine configuration
    .allowExperimentalOptions(true);

Usage Examples:

// Secure configuration for untrusted code
Context.Builder secureConfig = Context.newBuilder("js")
    .allowHostAccess(HostAccess.NONE)
    .allowHostClassLookup(s -> false)
    .allowHostClassLoading(false)
    .allowNativeAccess(false)
    .allowCreateThread(false)
    .allowIO(IOAccess.NONE)
    .option("js.ecmascript-version", "2022");

GraalJSScriptEngine secureEngine = GraalJSScriptEngine.create(null, secureConfig);

// Permissive configuration for trusted code
Context.Builder permissiveConfig = Context.newBuilder("js")
    .allowAllAccess(true)
    .allowHostClassLookup(s -> true)
    .option("js.nashorn-compat", "true")
    .option("js.ecmascript-version", "2022");

GraalJSScriptEngine permissiveEngine = GraalJSScriptEngine.create(null, permissiveConfig);

// Development configuration with custom I/O
StringWriter output = new StringWriter();
StringWriter errors = new StringWriter();

Context.Builder devConfig = Context.newBuilder("js")
    .allowAllAccess(true)
    .out(new WriterOutputStream(output, StandardCharsets.UTF_8))
    .err(new WriterOutputStream(errors, StandardCharsets.UTF_8))
    .option("js.syntax-extensions", "true");

GraalJSScriptEngine devEngine = GraalJSScriptEngine.create(null, devConfig);
devEngine.eval("console.log('Hello'); console.error('Warning');");
System.out.println("Output: " + output.toString());
System.out.println("Errors: " + errors.toString());

Nashorn Compatibility Mode

Special compatibility mode for applications migrating from Oracle Nashorn ScriptEngine.

// Enable via system property
System.setProperty("polyglot.js.nashorn-compat", "true");

// Enable via magic binding
bindings.put("polyglot.js.nashorn-compat", true);

// Enable via context builder
Context.Builder nashornConfig = Context.newBuilder("js")
    .option("js.nashorn-compat", "true");

Nashorn Compatibility Features:

  • Automatic host access permissions
  • Nashorn-style type conversions
  • Extended compatibility for legacy code
  • System.exit() support

Usage Examples:

// Global Nashorn compatibility
System.setProperty("polyglot.js.nashorn-compat", "true");
ScriptEngine nashornCompatEngine = new ScriptEngineManager().getEngineByName("js");

// Nashorn-style operations work automatically
nashornCompatEngine.put("javaObject", new Object());
nashornCompatEngine.eval("print(javaObject.getClass().getName());");

// Per-engine Nashorn compatibility  
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("polyglot.js.nashorn-compat", true);
bindings.put("polyglot.js.allowAllAccess", true);

engine.eval("var ArrayList = Java.type('java.util.ArrayList');");
engine.eval("var list = new ArrayList(); list.add('item');");

Resource Management

Proper lifecycle management of engines, contexts, and associated resources.

/**
 * Closes the current context and makes it unusable
 * Operations performed after closing will throw IllegalStateException
 */
void close();

/**
 * Check if context is closed
 */
boolean isClosed(); // Available on polyglot Context

Usage Examples:

// Automatic resource management
try (GraalJSScriptEngine engine = GraalJSScriptEngine.create()) {
    engine.eval("console.log('Working...');");
    // Engine automatically closed when leaving try block
}

// Manual resource management
GraalJSScriptEngine engine = GraalJSScriptEngine.create();
try {
    engine.eval("var result = computeExpensiveOperation();");
    Object result = engine.get("result");
    return result;
} finally {
    engine.close(); // Ensure cleanup
}

// Managing polyglot contexts directly
Context polyglotContext = engine.getPolyglotContext();
try {
    polyglotContext.eval("js", "performWork()");
} finally {
    if (!polyglotContext.isClosed()) {
        polyglotContext.close();
    }
}

// Resource sharing with custom engine
Engine sharedEngine = Engine.newBuilder().build();
try {
    GraalJSScriptEngine engine1 = GraalJSScriptEngine.create(sharedEngine, null);
    GraalJSScriptEngine engine2 = GraalJSScriptEngine.create(sharedEngine, null);
    
    // Use both engines - they share the polyglot engine
    engine1.eval("var shared = 'data';");
    engine2.eval("var other = 'data';");
    
    // Close individual engines
    engine1.close();
    engine2.close();
} finally {
    // Close shared engine when all ScriptEngines are done
    sharedEngine.close();
}

Configuration Best Practices

Security Configuration

  • Always use minimal required permissions
  • Disable host access for untrusted code
  • Use allowHostClassLookup predicates to restrict class access
  • Disable I/O and native access unless required

Performance Configuration

  • Reuse ScriptEngine instances when possible
  • Use CompiledScript for repeated execution
  • Share polyglot Engine across multiple ScriptEngines
  • Enable experimental options for performance tuning

Migration from Nashorn

  • Enable nashorn-compat mode initially
  • Gradually migrate to modern GraalJS features
  • Test thoroughly as compatibility is not 100%
  • Consider direct polyglot API for new development

Install with Tessl CLI

npx tessl i tessl/maven-org-graalvm-js--js-scriptengine

docs

configuration.md

engine-factory.md

index.md

script-engine.md

tile.json