我一直在试验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回归类型?
乐观打字不应该这样做吗?
答案 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。
希望有所帮助。