How can I determine which javascript engine, rhino or nashorn is running my code?
Asked Answered
M

3

8

There are several questions how to determine the javascript engine in browser. I have to write javascript code that has to run on rhino and nashorn.

How can I determine if my code is running on rhino or nashorn? Are there typcial functions, variables, constants where you can determine the engine?

Marduk answered 9/6, 2016 at 6:46 Comment(4)
Interesting. Why do you need the check?Borate
If you're asking about rhino vs nashorn, aren't you just asking about JDK SE 8 vs >8?Provost
@JohnGreen: "...about JDK SE 8 vs >8?" Nashorn was added in JDK8 (not >8). And it's still possible to run Rhino in JDK8 (but you have to add a jar and do it on purpose).Borate
We allow customers to setup there favorite engine. I just want to be shure what is set up, when no one can tell me, what they have done.Marduk
B
1

Looking at the Rhino to Nashorn migration guide, I see several possible ways.

If you're not using the Rhino compatibility script, this would do it:

var usingNashorn = typeof importClass !== "function";

...since importClass is defined for Rhino but not for Nashorn (unless you include the compatibility script).

I think Java.type is Nashorn-specific, so:

var usingNashorn = typeof Java !== "undefined" && Java && typeof Java.type === "function";

You could check for wrapping of exceptions:

var usingNashorn;
try {
    // Anything that will throw an NPE from the Java layer
    java.lang.System.loadLibrary(null);
} catch (e) {
    // false!
    usingNashorn = e instanceof java.lang.NullPointerException;
}

...since the migration guide says that will be true for Nashorn but false for Rhino. It does involve throwing an exception, which is unfortunate.

Borate answered 9/6, 2016 at 7:1 Comment(1)
#2 is my favorite. Checking on Java is realy testing a Nashorn feature. I do not expect, that this will be added to Rhino.Marduk
D
1

With --no-java option, "Java" is not defined as object in Nashorn. Best would be to check something that is available always in Nashorn. Something like DIR or FILE variable is a good candidate. Always there in nashorn.

jjs> typeof DIR

string

If you are using javax.script API (and not jjs), you can get the engine name and check as well:

import javax.script.*;

public class Main {
   public static void main(String[] args) throws Exception {
     ScriptEngineManager m = new ScriptEngineManager();
     ScriptEngine e = m.getEngineByName("nashorn");
     System.out.println(e.getFactory().getEngineName());
   }
}

With Nashorn, you'd see "Oracle Nashorn" as the engine name.

Demisemiquaver answered 10/6, 2016 at 4:17 Comment(2)
We are using the engine embedded in a java application. It is a feature to have full access to classes from application around.Marduk
Okay. In that case, I'd recommend checking the engine name to be "Oracle Nashorn". That name is expected to be stable for Nashorn.Demisemiquaver
A
0

I think the most robust and straightforward way to do this is to rely on the fact that both Rhino and Nashorn create per-thread execution contexts to do their work.

Nashorn's can be a bit more complicated to access because of the module (JPMS) changes introduced in Java 9, which makes it more difficult for scripts to access some of the classes involved.

Here's some code I use for this, in the SLIME codebase (which similarly supports both engines, and JDK 8/11 as of this writing). In each case, the engine provides a running method that returns the engine-specific context if we are running inside that engine (so for simple detection, it could be wrapped inside a call to Boolean or whatever). It is tested but as it's copy-paste out of context, it contains a bit of extraneous information, apologies for that:

Rhino (in this implementation you must call isPresent() and ensure it returns true before calling running(), although you could improve that):

        var rhino = (
            function(global) {
                return {
                    isPresent: function() {
                        return typeof(global.Packages.org.mozilla.javascript.Context.getCurrentContext) == "function";
                    },
                    running: function() {
                        return global.Packages.org.mozilla.javascript.Context.getCurrentContext();
                    }
                }
            }
        )(this);

Nashorn (relies on Packages, provided by the Mozilla compatibility layer, mostly to improve static type-checking, but could easily be rewritten to use the Java equivalents for Nashorn):

        var nashorn = (
            function() {
                //  TODO    A little bit of this logic is duplicated in loader/jrunscript/nashorn.js; we could make this method
                //          available there somehow, perhaps, although it might be tricky getting things organized between
                //          bootstrapping Nashorn in the loader and loading the launcher bootstrap script
                var Context = Packages.jdk.nashorn.internal.runtime.Context;
                var $getContext;
                if (typeof(Context) == "function") {
                    try {
                        //  TODO    is there any way to avoid repeating the class name?
                        $getContext = Packages.java.lang.Class.forName("jdk.nashorn.internal.runtime.Context").getMethod("getContext");
                    } catch (e) {
                        //  do nothing; $getContext will remain undefined
                    }
                }

                var isPresent = function() {
                    if (!new Packages.javax.script.ScriptEngineManager().getEngineByName("nashorn")) {
                        $api.debug("Nashorn not detected via javax.script; removing.");
                        return false;
                    }
                    if (typeof(Context.getContext) != "function" && !$getContext) {
                        $api.debug("jdk.nashorn.internal.runtime.Context.getContext not accessible; removing Nashorn.")
                        return false;
                    }
                    return true;
                }

                return {
                    isPresent: isPresent,
                    running: function() {
                        if ($getContext) {
                            try {
                                return $getContext.invoke(null);
                            } catch (e) {
                                return null;
                            }
                        } else {
                            return null;
                        }
                    }
                }
            }
        )();

Sure, the Nashorn implementation uses non-public APIs. But I still think this is better, and more straightforward, than relying on what are essentially side effects (existence of various cherry-picked APIs that one or the other engine might provide). These techniques are potentially breakable if either engine seeks to improve compatibility with the other.

Adage answered 1/1, 2023 at 5:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.