JDK 1.8.0_92 Nashorn JS引擎indexOf行为

5

我正在使用java8中的"nashorn" javascript引擎在运行时评估一些表达式。我有一个工具类来实现这个功能,其中有一个方法:

    public static String evaluateJavaScriptExpression(String expression) throws ScriptException {
    if (expression == null) {
        return null;
    }
    ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
    ScriptEngine javaScriptEngine = scriptEngineManager.getEngineByName(JAVASCRIPT_ENGINE);
    return String.valueOf(javaScriptEngine.eval(expression));
}

我创建了一些单元测试,其中之一如下:

    String expression = "var arr = [1, 3, 2, 5, 4]; arr.indexOf(0);";
    assertEquals("-1", ExpressionEvaluatorUtil.evaluateJavaScriptExpression(expression));

在我使用Java版本“1.8.0_91”时,一切都很正常。但是有人使用了Java版本“1.8.0_92”,报告测试失败。我将我的版本切换到92版,结果也失败了。它的实际结果是“-1.0”。 此外,我在Chrome控制台中尝试了相同的JS代码,结果与91版返回的“-1”相同。
有人知道为什么两个JDK版本之间会有这样的结果差异吗?这是一个错误还是被故意更改的?
2个回答

4
好的,如果您知道更改的精确版本号,则可以知道在哪里查看:1.8u92错误修正列表列出了一些有关Nashorn的修复措施,其中最有趣的是JDK-8144020

删除长作为内部数字类型

ECMA定义double为JavaScript中唯一的数字类型。在Nashorn中,我们将数字作为int、long和double进行内部表示。使用long是有问题的,因为它向double提供的53位添加了额外的精度。…

乍一看,这似乎只是一个内部更改,但是如果您意识到您以前的结果源自于先前,eval为此代码返回了Long,并被格式化为"-1",那么情况就会发生改变。
现在考虑一下这个错误报告的第一句话:“ECMA将double定义为JavaScript中唯一的数字类型”。这导致得出结论,返回Long不是指定的结果类型,而是实现工件。
因此,显然,当从内部使用中删除long时,消除了返回Long的可能性,引擎现在不会返回Integer,而是返回Double,作为更改的副产品。
这就解释了为什么还有其他脚本(如"var str ='abcd'; str.indexOf('x');)可以产生没有小数位的输出。后面的脚本求值为Integer,并且仍然是这样。由于输出类型的更改是删除内部long使用的副产品,而不是故意更改所有非Double数值结果,因此未受到使用int的内部位置的影响。
与Chrome引擎的结果进行比较时,您必须考虑到您正在比较带有格式化输出的结果,并且数字转换成字符串不是脚本的一部分。因此,结果是未指定的。浏览器可能有权呈现所有匹配整数值而没有小数位的数值。

非常感谢提供的详细信息。 还有两个问题: 1)为什么 var str ='abcd'; str.indexOf('x'); 会被评估为整数,而其他代码之前被评估为长整型? 2)考虑到所有内容都应该是双精度浮点数,那么 str.indexOf() 被评估为整数是否也是一个错误,并且将以同样的方式被删除? - dty
1
我不知道JS数组实现是否准备处理超过2³¹个元素,或者是否有其他原因。我猜,在涉及到String操作时,会在某个地方委托给Java的String.indexOf,它返回int。我记得曾经在某个地方读到,Nashorn数值结果的唯一保证返回类型是Number,因此IntegerLongDouble都在该规范内。因此,返回Integer不是一个错误,但仍可能随时更改。 - Holger
因此,如果您想要一个稳定的String表示形式,您应该测试结果是否为Number,如果是,则读取其doubleValue()并自行测试,是否可以表示为无小数部分(或始终格式化为double)。换句话说,不要依赖于返回的数字类型的toString()实现... - Holger

0
如果我们看一下ScriptEngine的Java文档.eval(String input)方法,它返回一个对象,然后由开发人员使用来将对象强制转换为JavaScript返回类型的正确类型。所以,我认为问题出在String.valueOf()方法调用上,表达式的求值返回一个浮点数。
我认为你需要改变你的代码,使用Integer.valueOf(javaScriptEngine.eval(expression));
希望这可以帮助你 :)

谢谢您的回复。我认为您关于JS返回浮点数是正确的。测试方法是通用的,用于许多地方,不应该返回整数,因此必须始终返回字符串。我无法理解的是在版本91和92中出现了不同的行为。此外,我有一个类似于这样的测试 var str ='abcd'; str.indexOf('x'); 它可以正常工作,返回的结果确实是“-1”!我觉得有点困惑。 - dty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接