无缝传递Nashorn的数组和列表 [英] Seamlessly pass Arrays and Lists to and from Nashorn
问题描述
我知道您可以在Nashorn中使用Java数组,并且有很多示例说明如何执行此操作。标准方法的问题在于它使javascript代码明确地意识到它的运行时环境。目前我有一个使用Rhino的解决方案,它可以在Java类型和Native javascript类型之间无缝转换。
I know you can work with Java arrays in Nashorn and there are plenty of examples of how to do this. The problem for me with the standard approach is that it makes the javascript code explicitly aware of it's runtime environment. Currently I have a solution that makes use of Rhino and it seamlessly converts between Java type and Native javascript types.
对于Rhino,我通过实现来实现这一点。 org.mozilla.javascript.ContextFactory
和 org.mozilla.javascript.WrapFActory
并设置 WrapFactory
当调用 makeContext
时,在上下文
上。这个WrapFactory实现负责在Java数组和Lists以及Native javascript数组和列表之间进行转换。它还提到我必须从JDK获取Rhino源代码以使这种方法起作用。
For Rhino I accomplished this by implementing org.mozilla.javascript.ContextFactory
and org.mozilla.javascript.WrapFActory
and setting WrapFactory
on the Context
when makeContext
is called. This WrapFactory implementation takes care of converting between Java arrays and Lists and Native javascript arrays and lists. It's also wroth mentioning that I had to get the Rhino source code from the JDK to get this approach to work.
我需要为Nashorn找到类似的解决方案。这是我想要完成的一个例子。
I need to find a similar solution for Nashorn. Here is an example of what I am trying to accomplish.
public static void main(String args[]) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine();
try {
engine.eval("function print_array(arr) { print(arr); }");
engine.eval("function print_native() { print_array([1, 2, 3, 4]); }");
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("print_array", new int[]{1, 2, 3, 4});
invocable.invokeFunction("print_array", Arrays.asList(1, 2, 3, 4));
invocable.invokeFunction("print_native");
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}
此代码的输出为
[I @ 169e6180
[I@169e6180
[1,2,3,4]
[1, 2, 3, 4]
1,2,3,4
我正在寻找一种实现ScriptObjectMirror的方法,假设这甚至是正确的,那将使这三个 invokeFunction
调用的输出相同。
I am looking for a way to implement a ScriptObjectMirror, assuming that is even correct, that would make the output of those three invokeFunction
calls be the same.
I尝试在 ScriptUtils
上使用 wrap
函数,但结果仍然是错误的
I have tried using wrap
function on ScriptUtils
, but still the result is wrong
更新
我尝试创建类型为 Invocable
并在 InvocationHandler
中进行转换。要使用Nashorn创建NativeArray,您似乎应该使用 jdk.nashorn.internal.objects.Global.allocate
,但这总是会引发异常。
I tried to create a dynamic proxy of type Invocable
and do conversions in the InvocationHandler
. To create a NativeArray with Nashorn it seems you should use jdk.nashorn.internal.objects.Global.allocate
, but this always raises an exception.
Global.allocate(new int[] {1, 2, 3, 4})
提高
Exception in thread "main" java.lang.NullPointerException
at jdk.nashorn.internal.objects.Global.instance(Global.java:491)
at jdk.nashorn.internal.objects.NativeArray.<init>(NativeArray.java:141)
at jdk.nashorn.internal.objects.Global.allocate(Global.java:1584)
推荐答案
我认为你必须走下一个实现一个AbstractJSObject的艰难道路。我认为像getMember这样的很多函数都可以通过Refelction来完成。但你会怎么做呢?有人认为它是一个JS数组并尝试扩展原型?你也想处理这个吗?在这种情况下,我会在一个像包装类这样的列表中实现一个JS Array作为属性,并将所有set / add委托给一个更新JS对象的JS函数。
I think you have to go the hard road down an implement a AbstractJSObject. I think a lot of Functions like getMember can be done via Refelction. But what would you do is somebody thinking it is a JS Array and try to extend the Prototype? Do you want to handle this too? In that case I would implement a JS Array as property in a list like wrapper class and delegate all set/add to a JS function updating the JS object.
解决方案1:
public static void main(String args[]) {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine();
try {
engine.eval("function print_array(arr) { print(arr); for(var i=0; i<arr.length; i++) {print(arr[i]);}}");
engine.eval("function print_native() { print_array([1, 2, 3, 4]); }");
engine.eval("function get_native() { return [1, 2, 3, 4]; }");
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("print_array", new int[]{1, 2, 3, 4});
invocable.invokeFunction("print_array", Arrays.asList(1, 2, 3, 4));
invocable.invokeFunction("print_array", new Foo());
invocable.invokeFunction("print_native");
ScriptObjectMirror a = (ScriptObjectMirror) invocable.invokeFunction("get_native");
System.out.println(invocable.invokeFunction("get_native"));
} catch (Exception e) {
e.printStackTrace();
}
}
static class Foo extends AbstractJSObject {
Map<Integer, Object> arrayValues = new HashMap<>();
public Foo() {
arrayValues.put(0, 1);
arrayValues.put(1, 2);
arrayValues.put(2, 3);
}
@Override
public Object call(Object thiz, Object... args) {
System.out.println("call");
return super.call(thiz, args);
}
@Override
public Object newObject(Object... args) {
System.out.println("new Object");
return super.newObject(args);
}
@Override
public Object eval(String s) {
System.out.println("eval");
return super.eval(s);
}
@Override
public Object getMember(String name) {
System.out.println("getMember " + name);
return name.equals("length") ? arrayValues.size() : arrayValues.get(Integer.valueOf(name));
}
@Override
public Object getSlot(int index) {
//System.out.println("getSlot");
return arrayValues.get(index);
}
@Override
public boolean hasMember(String name) {
System.out.println("hasMember");
return super.hasMember(name);
}
@Override
public boolean hasSlot(int slot) {
System.out.println("hasSlot");
return super.hasSlot(slot);
}
@Override
public void removeMember(String name) {
System.out.println("removeMember");
super.removeMember(name);
}
@Override
public void setMember(String name, Object value) {
System.out.println("setMember");
super.setMember(name, value);
}
@Override
public void setSlot(int index, Object value) {
System.out.println("setSlot");
super.setSlot(index, value);
}
@Override
public Set<String> keySet() {
System.out.println("keySet");
return arrayValues.keySet().stream().map(k -> "" + k).collect(Collectors.toSet());
}
@Override
public Collection<Object> values() {
System.out.println("values");
return arrayValues.values();
}
@Override
public boolean isInstance(Object instance) {
System.out.println("isInstance");
return super.isInstance(instance);
}
@Override
public boolean isInstanceOf(Object clazz) {
System.out.println("isINstanceOf");
return super.isInstanceOf(clazz);
}
@Override
public String getClassName() {
System.out.println("getClassName");
return super.getClassName();
}
@Override
public boolean isFunction() {
return false;
}
@Override
public boolean isStrictFunction() {
return false;
}
@Override
public double toNumber() {
return super.toNumber();
}
@Override
public boolean isArray() {
return true;
}
@Override
public String toString() {
return arrayValues.values().toString();
}
}
解决方案2将是(伪代码):
Solution 2 would be (in pseudo code):
static class FooList implements List {
final ScriptObjectMirror wrapped;
public FooList(ScriptObjectMirror wrapped) {
this.wrapped = wrapped;
}
@Override
public int size() {
return engine.eval("get length of wrapped JS object");
}
... and so on ...
}
这篇关于无缝传递Nashorn的数组和列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!