diff --git a/js/src/jsobj.c b/js/src/jsobj.c index 09d9638bb806..8ff820400d9b 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -2380,6 +2380,59 @@ bad: return JS_FALSE; } +/* + * Given pc pointing after a property accessing bytecode, return true if the + * access is a "object-detecting" in the sense used by web pages, e.g., when + * checking whether document.all is defined. + */ +static JSBool +Detecting(JSContext *cx, jsbytecode *pc) +{ + JSScript *script; + jsbytecode *endpc; + JSOp op; + JSAtom *atom; + + script = cx->fp->script; + for (endpc = script->code + script->length; pc < endpc; pc++) { + /* General case: a branch or equality op follows the access. */ + op = (JSOp) *pc; + if (js_CodeSpec[op].format & JOF_DETECTING) + return JS_TRUE; + + /* + * Special case #1: handle (document.all == null). Don't sweat about + * JS1.2's revision of the equality operators here. + */ + if (op == JSOP_NULL) { + if (++pc < endpc) + return *pc == JSOP_EQ || *pc == JSOP_NE; + break; + } + + /* + * Special case #2: handle (document.all == undefined). Don't worry + * about someone redefining undefined, which was added by Edition 3, + * so was read/write for backward compatibility. + */ + if (op == JSOP_NAME) { + atom = GET_ATOM(cx, script, pc); + if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && + (pc += js_CodeSpec[op].length) < endpc) { + op = (JSOp) *pc; + return op == JSOP_EQ || op == JSOP_NE || + op == JSOP_NEW_EQ || op == JSOP_NEW_NE; + } + break; + } + + /* At this point, anything but grouping means we're not detecting. */ + if (op != JSOP_GROUP) + break; + } + return JS_FALSE; +} + #if defined JS_THREADSAFE && defined DEBUG JS_FRIEND_API(JSBool) _js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, @@ -2466,9 +2519,7 @@ js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, flags |= JSRESOLVE_ASSIGNING; } else { pc += cs->length; - while (*pc == JSOP_GROUP) - pc++; - if (js_CodeSpec[*pc].format & JOF_DETECTING) + if (Detecting(cx, pc)) flags |= JSRESOLVE_DETECTING; } } @@ -2695,21 +2746,15 @@ js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) cx->fp && cx->fp->pc && (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM)) { - jsbytecode *pc, *endpc; + jsbytecode *pc; JSString *str; /* Kludge to allow (typeof foo == "undefined") tests. */ JS_ASSERT(cx->fp->script); pc = cx->fp->pc; pc += js_CodeSpec[*pc].length; - endpc = cx->fp->script->code + cx->fp->script->length; - while (pc < endpc) { - if (*pc == JSOP_TYPEOF) - return JS_TRUE; - if (*pc != JSOP_GROUP) - break; - pc++; - } + if (Detecting(cx, pc)) + return JS_TRUE; /* Ok, bad undefined property reference: whine about it. */ str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 112925780c18..b6801eb93110 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -93,8 +93,8 @@ OPDEF(JSOP_SETCONST, 14, "setconst", NULL, 3, 1, 1, 1, JOF_CONST| OPDEF(JSOP_BITOR, 15, "bitor", "|", 1, 2, 1, 2, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_BITXOR, 16, "bitxor", "^", 1, 2, 1, 3, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_BITAND, 17, "bitand", "&", 1, 2, 1, 4, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) -OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC) +OPDEF(JSOP_EQ, 18, "eq", "==", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) +OPDEF(JSOP_NE, 19, "ne", "!=", 1, 2, 1, 5, JOF_BYTE|JOF_LEFTASSOC|JOF_DETECTING) OPDEF(JSOP_LT, 20, "lt", "<", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_LE, 21, "le", "<=", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) OPDEF(JSOP_GT, 22, "gt", ">", 1, 2, 1, 6, JOF_BYTE|JOF_LEFTASSOC) @@ -114,7 +114,7 @@ OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 10, JOF_UINT16 OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_DEL) OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_DEL) OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 10, JOF_BYTE |JOF_ELEM|JOF_DEL) -OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE) +OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE|JOF_DETECTING) OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 10, JOF_BYTE) OPDEF(JSOP_INCNAME, 41, "incname", NULL, 3, 0, 1, 10, JOF_CONST|JOF_NAME|JOF_INC) OPDEF(JSOP_INCPROP, 42, "incprop", NULL, 3, 1, 1, 10, JOF_CONST|JOF_PROP|JOF_INC) @@ -151,8 +151,8 @@ OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLES OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH) /* New, infallible/transitive identity ops. */ -OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 5, JOF_BYTE) -OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 5, JOF_BYTE) +OPDEF(JSOP_NEW_EQ, 72, "eq", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) +OPDEF(JSOP_NEW_NE, 73, "ne", NULL, 1, 2, 1, 5, JOF_BYTE|JOF_DETECTING) /* Lexical closure constructor. */ OPDEF(JSOP_CLOSURE, 74, "closure", NULL, 3, 0, 0, 0, JOF_CONST)