Как я могу определить, какой движок 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-интерфейсов, которые может предоставить тот или иной движок). Эти методы потенциально уязвимы, если один из движков стремится улучшить совместимость с другим.