It would have been a long time to write the details of the matter, so I will write the conclusion first.
In java version" 1.8.0_92 "
, some modifications have been made to Nashorn, which may change the behavior of JavaScript running on Nashorn by updating Java. The changes that seem to have the most impact (and I'm really into) are:
*** When Java long type and int type are passed to JavaScript executed by Nashorn, they were treated as Number
type in the past, but after modification, they are treated as ʻObject` type ***
There are likely to be some patterns in the code that will be affected by this change (behavior will change), but a typical (actually I'm addicted to) one is doing an exact equality comparison. I will.
// JavaScript
function hoge(arg) { // <-arg is an object that stores the arguments passed from Java
if arg.getLongValue() === 0 return 0 // <-Here arg.getLongValue()Is 0L on Java===The result of is false and else is executed
else arg.getLongValue / 2;
};
There are two possible countermeasures for the above cases.
===
) and change to equality comparison (==
) for the part where the numerical value (long, int) passed from Java and the Number type of JS are compared. ..Which one should be adopted depends on the situation, but if you want to modify the Java side, you need to build, deploy, and restart, so in many cases, I think that 1. is easier.
A JavaScript engine for executing JavaScript in Java.
In Java SE 6-7
, there was a JavaScript engine made by Mozilla called Rhino
, but in Java SE 8
this was replaced by Nashorn
made by Oracle. Compared to Rhino
, it is compliant with ʻECMAScript-262`, and the execution speed is greatly improved, which seems to be a big difference.
I think there are many uses for Nashorn, but for example, there is a need to enable dynamic replacement of logic
. In a certain calculation process, I would like to do something like an AB test of logic to see what kind of change will occur in performance, and if a positive change is seen, gradually move to new logic. I see.
I think the explanation is abstract and difficult to understand, but in short
// Java
Object a: = doSomething() // step 1
Object b = executeDynamicLogic(a) // step 2 <--I want to replace it easily!
return b // step 3
The atmosphere is like this.
In the above example, pass the argument ʻato the script code fetched from DB etc. and execute it, and store the result in the variable
b`. .. .. I want to be able to change the behavior of the application just by UPDATE the script stored in the DB. It is an image that treats logic as data.
If you can achieve the purpose, you can use Python or Ruby instead of JavaScript, but in my case,
--The official module of Java SE
--Good execution performance
--Population ratio around (JS> Python> Ruby)
From such a point of view, I remember that I decided to use JavaScript.
At one point, JavaScript running on Nashorn, which had never had a problem before, started to give an error.
When I first started investigating, I naturally suspected a change in my code, but I didn't make any changes around Nashorn, and the script being executed is also passed to the script. There was nothing suspicious about the data. When I started thinking that I didn't understand this ...
** "Oh, maybe I upgraded the Java version ..." **
A ray of light. At this point, I began to doubt the possibility that "Isn't Nashorn affected by some changes in the update?", And when I looked it up, I found it very quickly.
JDK-8144020 | Remove long as an internal numeric type
In summary,
*** Until now, ʻintand
long were also treated internally as
doubleand mapped to JS
number (in Java, the ECMA-like definition of
numberis satisfied. (Because it is only
double) However, when treating
longas
double`, some problems occur due to the addition of extra precision of 53 bits, so stop this internal conversion process YO! *** ***
.. .. .. Really? So, when you check the operating environment
$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
Hmmm. It is higher than 1.8.0_92
. .. ..
The code that caused the problem was something like this:
// JavaScript
function hoge(arg) { // <-arg is an object that stores the arguments passed from Java
if arg.getLongValue1() === 0 return 0
else arg.getValue1() / arg.getLongValue2();
};
The point is that we are trying to avoid division by zero, but the problem is
arg.getValue1() === 0
It is the part of. Exact equality comparison (check if both types and values match) between ʻarg.getValue1 (), which is the
long type in the Java world, and
0, which is the literal
type, which is the Number
type in the JS world. )doing.
For Nashorn up to Java8u91
, if ʻarg.getValue1 () == 0L is
trueon the Java side, this expression returns
true. However, since
Java8u92, the result is
false. The reason is that the values on the left and right sides of **
===are
0`, but the types are different **.
//Arg on Java side.getValue1()If is 0L
arg.getValue1() === 0 // arg.getValue1()Is Number(0)And the result is true
//Arg on Java side.getValue1()If is 0L
arg.getValue1() === 0 // arg.getValue1()Is Object(0)And the result is false
In other words, even if the type of the value passed from the Java side has not changed with long
, when the Java version goes up from 8u91
or less to 8u92
or more, the type on the JS side mapped by Nashorn will beNumber. It changes from
to ʻObject. As a result, in the above code, the result returned by the exact equality operation
===changes, the division by zero that was trying to avoid is unavoidable, and
PositiveInfinity` is mixed in with the value in the middle of calculation. After that, all the calculations became unintended values, the results were returned to the Java side, and an error occurred.
I felt that it was important to be prepared to doubt without believing it from the head just because it was official.
Recommended Posts