Nashorn access non-static Java method

China☆狼群 提交于 2019-12-07 20:11:08

问题


In Java 7 (1.7), I could access a Java method from JavaScript by running this:

ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("importClass(net.apocalypselabs.symat.Functions);");
jse.eval("SyMAT_Functions = new net.apocalypselabs.symat.Functions();");

String input = "notify(\"Foo\");"; // This is user input

jse.eval("with(SyMAT_Functions){ "+input+" }");

Which would run the notify() function from the Functions java class:

public class Functions {
    private Object someObjectThatCannotBeStatic;
    public void notify(Object message) {
        JOptionPane.showMessageDialog(null, message.toString());
    }
    /* Lots more functions in here, several working with the same non-static variable */
}

How do I access the Functions class in Java 1.8 with the Nashorn engine? My goal is to run different code for the first snippet if the user has Java 1.8, while still allowing people with 1.7 to use the app.

I've tried http://www.doublecloud.org/2014/04/java-8-new-features-nashorn-javascript-engine/ , https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html , and How to instantiate a Java class in JavaScript using Nashorn? without luck. None of them seem to allow me the same thing as Java 1.7 did, instead assuming I only want to access static functions and objects.

The most common error I get:

I start with...

ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("var SyMAT_Functions;with (new JavaImporter(Packages.net.apocalypselabs.symat)) {"
                    + "SyMAT_Functions = new Functions();}");

...then...

jse.eval("with(SyMAT_Functions){ "+input+" }");

...spits out...

TypeError: Cannot apply "with" to non script object in <eval> at line number 1

回答1:


I was able to reproduce. First of all, Nashorn doesn't try to make it difficult to use Java objects (non-static or otherwise) in general. I have used it in other projects and not had any major issue converting from Rhino in Java 7 beyond what is covered in the migration guide. However, the issue here appears to deal with the use of the with statement which is "not recommended" and is even disallowed in strict mode of ECMAScript 5.1, both according to MDN.

Meanwhile, I found a thread on the Nashorn-dev mailing list discussing a similar case. The relevant part of the response was:

Nashorn allows only script objects (i.e., objects created by a JS constructor or JS object literal expression) as scope expression for "with" statement. Arbitrary objects . . . can not be used as 'scope' expression for 'with'.

In jdk9, support has been added to support script objects mirror other script engines or other globals (which are instances of ScriptObjectMirror).

It's not the most elegant solution but, without using JDK 9, I was able to get your intended use of with to function by writing a proxy object inside the Javascript to mirror the public API of the Java class:

package com.example;

import javax.script.*;

public class StackOverflow27120811
{
    public static void main(String... args) throws Exception {
        ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
        jse.eval(
            "var real = new Packages.com.example.StackOverflow27120811(); " +
            "var proxy = { doSomething: function(str) { return real.doSomething(str); } }; "
        );
        jse.eval("with (proxy) { doSomething(\"hello, world\"); } ");
    }

    public void doSomething(String foo) {
        System.out.println(foo);
    }
}

Attila Szegedi pointed out the non-standard Nashorn Object.bindProperties function. While it can't be expected to work with anything but the Nashorn engine, it does eliminate the complexity of re-declaring all of the public API inside the proxy object. Using this approach, the first jse.eval(...) call can be replaced by:

jse.eval(
    "var real = new Packages.com.example.StackOverflow27120811(); " +
    "var proxy = { }; " +
    "Object.bindProperties(proxy, real); " // Nashorn-only feature
);



回答2:


I decided to compile and bundle the "old" Rhino interpreter with my application instead of using Nashorn.

https://wiki.openjdk.java.net/display/Nashorn/Using+Rhino+JSR-223+engine+with+JDK8



来源:https://stackoverflow.com/questions/27120811/nashorn-access-non-static-java-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!