无缝传递Nashorn的数组和列表 [英] Seamlessly pass Arrays and Lists to and from Nashorn

查看:138
本文介绍了无缝传递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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆