访问未定义的值后 Nashorn 中的输出不一致

Inconsistent output in Nashorn after accessing undefined value

我正在尝试使用 Nashorn 创建 Java 程序的控制台界面。所以我想根据一些默认导入来评估一些输入。问题是当它访问一个未定义的值时。理想情况下,engine.eval 应该抛出 ReferenceError 或 return null,但我稍后会得到不一致的值:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class Main {
    public static void main(String[] args) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x = 1 }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));
    }
}

ideone 和 Windows Oracle JDK 1.8.0_101 的结果是:

null
1
null

最后的输出是 null,但添加 x == null 给出 false。

如果我在第一个字符串中添加任何空格,我会得到这个输出:

null
1
1

如果第一个和最后一个 engine.eval 参数完全相同,包括空格,那么最后一个值似乎是 null

x 的值可以在 null1 之间返回,因此它看起来像是引擎中的某种缓存:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class Main {
    public static void main(String[] args) throws Exception {
        ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x = 1 }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x  }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x + x }"));
        System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));
    }
}

给出:

null
1
null
1
2.0
null

有什么方法可以使它始终如一地给出 null, 1, 1 结果吗?

强制引擎确认变量声明。

只需在第一个脚本中使用 var x

System.out.println(engine.eval("with(JavaImporter(java.util)) { var x }")); System.out.println(engine.eval("with(JavaImporter(java.util)) { x = 1 }")); System.out.println(engine.eval("with(JavaImporter(java.util)) { x }"));

引擎中有一个 class 缓存,可以通过以下方式创建它来禁用它:

NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(new String[] { "--class-cache-size = 0" });

通过强制重新编译每个表达式,似乎可以解决问题。看起来相同的表达式会根据范围内的变量进行不同的编译。