Embedding Languages

The GraalVM Polyglot API lets you embed and run code from guest languages in JVM-based host applications.

Throughout this section, you will learn how to create a host application in Java that runs on GraalVM and directly calls a guest language. You can use the tabs beneath each code example to choose between JavaScript, R, Ruby, and Python.

Ensure you set up GraalVM before you begin.

Compile and Run a Polyglot Application

GraalVM can run polyglot applications written in any language implemented with the Truffle language implementation framework. These languages are henceforth referenced as guest languages.

Complete the steps in this section to create a sample polyglot application that runs on GraalVM and demonstrates programming language interoperability.

1. Create a hello-polyglot project directory.

2. In your project directory, add a HelloPolyglot.java file that includes the following code:

  // COMPILE-CMD: javac {file}
// RUN-CMD: java {file}
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
// END-SNIPPET

public class hello_polyglot_js {

static
// BEGIN-SNIPPET
public class HelloPolyglot {
    public static void main(String[] args) {
        System.out.println("Hello Java!");
        try (Context context = Context.create()) {
            context.eval("js", "print('Hello JavaScript!');");
        }
    }
}
// END-SNIPPET

    public static void main(String[] args) {
        HelloPolyglot.main(null);
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java {file}
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
// END-SNIPPET

public class hello_polyglot_R {

static
// BEGIN-SNIPPET
public class HelloPolyglot {
    public static void main(String[] args) {
        System.out.println("Hello Java!");
        try (Context context = Context.newBuilder()
                                   .allowAllAccess(true)
                               .build()) {
            context.eval("R", "print('Hello R!');");
        }
    }
}
// END-SNIPPET

    public static void main(String[] args) {
        HelloPolyglot.main(null);
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java {file}
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
// END-SNIPPET

public class hello_polyglot_ruby {

static
// BEGIN-SNIPPET
public class HelloPolyglot {
    public static void main(String[] args) {
        System.out.println("Hello Java!");
        try (Context context = Context.create()) {
            context.eval("ruby", "puts 'Hello Ruby!'");
        }
    }
}
// END-SNIPPET

    public static void main(String[] args) {
        HelloPolyglot.main(null);
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java {file}
// BEGIN-SNIPPET
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
// END-SNIPPET

public class hello_polyglot_python {

static
// BEGIN-SNIPPET
public class HelloPolyglot {
    public static void main(String[] args) {
        System.out.println("Hello Java!");
        try (Context context = Context.create()) {
            context.eval("python", "print('Hello Python!')");
        }
    }
}
// END-SNIPPET

    public static void main(String[] args) {
        HelloPolyglot.main(null);
    }
}

  

 In this code:

3. Run javac HelloPolyglot.java to compile HelloPolyglot.java with GraalVM.

4. Run java HelloPolyglot to run the application on GraalVM.

You now have a polyglot application that consists of a Java host application and guest language code that run on GraalVM. You can use this application with other code examples to demonstrate more advanced capabilities of the Polyglot API.

To use other code examples in this section, you simply need to do the following:

1. Add the code snippet to the main method of HelloPolyglot.java.

2. Compile and run your polyglot application.

Define Guest Language Functions as Java Values

Polyglot applications let you take values from one programming language and use them with other languages.

Use the code example in this section with your polyglot application to show how the Polyglot API can return JavaScript, R, Ruby, or Python functions as Java values.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class function_js {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value function = context.eval("js", "x => x+1");
    assert function.canExecute();
    int x = function.execute(41).asInt();
    assert x == 42;
}
// END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class function_R {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    Value function = context.eval("R", "function(x) x + 1");
    assert function.canExecute();
    int x = function.execute(41).asInt();
    assert x == 42;
}
// END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class function_ruby {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value function = context.eval("ruby", "proc { |x| x + 1 }");
    assert function.canExecute();
    int x = function.execute(41).asInt();
    assert x == 42;
}
 // END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class function_python {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value function = context.eval("python", "lambda x: x + 1");
    assert function.canExecute();
    int x = function.execute(41).asInt();
    assert x == 42;
}
 // END-SNIPPET
    }
}

  

 In this code:

Access Guest Languages Directly from Java

Polyglot applications can readily access most language types and are not limited to functions. Host languages, such as Java, can directly access guest language values embedded in the polyglot application.

Use the code example in this section with your polyglot application to show how the Polyglot API can access objects, numbers, strings, and arrays.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class access_js_from_java {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value result = context.eval("js", 
                    "({ "                   +
                        "id   : 42, "       +
                        "text : '42', "     +
                        "arr  : [1,42,3] "  +
                    "})");
    assert result.hasMembers();

    int id = result.getMember("id").asInt();
    assert id == 42;

    String text = result.getMember("text").asString();
    assert text.equals("42");

    Value array = result.getMember("arr");
    assert array.hasArrayElements();
    assert array.getArraySize() == 3;
    assert array.getArrayElement(1).asInt() == 42;
}
// END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class access_R_from_java {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    Value result = context.eval("R", 
                    "list("                +
                        "id   = 42, "      +
                        "text = '42', "    +
                        "arr  = c(1,42,3)" +
                    ")");
    assert result.hasMembers();
    
    int id = result.getMember("id").asInt();
    assert id == 42;
    
    String text = result.getMember("text").asString();
    assert text.equals("42");
    
    Value array = result.getMember("arr");
    assert array.hasArrayElements();
    assert array.getArraySize() == 3;
    assert array.getArrayElement(1).asInt() == 42;
}
// END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class access_ruby_from_java {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value result = context.eval("ruby", 
                    "o = Struct.new(:id, :text, :arr).new(" +
                        "42, "       +
                        "'42', "     +
                        "[1,42,3] "  +
                    ")");
    assert result.hasMembers();
    
    int id = result.getMember("id").asInt();
    assert id == 42;
    
    String text = result.getMember("text").asString();
    assert text.equals("42");
    
    Value array = result.getMember("arr");
    assert array.hasArrayElements();
    assert array.getArraySize() == 3;
    assert array.getArrayElement(1).asInt() == 42;
}
// END-SNIPPET
    }
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;

public class access_python_from_java {
    public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.create()) {
    Value result = context.eval("python", 
                    "type('obj', (object,), {" +
                        "'id'  : 42, "         +
                        "'text': '42', "       +
                        "'arr' : [1,42,3]"     +
                    "})()");
    assert result.hasMembers();
    
    int id = result.getMember("id").asInt();
    assert id == 42;
    
    String text = result.getMember("text").asString();
    assert text.equals("42");
    
    Value array = result.getMember("arr");
    assert array.hasArrayElements();
    assert array.getArraySize() == 3;
    assert array.getArrayElement(1).asInt() == 42;
}
// END-SNIPPET
    }
}

  

 In this code:

Access Java from Guest Languages

Polyglot applications offer bi-directional access between guest languages and host languages. As a result, you can pass Java objects to guest languages.

Use the code example in this section with your polyglot application to show how guest languages can access primitive Java values, objects, arrays, and functional interfaces.

To permit guest languages to access any public method or field of a Java object, set allowAllAccess(true) when the context is built. In this mode, the guest language code must be fully trusted, as it can access other not explicitly exported Java methods using reflection.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import java.util.concurrent.Callable;
import org.graalvm.polyglot.*;

public class access_java_from_js {

// BEGIN-SNIPPET
public static class MyClass {
    public int               id    = 42;
    public String            text  = "42";
    public int[]             arr   = new int[]{1, 42, 3};
    public Callable<Integer> ret42 = () -> 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        context.getBindings("js").putMember("javaObj", new MyClass());
        boolean valid = context.eval("js",
               "    javaObj.id         == 42"          +
               " && javaObj.text       == '42'"        +
               " && javaObj.arr[1]     == 42"          +
               " && javaObj.ret42()    == 42")
           .asBoolean();
        assert valid == true;
    }
}
// END-SNIPPET
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import java.util.concurrent.Callable;
import org.graalvm.polyglot.*;

public class access_java_from_R {

// BEGIN-SNIPPET
public static class MyClass {
    public int               id    = 42;
    public String            text  = "42";
    public int[]             arr   = new int[]{1, 42, 3};
    public Callable<Integer> ret42 = () -> 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        context.getBindings("R").putMember("javaObj", new MyClass());
        boolean valid = context.eval("R",
               "    javaObj$id         == 42"   +
               " && javaObj$text       == '42'" +
               " && javaObj$arr[[2]]   == 42"   +
               " && javaObj$ret42()    == 42")
           .asBoolean();
        assert valid == true;
    }
}
// END-SNIPPET
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import java.util.concurrent.Callable;
import org.graalvm.polyglot.*;

public class access_java_from_ruby {

// BEGIN-SNIPPET
public static class MyClass {
    public int               id    = 42;
    public String            text  = "42";
    public int[]             arr   = new int[]{1, 42, 3};
    public Callable<Integer> ret42 = () -> 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        context.getPolyglotBindings().putMember("javaObj", new MyClass());
        boolean valid = context.eval("ruby",
               "javaObj = Polyglot.import('javaObj')\n" +
               "    javaObj[:id]         == 42"         +
               " && javaObj[:text]       == '42'"       +
               " && javaObj[:arr][1]     == 42"         +
               " && javaObj[:ret42].call == 42")
           .asBoolean();
        assert valid == true;
    }
}
// END-SNIPPET
}
 
  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import java.util.concurrent.Callable;
import org.graalvm.polyglot.*;

public class access_java_from_python {

// BEGIN-SNIPPET
public static class MyClass {
    public int               id    = 42;
    public String            text  = "42";
    public int[]             arr   = new int[]{1, 42, 3};
    public Callable<Integer> ret42 = () -> 42;
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        context.getPolyglotBindings().putMember("javaObj", new MyClass());
        boolean valid = context.eval("python",
               "import polyglot \n"                            +
               "javaObj =  polyglot.import_value('javaObj')\n" +
               "javaObj.id                   == 42"            +
               " and javaObj.text            == '42'"          +
               " and javaObj.arr[1]          == 42"            +
               " and javaObj.ret42() == 42")
           .asBoolean();
        assert valid == true;
    }
}
// END-SNIPPET
}

  

 In this code:

Lookup Java Types from Guest Languages

In addition to passing Java objects to the guest language, it is possible to allow the lookup of Java types in the guest language.

Use the code example in this section with your polyglot application to show how guest languages lookup Java types and instantiate them.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;

public class lookup_java_from_js {


public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    java.math.BigDecimal v = context.eval("js",
            "var BigDecimal = Java.type('java.math.BigDecimal');" +
            "BigDecimal.valueOf(10).pow(20)")
        .asHostObject();
    assert v.toString().equals("100000000000000000000");
}
// END-SNIPPET
}
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;

public class lookup_java_from_R {


public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    java.math.BigDecimal v = context.eval("R",
            "BigDecimal = java.type('java.math.BigDecimal');\n" + 
            "BigDecimal$valueOf(10)$pow(20)")
        .asHostObject();
    assert v.toString().equals("100000000000000000000");
}
// END-SNIPPET
}
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;

public class lookup_java_from_ruby {

public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    java.math.BigDecimal v = context.eval("ruby",
            "BigDecimal = Java.type('java.math.BigDecimal')\n" + 
            "BigDecimal.valueOf(10).pow(20)")
        .asHostObject();
    assert v.toString().equals("100000000000000000000");
}
// END-SNIPPET
}
}
 
  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;

public class lookup_java_from_python {


public static void main(String[] args) {
// BEGIN-SNIPPET
try (Context context = Context.newBuilder()
                           .allowAllAccess(true)
                       .build()) {
    java.math.BigDecimal v = context.eval("python",
            "import java\n" +
            "BigDecimal = java.type('java.math.BigDecimal')\n" + 
            "BigDecimal.valueOf(10).pow(20)")
        .asHostObject();
    assert v.toString().equals("100000000000000000000");
}
// END-SNIPPET
}
}

  

 In this code:

Computed Arrays Using Polyglot Proxies

The Polyglot API includes polyglot proxy interfaces that let you customize Java interoperability by mimicking guest language types, such as objects, arrays, native objects, or primitives.

Use the code example in this section with your polyglot application to see how you can implement arrays that compute their values lazily.

Note: The Polyglot API supports polyglot proxies either on the JVM or in Native Image.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;

public class proxy_js {

// BEGIN-SNIPPET
static class ComputedArray implements ProxyArray {
    public Object get(long index) {
        return index * 2;
    }
    public void set(long index, Value value) {
        throw new UnsupportedOperationException();
    }
    public long getSize() {
        return Long.MAX_VALUE;
    }
}

public static void main(String[] args) {
    try (Context context = Context.create()) {
        ComputedArray arr = new ComputedArray();
        context.getBindings("js").putMember("arr", arr);
        long result = context.eval("js",
                    "arr[1] + arr[1000000000]")
                .asLong();
        assert result == 2000000002L;
    }
}
// END-SNIPPET
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyArray;

public class proxy_R {

// BEGIN-SNIPPET
static class ComputedArray implements ProxyArray {
    public Object get(long index) {
        return index * 2;
    }
    public void set(long index, Value value) {
        throw new UnsupportedOperationException();
    }
    public long getSize() {
        return Long.MAX_VALUE;
    }
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        ComputedArray arr = new ComputedArray();
        context.getPolyglotBindings().putMember("arr", arr);
        long result = context.eval("R",
               "arr <- import('arr');" +
               "arr[2] + arr[1000000001]")
           .asLong();
        assert result == 2000000002L;
    }
}
// END-SNIPPET
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyArray;

public class proxy_ruby {

// BEGIN-SNIPPET
static class ComputedArray implements ProxyArray {
    public Object get(long index) {
        return index * 2;
    }
    public void set(long index, Value value) {
        throw new UnsupportedOperationException();
    }
    public long getSize() {
        return Long.MAX_VALUE;
    }
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        ComputedArray arr = new ComputedArray();
        context.getPolyglotBindings().putMember("arr", arr);
        long result = context.eval("ruby",
               "arr = Polyglot.import('arr') \n" +
               "arr[1] + arr[1000000000]")
           .asLong();
        assert result == 2000000002L;
    }
}
// END-SNIPPET
}

  
  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyArray;

public class proxy_python {

// BEGIN-SNIPPET
static class ComputedArray implements ProxyArray {
    public Object get(long index) {
        return index * 2;
    }
    public void set(long index, Value value) {
        throw new UnsupportedOperationException();
    }
    public long getSize() {
        return Long.MAX_VALUE;
    }
}

public static void main(String[] args) {
    try (Context context = Context.newBuilder()
                               .allowAllAccess(true)
                           .build()) {
        ComputedArray arr = new ComputedArray();
        context.getPolyglotBindings().putMember("arr", arr);
        long result = context.eval("python",
               "import polyglot\n" +
               "arr = polyglot.import_value('arr') \n" +
               "arr[1] + arr[1000000000]")
           .asLong();
        assert result == 2000000002L;
    }
}
// END-SNIPPET
}

  

 In this code:

For more information about the polyglot proxy interfaces, see the Polyglot API JavaDoc.

Host Access

The Polyglot API by default restricts access to certain critical functionality, such as file I/O. These restrictions can be lifted entirely by setting allowAllAccess to true.

Note: The access restrictions are currently only supported with JavaScript.

Controlling Access to Host Functions

It might be desireable to limit the access of guest applications to the host. For example, if a Java method is exposed that calls System.exit then the guest application will be able to exit the host process. In order to avoid accidentally exposed methods, no host access is allowed by default and every public method or field needs to be annotated with @HostAccess.Export explicitly.

  // COMPILE-CMD: javac {file}
// RUN-CMD: java -ea {file}
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotException;

public class explicit_access_java_from_js {

static
// BEGIN-SNIPPET
public class Employee {
    private final String name;
    Employee(String name) {this.name = name;}

    @HostAccess.Export
    public String getName() {
        return name;
    }
}
//END-SNIPPET
static
//BEGIN-SNIPPET
public class Services {
    @HostAccess.Export
    public Employee createEmployee(String name) {
        return new Employee(name);
    }
    
    public void exitVM() {
        System.exit(1);
    }
}

public static void main(String[] args) {
    try (Context context = Context.create()) {
        Services services = new Services();
        context.getBindings("js").putMember("services", services);
        String name = context.eval("js",
                "let emp = services.createEmployee('John Doe');" + 
                "emp.getName()").asString();
        assert name.equals("John Doe");
        
        try {
            context.eval("js", "services.exitVM()");
            assert false;
        } catch (PolyglotException e) {
            assert e.getMessage().endsWith(
                    "Unknown identifier: exitVM");
        }
    }
}
// END-SNIPPET
}

  

 In this code:

Host access is fully customizable by creating a custom HostAccess policy.

Controlling Host Callback Parameter Scoping

By default, a Value lives as long as the corresponding Context. However, it may be desireable to change this default behavior and bind a value to a scope, such that when execution leaves the scope, the value is invalidated. An example for such a scope are guest-to-host callbacks, where a Value may be passed as a callback parameter. We have already seen above how this works with the default HostAccess.EXPLICIT:

public class Services {
    Value lastResult;

    @HostAccess.Export
    public void callback(Value result) {
        this.lastResult = result;
    }

    String getResult() {
        return this.lastResult.asString();
    }
}

public static void main(String[] args) {
    Services s = new Services()
    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.EXPLICIT).build()) {
        context.getBindings("js").putMember("services", s);
        context.eval("js", "services.callback('Hello from JS');");
        System.out.println(s.getResult());
    }
}

In this example, lastResult maintains a reference to the value from the guest is stored on the host and remains accessible until after the scope of callback() has ended.

However, this is not always desireable, as keeping the value alive may block resources unnecessarily or not reflect the behavior of ephemeral values correctly. For these cases, HostAccess.SCOPED can be used, which changes the default behavior for all callbacks, such that values that are passed as callback parameters are only valid for the duration of the callback.

To make the above code work with HostAccess.SCOPED, individual values passed as a callback parameters can be pinned to extend their validity until after the callback returns:

public class Services {
    Value lastResult;

    @HostAccess.Export
    void callback(Value result, Value notneeded) {
        this.lastResult = result;
        this.lastResult.pin();
    }

    String getResult() {
        return this.lastResult.asString();
    }
}

public static void main(String[] args) {
    Services s = new Services()
    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.SCOPED).build()) {
        context.getBindings("js").putMember("services", s);
        context.eval("js", "services.callback('Hello from JS', 'foobar');");
        System.out.println(services.getResult());
    }
}

Alternatively, the entire callback method can opt out from scoping if annotated with @HostAccess.DisableMethodScope, maintaining regular semantics for all parameters of the callback:

public class Services {
    Value lastResult;
    Value metaInfo;

    @HostAccess.Export
    @HostAccess.DisableMethodScope
    void callback(Value result, Value metaInfo) {
        this.lastResult = result;
        this.metaInfo = metaInfo;
    }

    String getResult() {
        return this.lastResult.asString() + this.metaInfo.asString();
    }
}

public static void main(String[] args) {
    Services s = new Services()
    try (Context context = Context.newBuilder().allowHostAccess(HostAccess.SCOPED).build()) {
        context.getBindings("js").putMember("services", s);
        context.eval("js", "services.callback('Hello from JS', 'foobar');");
        System.out.println(services.getResult());
    }
}

Access Privilege Configuration

It is possible to configure fine-grained access privileges for guest applications. The configuration can be provided using the Context.Builder class when constructing a new context. The following access parameters may be configured:

Note: Granting access to class loading, native APIs, or host I/O effectively grants all access, as these privileges can be used to bypass other access restrictions.

Build Native Executables from Polyglot Applications

Polyglot embeddings can also be compiled ahead-of-time using Native Image. By default, no language is included if the Polyglot API is used. To enable guest languages, the --language:<languageId> (e.g., --language:js) option needs to be specified. Currently, it is required to set the --initialize-at-build-time option when building a polyglot native executable. All examples on this page can be converted to native executables with the native-image builder.

The following example shows how a simple HelloWorld JavaScript application can be built using native-image:

javac HelloPolyglot.java
native-image --language:js --initialize-at-build-time -cp . HelloPolyglot
./HelloPolyglot

It should be mentioned that you can also include a guest language into the native executable, but exclude the JIT compiler by passing the -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime option to the builder. Be aware, the flag -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime has to placed after all the Truffle language/tool options, so that it will override the default settings.

You can build the above example again but this time the created image will only contain the Truffle language interpreter (the GraalVM compiler will not be included in the image) by running:

native-image --language:js -Dtruffle.TruffleRuntime=com.oracle.truffle.api.impl.DefaultTruffleRuntime --initialize-at-build-time -cp . HelloPolyglotInterpreter

Configuring Native Host Reflection

Accessing host Java code from the guest application requires Java reflection in order to work. When reflection is used within a native executable, the reflection configuration file is required.

For this example we use JavaScript to show host access with native executables. Copy the following code in a new file named AccessJavaFromJS.java.

import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
import java.util.concurrent.*;

public class AccessJavaFromJS {

    public static class MyClass {
        public int               id    = 42;
        public String            text  = "42";
        public int[]             arr   = new int[]{1, 42, 3};
        public Callable<Integer> ret42 = () -> 42;
    }

    public static void main(String[] args) {
        try (Context context = Context.newBuilder()
                                   .allowAllAccess(true)
                               .build()) {
            context.getBindings("js").putMember("javaObj", new MyClass());
            boolean valid = context.eval("js",
                   "    javaObj.id         == 42"          +
                   " && javaObj.text       == '42'"        +
                   " && javaObj.arr[1]     == 42"          +
                   " && javaObj.ret42()    == 42")
               .asBoolean();
            System.out.println("Valid " + valid);
        }
    }
}

Copy the following code into reflect.json:

[
  { "name": "AccessJavaFromJS$MyClass", "allPublicFields": true },
  { "name": "java.util.concurrent.Callable", "allPublicMethods": true }
]

Now you can create a native executable that supports host access:

javac AccessJavaFromJS.java
native-image --language:js --initialize-at-build-time -H:ReflectionConfigurationFiles=reflect.json -cp . AccessJavaFromJS
./accessjavafromjs

Note that in case assertions are needed in the image, the -H:+RuntimeAssertions option can be passed to native-image. For production deployments, this option should be omitted.

Code Caching Across Multiple Contexts

The GraalVM Polyglot API allows code caching across multiple contexts. Code caching allows compiled code to be reused and allows sources to be parsed only once. Code caching can often reduce memory consumption and warm-up time of the application.

By default, code is cached within a single context instance only. To enable code caching between multiple contexts, an explicit engine needs to be specified. The engine is specified when creating the context using the context builder. The scope of code sharing is determined by the engine instance. Code is only shared between contexts associated with one engine instance.

All sources are cached by default. Caching may be disabled explicitly by setting cached(boolean cached) to false. Disabling caching may be useful in case the source is known to only be evaluated once.

Consider the following code snippet as an example:

public class Main {
    public static void main(String[] args) {
        try (Engine engine = Engine.create()) {
            Source source = Source.create("js", "21 + 21");
            try (Context context = Context.newBuilder()
                .engine(engine)
                .build()) {
                    int v = context.eval(source).asInt();
                    assert v == 42;
            }
            try (Context context = Context.newBuilder()
                .engine(engine)
                .build()) {
                    int v = context.eval(source).asInt();
                    assert v == 42;
            }
        }
    }
}

In this code:

Embed Guest languages in Guest Languages

The GraalVM Polyglot API can be used from within a guest language using Java interoperability. This can be useful if a script needs to run isolated from the parent context. In Java as a host language a call to Context.eval(Source) returns an instance of Value, but since we executing this code as part of a guest language we can use the language-specific interoperability API instead. It is therefore possible to use values returned by contexts created inside of a language, like regular values of the language. In the example below we can conveniently write value.data instead of value.getMember("data"). Please refer to the individual language documentation for details on how to interoperate with foreign values. More information on value sharing between multiple contexts can be found here.

Consider the following code snippet as an example:

import org.graalvm.polyglot.*;

public class Main {
    public static void main(String[] args) {
        try (Context outer = Context.newBuilder()
                                   .allowAllAccess(true)
                               .build()) {
            outer.eval("js", "inner = Java.type('org.graalvm.polyglot.Context').create()");
            outer.eval("js", "value = inner.eval('js', '({data:42})')");
            int result = outer.eval("js", "value.data").asInt();
            outer.eval("js", "inner.close()");

            System.out.println("Valid " + (result == 42));
        }
    }
}

In this code:

Build a Shell for Many Languages

With just a few lines of code, the GraalVM Polyglot API lets you build applications that integrate with any guest language supported by GraalVM.

This shell implementation is agnostic to any particular guest language.

BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
PrintStream output = System.out;
Context context = Context.newBuilder().allowAllAccess(true).build();
Set<String> languages = context.getEngine().getLanguages().keySet();
output.println("Shell for " + languages + ":");
String language = languages.iterator().next();
for (;;) {
    try {
        output.print(language + "> ");
        String line = input.readLine();
        if (line == null) {
            break;
        } else if (languages.contains(line)) {
            language = line;
            continue;
        }
        Source source = Source.newBuilder(language, line, "<shell>")
                        .interactive(true).buildLiteral();
        context.eval(source);
    } catch (PolyglotException t) {
        if(t.isExit()) {
            break;
        }
        t.printStackTrace();
    }
}

Step Through with Execution Listeners

The GraalVM Polyglot API allows users to instrument the execution of guest languages through ExecutionListener class. For example, it lets you attach an execution listener that is invoked for every statement of the guest language program. Execution listeners are designed as simple API for polyglot embedders and may become handy in, e.g., single-stepping through the program.

import org.graalvm.polyglot.*;
import org.graalvm.polyglot.management.*;

public class ExecutionListenerTest {
    public static void main(String[] args) {
        try (Context context = Context.create("js")) {
            ExecutionListener listener = ExecutionListener.newBuilder()
                      .onEnter((e) -> System.out.println(
                              e.getLocation().getCharacters()))
                      .statements(true)
                      .attach(context.getEngine());
            context.eval("js", "for (var i = 0; i < 2; i++);");
            listener.close();
        }
    }
}

In this code:

Polyglot Isolates

On GraalVM Enterprise, a Polyglot engine can be configured to run in a dedicated native-image isolate. This experimental feature is enabled with the --engine.SpawnIsolate option. An engine running in this mode executes within a VM-level fault domain with its own garbage collector and JIT compiler. The fact that an engine runs within an isolate is completely transparent with respect to the Polyglot API and interoperability:

import org.graalvm.polyglot.*;

public class PolyglotIsolate {
  public static void main(String[] args) {
    Context context = Context.newBuilder("js")
      .allowHostAccess(HostAccess.SCOPED)
      .allowExperimentalOptions(true)
      .option("engine.SpawnIsolate", "true").build();
    Value function = context.eval("js", "x => x+1")
    assert function.canExecute();
    int x = function.execute(41).asInt();
    assert x == 42;
  }
}

Since the host’s GC and the isolate’s GC are not aware of one another, cyclic references between objects on both heaps may occur. We thus strongly recommend to use scoped parameters for host callbacks to avoid cyclic references.

Multiple contexts can be spawned in the same isolated engine by sharing engines:

public class PolyglotIsolateMultipleContexts {
    public static void main(String[] args) {
        try (Engine engine = Engine.newBuilder()
                .allowExperimentalOptions(true)
                .option("engine.SpawnIsolate", "js").build()) {
            Source source = Source.create("js", "21 + 21");
            try (Context context = Context.newBuilder()
                .engine(engine)
                .build()) {
                    int v = context.eval(source).asInt();
                    assert v == 42;
            }
            try (Context context = Context.newBuilder()
                .engine(engine)
                .build()) {
                    int v = context.eval(source).asInt();
                    assert v == 42;
            }
        }
    }
}

Note how we need to specify the language for the isolated engine as a parameter to --engine.SpawnIsolate in this case. The reason is that an isolated engine needs to know which set of languages should be available. Behind the scenes, GraalVM will then locate the corresponding Native Image language library. If only a single language is selected, then the library for the language will be loaded. If multiple languages are selected, then libpolyglot, the library containing all Truffle languages shipped with GraalVM, will be loaded. If a matching library is not available, creation of the engine will fail.

Only one language library can be loaded during GraalVM’s lifetime. This means that the first isolated engine that is created sets the default for the remainder of the execution: if an isolated engine with solely Javascript was created first, only Javascript will be available in isolated languages.

Passing Native Image Runtime Options

Engines running in an isolate can make use of Native Image runtime options by passing --engine.IsolateOption.<option> to the engine builder. For example, this can be used to limit the maximum heap memory used by an engine by setting the maximum heap size for the isolate via --engine.IsolateOption.MaxHeapSize=128m:

import org.graalvm.polyglot.*;

public class PolyglotIsolateMaxHeap {
  public static void main(String[] args) {
    try {
      Context context = Context.newBuilder("js")
        .allowHostAccess(HostAccess.SCOPED)
        .allowExperimentalOptions(true)
        .option("engine.SpawnIsolate", "true")
        .option("engine.IsolateOption.MaxHeapSize", "64m").build()
      context.eval("js", "var a = [];while (true) {a.push('foobar');}");
    } catch (PolyglotException ex) {
      if (ex.isResourceExhausted()) {
        System.out.println("Resource exhausted");
      }
    }
  }
}

Exceeding the maximum heap size will automatically close the context and raise a PolyglotException.

Dependency Setup

To best make use of the embedding API of GraalVM (i.e. org.graalvm.polyglot.*) your project should use a GraalVM as JAVA_HOME. In addition to that, you should specify the graal-sdk.jar (which is included in GraalVM) as a provided dependency to your projects. This is mainly to provide IDEs and other tools with the information that the project uses this API. An example of this for Maven means adding the following to the pom.xml file.

<dependency>
    <groupId>org.graalvm.sdk</groupId>
    <artifactId>graal-sdk</artifactId>
    <version>${graalvm.version}</version>
    <scope>provided</scope>
</dependency>

Additionally, when using Java modules, your modue-info.java file should require org.graalvm.sdk.

module com.mycompany.app {
  requires org.graalvm.sdk;

}

7c86aa8c43a (Native image > native executable)