javagraalvmpolyglot

Passing classes and methods as Value with GraalVM Polyglot


I'm trying to use the Polyglot API of GraalVM, and an example I see in the documentation does not work.

The documentation of Context.asValue states:

When Java host objects are passed to guest languages, their public methods and fields are provided as members.

And a code example is provided:

class JavaRecord {
  public int x = 42;
  public double y = 42.0;
  public String name() {
    return "foo";
  }
}
Context context = Context.create();
Value record = context.asValue(new JavaRecord());
assert record.getMember("x").asInt() == 42;
...

However this code doesn't work for me. record.getMember("x") returns null. record.getMemberKeys also returns just an empty list ([]).

The same thing happens for another example, this time passing a method to Context.asValue. The documentation states:

And single method interfaces annotated with FunctionalInterface are executable directly.

Illustrating with the example:

Context context = Context.create();
...
assert context.asValue((Supplier<Integer>) () -> 42).execute().asInt() == 42;

But when I run this example myself, I get UnsupportedOperationException: Unsupported operation Value.execute(Object...) ...

I'm using org.graalvm.polyglot:polyglot:24.0.1, the latest version I found on the maven repository. Does anybody know how I can get this working? How can I pass functions and data to guest languages?


Solution

  • What the documentation omits is 1) you need to configure your Context instance: allow the access to the host (==Java):

    Context.newBuilder().allowHostAccess(HostAccess.ALL).build()
    

    and 2) the stuff you're accessing must be public, including the class that declares them. Full example:

    import org.graalvm.polyglot.*;
    
    public class Main {
        public static class JavaRecord {
            public int x = 42;
            public double y = 42.0;
            public String name() {
                return "foo";
            }
        }
    
        public static void main(String[] args) {
            try (Context context = Context.newBuilder()
                    .allowHostAccess(HostAccess.ALL)
                    .build()) {
                Value record = context.asValue(new JavaRecord());
                System.out.println(record.getMember("x").asInt());
            }
        }
    }