Почему ваш код не работает
Глобальная область может использоваться только для простого отображения переменных. Например:
ScriptContext defCtx = engine.getContext();
defCtx.getBindings(ScriptContext.GLOBAL_SCOPE).put("foo", "hello");
Object
находится в области действия движка, и поэтому в глобальной области даже не выполняется поиск каких-либо сопоставлений, связанных с ней (Object.prototype.test
в вашем случае).
Выдержка из документации:
ENGINE_SCOPE контекста по умолчанию - это обернутый экземпляр «глобального» объекта ECMAScript, который является «this» в выражениях сценария верхнего уровня. Таким образом, вы можете получить доступ к объектам верхнего уровня ECMAScript, таким как "Object", "Math", "RegExp", "undefined" из этого объекта области. Глобальный объект области Nashorn представлен внутренним классом реализации с именем jdk.nashorn.internal.objects.Global. Экземпляр этого класса обернут как экземпляр jdk.nashorn.api.scripting.ScriptObjectMirror. Класс ScriptObjectMirror реализует интерфейс javax.script.Bindings. Обратите внимание, что привязки GLOBAL_SCOPE контекста и глобальный объект nashorn различны. Глобальный объект Nashorn связан с ENGINE_SCOPE, а не с GLOBAL_SCOPE. Объект GLOBAL_SCOPE контекста сценария по умолчанию является экземпляром javax.script.SimpleBindings. Пользователь может заполнить его парами имя-значение из кода java.
https://wiki.openjdk.java.net/display/Nashorn/Nashorn+jsr223+engine+notes
Решения
- Продолжайте использовать объем двигателя
- Используйте
--global-per-engine option
, указав -Dnashorn.args=--global-per-engine
в вашей Java командной строке. Затем Nashorn будет использовать один экземпляр глобального объекта для всех оценок сценариев независимо от переданного ScriptContext. - Использовать полноценный ScriptContext вместо привязок:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
SimpleScriptContext context = new SimpleScriptContext();
engine.eval("Object.prototype.test = function(arg){print(arg);}", context);
engine.eval("var x = {}; x.test('hello');", context);
Как запустить несколько сценариев, каждый раз с загруженными библиотеками, но ничего не осталось от предыдущих выполнений
Каждый раз, когда вам нужен новый контекст с библиотеками, просто создайте его:
public static void main(String[] args) throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
SimpleScriptContext context1 = createContextWithLibraries(engine);
//works as expected, printing "hello"
engine.eval("var x = {}; x.test('hello'); var y = 'world';", context1);
SimpleScriptContext context2 = createContextWithLibraries(engine);
//works as expected, printing "hello"
engine.eval("var x = {}; x.test('hello');", context2);
//works as expected, printing "world"
engine.eval("print(y);", context1);
//fails with exception since there is no "y" variable in context2
engine.eval("print(y);", context2);
}
private static SimpleScriptContext createContextWithLibraries(ScriptEngine engine) throws ScriptException {
SimpleScriptContext context = new SimpleScriptContext();
engine.eval("Object.prototype.test = function(arg){print(arg);}", context);
return context;
}