Why does the value of typeof null change inside a loop?
Executing this snippet in the Chrome console:
function foo() {
return typeof null === 'undefined';
}
for(var i = 0; i < 1000; i++) console.log(foo());
should print 1000 times false
, but on some machines will print false
for a number of iterations, then true
for the rest.
Why is this happening? Is it just a bug?
Solution 1:
There is a chromium bug open for this:
Issue 604033 - JIT compiler not preserving method behavior
So yes It's just a bug!
Solution 2:
It's actually a V8 JavaScript engine (Wiki) bug.
This engine is used in Chromium, Maxthron, Android OS, Node.js etc.
Relatively simple bug description you can find in this Reddit topic:
Modern JavaScript engines compile JS code into optimized machine code when it is executed (Just In Time compilation) to make it run faster. However, the optimization step has some initial performance cost in exchange for a long term speedup, so the engine dynamically decides whether a method is worth it depending on how commonly it is used.
In this case there appears to be a bug only in the optimized path, while the unoptimized path works fine. So at first the method works as intended, but if it's called in a loop often enough at some point the engine will decide to optimize it and replaces it with the buggy version.
This bug seems to have been fixed in V8 itself (commit), aswell as in Chromium (bug report) and NodeJS (commit).
Solution 3:
To answer the direct question of why it changes, the bug is in the "JIT" optimisation routine of the V8 JS engine used by Chrome. At first, the code is run exactly as written, but the more you run it, the more potential there is for the benefits of optimisation to outweigh the costs of analysis.
In this case, after repeated execution in the loop, the JIT compiler analyses the function, and replaces it with an optimised version. Unfortunately, the analysis makes an incorrect assumption, and the optimised version doesn't actually produce the correct result.
Specifically, Reddit user RainHappens suggests that it is an error in type propagation:
It also does some type propagation (as in what types a variable etc can be). There's a special "undetectable" type for when a variable is undefined or null. In this case the optimizer goes "null is undetectable, so it can be replaced with the "undefined" string for the comparison.
This is one of the hard problems with optimising code: how to guarantee that code which has been rearranged for performance will still have the same effect as the original.
Solution 4:
This was fixed two month ago and will land in Chrome soon (already in Canary).
V8 Issue 1912553002 - Fix 'typeof null' canonicalization in crankshaft
Chromium Issue 604033 - JIT compiler not preserving method behavior