- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我有一个自定义的 Nashorn 运行时,我设置了一些全局函数和对象——其中一些是无状态的,一些是有状态的。针对此运行时,我正在运行一些自定义脚本。
对于每次执行,我计划创建一个由全局上下文支持的新上下文:
myContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
engine.eval(myScript, myContext);
根据我阅读的内容,对全局范围的任何修改(从脚本的 Angular 来看)都将限于我创建的新上下文。
这些脚本在评估时会公开一些对象(具有明确定义的名称和方法名称)。我可以通过将 engine
转换为 Invocable
来调用对象的方法。但是我怎么知道函数运行的上下文呢?这甚至是一个问题,还是该函数的执行上下文是根据评估它的上下文设置的?
在所有线程共享同一个脚本引擎实例并且它们都尝试运行同一个脚本(公开一个全局对象)的多线程情况下,我可以期待什么行为?当我随后调用对象上的方法时,该函数将在哪个上下文中运行?它如何知道要使用对象的哪个实例?
我期待看到一个 invoke
方法,我可以在其中指定上下文,但事实似乎并非如此。有没有办法做到这一点,还是我的做法完全错误?
我知道解决这个问题的一个简单方法是在每次执行时创建一个新的脚本引擎实例,但据我所知,我会失去优化(尤其是在共享代码上)。话虽这么说,预编译在这里有帮助吗?
最佳答案
我想通了。我遇到的问题是 invokeFunction
会抛出 NoSuchMethodException
,因为自定义脚本公开的函数在引擎默认范围的绑定(bind)中不存在:
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
抛出未经检查的异常(包裹在 RuntimeException
中的 Throwable
或已用初始化的 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);
我用多个线程对此进行了测试,我能够确保状态更改仅限于属于各个线程的上下文。这是因为编译表示是在特定上下文中执行的,这意味着它公开的任何实例都在该上下文范围内。
这里的一个小缺点是您可能会不必要地重新评估不需要具有线程特定状态的对象的对象定义。为了解决这个问题,直接在引擎上评估它们,这会将这些对象的绑定(bind)添加到引擎的 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));
您需要做的一件事是确保您公开的任何此类对象都已被卡住。否则,可以重新定义或向它们添加属性。
关于javascript - 在 Nashorn 中的特定上下文中执行函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33620318/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!