代码之家  ›  专栏  ›  技术社区  ›  Michaela Elschner

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

  •  0
  • Michaela Elschner  · 技术社区  · 8 年前

    我一直在用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);
        }
    }
    

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

    有什么方法可以“预测”纳索恩回报类型吗?

    乐观的打字不应该做到这一点吗?

    2 回复  |  直到 8 年前
        1
  •  1
  •   Attila Szegedi    8 年前

    您在这里看到的是,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 它将打印已生成函数的类型专门化。)

    所以这与 添加 定义于 calculator.js 而是你的呼叫站点 Invocable.invokeFunction 类型为获取所有对象参数。

    ScriptRuntime.ADD(Object, Object) 不要过于聪明,缩小结果的范围;这是一般情况下的慢路径 + 当没有关于参数类型的静态信息时使用的运算符实现,它必须准备好处理JavaScript的奇怪情况,例如 "2" + [] 它的代码当然可以包含特殊的优化情况,例如“如果操作的结果适合 Integer return one”,但我们认为额外的复杂性是不值得的,因为您已经丢失了输入的静态类型信息。因此,当参数的静态类型为 Object .

    另一方面,如果你执行 nashorn.eval("add(2, 3)") 那么它将调用 加法(int,int) 专业化,这是乐观的,最终的返回值是整数5。

    希望有帮助。

        2
  •  1
  •   A. Sundararajan    8 年前

    用于表示JS值的Java类型 实施 aspect(它不是ECMAScript规范的一部分。只要typeof表示“number”并且算术运算按预期工作,任何Java/JVM类型都可以)。使用Java“int”对ECMAScript数值进行优化。这种优化并非总是可以做到的。只能进行安全类型推断。Java端应该期望为ECMAScript“Number”值返回任何Number值。必须编写这些测试才能使用java.lang.Number类型。您可以对返回值调用“intValue”和“doubleValue”方法(在将返回的Object强制转换为java.lang.Number之后)并断言预期值。