为什么Nashorn乐观的打字会产生不可预测的结果?

时间:2016-03-10 15:06:12

标签: javascript java nashorn

我一直在试验Nashorn,并且在eval()的返回类型方面得到了一些奇怪的结果。

// calculator.js
var add = function(a, b) {
    return a + b;
}

以下Java代码:

// CalculatorTest.java
import java.io.InputStreamReader;
import javax.script.*
import org.junit.*
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

public class CalculatorTest {

    ScriptEngine nashorn;

    @Before
    public void setup() throws ScriptException {
        NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
        nashorn = factory.getScriptEngine("–optimistic-types=true");
        nashorn.eval(new InputStreamReader(
                getClass().getClassLoader().getResourceAsStream("calculator.js")));
    }

    @Test
    public void addViaInvokeFuntion() throws Exception {
        Object result = ((Invocable) nashorn).invokeFunction("add", 2, 3);
        Assert.assertEquals(5.0, result);
    }

    @Test
    public void addViaSimple() throws Exception {
        Object result = nashorn.eval("2+3");
        Assert.assertEquals(5, result);
    }

    @Test
    public void addViaMoreComplexEval() throws Exception {
        Object result = nashorn.eval(
                "var anotherAdd = function(a, b) {\n" +
                "    return a + b;\n" +
                "};\n" +
                "anotherAdd(2, 3);");
        Assert.assertEquals(5L, result);
    }
}

为什么这些测试会以这种方式成功?

有没有办法预测" Nashorn回归类型?

乐观打字不应该这样做吗?

2 个答案:

答案 0 :(得分:1)

用于表示实现方面的JS值的Java类型(它不是ECMAScript规范的一部分。只要typeof表示“数字”并且算术运算按预期工作,任何Java / JVM类型应该没问题)。使用Java“int”来对ECMAScript数值进行优化。无法始终进行优化。只能进行安全类型推断。 Java端应该期望为ECMAScript“number”值返回任何Number值。这些测试必须编写为使用java.lang.Number类型。您可以在返回值上调用“intValue”,“doubleValue”方法(在将返回的Object作为java.lang.Number转换后)并声明预期值。

答案 1 :(得分:1)

你在这里看到的是因为Nashorn根据参数的输入类型创建了专门版本的函数(而不是乐观的打字本身)。当您通过add界面调用Invocable时,您最终会调用专业化add(Object, Object)而不是add(int, int)。如果参数类型是对象,那么杠杆作用就没有乐观了;你得到以下字节码:

public static add(Object;Object;Object;)Object; aload 1 aload 2 invokestatic ScriptRuntime.ADD(Object;Object;)Object; areturn

(如果将--print-code添加到引擎命令行参数,则可以自己检查;另一个有用的是--log=recompile,它将打印已生成的函数的类型特化。)

因此,它与add中定义的calculator.js无关,而是与您的调用网站Invocable.invokeFunction输入为所有对象参数的事实无关。

ScriptRuntime.ADD(Object, Object)不会试图过于聪明并缩小其结果的类型;它是通用情况下的慢路径+运算符实现,当没有关于参数类型的静态信息时使用它,并且它必须准备好处理JavaScripts的奇怪情况,例如"2" + []等。它的代码当然可以包含特殊的优化案例,例如“如果操作的结果适合Integer返回一个”,但是我们认为额外的复杂性不值得麻烦,因为你在输入上丢失了静态类型信息。因此,当参数的静态类型为Object时,您将获得所有数字加法的双精度。

另一方面,如果你执行nashorn.eval("add(2, 3)"),它会调用add(int, int)特化,这是乐观的,你的确最终返回值为整数5。

希望有所帮助。