explanation of https://twitter.com/getify/status/780249160662605824 in terms of SpiderMonkey (Firefox) internals
edit: The tweet linked above has since been deleted, but the original code read something like:
' \t\n\r\u000c\u000b\uFEFF\u0020' == 0 // true-
The script is parsed and compiled to bytecode.
-
For simplicity's sake, we assume the code runs through the interpreter rather than one of the JIT compiler backends. The interpreter sees the
JSOP_EQbytecode, representing the non-strict equality operator (==).CASE(JSOP_EQ) if (!LooseEqualityOp<true>(cx, REGS)) goto error; END_CASE(JSOP_EQ)
-
The interpreter calls
LooseEqualityOptemplate <bool Eq> static MOZ_ALWAYS_INLINE bool LooseEqualityOp(JSContext* cx, InterpreterRegs& regs) { HandleValue rval = regs.stackHandleAt(-1); HandleValue lval = regs.stackHandleAt(-2); bool cond; if (!LooselyEqual(cx, lval, rval, &cond)) return false; cond = (cond == Eq); regs.sp--; regs.sp[-1].setBoolean(cond); return true; }
which in turn calls
js::LooselyEqual// ES6 draft rev32 7.2.12 Abstract Equality Comparison bool js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* result) {
-
js::LooselyEqualperforms some tests on the left- and right-hand values of the==operator until it comes to// Step 7. if (lval.isString() && rval.isNumber()) { double num; if (!StringToNumber(cx, lval.toString(), &num)) return false;
The left-hand value is a string and the right-hand value is a number, so
StringToNumberis called to convert the lval to a number. -
StringToNumberdelegates to the function CharsToNumber, which skips the spaces in the string.\t,\n, ...,\uFEFF,\u0020are all space characters, so the result is an empty array of characters (""). -
CharsToNumberin turn callsjs_strtod(double dis the result number)const CharT* ep; double d; if (!js_strtod(cx, bp, end, &ep, &d)) { *result = GenericNaN(); return false; }
-
In this case,
js_strtodperforms some checks and ultimately callsjs_strtod_harder(better, faster, stronger...)*d = js_strtod_harder(cx->dtoaState(), chars.begin(), &ep, &err);and
js_strtod_harderis a thin wrapper around_strtoddouble js_strtod_harder(DtoaState* state, const char* s00, char** se, int* err) { double retval; if (err) *err = 0; retval = _strtod(state, s00, se); return retval; }
-
_strtod("string to double") is an insanely convoluted function, complicated by lots of platform-specific#ifdefs. It's part ofdtoa.c, originally written by David M. Gay and pulled in by a bunch of projects including Firefox. In this case, it ends up seeing that the input char array is emptycase 0: goto ret0;
and ultimately returns zero.
-
The zero result propagates all the way back to
js::LooselyEqual; the left-hand operand of==has been converted to the number0. Finally, this number 0 is compared with the number 0 that is the right-hand operand*result = (num == rval.toNumber()); return true;resultistrue, which is the value of==given back to the script.return truehere indicates that no error occurred (SpiderMonkey uses return codes instead of C++ exceptions).