1

I want to use graal js to provide some scripting extension to my application

How can i initialize a new java object on the javascript side?

Context ctx = Context ctx = Context.newBuilder().allowHostAccess(HostAccess.ALL).allowAllAccess(true).build().create();
Value binding = ctx.getBindings("js");
binding.putMember("ArrayList", ArrayList.class);
ctx.eval("js","let list = new ArrayList();list.add(\"1\")");
List list = binding.getMember("list").as(List.class);
assert list.size() == 1;

following code throws exception

Exception in thread "main" TypeError: instantiate on JavaClass[java.util.ArrayList] failed due to: Message not supported.
    at <js> :program(Unnamed:1:13-27)
    at org.graalvm.polyglot.Context.eval(Context.java:371)

Running graalvm-ce-java11 19.3.2

user2749903
  • 1,275
  • 1
  • 10
  • 20

2 Answers2

1

You need to use Java.type.

Here is an example taken from https://www.graalvm.org/docs/reference-manual/polyglot/

var array = new (Java.type("int[]"))(4);
array[2] = 42;
console.log(array[2])

Here is a fully runnable example tested with GraalVM 20.0.0

import org.graalvm.polyglot.*;

class M {
    public static void main(String[] args) {
        try (Context context = Context.newBuilder().allowAllAccess(true).build()) {
            java.util.ArrayList v = context.eval("js",
            "var ArrayList = Java.type('java.util.ArrayList');" +
            "var list = new ArrayList();" + 
            "list.add(1); list").asHostObject();
            System.out.println(v.get(0));
            assert v.get(0).equals(1);
        }
    }
}

Run with

graalvm-ce-java8-20.0.0/bin/javac M.java 
graalvm-ce-java8-20.0.0/bin/java -ea M

To get 1 as output.

BoriS
  • 907
  • 5
  • 13
  • That means I have to opt in for nashorn compact mode? No other way around? If so then how can i initialize polyglot context with these options Calling `Context.newBuilder().option("experimental-options","true").option("js.nashorn-compat", "true").build().create();` throws `java.lang.IllegalArgumentException: Could not find option with name experimental-options.` – user2749903 May 17 '20 at 10:25
  • See my edited comment. I added an example you can run. No nashorn mentioned. – BoriS May 17 '20 at 12:27
0

You could also use the sj4js library. There you can define constructors which seamlessly blend into JS.

// we create a new JS engine and add
// TestClass as a constructor. This constructor is added to globalThis such that it
// can be called as a costructor.
try (JScriptEngine engine = new JScriptEngine(new JsGlobalThis(),"gt")) {
    engine.addConstructor(new TestClass("empty"));
            
    /* call your js code here */
}

Your JS code than looks like this you would expect it.

// we create a new variable from the constructor
var tc = new TestClass("test");

console.log(tc.name)
// test