原型在nashorn中从globalscope更改为enginescope

我正在尝试将一些库预加载到全局范围内(诸如chai.js之类的东西)。这改变了某些对象的原型,我意识到这适用于ENGINE_SCOPE,但不适用于GLOBAL_SCOPE。

最小示例:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
Bindings globalBindings = engine.createBindings();
engine.eval("Object.prototype.test = function(arg){print(arg);}",globalBindings);

//works as expected,printing "hello"
engine.getcontext().setBindings(globalBindings,ScriptContext.ENGINE_SCOPE);
engine.eval("var x = {}; x.test('hello');");

//throws TypeError: null is not a function in <eval> at line number 1
engine.getcontext().setBindings(engine.createBindings(),ScriptContext.ENGINE_SCOPE);
engine.getcontext().setBindings(globalBindings,ScriptContext.GLOBAL_SCOPE);
engine.eval("var x = {}; x.test('hello');");

是否有一种变通方法以使其按预期工作,即更改已从全局范围正确传播到引擎范围?

sunqilove 回答:原型在nashorn中从globalscope更改为enginescope

为什么您的代码不起作用

全局作用域只能用于简单的变量映射。例如:

ScriptContext defCtx = engine.getContext();
defCtx.getBindings(ScriptContext.GLOBAL_SCOPE).put("foo","hello");

Object位于引擎范围内,因此甚至不会在全局范围内搜索与其相关的任何映射(在您的情况下为Object.prototype.test)。

文档摘录:

默认上下文的ENGINE_SCOPE是ECMAScript“全局”对象的包装实例-顶级脚本表达式中的“ this”。因此,您可以从此范围对象访问ECMAScript顶级对象,例如“对象”,“数学”,“ RegExp”,“未定义”。 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

解决方案

  1. 继续使用引擎范围
  2. 通过在Java命令行中指定--global-per-engine option来使用-Dnashorn.args=--global-per-engine。然后,Nashorn将使用全局对象的单个实例进行所有脚本评估,而与传递的ScriptContext无关。
  3. 使用成熟的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;
}
,

Denis已经解释了这个问题,所以我不再重复他说的话。但是,我将给出另一种解决方案。为了避免一遍又一遍地解析相同的库的开销,可以对其进行编译。这是一种实现方法:

import java.util.*;
import javax.script.*;

public class Test
{
    public static List<CompiledScript> compileScripts(ScriptEngine engine,String... scripts) throws ScriptException
    {
        Compilable compilable = (Compilable)engine;
        ArrayList<CompiledScript> list = new ArrayList<>();
        for(String script : scripts)
            list.add(compilable.compile(script));
        return list;
    }

    public static void execute(ScriptEngine engine,List<CompiledScript> libs,String script) throws ScriptException
    {
        engine.setBindings(engine.createBindings(),ScriptContext.ENGINE_SCOPE);
        for(CompiledScript lib : libs)
            lib.eval();
        engine.eval(script);
    }

    public static void main(String[] args) throws ScriptException
    {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("nashorn");
        List<CompiledScript> libs = compileScripts(engine,"var x = 1","Object.prototype.test = function(arg){print(arg)}");

        // Prints 'hello'
        execute(engine,libs,"x.test('hello')");

        // Defines y
        execute(engine,"var y = 2");

        // Checks that executions have a clean (non-polluted) context
        // Throws ReferenceError: "y" is not defined in <eval> at line number 1
        execute(engine,"print(y)");
    }
}
本文链接:https://www.f2er.com/2545300.html

大家都在问