Skip to content

Instantly share code, notes, and snippets.



Last active Mar 15, 2017
What would you like to do?
js equality implementation (in spidermonkey)

explanation of 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
  1. The script is parsed and compiled to bytecode.

  2. 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_EQ bytecode, representing the non-strict equality operator (==).


        if (!LooseEqualityOp<true>(cx, REGS))
            goto error;
  3. The interpreter calls LooseEqualityOp


    template <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);
        return true;

    which in turn calls js::LooselyEqual


    // ES6 draft rev32 7.2.12 Abstract Equality Comparison
    js::LooselyEqual(JSContext* cx, HandleValue lval, HandleValue rval, bool* result)
  4. js::LooselyEqual performs 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 StringToNumber is called to convert the lval to a number.

  5. StringToNumber delegates to the function CharsToNumber, which skips the spaces in the string. \t, \n, ..., \uFEFF, \u0020 are all space characters, so the result is an empty array of characters ("").

  6. CharsToNumber in turn calls js_strtod (double d is the result number)


        const CharT* ep;
        double d;
        if (!js_strtod(cx, bp, end, &ep, &d)) {
            *result = GenericNaN();
            return false;
  7. In this case, js_strtod performs some checks and ultimately calls js_strtod_harder (better, faster, stronger...)


        *d = js_strtod_harder(cx->dtoaState(), chars.begin(), &ep, &err);

    and js_strtod_harder is a thin wrapper around _strtod


    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;
  8. _strtod ("string to double") is an insanely convoluted function, complicated by lots of platform-specific #ifdefs. It's part of dtoa.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 empty


    		case 0:
        		goto ret0;

    and ultimately returns zero.

  9. The zero result propagates all the way back to js::LooselyEqual; the left-hand operand of == has been converted to the number 0. Finally, this number 0 is compared with the number 0 that is the right-hand operand


            *result = (num == rval.toNumber());
            return true;

    result is true, which is the value of == given back to the script. return true here indicates that no error occurred (SpiderMonkey uses return codes instead of C++ exceptions).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.