Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nashorn no longer working with BigDecimal

Tags:

java

nashorn

We upgraded from Oracle JDK 8u77 to 8u92 and suddenly scripts that were previously working no longer work. A minimal reproducer is:

Map<String, Object> attributes = Collections.singletonMap("GROSSREIMBAMOUNT", BigDecimal.ZERO);
String script = "GROSSREIMBAMOUNT.toFixed(2)";

ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");

for (Entry<String, Object> entry : attributes.entrySet()) {
  jsEngine.put(entry.getKey(), entry.getValue());
}

System.out.println(jsEngine.eval(script));

Previously we got

0.00

But now we're getting.

TypeError: GROSSREIMBAMOUNT.toFixed is not a function

typeof now returns object where it would previously return number.

My question is this behavior intentional or a bug? I first though this would be a bug but JDK-8010732 seems to suggest otherwise.

like image 932
Philippe Marschall Avatar asked Apr 25 '16 12:04

Philippe Marschall


1 Answers

The initial release of Nashorn treated all numeric Java primitives and all subclasses of java.lang.Number as JavaScript numbers. However, JavaScript numbers as defined as doubles, and that means that numeric types that do not map to doubles such as longs or java.lang.BigDecimals will suffer from loss of precision when converted to a JavaScript number.

As you noticed, we fixed this between 8u77 and 8u92. Instances of java.lang.Number that can't be mapped cleanly to doubles are no longer treated as JavaScript numbers in Nashorn.

You have two options to work around this. One is to just treat these numbers as Java objects and use the methods provided by the Java class. This is usually the better option as the Java class was written to work with the numeric type at hand. The other option is to explicitly convert to a JavaScript number. This is usually done by calling the global Number() constructor without the "new" keyword, or by just prepending the unary "+" operator. Note however that this conversion may result in loss of precision without you noticing, so the first option is probably the safer path.

like image 167
Hannes Wallnöfer Avatar answered Nov 06 '22 09:11

Hannes Wallnöfer