Search code examples
javascriptjavanashorn

Run Javascript code with export function in Java with Nashorn


Is it possible to run a Javascript code containing export functions in Java with Nashorn? I'm new in Nashorn so I am not sure whether there is restriction for the js code to have. Also how do I pass the arguments from Java to the js code?

The Javascript code looks like this (taken from here):

/** Given two circles (containing a x/y/radius attributes),
returns the intersecting points if possible.
note: doesn't handle cases where there are infinitely many
intersection points (circles are equivalent):, or only one intersection point*/
function circleCircleIntersection(p1, p2) {
    var d = distance(p1, p2),
        r1 = p1.radius,
        r2 = p2.radius;

    // if to far away, or self contained - can't be done
    if ((d >= (r1 + r2)) || (d <= Math.abs(r1 - r2))) {
        return [];
    }

    var a = (r1 * r1 - r2 * r2 + d * d) / (2 * d),
        h = Math.sqrt(r1 * r1 - a * a),
        x0 = p1.x + a * (p2.x - p1.x) / d,
        y0 = p1.y + a * (p2.y - p1.y) / d,
        rx = -(p2.y - p1.y) * (h / d),
        ry = -(p2.x - p1.x) * (h / d);

    return [{x: x0 + rx, y : y0 - ry },
            {x: x0 - rx, y : y0 + ry }];
}

/** Returns the center of a bunch of points */
function getCenter(points) {
    var center = {x: 0, y: 0};
    for (var i =0; i < points.length; ++i ) {
        center.x += points[i].x;
        center.y += points[i].y;
    }
    center.x /= points.length;
    center.y /= points.length;
    return center;
}

As an example I would like to invoke getCenter function in the js by supplying multiple Points using the code:

    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    //      engine.eval("print('Hello World!');");
    engine.eval(new FileReader("circleintersection.js"));

    Invocable invocable = (Invocable) engine;

    Point a = new Point(3,2);
    Point b = new Point(5,3);
    Point c = new Point(1,4);
    Point d = new Point(2,5);
    Point e = new Point(6,6);

    Point[] points = {a,b,c,d,e};

    Point result = (Point) invocable.invokeFunction("getCenter", points);
    System.out.println(result.x);

But it gave me error like

Exception in thread "main" java.lang.ClassCastException: jdk.nashorn.api.scripting.ScriptObjectMirror cannot be cast to Point

How can I get the result from the js code?


Solution

  • Nashorn does not fully support ECMA6 features and export is one of them and not supported.

    References:

    ECMA 6 support in Nashorn

    http://openjdk.java.net/jeps/292

    It started to support some from JDK8 itself, and some in JDK 9. But I couldn't find evidence of it supporting export.

    Also how do I pass the arguments from Java to the js code? If you have the following function

    var fun1 = function(name) {
        print('Hi there from Javascript, ' + name);
        return "greetings from javascript";
    };
    
    var fun2 = function (object) {
        print("JS Class Definition: " + Object.prototype.toString.call(object));
    };
    

    Then, you can do,

    ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
    engine.eval(new FileReader("script.js"));
    
    Invocable invocable = (Invocable) engine;
    
    Object result = invocable.invokeFunction("fun1", "Peter Parker");
    System.out.println(result);
    System.out.println(result.getClass());
    
    // Hi there from Javascript, Peter Parker
    // greetings from javascript
    // class java.lang.String
    

    This was taken from https://winterbe.com/posts/2014/04/05/java8-nashorn-tutorial/