我知道你可以在Nashorn中使用Java数组,并且有很多如何做到这一点的例子。标准方法的问题在于它使javascript代码明确地意识到它的运行时环境。目前我有一个使用Rhino的解决方案,它可以在Java类型和Native javascript类型之间无缝转换。
对于Rhino,我通过实施org.mozilla.javascript.ContextFactory
和org.mozilla.javascript.WrapFActory
并在WrapFactory
调用Context
时设置makeContext
来实现此目的。这个WrapFactory实现负责在Java数组和Lists以及Native javascript数组和列表之间进行转换。它还提到我必须从JDK获取Rhino源代码才能使这种方法起作用。
我需要为Nashorn找到类似的解决方案。这是我想要完成的一个例子。
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
[1,2,3,4]
1,2,3,4
我正在寻找一种实现ScriptObjectMirror的方法,假设它甚至是正确的,这将使这三个invokeFunction
调用的输出相同。
我尝试在wrap
上使用ScriptUtils
功能,但结果仍然错误
更新
我尝试创建Invocable
类型的动态代理,并在InvocationHandler
中进行转换。要使用Nashorn创建NativeArray,您似乎应该使用jdk.nashorn.internal.objects.Global.allocate
,但这总是会引发异常。
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)
答案 0 :(得分:1)
我认为你必须走下一个实现一个AbstractJSObject的艰难道路。我认为像getMember这样的很多函数都可以通过Refelction来完成。但你会怎么做呢?有人认为它是一个JS数组并尝试扩展原型?你也想处理这个吗?在这种情况下,我会在一个像包装类的列表中实现一个JS Array作为属性,并将所有set / add委托给一个更新JS对象的JS函数。
解决方案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将是(伪代码):
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 ...
}