Я понял это. Проблема, с которой я столкнулся, заключалась в том, что invokeFunction
вызывал NoSuchMethodException
, потому что функции, предоставляемые пользовательским скриптом, не существовали в привязках из области действия движка по умолчанию:
ScriptContext context = new SimpleScriptContext();
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(customScriptSource, context);
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown
Итак, что мне нужно было сделать, это вытащить функцию из контекста по имени и вызвать ее явно так:
JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
function.call(null, args); //call to JSObject#isFunction omitted brevity
Это вызовет функцию, которая существует в вашем только что созданном контексте. Вы также можете вызывать методы для объектов следующим образом:
JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
JSObject method = (JSObject) object.getMember(name);
method.call(object, args);
call
генерирует непроверенное исключение (либо Throwable
, завернутое в RuntimeException
, либо NashornException
, которое было инициализировано информацией о стеке JavaScript), поэтому вам, возможно, придется явно обработать это, если вы хотите предоставить полезную обратную связь.
Таким образом, потоки не могут перешагивать друг через друга, потому что для каждого потока существует отдельный контекст. Я также смог разделить пользовательский код среды выполнения между потоками и убедиться, что изменения состояния изменяемых объектов, предоставляемых пользовательской средой выполнения, были изолированы контекстом.
Для этого я создаю экземпляр CompiledScript
, который содержит скомпилированное представление моей пользовательской библиотеки времени выполнения:
public class Runtime {
private ScriptEngine engine;
private CompiledScript compiledRuntime;
public Runtime() {
engine = new NashornScriptEngineFactory().getScriptEngine("-strict");
String source = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/runtime.js")
).useDelimiter("\\Z").next();
try {
compiledRuntime = ((Compilable) engine).compile(source);
} catch(ScriptException e) {
...
}
}
...
}
Затем, когда мне нужно выполнить скрипт, я оцениваю скомпилированный исходный код, а затем также оцениваю скрипт в этом контексте:
ScriptContext context = new SimpleScriptContext();
context.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
//Exception handling omitted for brevity
//Evaluate the compiled runtime in our new context
compiledRuntime.eval(context);
//Evaluate the source in the same context
engine.eval(source, context);
//Call a function
JSObject jsObject = (JSObject) context.getAttribute(function, ScriptContext.ENGINE_SCOPE);
jsObject.call(null, args);
Я протестировал это с несколькими потоками и смог убедиться, что изменения состояния ограничены контекстами, принадлежащими отдельным потокам. Это связано с тем, что скомпилированное представление выполняется в определенном контексте, а это означает, что экземпляры всего, что оно предоставляет, ограничено этим контекстом.
Один небольшой недостаток здесь заключается в том, что вы можете без необходимости переоценивать определения объектов для объектов, которым не нужно иметь состояние, зависящее от потока. Чтобы обойти это, оцените их непосредственно в движке, что добавит привязки для этих объектов к ENGINE_SCOPE
движка:
public Runtime() {
...
String shared = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/shared.js")
).useDelimiter("\\Z").next();
try {
...
nashorn.eval(shared);
...
} catch(ScriptException e) {
...
}
}
Позже вы можете заполнить специфичный для потока контекст из движка ENGINE_SCOPE
:
context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
Одна вещь, которую вам нужно сделать, это убедиться, что все такие объекты, которые вы выставляете, были заморожены. В противном случае их можно переопределить или добавить свойства.
person
Vivin Paliath
schedule
10.11.2015