Introduction

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

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

Ensure you set up GraalVM before you begin. SeeGet Started.

Alternatively, you can have a look at the reference documentation in Javadoc:

  • The Polyglot Package allows you to configure and run polyglot applications.
  • The Proxy Package allows you to mimic guest language objects using proxies.
  • The IO Package allows you to customize the file system access of languages.

Compile and Run a Polyglot Application

Polyglot applications run code written in any language implemented with the Truffle Language Implementation Framework.These languages are henceforth referenced as guests languages.

Complete the steps in this section to create a sample polyglotapplication that runs on GraalVM and demonstrates programming languageinteroperability.

  1. Create a hello-polyglot project directory.

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

Embedding Reference - 图1

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java {file}
  3. // BEGIN-SNIPPET
  4. import org.graalvm.polyglot.*;
  5. import org.graalvm.polyglot.proxy.*;
  6. // END-SNIPPET
  7. publicclass hello_polyglot_js {
  8. static
  9. // BEGIN-SNIPPET
  10. publicclassHelloPolyglot{
  11. publicstaticvoid main(String[] args){
  12. System.out.println("Hello Java!");
  13. try(Context context =Context.create()){
  14. context.eval("js","print('Hello JavaScript!');");
  15. }
  16. }
  17. }
  18. // END-SNIPPET
  19. publicstaticvoid main(String[] args){
  20. HelloPolyglot.main(null);
  21. }
  22. }

Embedding Reference - 图2

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java {file}
  3. // BEGIN-SNIPPET
  4. import org.graalvm.polyglot.*;
  5. import org.graalvm.polyglot.proxy.*;
  6. // END-SNIPPET
  7. publicclass hello_polyglot_R {
  8. static
  9. // BEGIN-SNIPPET
  10. publicclassHelloPolyglot{
  11. publicstaticvoid main(String[] args){
  12. System.out.println("Hello Java!");
  13. try(Context context =Context.newBuilder()
  14. .allowAllAccess(true)
  15. .build()){
  16. context.eval("R","print('Hello R!');");
  17. }
  18. }
  19. }
  20. // END-SNIPPET
  21. publicstaticvoid main(String[] args){
  22. HelloPolyglot.main(null);
  23. }
  24. }

Embedding Reference - 图3

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java {file}
  3. // BEGIN-SNIPPET
  4. import org.graalvm.polyglot.*;
  5. import org.graalvm.polyglot.proxy.*;
  6. // END-SNIPPET
  7. publicclass hello_polyglot_ruby {
  8. static
  9. // BEGIN-SNIPPET
  10. publicclassHelloPolyglot{
  11. publicstaticvoid main(String[] args){
  12. System.out.println("Hello Java!");
  13. try(Context context =Context.create()){
  14. context.eval("ruby","puts 'Hello Ruby!'");
  15. }
  16. }
  17. }
  18. // END-SNIPPET
  19. publicstaticvoid main(String[] args){
  20. HelloPolyglot.main(null);
  21. }
  22. }

Embedding Reference - 图4

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java {file}
  3. // BEGIN-SNIPPET
  4. import org.graalvm.polyglot.*;
  5. import org.graalvm.polyglot.proxy.*;
  6. // END-SNIPPET
  7. publicclass hello_polyglot_python {
  8. static
  9. // BEGIN-SNIPPET
  10. publicclassHelloPolyglot{
  11. publicstaticvoid main(String[] args){
  12. System.out.println("Hello Java!");
  13. try(Context context =Context.create()){
  14. context.eval("python","print('Hello Python!')");
  15. }
  16. }
  17. }
  18. // END-SNIPPET
  19. publicstaticvoid main(String[] args){
  20. HelloPolyglot.main(null);
  21. }
  22. }

In this code:

  • import org.graalvm.polyglot.* imports the base API for the Polyglot API.
  • import org.graalvm.polyglot.proxy.* imports the proxy classes of the Polyglot API, needed in later examples.
  • Context provides an execution environment for guest languages.R currently requires the allowAllAccess flag to be set to true to run the example.
  • eval evaluates the specified snippet of guest language code.
  • The try with resource statement initializes the Context and ensures that itis closed after use. Closing the context ensures that all resources includingpotential native resources are freed eagerly. Closing a context is optional butrecommended. Even if a context is not closed and no longer referenced it will befreed by the garbage collector automatically.
  1. Run javac HelloPolyglot.java to compile HelloPolyglot.java withGraalVM.

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

You now have a polyglot application that consists of a Java host applicationand guest language code that run on GraalVM. You can use this application withother code examples to demonstrate more advanced capabilities of thePolyglot 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 anduse them with other languages.

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

Embedding Reference - 图5

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass function_js {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Valuefunction= context.eval("js","x => x+1");
  9. assertfunction.canExecute();
  10. int x =function.execute(41).asInt();
  11. assert x ==42;
  12. }
  13. // END-SNIPPET
  14. }
  15. }

Embedding Reference - 图6

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass function_R {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. Valuefunction= context.eval("R","function(x) x + 1");
  11. assertfunction.canExecute();
  12. int x =function.execute(41).asInt();
  13. assert x ==42;
  14. }
  15. // END-SNIPPET
  16. }
  17. }

Embedding Reference - 图7

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass function_ruby {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Valuefunction= context.eval("ruby","proc { |x| x + 1 }");
  9. assertfunction.canExecute();
  10. int x =function.execute(41).asInt();
  11. assert x ==42;
  12. }
  13. // END-SNIPPET
  14. }
  15. }

Embedding Reference - 图8

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass function_python {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Valuefunction= context.eval("python","lambda x: x + 1");
  9. assertfunction.canExecute();
  10. int x =function.execute(41).asInt();
  11. assert x ==42;
  12. }
  13. // END-SNIPPET
  14. }
  15. }

In this code:

  • Value function is a Java value that refers to a function.
  • The eval call parses the script and returns the guest language function.
  • The first assertion checks that the value returned by the code snippet can beexecuted.
  • The execute call executes the function with the argument 41.
  • The asInt call converts the result to a Java int.
  • The second assertion verifies that the result was incremented by one as expected.

Access Guest Languages Directly from Java

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

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

Embedding Reference - 图9

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass access_js_from_java {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Value result = context.eval("js",
  9. "({ "+
  10. "id : 42, "+
  11. "text : '42', "+
  12. "arr : [1,42,3] "+
  13. "})");
  14. assert result.hasMembers();
  15. int id = result.getMember("id").asInt();
  16. assert id ==42;
  17. String text = result.getMember("text").asString();
  18. assert text.equals("42");
  19. Value array = result.getMember("arr");
  20. assert array.hasArrayElements();
  21. assert array.getArraySize()==3;
  22. assert array.getArrayElement(1).asInt()==42;
  23. }
  24. // END-SNIPPET
  25. }
  26. }

Embedding Reference - 图10

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass access_R_from_java {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. Value result = context.eval("R",
  11. "list("+
  12. "id = 42, "+
  13. "text = '42', "+
  14. "arr = c(1,42,3)"+
  15. ")");
  16. assert result.hasMembers();
  17. int id = result.getMember("id").asInt();
  18. assert id ==42;
  19. String text = result.getMember("text").asString();
  20. assert text.equals("42");
  21. Value array = result.getMember("arr");
  22. assert array.hasArrayElements();
  23. assert array.getArraySize()==3;
  24. assert array.getArrayElement(1).asInt()==42;
  25. }
  26. // END-SNIPPET
  27. }
  28. }

Embedding Reference - 图11

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass access_ruby_from_java {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Value result = context.eval("ruby",
  9. "o = Struct.new(:id, :text, :arr).new("+
  10. "42, "+
  11. "'42', "+
  12. "[1,42,3] "+
  13. ")");
  14. assert result.hasMembers();
  15. int id = result.getMember("id").asInt();
  16. assert id ==42;
  17. String text = result.getMember("text").asString();
  18. assert text.equals("42");
  19. Value array = result.getMember("arr");
  20. assert array.hasArrayElements();
  21. assert array.getArraySize()==3;
  22. assert array.getArrayElement(1).asInt()==42;
  23. }
  24. // END-SNIPPET
  25. }
  26. }

Embedding Reference - 图12

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. publicclass access_python_from_java {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.create()){
  8. Value result = context.eval("python",
  9. "type('obj', (object,), {"+
  10. "'id' : 42, "+
  11. "'text': '42', "+
  12. "'arr' : [1,42,3]"+
  13. "})()");
  14. assert result.hasMembers();
  15. int id = result.getMember("id").asInt();
  16. assert id ==42;
  17. String text = result.getMember("text").asString();
  18. assert text.equals("42");
  19. Value array = result.getMember("arr");
  20. assert array.hasArrayElements();
  21. assert array.getArraySize()==3;
  22. assert array.getArrayElement(1).asInt()==42;
  23. }
  24. // END-SNIPPET
  25. }
  26. }

In this code:

  • Value result is an Object that contains three members: a number named id,a string named text, and an array named arr.
  • The first assertion verifies that the return value can contain members, whichindicates that the value is an object-like structure.
  • The id variable is initialized by reading the member with the name id fromthe resulting object. The result is then converted to a Java intusing asInt().
  • The next assert verifies that result has a value of 42.
  • The text variable is initialized using the value of the member text, which is also converted to a Java String using asString().
  • The following assertion verifies the result value is equal to theJava String"42".
  • Next the arr member that holds an array is read.
  • Arrays return true for hasArrayElements. R array instances can havemembers and array elements at the same time.
  • The next assertion verifies that the size of the array equals three. ThePolyglot API supports big arrays, so the array length is of type long.
  • Finally we verify that the array element at index 1 equals 42. Arrayindexing with polyglot values is always zero-based, even for languages such asR where indices start with one.

Access Java from Guest Languages

Polyglot applications offer bi-directional access between guest languages andhost 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 howguest languages can access primitive Java values, objects, arrays, andfunctional interfaces.

To permit guest languages to access any public method or field of a Javaobject, set allowAllAccess(true) when the context is built. In this mode, the guestlanguage code must be fully trusted, as it can access other not explicitly exported Java methodsusing reflection. A later section describes how to run less trusted code with the Polyglot API.

Embedding Reference - 图13

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import java.util.concurrent.Callable;
  4. import org.graalvm.polyglot.*;
  5. publicclass access_java_from_js {
  6. // BEGIN-SNIPPET
  7. publicstaticclassMyClass{
  8. publicint id =42;
  9. publicString text ="42";
  10. publicint[] arr =newint[]{1,42,3};
  11. publicCallable<Integer> ret42 =()->42;
  12. }
  13. publicstaticvoid main(String[] args){
  14. try(Context context =Context.newBuilder()
  15. .allowAllAccess(true)
  16. .build()){
  17. context.getBindings("js").putMember("javaObj",newMyClass());
  18. boolean valid = context.eval("js",
  19. " javaObj.id == 42"+
  20. " && javaObj.text == '42'"+
  21. " && javaObj.arr[1] == 42"+
  22. " && javaObj.ret42() == 42")
  23. .asBoolean();
  24. assert valid ==true;
  25. }
  26. }
  27. // END-SNIPPET
  28. }

Embedding Reference - 图14

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import java.util.concurrent.Callable;
  4. import org.graalvm.polyglot.*;
  5. publicclass access_java_from_R {
  6. // BEGIN-SNIPPET
  7. publicstaticclassMyClass{
  8. publicint id =42;
  9. publicString text ="42";
  10. publicint[] arr =newint[]{1,42,3};
  11. publicCallable<Integer> ret42 =()->42;
  12. }
  13. publicstaticvoid main(String[] args){
  14. try(Context context =Context.newBuilder()
  15. .allowAllAccess(true)
  16. .build()){
  17. context.getBindings("R").putMember("javaObj",newMyClass());
  18. boolean valid = context.eval("R",
  19. " javaObj$id == 42"+
  20. " && javaObj$text == '42'"+
  21. " && javaObj$arr[[2]] == 42"+
  22. " && javaObj$ret42() == 42")
  23. .asBoolean();
  24. assert valid ==true;
  25. }
  26. }
  27. // END-SNIPPET
  28. }

Embedding Reference - 图15

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import java.util.concurrent.Callable;
  4. import org.graalvm.polyglot.*;
  5. publicclass access_java_from_ruby {
  6. // BEGIN-SNIPPET
  7. publicstaticclassMyClass{
  8. publicint id =42;
  9. publicString text ="42";
  10. publicint[] arr =newint[]{1,42,3};
  11. publicCallable<Integer> ret42 =()->42;
  12. }
  13. publicstaticvoid main(String[] args){
  14. try(Context context =Context.newBuilder()
  15. .allowAllAccess(true)
  16. .build()){
  17. context.getPolyglotBindings().putMember("javaObj",newMyClass());
  18. boolean valid = context.eval("ruby",
  19. "javaObj = Polyglot.import('javaObj')\n"+
  20. " javaObj[:id] == 42"+
  21. " && javaObj[:text] == '42'"+
  22. " && javaObj[:arr][1] == 42"+
  23. " && javaObj[:ret42].call == 42")
  24. .asBoolean();
  25. assert valid ==true;
  26. }
  27. }
  28. // END-SNIPPET
  29. }

Embedding Reference - 图16

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import java.util.concurrent.Callable;
  4. import org.graalvm.polyglot.*;
  5. publicclass access_java_from_python {
  6. // BEGIN-SNIPPET
  7. publicstaticclassMyClass{
  8. publicint id =42;
  9. publicString text ="42";
  10. publicint[] arr =newint[]{1,42,3};
  11. publicCallable<Integer> ret42 =()->42;
  12. }
  13. publicstaticvoid main(String[] args){
  14. try(Context context =Context.newBuilder()
  15. .allowAllAccess(true)
  16. .build()){
  17. context.getPolyglotBindings().putMember("javaObj",newMyClass());
  18. boolean valid = context.eval("python",
  19. "import polyglot \n"+
  20. "javaObj = polyglot.import_value('javaObj')\n"+
  21. "javaObj['id'] == 42"+
  22. " and javaObj['text'] == '42'"+
  23. " and javaObj['arr'][1] == 42"+
  24. " and javaObj['ret42'].call() == 42")
  25. .asBoolean();
  26. assert valid ==true;
  27. }
  28. }
  29. // END-SNIPPET
  30. }

In this code:

  • The Java class MyClass has four public fields id, text, arr andret42. The fields are initialized with 42, "42", new int[]{1, 42, 3} andlambda () -> 42 that always returns an int value of 42.
  • The Java class MyClass is instantiated and exported with the name javaObjinto the polyglot scope, which allows the host and guest languages to exchangesymbols.
  • A guest language script is evaluated that imports the javaObj symbol andassigns it to the local variable which is also named javaObj. To avoidconflicts with variables, every value in the polyglot scope must be explicitlyimported and exported in the top-most scope of the language.
  • The next two lines verify the contents of the Java object by comparing itto the number 42 and the string '42'.
  • The third verification reads from the second array position and compares itto the number 42. Whether arrays are accessed using 0-based or 1-based indicesdepends on the guest language. Independently of the language, the Java arraystored in field arr is always accessed using translated 0-based indices. Forexample, in the R language, arrays are 1-based so the second array element isaccessible using index 2. In the JavaScript and Ruby languages, the secondarray element is at index 1. In all language examples, the Java array is readfrom using the same index 1.
  • The last line invokes the Java lambda that is contained in the field ret42and compares the result to the number value 42.
  • After the guest language script executes, validation takes place to ensurethat the script returns a boolean value of true as a result.

Lookup Java Types from Guest Languages

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

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

Embedding Reference - 图17

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. publicclass lookup_java_from_js {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. java.math.BigDecimal v = context.eval("js",
  11. "var BigDecimal = Java.type('java.math.BigDecimal');"+
  12. "BigDecimal.valueOf(10).pow(20)")
  13. .asHostObject();
  14. assert v.toString().equals("100000000000000000000");
  15. }
  16. // END-SNIPPET
  17. }
  18. }

Embedding Reference - 图18

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. publicclass lookup_java_from_R {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. java.math.BigDecimal v = context.eval("R",
  11. "BigDecimal = java.type('java.math.BigDecimal');\n"+
  12. "BigDecimal$valueOf(10)$pow(20)")
  13. .asHostObject();
  14. assert v.toString().equals("100000000000000000000");
  15. }
  16. // END-SNIPPET
  17. }
  18. }

Embedding Reference - 图19

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. publicclass lookup_java_from_ruby {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. java.math.BigDecimal v = context.eval("ruby",
  11. "BigDecimal = Java.type('java.math.BigDecimal')\n"+
  12. "BigDecimal.valueOf(10).pow(20)")
  13. .asHostObject();
  14. assert v.toString().equals("100000000000000000000");
  15. }
  16. // END-SNIPPET
  17. }
  18. }

Embedding Reference - 图20

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. publicclass lookup_java_from_python {
  5. publicstaticvoid main(String[] args){
  6. // BEGIN-SNIPPET
  7. try(Context context =Context.newBuilder()
  8. .allowAllAccess(true)
  9. .build()){
  10. java.math.BigDecimal v = context.eval("python",
  11. "import java\n"+
  12. "BigDecimal = java.type('java.math.BigDecimal')\n"+
  13. "BigDecimal.valueOf(10).pow(20)")
  14. .asHostObject();
  15. assert v.toString().equals("100000000000000000000");
  16. }
  17. // END-SNIPPET
  18. }
  19. }

In this code:

  • A new context is created with all access enabled (allowAllAccess(true)).
  • A new context is created with all access enabled (allowAllAccess(true)).
  • A guest language script is evaluated.
  • The script looks the Java type java.math.BigDecimal up and stores it in a variable named BigDecimal.
  • The static method BigDecimal.valueOf(long) is invoked to create newBigDecimals with value 10. In addition to looking up static Java methods, itis also possible to directly instantiate the returned Java type., e.g. inJavaScript using the new keyword.
  • The new decimal is used to invoke the pow instance method with 20 which calculates 10^20.
  • The result of the script is converted to a host object by calling asHostObject(). The return value is automatically cast to the BigDecimal type.
  • The result decimal string is asserted to equal to "100000000000000000000".

Computed Arrays Using Polyglot Proxies

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

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

Note: The Polyglot API supports polyglot proxies either on the JVM orin a native image executable.

Embedding Reference - 图21

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.*;
  4. import org.graalvm.polyglot.proxy.*;
  5. publicclass proxy_js {
  6. // BEGIN-SNIPPET
  7. staticclassComputedArrayimplementsProxyArray{
  8. publicObjectget(long index){
  9. return index *2;
  10. }
  11. publicvoidset(long index,Value value){
  12. thrownewUnsupportedOperationException();
  13. }
  14. publiclong getSize(){
  15. returnLong.MAX_VALUE;
  16. }
  17. }
  18. publicstaticvoid main(String[] args){
  19. try(Context context =Context.create()){
  20. ComputedArray arr =newComputedArray();
  21. context.getBindings("js").putMember("arr", arr);
  22. long result = context.eval("js",
  23. "arr[1] + arr[1000000000]")
  24. .asLong();
  25. assert result ==2000000002L;
  26. }
  27. }
  28. // END-SNIPPET
  29. }

Embedding Reference - 图22

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. import org.graalvm.polyglot.Value;
  5. import org.graalvm.polyglot.proxy.ProxyArray;
  6. publicclass proxy_R {
  7. // BEGIN-SNIPPET
  8. staticclassComputedArrayimplementsProxyArray{
  9. publicObjectget(long index){
  10. return index *2;
  11. }
  12. publicvoidset(long index,Value value){
  13. thrownewUnsupportedOperationException();
  14. }
  15. publiclong getSize(){
  16. returnLong.MAX_VALUE;
  17. }
  18. }
  19. publicstaticvoid main(String[] args){
  20. try(Context context =Context.newBuilder()
  21. .allowAllAccess(true)
  22. .build()){
  23. ComputedArray arr =newComputedArray();
  24. context.getPolyglotBindings().putMember("arr", arr);
  25. long result = context.eval("R",
  26. "arr <- import('arr');"+
  27. "arr[2] + arr[1000000001]")
  28. .asLong();
  29. assert result ==2000000002L;
  30. }
  31. }
  32. // END-SNIPPET
  33. }

Embedding Reference - 图23

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. import org.graalvm.polyglot.Value;
  5. import org.graalvm.polyglot.proxy.ProxyArray;
  6. publicclass proxy_ruby {
  7. // BEGIN-SNIPPET
  8. staticclassComputedArrayimplementsProxyArray{
  9. publicObjectget(long index){
  10. return index *2;
  11. }
  12. publicvoidset(long index,Value value){
  13. thrownewUnsupportedOperationException();
  14. }
  15. publiclong getSize(){
  16. returnLong.MAX_VALUE;
  17. }
  18. }
  19. publicstaticvoid main(String[] args){
  20. try(Context context =Context.newBuilder()
  21. .allowAllAccess(true)
  22. .build()){
  23. ComputedArray arr =newComputedArray();
  24. context.getPolyglotBindings().putMember("arr", arr);
  25. long result = context.eval("ruby",
  26. "arr = Polyglot.import('arr') \n"+
  27. "arr[1] + arr[1000000000]")
  28. .asLong();
  29. assert result ==2000000002L;
  30. }
  31. }
  32. // END-SNIPPET
  33. }

Embedding Reference - 图24

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. import org.graalvm.polyglot.Value;
  5. import org.graalvm.polyglot.proxy.ProxyArray;
  6. publicclass proxy_python {
  7. // BEGIN-SNIPPET
  8. staticclassComputedArrayimplementsProxyArray{
  9. publicObjectget(long index){
  10. return index *2;
  11. }
  12. publicvoidset(long index,Value value){
  13. thrownewUnsupportedOperationException();
  14. }
  15. publiclong getSize(){
  16. returnLong.MAX_VALUE;
  17. }
  18. }
  19. publicstaticvoid main(String[] args){
  20. try(Context context =Context.newBuilder()
  21. .allowAllAccess(true)
  22. .build()){
  23. ComputedArray arr =newComputedArray();
  24. context.getPolyglotBindings().putMember("arr", arr);
  25. long result = context.eval("python",
  26. "import polyglot\n"+
  27. "arr = polyglot.import_value('arr') \n"+
  28. "arr[1] + arr[1000000000]")
  29. .asLong();
  30. assert result ==2000000002L;
  31. }
  32. }
  33. // END-SNIPPET
  34. }

In this code:

  • The Java class ComputedArray implements the proxy interface ProxyArray sothat guest languages treat instances of the Java class like arrays.
  • ComputedArray array overrides the method get and computes the valueusing an arithmetic expression.
  • The array proxy does not support write access. For this reason, it throwsan UnsupportedOperationException in the implementation of set.
  • The implementation for getSize returns Long.MAX_VALUE for its length.
  • The main method creates a new polyglot execution context.
  • A new instance of the ComputedArray class is then exported using the name arr.
  • The guest language script imports the arr symbol, which returns theexported proxy.
  • The second element and the 1000000000th element is accessed, summed up, andthen returned. Note that array indices from 1-based languages such as R areconverted to 0-based indices for proxy arrays.
  • The result of the language script is returned as a long value and verified.

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

Access Restrictions

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.

Important: Access restrictions are currently only supported with JavaScript.

Configuring Host Access

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

Embedding Reference - 图25

  1. // COMPILE-CMD: javac {file}
  2. // RUN-CMD: java -ea {file}
  3. import org.graalvm.polyglot.Context;
  4. import org.graalvm.polyglot.HostAccess;
  5. import org.graalvm.polyglot.PolyglotException;
  6. publicclass explicit_access_java_from_js {
  7. static
  8. // BEGIN-SNIPPET
  9. publicclassEmployee{
  10. privatefinalString name;
  11. Employee(String name){this.name = name;}
  12. @HostAccess.Export
  13. publicString getName(){
  14. return name;
  15. }
  16. }//END-SNIPPET
  17. static//BEGIN-SNIPPET
  18. publicclassServices{
  19. @HostAccess.Export
  20. publicEmployee createEmployee(String name){
  21. returnnewEmployee(name);
  22. }
  23. publicvoid exitVM(){
  24. System.exit(1);
  25. }
  26. }
  27. publicstaticvoid main(String[] args){
  28. try(Context context =Context.create()){
  29. Services services =newServices();
  30. context.getBindings("js").putMember("services", services);
  31. String name = context.eval("js",
  32. "let emp = services.createEmployee('John Doe');"+
  33. "emp.getName()").asString();
  34. assert name.equals("John Doe");
  35. try{
  36. context.eval("js","services.exitVM()");
  37. assertfalse;
  38. }catch(PolyglotException e){
  39. assert e.getMessage().endsWith(
  40. "Unknown identifier: exitVM");
  41. }
  42. }
  43. }
  44. // END-SNIPPET
  45. }

In this code:

  • The class Employee is declared with a field name of type String. Access to the getName method is explicitly allowed by annotating the method with @HostAccess.Export.
  • The Services class exposes two methods createEmployee and exitVM. The createEmployee method takes the name of the employee as an argument and creates a new Employee instance. The createEmployee method is annotated with @HostAccess.Export and therefore accessible to the guest application. The exitVM method is not explicitly exported and therefore not accessible.
  • The main method first creates new polyglot context in the default configuration, disallowing host access except for methods annotated with @HostAccess.Export.
  • A new Services instance is created and put into the context as global variable services.
  • The first evaluated script creates a new employee using the services object and returns its name.
  • The returned name is asserted to equal the expected name John Doe.
  • A second script is evaluated that calls the exitVM method on the services object. This fails with a PolyglotException as the exitVM method is not exposed to the guest application.

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

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:

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

Build Native Images from Polyglot Applications

Polyglot embeddings can also be compiled using GraalVM 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) native image option needs to be specified.All examples on this page are also supported when compiled using Native Image Generator – the native-image utility.Currently it is required to set the —initialize-at-build-time option when building with a polyglot language. We plan to lift this restriction in future versions.

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

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

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 image, the reflection configuration file is required.

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

  1. import org.graalvm.polyglot.*;
  2. import org.graalvm.polyglot.proxy.*;
  3. import java.util.concurrent.*;
  4. publicclassAccessJavaFromJS{
  5. publicstaticclassMyClass{
  6. publicint id =42;
  7. publicString text ="42";
  8. publicint[] arr =newint[]{1,42,3};
  9. publicCallable<Integer> ret42 =()->42;
  10. }
  11. publicstaticvoid main(String[] args){
  12. try(Context context =Context.newBuilder()
  13. .allowAllAccess(true)
  14. .build()){
  15. context.getBindings("js").putMember("javaObj",newMyClass());
  16. boolean valid = context.eval("js",
  17. " javaObj.id == 42"+
  18. " && javaObj.text == '42'"+
  19. " && javaObj.arr[1] == 42"+
  20. " && javaObj.ret42() == 42")
  21. .asBoolean();
  22. System.out.println("Valid "+ valid);
  23. }
  24. }
  25. }

Next, copy the following code into reflect.json:

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

Now the a native image can be created that supports host access:

  1. $ javac AccessJavaFromJS.java
  2. $ native-image --language:js --initialize-at-build-time -H:ReflectionConfigurationFiles=reflect.json -cp .AccessJavaFromJS
  3. $ ./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 enabling code caching across multiple contexts.Code caching allows compiled code to be reused and allows sources to be parsed only once.Often, code caching can reduce memory consumption and warmup time of the application.

By default, code is cached within a single context instance only.In order 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.

  1. import org.graalvm.polyglot.*;
  2. publicclassMain{
  3. publicstaticvoid main(String[] args){
  4. try(Engine engine =Engine.create()){
  5. Source source =Source.create("js","21 + 21");
  6. try(Context context =Context.newBuilder()
  7. .engine(engine)
  8. .build()){
  9. int v = context.eval(source).asInt();
  10. assert v ==42;
  11. }
  12. try(Context context =Context.newBuilder()
  13. .engine(engine)
  14. .build()){
  15. int v = context.eval(source).asInt();
  16. assert v ==42;
  17. }
  18. }
  19. }
  20. }

In this code:


  • import org.graalvm.polyglot.* imports the base API for the Polyglot API.
  • Engine.create() creates a new engine instance with the default configuration.
  • Source.create() creates a source object for the expression “21 + 21”with “js” language, which is the language identifier for JavaScript.
  • Context.newBuilder().engine(engine).build() builds a new context withan explicit engine assigned to it. All contexts associated with an engine share the code.
  • context.eval(source).asInt() evaluates the source and returns the result as Value instance.

Build a Shell for Many Languages

With just a few lines of code, the GraalVM Polyglot API lets you buildapplications that integrate with any guest language that is available bydefault with GraalVM.

This shell implementation is agnostic to any particular guest language.

  1. BufferedReader input =newBufferedReader(newInputStreamReader(System.in));
  2. PrintStream output =System.out;
  3. Context context =Context.newBuilder().allowAllAccess(true).build();
  4. Set<String> languages = context.getEngine().getLanguages().keySet();
  5. output.println("Shell for "+ languages +":");
  6. String language = languages.iterator().next();
  7. for(;;){
  8. try{
  9. output.print(language +"> ");
  10. String line = input.readLine();
  11. if(line ==null){
  12. break;
  13. }elseif(languages.contains(line)){
  14. language = line;
  15. continue;
  16. }
  17. Source source =Source.newBuilder(language, line,"<shell>")
  18. .interactive(true).buildLiteral();
  19. context.eval(source);
  20. }catch(PolyglotException t){
  21. if(t.isExit()){
  22. break;
  23. }
  24. t.printStackTrace();
  25. }
  26. }

Step through with Execution Listeners

GraalVM Polyglot API allows 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 listenersare designed as simple API for polyglot embedders and may become handy in, e.g., single-stepping through the program.

  1. import org.graalvm.polyglot.*;
  2. import org.graalvm.polyglot.management.*;
  3. publicclassExecutionListenerTest{
  4. publicstaticvoid main(String[] args){
  5. try(Context context =Context.create("js")){
  6. ExecutionListener listener =ExecutionListener.newBuilder()
  7. .onEnter((e)->System.out.println(
  8. e.getLocation().getCharacters()))
  9. .statements(true)
  10. .attach(context.getEngine());
  11. context.eval("js","for (var i = 0; i < 2; i++);");
  12. listener.close();
  13. }
  14. }
  15. }

In this code:

  • The Context.create() call creates a new context for the guest language.
  • Create an execution listener builder by invoking ExecutionListeners.newBuilder().
  • Set onEnter event to notify when element’s execution is entered and consumed. At least one event consumer and one filtered source element needs to be enabled.
  • To complete the listener attachment, attach() needs to be invoked.
  • The statements(true) filters execution listeners to statements only.
  • The context.eval() call evaluates a specified snippet of guest language code.
  • The listener.close() closes a listener earlier, however execution listeners are automatically closed with the engine.