Search code examples
javanashorn

How to wrap to NativeArray in Nashorn without using classes from "internal" subpackage


I have a Java array, say, Object[], which I need to pass to the JS execution environment, which is ScriptEngine.

I cannot simply put it as a property in the following way:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
Object[] array = {1, 2, 3};
engine.put("prop", array);

because in the JS environment expression Array.isArray(prop) will be evaluated to false, whereas I need it to be true.

jdk.nashorn.internal.objects.NativeArray constructors are closed, which means you cannot instantiate NativeArray explicitly.

One can, however, use jdk.nashorn.internal.objects.Global.instance().wrapAsObject to convert Java array Object[] to NativeArray, and the resultant object would be recognized as a JS array, i.e. Array.isArray would return true for this object.

Although this gives the desired result, using classes from internal package is not a very good idea, and even worse idea if you are using Java 9.

Thus, I am wondering, is there a better way to provide Java object to the JS execution environment assuming I cannot change the JS source so that the object is recognized a true JS array, i.e. Array.isArray returns true?


Solution

  • You can create a native array through javascript, and then convert the corresponding ScriptObjectMirror to a List<Object>, it looks like the list will use the native array as underlying storage:

    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");     
    ScriptObjectMirror jsArray = (ScriptObjectMirror) engine.eval("var arr = []; arr");
    
    @SuppressWarnings("unchecked")
    List<Object> ls = jsArray.to(List.class);
    ls.add(1);
    ls.add(2);
    ls.add(3);
    
    System.out.println(ls); // [1, 2, 3]
    engine.eval("print(arr)"); // 1,2,3
    engine.eval("print(Array.isArray(arr))"); // true
    

    You can then use this list on the Java side.