Как я могу определить, какой движок JavaScript, Rhino или Nashorn выполняет мой код?

Есть несколько вопросов, как определить движок JavaScript в браузере. Я должен написать код JavaScript, который должен работать на носорога и nashorn.

Как я могу определить, работает ли мой код на Rhino или Nashorn? Есть ли типичные функции, переменные, константы, где вы можете определить движок?

3 ответа

Решение

Глядя на руководство по миграции с носорога в Нашорн, я вижу несколько возможных путей.

Если вы не используете скрипт совместимости с Rhino, это можно сделать следующим образом:

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

...поскольку importClass определяется для Rhino, но не для Nashorn (если вы не включите скрипт совместимости).

Я думаю Java.type является специфичным для Nashorn, так что:

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

Вы можете проверить наличие исключений:

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;
}

... так как руководство по миграции говорит, что будет true для Нашорна, но false для носорога. Это включает в себя бросание исключения, что вызывает сожаление.

С опцией --no-java "Java" не определяется как объект в Nashorn. Лучше всего было бы проверить то, что всегда доступно в Nashorn. Что-то вроде переменной DIR или FILE является хорошим кандидатом. Всегда в Нашорне.

jjs> typeof DIR

строка

Если вы используете API javax.script (а не jjs), вы можете получить имя движка и проверить также:

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());
   }
}

С Nashorn вы увидите "Oracle Nashorn" в качестве имени движка.

Я думаю, что самый надежный и простой способ сделать это — положиться на тот факт, что и Rhino, и Nashorn создают контексты выполнения для каждого потока для выполнения своей работы.

Доступ к Nashorn может быть немного сложнее из-за изменений модуля (JPMS), внесенных в Java 9, что затрудняет для скриптов доступ к некоторым задействованным классам.

Вот некоторый код, который я использую для этого, в кодовой базе SLIME (которая аналогично поддерживает оба движка и JDK 8/11 на момент написания этой статьи). В каждом случае двигатель обеспечиваетrunningметод, который возвращает специфичный для движка контекст, если мы работаем внутри этого движка (поэтому для простого обнаружения его можно обернуть внутри вызоваBooleanили что-то другое). Проверено, но так как копипаст вырван из контекста, содержит немного лишней информации, за что извиняюсь:

Rhino (в этой реализации вы должны вызватьisPresent()и убедитесь, что он возвращаетсяtrueпрежде чем звонитьrunning(), хотя вы могли бы улучшить это):

              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);

Нашхорн (полагается наPackages, предоставляемый уровнем совместимости Mozilla, в основном для улучшения статической проверки типов, но его можно легко переписать для использованияJavaэквиваленты для Насхорна):

              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;
                        }
                    }
                }
            }
        )();

Конечно, реализация Nashorn использует закрытые API. Но я все еще думаю, что это лучше и проще, чем полагаться на то, что по сути является побочными эффектами (наличие различных API-интерфейсов, которые может предоставить тот или иной движок). Эти методы потенциально уязвимы, если один из движков стремится улучшить совместимость с другим.

Другие вопросы по тегам