Graal.js返回SafeInteger致断言失败:TestNG与独立Java应用差异及解决
问题:Nashorn迁移Graal.js的数值映射异常——TestNG与独立应用行为差异
背景
我们正将遗留代码从Nashorn迁移至Graal.js,遇到数值内部映射不一致问题:
- Nashorn将15位大整数映射为
java.lang.Double - Graal.js则解析为实现
TruffleObject的SafeInteger
这导致多语言互操作逻辑中的断言assert !(result instanceof TruffleObject);失效。更反常的是:该断言在TestNG测试中失败,但在独立Java应用中可通过——独立应用返回SafeInteger,测试环境返回null。
简化测试用例
@Test public static void test() throws ScriptException { Scope scope = ScriptManager.createScope(); String test = "314159265358979"; scope.setValue(new Variable("maxPrecisionJavascript", DataType.NUMBER), test); Expression expr1 = ScriptManager.createExpression("result = maxPrecisionJavascript;"); expr1.eval(scope); String actual = scope.getValueAsString("result"); System.out.println("Result: " + actual); }
错误栈信息
测试环境执行scope.getValueAsString("result")时触发断言错误:
org.graalvm.polyglot.PolyglotException: java.lang.AssertionError at com.oracle.truffle.polyglot.HostToGuestRootNode.execute(HostToGuestRootNode.java:125) ... at com.tibco.bpm.se.core.test.GraalTest.test(GraalTest.java:56) Caused by: java.lang.AssertionError at com.oracle.truffle.polyglot.HostToGuestRootNode.execute(HostToGuestRootNode.java:125)
核心疑问
- 为何
SafeInteger(TruffleObject实现类)会导致断言在TestNG测试失败但独立应用通过? - TestNG与运行时环境在Graal.js行为或多语言引擎配置上有已知差异吗?
- 如何解决大数值适配问题?
解答
一、TestNG与独立应用的行为差异原因
- 断言启用状态不同:TestNG默认可能启用了Java断言(
-ea参数),而独立应用未开启。Graal.js内部HostToGuestRootNode的断言在测试环境被触发,独立应用因断言关闭而跳过,这是最常见的诱因。 - 引擎配置差异:TestNG测试环境可能使用了不同的Polyglot引擎配置,比如是否开启
js.safe-integers属性,或者测试框架的类加载器隔离导致Graal.js内部对象处理逻辑异常,进而出现返回null的情况。 - Scope生命周期问题:测试环境中
Scope对象可能被Test框架提前回收或重置,导致result变量未正确保留,最终返回null触发断言失败;独立应用中Scope生命周期更可控,值能正常留存。
二、TestNG与Graal.js的已知配置差异
TestNG的测试上下文通常使用自定义类加载器,可能影响Graal.js的Polyglot引擎初始化:
- Graal.js依赖Truffle框架的类加载机制,TestNG的类加载隔离可能导致
instanceof TruffleObject的类型判断出现偏差。 - 部分TestNG插件或配置会修改JVM参数,意外开启/关闭Graal.js的特性(如安全整数模式)。
三、大数值适配的解决方案
1. 调整Graal.js数值映射行为
通过Polyglot引擎配置强制将大整数转为Java原生类型,避免生成SafeInteger:
Engine engine = Engine.newBuilder() .option("js.safe-integers", "false") // 关闭安全整数模式,大整数转为Double .build();
若需保留整数精度,可在脚本中主动将数值转为BigInt后再交互,或使用Number.prototype.toString()显式转换。
2. 修改断言逻辑适配Graal.js
放弃对TruffleObject的强断言,改为判断数值类型:
// 替换原断言 assert result instanceof Number || result instanceof BigInteger; // 或使用Graal.js工具类判断 assert Value.asValue(result).isNumber();
3. 统一测试与生产环境的JVM参数
确保TestNG测试和独立应用使用相同的JVM参数,尤其是断言开关(-ea/-da)和Graal.js相关配置,消除环境差异导致的行为不一致。
4. 修复Scope变量生命周期问题
检查测试代码中Scope的创建与销毁逻辑,确保expr1.eval(scope)后result变量未被意外清空。可在测试中显式打印scope内的所有变量,验证值是否正确存在。
内容的提问来源于stack exchange,提问作者Srikanth Allapu
相关产品推荐
相关产品推荐

