无缝传递Nashorn的数组和列表

时间:2015-06-01 10:57:02

标签: java arrays nashorn

我知道你可以在Nashorn中使用Java数组,并且有很多如何做到这一点的例子。标准方法的问题在于它使javascript代码明确地意识到它的运行时环境。目前我有一个使用Rhino的解决方案,它可以在Java类型和Native javascript类型之间无缝转换。

对于Rhino,我通过实施org.mozilla.javascript.ContextFactoryorg.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)

1 个答案:

答案 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 ...
    }