Ich habe das herausgefunden. Das Problem, das ich in lief war, dass invokeFunction
ein NoSuchMethodException
werfen würde, weil die von der benutzerdefinierten Skript ausgesetzten Funktionen nicht von dem Standardbereich des Motors in den Bindungen vorhanden waren:
ScriptContext context = new SimpleScriptContext();
context.setBindings(nashorn.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(customScriptSource, context);
((Invocable) engine).invokeFunction(name, args); //<- NoSuchMethodException thrown
Also, was ich aus zu tun hatte, wurde zu ziehen die Funktion aus dem Kontext namentlich es und explizit nennen wie so:
JSObject function = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
function.call(null, args); //call to JSObject#isFunction omitted brevity
Dies wird die Funktion aufrufen, die in Ihrem Kontext neu erstellte existiert. Sie können auch Methoden auf Objekte auf diese Weise aufrufen:
JSObject object = (JSObject) context.getAttribute(name, ScriptContext.ENGINE_SCOPE);
JSObject method = (JSObject) object.getMember(name);
method.call(object, args);
call
einen ungeprüften Ausnahme auslöst (entweder Throwable
in einem gewickelten RuntimeException
oder NashornException
, die mit JavaScript StackFrame- Informationen initialisiert wurde), so können Sie explizit behandeln müssen, dass, wenn Sie möchte nützliches Feedback geben.
Auf diese Weise können sich Threads nicht gegenseitig übertreten, da es einen separaten Kontext pro Thread gibt. Ich war auch in der Lage, benutzerdefinierten Laufzeitcode zwischen den Threads freizugeben und sicherzustellen, dass Statusänderungen an veränderbaren Objekten, die von der benutzerdefinierten Laufzeitumgebung verfügbar gemacht wurden, durch Kontext isoliert wurden.
Um dies zu tun, ich eine CompiledScript
Instanz, die eine kompilierte Darstellung meiner benutzerdefinierten Runtime-Bibliothek enthält:
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) {
...
}
}
...
}
Dann, wenn ich ein Skript ausführen muss ich die kompilierte Quelle zu bewerten und dann die Evaluierung Skript gegen diesen Zusammenhang auch:
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);
getestet habe ich diese mit mehreren Threads und ich war in der Lage, um sicherzustellen, dass Statusänderungen wurden auf die Kontexte beschränkt, die einzelnen Fäden gehören. Dies liegt daran, dass die kompilierte Repräsentation in einem bestimmten Kontext ausgeführt wird, was bedeutet, dass Instanzen von allem, was von ihr exponiert wird, auf diesen Kontext beschränkt sind.
Ein kleiner Nachteil hier ist, dass Sie Objektdefinitionen für Objekte, die keinen threadspezifischen Zustand haben müssen, unnötig neu bewerten müssen. Um dies zu umgehen, bewerten direkt am Motor, die ENGINE_SCOPE
Bindings für diese Objekte auf den Motor hinzufügen wird:
public Runtime() {
...
String shared = new Scanner(
this.getClass().getClassLoader().getResourceAsStream("runtime/shared.js")
).useDelimiter("\\Z").next();
try {
...
nashorn.eval(shared);
...
} catch(ScriptException e) {
...
}
}
Dann später, können Sie den Faden spezifischen Kontext aus dem Motor ENGINE_SCOPE
bevölkern:
context.getBindings(ScriptContext.ENGINE_SCOPE).putAll(engine.getBindings(ScriptContext.ENGINE_SCOPE));
Eine Sache, die Sie tun müssen, ist sicherzustellen, dass solche Objekte, die Sie ausgesetzt sind, eingefroren wurden. Ansonsten ist es möglich, Eigenschaften neu zu definieren oder hinzuzufügen.
Ich habe gelesen, dass Pre-Compiling ist eigentlich ein No-Op in Nashorn, so dass es nicht hilft. Die Quelle kann jedoch nicht gefunden werden. Ich könnte falsch liegen. –