bug 487039 - caching only white-listed non-globals on the scope chain. r=brendan
authorIgor Bukanov <igor@mir2.org>
Sat, 18 Apr 2009 20:10:59 +0200
changeset 27539 1b3a286e71a1cf299b0dc0fa5a1e249ba9b896af
parent 27538 4ea30a50bc4fe1560ca696455b1ba6a527c6f60e
child 27540 e8588a4a11539dd6a10a8f4467839d22ec396dfc
push id6604
push userrsayre@mozilla.com
push dateMon, 20 Apr 2009 18:44:02 +0000
treeherdermozilla-central@83068fe4a1ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs487039
milestone1.9.2a1pre
bug 487039 - caching only white-listed non-globals on the scope chain. r=brendan
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsproto.tbl
js/src/jstracer.cpp
js/src/jsxdrapi.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1327,17 +1327,16 @@ JS_InitStandardClasses(JSContext *cx, JS
     /* Function and Object require cooperative bootstrapping magic. */
     if (!js_InitFunctionAndObjectClasses(cx, obj))
         return JS_FALSE;
 
     /* Initialize the rest of the standard objects and functions. */
     return js_InitArrayClass(cx, obj) &&
            js_InitBlockClass(cx, obj) &&
            js_InitBooleanClass(cx, obj) &&
-           js_InitCallClass(cx, obj) &&
            js_InitExceptionClasses(cx, obj) &&
            js_InitMathClass(cx, obj) &&
            js_InitNumberClass(cx, obj) &&
            js_InitJSONClass(cx, obj) &&
            js_InitRegExpClass(cx, obj) &&
            js_InitStringClass(cx, obj) &&
            js_InitEval(cx, obj) &&
 #if JS_HAS_SCRIPT_OBJECT
@@ -1398,17 +1397,16 @@ static JSStdName standard_class_atoms[] 
     {js_InitFunctionAndObjectClasses,   EAGER_ATOM_AND_CLASP(Object)},
     {js_InitArrayClass,                 EAGER_ATOM_AND_CLASP(Array)},
     {js_InitBlockClass,                 EAGER_ATOM_AND_CLASP(Block)},
     {js_InitBooleanClass,               EAGER_ATOM_AND_CLASP(Boolean)},
     {js_InitDateClass,                  EAGER_ATOM_AND_CLASP(Date)},
     {js_InitMathClass,                  EAGER_ATOM_AND_CLASP(Math)},
     {js_InitNumberClass,                EAGER_ATOM_AND_CLASP(Number)},
     {js_InitStringClass,                EAGER_ATOM_AND_CLASP(String)},
-    {js_InitCallClass,                  EAGER_ATOM_AND_CLASP(Call)},
     {js_InitExceptionClasses,           EAGER_ATOM_AND_CLASP(Error)},
     {js_InitRegExpClass,                EAGER_ATOM_AND_CLASP(RegExp)},
 #if JS_HAS_SCRIPT_OBJECT
     {js_InitScriptClass,                EAGER_ATOM_AND_CLASP(Script)},
 #endif
 #if JS_HAS_XML_SUPPORT
     {js_InitXMLClass,                   EAGER_ATOM_AND_CLASP(XML)},
     {js_InitNamespaceClass,             EAGER_ATOM_AND_EXT_CLASP(Namespace)},
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -627,17 +627,19 @@ js_GetCallObject(JSContext *cx, JSStackF
         JSObject *env = js_NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL,
                                                    fp->scopeChain, 0);
         if (!env)
             return JS_FALSE;
 
         /* Root env. */
         fp->scopeChain = env;
     }
-    callobj = js_NewObject(cx, &js_CallClass, NULL, fp->scopeChain, 0);
+
+    callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL,
+                                         fp->scopeChain, 0);
     if (!callobj)
         return NULL;
 
     JS_SetPrivate(cx, callobj, fp);
     JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee));
     STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee));
     if (lambdaName &&
         !js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
@@ -990,21 +992,20 @@ call_reserveSlots(JSContext *cx, JSObjec
 {
     JSFunction *fun;
 
     fun = GetCallObjectFunction(obj);
     return JS_GET_LOCAL_NAME_COUNT(fun);
 }
 
 JS_FRIEND_DATA(JSClass) js_CallClass = {
-    js_Call_str,
+    "Call",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
-    JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS |
-    JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Call),
+    JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
     JS_PropertyStub,    JS_PropertyStub,
     JS_PropertyStub,    JS_PropertyStub,
     call_enumerate,     (JSResolveOp)call_resolve,
     call_convert,       JS_FinalizeStub,
     NULL,               NULL,
     NULL,               NULL,
     NULL,               NULL,
     JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
@@ -2093,34 +2094,16 @@ js_InitFunctionClass(JSContext *cx, JSOb
 #endif
     return proto;
 
 bad:
     cx->weakRoots.newborn[GCX_OBJECT] = NULL;
     return NULL;
 }
 
-JSObject *
-js_InitCallClass(JSContext *cx, JSObject *obj)
-{
-    JSObject *proto;
-
-    proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
-                         NULL, NULL, NULL, NULL);
-    if (!proto)
-        return NULL;
-
-    /*
-     * Null Call.prototype's proto slot so that Object.prototype.* does not
-     * pollute the scope of heavyweight functions.
-     */
-    OBJ_CLEAR_PROTO(cx, proto);
-    return proto;
-}
-
 JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
                uintN flags, JSObject *parent, JSAtom *atom)
 {
     JSFunction *fun;
 
     if (funobj) {
         JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -192,19 +192,16 @@ extern JS_FRIEND_DATA(JSClass) js_Functi
      (JSFunction *) OBJ_GET_PRIVATE(cx, funobj))
 
 extern JSObject *
 js_InitFunctionClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitArgumentsClass(JSContext *cx, JSObject *obj);
 
-extern JSObject *
-js_InitCallClass(JSContext *cx, JSObject *obj);
-
 extern JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
                uintN flags, JSObject *parent, JSAtom *atom);
 
 extern void
 js_TraceFunction(JSTracer *trc, JSFunction *fun);
 
 extern void
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1928,65 +1928,46 @@ js_EnterWith(JSContext *cx, jsint stackI
         return JS_FALSE;
 
     withobj = js_NewWithObject(cx, obj, parent,
                                sp + stackIndex - StackBase(fp));
     if (!withobj)
         return JS_FALSE;
 
     fp->scopeChain = withobj;
-    js_DisablePropertyCache(cx);
     return JS_TRUE;
 }
 
 JS_STATIC_INTERPRET JS_REQUIRES_STACK void
 js_LeaveWith(JSContext *cx)
 {
     JSObject *withobj;
 
     withobj = cx->fp->scopeChain;
     JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
     JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
     JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
     cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
     JS_SetPrivate(cx, withobj, NULL);
-    js_EnablePropertyCache(cx);
 }
 
 JS_REQUIRES_STACK JSClass *
 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
 {
     JSClass *clasp;
 
     clasp = OBJ_GET_CLASS(cx, obj);
     if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
         OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
         OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
         return clasp;
     }
     return NULL;
 }
 
-JS_STATIC_INTERPRET JS_REQUIRES_STACK jsint
-js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)
-{
-    jsint n;
-    JSObject *obj;
-    JSClass *clasp;
-
-    n = 0;
-    for (obj = fp->scopeChain;
-         (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;
-         obj = OBJ_GET_PARENT(cx, obj)) {
-        if (clasp == &js_WithClass)
-            ++n;
-    }
-    return n;
-}
-
 /*
  * Unwind block and scope chains to match the given depth. The function sets
  * fp->sp on return to stackDepth.
  */
 JS_REQUIRES_STACK JSBool
 js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
                JSBool normalUnwind)
 {
@@ -2921,19 +2902,16 @@ js_Interpret(JSContext *cx)
         js_SetVersion(cx, currentVersion);
 
     /* Update the static-link display. */
     if (script->staticLevel < JS_DISPLAY_SIZE) {
         JSStackFrame **disp = &cx->display[script->staticLevel];
         fp->displaySave = *disp;
         *disp = fp;
     }
-#ifdef DEBUG
-    fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
-#endif
 
 # define CHECK_INTERRUPT_HANDLER()                                            \
     JS_BEGIN_MACRO                                                            \
         if (cx->debugHooks->interruptHandler)                                 \
             ENABLE_INTERRUPTS();                                              \
     JS_END_MACRO
 
     /*
@@ -2960,18 +2938,16 @@ js_Interpret(JSContext *cx)
 
         JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
         gen = FRAME_TO_GENERATOR(fp);
         JS_ASSERT(fp->regs == &gen->savedRegs);
         regs = gen->savedRegs;
         fp->regs = &regs;
         JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
         JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
-        JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
-        JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->throwing is set.
          */
         if (cx->throwing) {
 #ifdef DEBUG_NOT_THROWING
             if (cx->exception != JSVAL_ARETURN) {
@@ -3189,17 +3165,16 @@ js_Interpret(JSContext *cx)
             }
             ok = JS_TRUE;
             if (inlineCallCount)
           inline_return:
             {
                 JSInlineFrame *ifp = (JSInlineFrame *) fp;
                 void *hookData = ifp->hookData;
 
-                JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
                 JS_ASSERT(!fp->blockChain);
                 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
 
                 if (script->staticLevel < JS_DISPLAY_SIZE)
                     cx->display[script->staticLevel] = fp->displaySave;
 
                 if (hookData) {
                     JSInterpreterHook hook;
@@ -4189,17 +4164,17 @@ js_Interpret(JSContext *cx)
                     JS_UNLOCK_OBJ(cx, obj2);
                     LOAD_ATOM(0);
                 }
             } else {
                 entry = NULL;
                 LOAD_ATOM(0);
             }
             id = ATOM_TO_JSID(atom);
-            if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
+            if (!js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry))
                 goto error;
             if (!prop)
                 goto atom_not_defined;
             OBJ_DROP_PROPERTY(cx, obj2, prop);
           }
 
           do_incop:
           {
@@ -5045,20 +5020,16 @@ js_Interpret(JSContext *cx)
                     newifp->frame.dormantNext = NULL;
                     newifp->frame.xmlNamespace = NULL;
                     newifp->frame.blockChain = NULL;
                     if (script->staticLevel < JS_DISPLAY_SIZE) {
                         JSStackFrame **disp = &cx->display[script->staticLevel];
                         newifp->frame.displaySave = *disp;
                         *disp = &newifp->frame;
                     }
-#ifdef DEBUG
-                    newifp->frame.pcDisabledSave =
-                        JS_PROPERTY_CACHE(cx).disabled;
-#endif
                     newifp->mark = newmark;
 
                     /* Compute the 'this' parameter now that argv is set. */
                     JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
                     JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
                     newifp->frame.thisp = (JSObject *)vp[1];
 
                     newifp->frame.regs = NULL;
@@ -5281,17 +5252,17 @@ js_Interpret(JSContext *cx)
                     goto do_native_get;
                 }
             } else {
                 entry = NULL;
                 LOAD_ATOM(0);
             }
 
             id = ATOM_TO_JSID(atom);
-            if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
+            if (!js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry))
                 goto error;
             if (!prop) {
                 /* Kludge to allow (typeof foo == "undefined") tests. */
                 endpc = script->code + script->length;
                 op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
                 if (op2 == JSOP_TYPEOF) {
                     PUSH_OPND(JSVAL_VOID);
                     len = JSOP_NAME_LENGTH;
@@ -7335,30 +7306,27 @@ js_Interpret(JSContext *cx)
 #endif
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(fp->flags & JSFRAME_YIELDING)) {
         JSGenerator *gen;
 
         gen = FRAME_TO_GENERATOR(fp);
         gen->savedRegs = regs;
         gen->frame.regs = &gen->savedRegs;
-        JS_PROPERTY_CACHE(cx).disabled -= js_CountWithBlocks(cx, fp);
-        JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
     } else
 #endif /* JS_HAS_GENERATORS */
     {
         JS_ASSERT(!fp->blockChain);
         JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
         fp->regs = NULL;
     }
 
     /* Undo the remaining effects committed on entry to js_Interpret. */
     if (script->staticLevel < JS_DISPLAY_SIZE)
         cx->display[script->staticLevel] = fp->displaySave;
-    JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
     if (cx->version == currentVersion && currentVersion != originalVersion)
         js_SetVersion(cx, originalVersion);
     --cx->interpLevel;
 
 #ifdef JS_TRACER
     if (tr) {
         SET_TRACE_RECORDER(cx, tr);
         if (!tr->wasDeepAborted()) {
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -124,19 +124,16 @@ struct JSStackFrame {
 
     uintN           sharpDepth;     /* array/object initializer depth */
     JSObject        *sharpArray;    /* scope for #n= initializer vars */
     uint32          flags;          /* frame flags -- see below */
     JSStackFrame    *dormantNext;   /* next dormant frame chain */
     JSObject        *xmlNamespace;  /* null or default xml namespace in E4X */
     JSStackFrame    *displaySave;   /* previous value of display entry for
                                        script->staticLevel */
-#ifdef DEBUG
-    jsrefcount      pcDisabledSave; /* for balanced property cache control */
-#endif
 
 #ifdef __cplusplus /* Aargh, LiveConnect, bug 442399. */
     inline void assertValidStackDepth(uintN depth);
 #endif
 };
 
 #ifdef __cplusplus
 static JS_INLINE uintN
@@ -611,19 +608,16 @@ extern JS_REQUIRES_STACK JSBool
 js_EnterWith(JSContext *cx, jsint stackIndex);
 
 extern JS_REQUIRES_STACK void
 js_LeaveWith(JSContext *cx);
 
 extern JS_REQUIRES_STACK JSClass *
 js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth);
 
-extern jsint
-js_CountWithBlocks(JSContext *cx, JSStackFrame *fp);
-
 /*
  * Unwind block and scope chains to match the given depth. The function sets
  * fp->sp on return to stackDepth.
  */
 extern JS_REQUIRES_STACK JSBool
 js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
                JSBool normalUnwind);
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -191,21 +191,28 @@ obj_getSlot(JSContext *cx, JSObject *obj
         return JS_FALSE;
 
     pobj = JSVAL_TO_OBJECT(*vp);
     if (pobj) {
         clasp = OBJ_GET_CLASS(cx, pobj);
         if (clasp == &js_CallClass || clasp == &js_BlockClass) {
             /* Censor activations and lexical scopes per ECMA-262. */
             *vp = JSVAL_NULL;
-        } else if (pobj->map->ops->thisObject) {
-            pobj = pobj->map->ops->thisObject(cx, pobj);
-            if (!pobj)
-                return JS_FALSE;
-            *vp = OBJECT_TO_JSVAL(pobj);
+        } else {
+            /*
+             * DeclEnv only exists as a parent for a Call object which we
+             * censor. So it cannot escape to scripts.
+             */
+            JS_ASSERT(clasp != &js_DeclEnvClass);
+            if (pobj->map->ops->thisObject) {
+                pobj = pobj->map->ops->thisObject(cx, pobj);
+                if (!pobj)
+                    return JS_FALSE;
+                *vp = OBJECT_TO_JSVAL(pobj);
+            }
         }
     }
     return JS_TRUE;
 }
 
 static JSBool
 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
@@ -4001,99 +4008,140 @@ js_LookupPropertyWithFlags(JSContext *cx
     }
 
 out:
     *objp = NULL;
     *propp = NULL;
     return protoIndex;
 }
 
-int
-js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
-                      JSObject **pobjp, JSProperty **propp,
-                      JSPropCacheEntry **entryp)
-{
-    JSObject *obj, *pobj, *lastobj;
-    uint32 shape;
-    int scopeIndex, protoIndex;
-    JSProperty *prop;
-    JSScopeProperty *sprop;
-
-    JS_ASSERT_IF(entryp, !JS_ON_TRACE(cx));
-    obj = js_GetTopStackFrame(cx)->scopeChain;
-    shape = OBJ_SHAPE(obj);
-    for (scopeIndex = 0; ; scopeIndex++) {
-        if (obj->map->ops->lookupProperty == js_LookupProperty) {
-            protoIndex =
-                js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
-                                           &pobj, &prop);
-            if (protoIndex < 0)
-                return -1;
-        } else {
-            if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
-                return -1;
-            protoIndex = -1;
-        }
-
-        if (prop) {
-            if (entryp) {
-                if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) {
-                    sprop = (JSScopeProperty *) prop;
-                    js_FillPropertyCache(cx, cx->fp->scopeChain, shape,
-                                         scopeIndex, protoIndex, pobj, sprop,
-                                         entryp);
-                } else {
-                    PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
-                    *entryp = NULL;
-                }
-            }
-            SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
-            *objp = obj;
-            *pobjp = pobj;
-            *propp = prop;
-            return scopeIndex;
-        }
-
-        lastobj = obj;
-        obj = OBJ_GET_PARENT(cx, obj);
-        if (!obj)
-            break;
-    }
-
-    *objp = lastobj;
-    *pobjp = NULL;
-    *propp = NULL;
-    return scopeIndex;
-}
-
-JS_FRIEND_API(JSBool)
-js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
-                JSProperty **propp)
-{
-    return js_FindPropertyHelper(cx, id, objp, pobjp, propp, NULL) >= 0;
-}
-
 /*
- * We cache property lookup results for JSOP_BIND only for the global object or
- * for native non-global objects without resolve hooks, see bug 462734.
+ * We cache name lookup results only for the global object or for native
+ * non-global objects without prototype or with prototype that never mutates,
+ * see bug 462734 and bug 487039.
  */
 static inline bool
 IsCacheableNonGlobalScope(JSObject *obj)
 {
     JS_ASSERT(STOBJ_GET_PARENT(obj));
 
     JSClass *clasp = STOBJ_GET_CLASS(obj);
     bool cacheable = (clasp == &js_CallClass ||
                       clasp == &js_BlockClass ||
                       clasp == &js_DeclEnvClass);
 
     JS_ASSERT_IF(cacheable, obj->map->ops->lookupProperty == js_LookupProperty);
     return cacheable;
 }
 
+int
+js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
+                      JSObject **pobjp, JSProperty **propp,
+                      JSPropCacheEntry **entryp)
+{
+    JSObject *scopeChain, *obj, *parent, *pobj;
+    uint32 shape;
+    int scopeIndex, protoIndex;
+    JSProperty *prop;
+    JSScopeProperty *sprop;
+
+    JS_ASSERT_IF(entryp, !JS_ON_TRACE(cx));
+    scopeChain = js_GetTopStackFrame(cx)->scopeChain;
+    shape = OBJ_SHAPE(scopeChain);
+
+    /* Scan entries on the scope chain that we can cache across. */
+    obj = scopeChain;
+    parent = OBJ_GET_PARENT(cx, obj);
+    for (scopeIndex = 0;
+         parent
+         ? IsCacheableNonGlobalScope(obj)
+         : obj->map->ops->lookupProperty == js_LookupProperty;
+         ++scopeIndex) {
+        protoIndex =
+            js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
+                                       &pobj, &prop);
+        if (protoIndex < 0)
+            return false;
+
+        if (prop) {
+#ifdef DEBUG
+            if (parent) {
+                JSClass *clasp = OBJ_GET_CLASS(cx, obj);
+                JS_ASSERT(OBJ_IS_NATIVE(pobj));
+                JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == clasp);
+                if (clasp == &js_BlockClass) {
+                    /*
+                     * Block instances on the scope chain are immutable and
+                     * always share their scope with compile-time prototypes.
+                     */
+                    JS_ASSERT(pobj == OBJ_GET_PROTO(cx, obj));
+                    JS_ASSERT(OBJ_SCOPE(obj)->object == pobj);
+                    JS_ASSERT(protoIndex == 1);
+                } else {
+                    /* Call and DeclEnvClass objects have no prototypes. */
+                    JS_ASSERT(!OBJ_GET_PROTO(cx, obj));
+                    JS_ASSERT(protoIndex == 0);
+                }
+            }
+#endif
+            if (entryp) {
+                sprop = (JSScopeProperty *) prop;
+                js_FillPropertyCache(cx, scopeChain, shape,
+                                     scopeIndex, protoIndex, pobj, sprop,
+                                     entryp);
+            }
+            SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex);
+            goto out;
+        }
+
+        if (!parent) {
+            pobj = NULL;
+            goto out;
+        }
+        obj = parent;
+        parent = OBJ_GET_PARENT(cx, obj);
+    }
+
+    for (;;) {
+        if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
+            return false;
+        if (prop) {
+            PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
+            if (entryp)
+                *entryp = NULL;
+            goto out;
+        }
+
+        /*
+         * We conservatively assume that a resolve hook could mutate the scope
+         * chain during OBJ_LOOKUP_PROPERTY. So we read parent here again.
+         */
+        parent = OBJ_GET_PARENT(cx, obj);
+        if (!parent) {
+            pobj = NULL;
+            break;
+        }
+        obj = parent;
+    }
+
+  out:
+    JS_ASSERT(!!pobj == !!prop);
+    *objp = obj;
+    *pobjp = pobj;
+    *propp = prop;
+    return true;
+}
+
+JS_FRIEND_API(JSBool)
+js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
+                JSProperty **propp)
+{
+    return js_FindPropertyHelper(cx, id, objp, pobjp, propp, NULL) >= 0;
+}
+
 JSObject *
 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id,
                       JSPropCacheEntry *entry)
 {
     /*
      * This function should not be called for a global object or from the
      * trace and should have a valid cache entry for native scopeChain.
      */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -662,17 +662,17 @@ js_LookupProperty(JSContext *cx, JSObjec
  * Specialized subroutine that allows caller to preset JSRESOLVE_* flags and
  * returns the index along the prototype chain in which *propp was found, or
  * the last index if not found, or -1 on error.
  */
 extern int
 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
                            JSObject **objp, JSProperty **propp);
 
-extern int
+extern JSBool
 js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp,
                       JSObject **pobjp, JSProperty **propp,
                       JSPropCacheEntry **entryp);
 
 /*
  * Return the index along the scope chain in which id was found, or the last
  * index if not found, or -1 on error.
  */
--- a/js/src/jsproto.tbl
+++ b/js/src/jsproto.tbl
@@ -83,17 +83,17 @@
  * the JS XDR API.  Client modules including jsproto.tbl should consider
  * wrapping the inclusion with JS_BEGIN_EXTERN_C and JS_END_EXTERN_C.
  */
 JS_PROTO(Null,                   0,     js_InitNullClass)
 JS_PROTO(Object,                 1,     js_InitFunctionAndObjectClasses)
 JS_PROTO(Function,               2,     js_InitFunctionAndObjectClasses)
 JS_PROTO(Array,                  3,     js_InitArrayClass)
 JS_PROTO(Boolean,                4,     js_InitBooleanClass)
-JS_PROTO(Call,                   5,     js_InitCallClass)
+JS_PROTO(JSON,                   5,     js_InitJSONClass)
 JS_PROTO(Date,                   6,     js_InitDateClass)
 JS_PROTO(Math,                   7,     js_InitMathClass)
 JS_PROTO(Number,                 8,     js_InitNumberClass)
 JS_PROTO(String,                 9,     js_InitStringClass)
 JS_PROTO(RegExp,                10,     js_InitRegExpClass)
 JS_PROTO(Script,                11,     SCRIPT_INIT)
 JS_PROTO(XML,                   12,     XML_INIT)
 JS_PROTO(Namespace,             13,     NAMESPACE_INIT)
@@ -111,17 +111,16 @@ JS_PROTO(URIError,              24,     
 JS_PROTO(Generator,             25,     GENERATOR_INIT)
 JS_PROTO(Iterator,              26,     js_InitIteratorClasses)
 JS_PROTO(StopIteration,         27,     js_InitIteratorClasses)
 JS_PROTO(UnusedProto28,         28,     js_InitNullClass)
 JS_PROTO(File,                  29,     FILE_INIT)
 JS_PROTO(Block,                 30,     js_InitBlockClass)
 JS_PROTO(XMLFilter,             31,     XMLFILTER_INIT)
 JS_PROTO(NoSuchMethod,          32,     NO_SUCH_METHOD_INIT)
-JS_PROTO(JSON,                  33,     js_InitJSONClass)
 
 #undef SCRIPT_INIT
 #undef XML_INIT
 #undef NAMESPACE_INIT
 #undef QNAME_INIT
 #undef ANYNAME_INIT
 #undef ATTRIBUTE_INIT
 #undef GENERATOR_INIT
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3406,19 +3406,16 @@ js_SynthesizeFrame(JSContext* cx, const 
     newifp->frame.regs->sp = newsp + script->nfixed;
     newifp->frame.imacpc = NULL;
     newifp->frame.slots = newsp;
     if (script->staticLevel < JS_DISPLAY_SIZE) {
         JSStackFrame **disp = &cx->display[script->staticLevel];
         newifp->frame.displaySave = *disp;
         *disp = &newifp->frame;
     }
-#ifdef DEBUG
-    newifp->frame.pcDisabledSave = 0;
-#endif
 
     /*
      * Note that fp->script is still the caller's script; set the callee
      * inline frame's idea of caller version from its version.
      */
     newifp->callerVersion = (JSVersion) fp->script->version;
 
     // After this paragraph, fp and cx->fp point to the newly synthesized frame.
@@ -5955,17 +5952,17 @@ TraceRecorder::test_property_cache(JSObj
     } else {
         // Miss: pre-fill the cache for the interpreter, as well as for our needs.
         // FIXME: 452357 - correctly propagate exceptions into the interpreter from
         // js_FindPropertyHelper, js_LookupPropertyWithFlags, and elsewhere.
         jsid id = ATOM_TO_JSID(atom);
         JSProperty* prop;
         if (JOF_OPMODE(*pc) == JOF_NAME) {
             JS_ASSERT(aobj == obj);
-            if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
+            if (!js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry))
                 ABORT_TRACE("failed to find name");
         } else {
             int protoIndex = js_LookupPropertyWithFlags(cx, aobj, id,
                                                         cx->resolveFlags,
                                                         &obj2, &prop);
             if (protoIndex < 0)
                 ABORT_TRACE("failed to lookup property");
 
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 46)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 47)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool