bug 491013 - fixing shared setter regression and eliminating several useless anonymous prototype objects. r=brendan
☠☠ backed out by 38512deaca7e ☠ ☠
authorIgor Bukanov <igor@mir2.org>
Sun, 03 May 2009 20:43:55 -0400
changeset 28006 6534f8b9aa74ba67ee31dc87e5250eeeb3c185aa
parent 28005 c7515a091bff3ad12e7fa6d0fb96ac83c2776171
child 28007 38512deaca7e5ca4fcd39ca9c60f2640fa3ad773
push id6839
push userrsayre@mozilla.com
push dateTue, 05 May 2009 18:41:02 +0000
treeherdermozilla-central@3065dcc6f64e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs491013
milestone1.9.2a1pre
bug 491013 - fixing shared setter regression and eliminating several useless anonymous prototype objects. r=brendan
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsproto.tbl
js/src/jsregexp.cpp
js/src/jsregexp.h
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsxdrapi.h
js/src/jsxml.cpp
js/src/jsxml.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_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) &&
@@ -1393,17 +1392,16 @@ StdNameToAtom(JSContext *cx, JSStdName *
 /*
  * Table of class initializers and their atom offsets in rt->atomState.
  * If you add a "standard" class, remember to update this table.
  */
 static JSStdName standard_class_atoms[] = {
     {js_InitFunctionAndObjectClasses,   EAGER_ATOM_AND_CLASP(Function)},
     {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_InitExceptionClasses,           EAGER_ATOM_AND_CLASP(Error)},
     {js_InitRegExpClass,                EAGER_ATOM_AND_CLASP(RegExp)},
 #if JS_HAS_SCRIPT_OBJECT
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1252,18 +1252,18 @@ fun_convert(JSContext *cx, JSObject *obj
       default:
         return js_TryValueOf(cx, obj, type, vp);
     }
 }
 
 #if JS_HAS_XDR
 
 /* XXX store parent and proto, if defined */
-static JSBool
-fun_xdrObject(JSXDRState *xdr, JSObject **objp)
+JSBool
+js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
 {
     JSContext *cx;
     JSFunction *fun;
     uint32 nullAtom;            /* flag to indicate if fun->atom is NULL */
     uintN nargs, nvars, nupvars, n;
     uint32 localsword;          /* word to xdr argument and variable counts */
     uint32 flagsword;           /* word to xdr upvars count and fun->flags */
     JSTempValueRooter tvr;
@@ -1435,17 +1435,17 @@ out:
 
 bad:
     ok = JS_FALSE;
     goto out;
 }
 
 #else  /* !JS_HAS_XDR */
 
-#define fun_xdrObject NULL
+#define js_XDRFunctionObject NULL
 
 #endif /* !JS_HAS_XDR */
 
 /*
  * [[HasInstance]] internal method for Function objects: fetch the .prototype
  * property of its 'this' parameter, and walks the prototype chain of v (only
  * if v is an object) returning true if .prototype is found.
  */
@@ -1557,17 +1557,17 @@ JS_FRIEND_DATA(JSClass) js_FunctionClass
     JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     JS_PropertyStub,  JS_PropertyStub,
     JS_PropertyStub,  JS_PropertyStub,
     fun_enumerate,    (JSResolveOp)fun_resolve,
     fun_convert,      fun_finalize,
     NULL,             NULL,
     NULL,             NULL,
-    fun_xdrObject,    fun_hasInstance,
+    js_XDRFunctionObject, fun_hasInstance,
     JS_CLASS_TRACE(fun_trace), fun_reserveSlots
 };
 
 static JSBool
 fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp)
 {
     jsval fval;
     JSObject *obj;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -258,17 +258,17 @@ js_GetArgsProperty(JSContext *cx, JSStac
 
 extern JSObject *
 js_GetArgsObject(JSContext *cx, JSStackFrame *fp);
 
 extern JS_FRIEND_API(JSBool)
 js_PutArgsObject(JSContext *cx, JSStackFrame *fp);
 
 extern JSBool
-js_XDRFunction(JSXDRState *xdr, JSObject **objp);
+js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp);
 
 typedef enum JSLocalKind {
     JSLOCAL_NONE,
     JSLOCAL_ARG,
     JSLOCAL_VAR,
     JSLOCAL_CONST,
     JSLOCAL_UPVAR
 } JSLocalKind;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -956,44 +956,22 @@ js_ComputeThis(JSContext *cx, JSBool laz
 
 #if JS_HAS_NO_SUCH_METHOD
 
 #define JSSLOT_FOUND_FUNCTION   JSSLOT_PRIVATE
 #define JSSLOT_SAVED_ID         (JSSLOT_PRIVATE + 1)
 
 JSClass js_NoSuchMethodClass = {
     "NoSuchMethod",
-    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),
+    JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-JS_BEGIN_EXTERN_C
-
-JSObject*
-js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);
-
-JS_END_EXTERN_C
-
-JSObject*
-js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)
-{
-    JSObject *proto;
-
-    proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,
-                         NULL, NULL, NULL);
-    if (!proto)
-        return NULL;
-
-    OBJ_CLEAR_PROTO(cx, proto);
-    return proto;
-}
-
 /*
  * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
  * the base object, we search for the __noSuchMethod__ method in the base.
  * If it exists, we store the method and the property's id into an object of
  * NoSuchMethod class and store this object into the callee's stack slot.
  * Later, js_Invoke will recognise such an object and transfer control to
  * NoSuchMethod that invokes the method like:
  *
@@ -1029,17 +1007,18 @@ js_OnUnknownMethod(JSContext *cx, jsval 
             obj = JSVAL_TO_OBJECT(vp[0]);
             ok = js_IsFunctionQName(cx, obj, &id);
             if (!ok)
                 goto out;
             if (id != 0)
                 vp[0] = ID_TO_VALUE(id);
         }
 #endif
-        obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);
+        obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass,
+                                         NULL, NULL, 0);
         if (!obj) {
             ok = JS_FALSE;
             goto out;
         }
         obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;
         obj->fslots[JSSLOT_SAVED_ID] = vp[0];
         vp[0] = OBJECT_TO_JSVAL(obj);
     }
@@ -4626,40 +4605,49 @@ js_Interpret(JSContext *cx)
                         JS_LOCK_OBJ(cx, obj);
                         scope = OBJ_SCOPE(obj);
                         if (scope->shape == kshape) {
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                             sprop = PCVAL_TO_SPROP(entry->vword);
                             JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
                             JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj)));
 
-                            if (scope->object == obj) {
-                                /*
-                                 * Fastest path: the cached sprop is already
-                                 * in scope. Just NATIVE_SET and break to get
-                                 * out of the do-while(0).
-                                 */
+                            /*
+                             * Fastest path: check whether the cached sprop is
+                             * already in scope and call NATIVE_GET and break
+                             * to get out of the do-while(0). But we can call
+                             * NATIVE_GET only if obj owns scope or sprop is
+                             * shared.
+                             */
+                            bool checkForAdd;
+                            if (scope->object == obj ||
+                                (sprop->attrs & JSPROP_SHARED)) {
                                 if (sprop == scope->lastProp ||
                                     SCOPE_HAS_PROPERTY(scope, sprop)) {
                                     PCMETER(cache->pchits++);
                                     PCMETER(cache->setpchits++);
                                     NATIVE_SET(cx, obj, sprop, entry, &rval);
                                     JS_UNLOCK_SCOPE(cx, scope);
                                     break;
                                 }
+                                checkForAdd =
+                                    !(sprop->attrs & JSPROP_SHARED) &&
+                                    sprop->parent == scope->lastProp &&
+                                    !SCOPE_HAD_MIDDLE_DELETE(scope);
+
                             } else {
                                 scope = js_GetMutableScope(cx, obj);
                                 if (!scope) {
                                     JS_UNLOCK_OBJ(cx, obj);
                                     goto error;
                                 }
+                                checkForAdd = !sprop->parent;
                             }
 
-                            if (sprop->parent == scope->lastProp &&
-                                !SCOPE_HAD_MIDDLE_DELETE(scope) &&
+                            if (checkForAdd &&
                                 SPROP_HAS_STUB_SETTER(sprop) &&
                                 (slot = sprop->slot) == scope->map.freeslot) {
                                 /*
                                  * Fast path: adding a plain old property that
                                  * was once at the frontier of the property
                                  * tree, whose slot is next to claim among the
                                  * allocated slots in obj, where scope->table
                                  * has not been created yet.
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2406,34 +2406,24 @@ js_NewWithObject(JSContext *cx, JSObject
     STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
     return obj;
 }
 
 JSObject *
 js_NewBlockObject(JSContext *cx)
 {
-    JSObject *obj;
-    JSBool ok;
-
     /*
      * Null obj's proto slot so that Object.prototype.* does not pollute block
-     * scopes.  Make sure obj has its own scope too, since clearing proto does
-     * not affect OBJ_SCOPE(obj).
+     * scopes and to give the block object its own scope.
      */
-    obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0);
-    if (!obj)
-        return NULL;
-    JS_LOCK_OBJ(cx, obj);
-    ok = js_GetMutableScope(cx, obj) != NULL;
-    JS_UNLOCK_OBJ(cx, obj);
-    if (!ok)
-        return NULL;
-    OBJ_CLEAR_PROTO(cx, obj);
-    return obj;
+    JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass,
+                                                    NULL, NULL, 0);
+    JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj));
+    return blockObj;
 }
 
 JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
                     JSStackFrame *fp)
 {
     JSObject *clone;
 
@@ -2441,16 +2431,17 @@ js_CloneBlockObject(JSContext *cx, JSObj
     JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
     clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0);
     if (!clone)
         return NULL;
     STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp));
     STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH,
                    OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH));
     JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
+    JS_ASSERT(OBJ_SCOPE(clone)->object == proto);
     return clone;
 }
 
 JS_REQUIRES_STACK JSBool
 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
 {
     JSStackFrame *fp;
     JSObject *obj;
@@ -2568,18 +2559,18 @@ FindObjectIndex(JSObjectArray *array, JS
             if (array->vector[--i] == obj)
                 return i;
         } while (i != 0);
     }
 
     return NO_PARENT_INDEX;
 }
 
-static JSBool
-block_xdrObject(JSXDRState *xdr, JSObject **objp)
+JSBool
+js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
 {
     JSContext *cx;
     uint32 parentId;
     JSObject *obj, *parent;
     uint16 depth, count, i;
     uint32 tmp;
     JSTempValueRooter tvr;
     JSScopeProperty *sprop;
@@ -2682,49 +2673,32 @@ block_xdrObject(JSXDRState *xdr, JSObjec
             }
         }
     }
 
     JS_POP_TEMP_ROOT(cx, &tvr);
     return ok;
 }
 
-#else
-# define block_xdrObject NULL
 #endif
 
 static uint32
 block_reserveSlots(JSContext *cx, JSObject *obj)
 {
     return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0;
 }
 
 JSClass js_BlockClass = {
     "Block",
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
-    JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
+    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,  JS_PropertyStub,  block_getProperty, block_setProperty,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
-    NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, block_reserveSlots
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots
 };
 
-JSObject*
-js_InitBlockClass(JSContext *cx, JSObject* obj)
-{
-    JSObject *proto;
-
-    proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
-                         NULL, NULL, NULL);
-    if (!proto)
-        return NULL;
-
-    OBJ_CLEAR_PROTO(cx, proto);
-    return proto;
-}
-
 JSObject *
 js_InitEval(JSContext *cx, JSObject *obj)
 {
     /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
     if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
                            obj_eval, 1, 0)) {
         return NULL;
     }
@@ -4272,17 +4246,17 @@ js_NativeSet(JSContext *cx, JSObject *ob
     uint32 slot;
     int32 sample;
     JSTempValueRooter tvr;
     JSBool ok;
 
     JS_ASSERT(OBJ_IS_NATIVE(obj));
     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
     scope = OBJ_SCOPE(obj);
-    JS_ASSERT(scope->object == obj);
+    JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED));
 
     slot = sprop->slot;
     if (slot != SPROP_INVALID_SLOT) {
         OBJ_CHECK_SLOT(obj, slot);
 
         /* If sprop has a stub setter, keep scope locked and just store *vp. */
         if (SPROP_HAS_STUB_SETTER(sprop))
             goto set_slot;
@@ -4306,17 +4280,17 @@ js_NativeSet(JSContext *cx, JSObject *ob
     JS_UNLOCK_SCOPE(cx, scope);
     JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);
     ok = js_SetSprop(cx, sprop, obj, vp);
     JS_POP_TEMP_ROOT(cx, &tvr);
     if (!ok)
         return JS_FALSE;
 
     JS_LOCK_SCOPE(cx, scope);
-    JS_ASSERT(scope->object == obj);
+    JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED));
     if (SLOT_IN_SCOPE(slot, scope) &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
          SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) {
   set_slot:
         LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, *vp);
     }
 
     return JS_TRUE;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -429,16 +429,19 @@ js_NewBlockObject(JSContext *cx);
 
 extern JSObject *
 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
                     JSStackFrame *fp);
 
 extern JS_REQUIRES_STACK JSBool
 js_PutBlockObject(JSContext *cx, JSBool normalUnwind);
 
+JSBool
+js_XDRBlockObject(JSXDRState *xdr, JSObject **objp);
+
 struct JSSharpObjectMap {
     jsrefcount  depth;
     jsatomid    sharpgen;
     JSHashTable *table;
 };
 
 #define SHARP_BIT       ((jsatomid) 1)
 #define BUSY_BIT        ((jsatomid) 2)
@@ -470,19 +473,16 @@ js_HasOwnPropertyHelper(JSContext *cx, J
 extern JSBool
 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
                   jsval *vp);
 
 extern JSBool
 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
 extern JSObject *
-js_InitBlockClass(JSContext *cx, JSObject* obj);
-
-extern JSObject *
 js_InitEval(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitObjectClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
              JSClass *clasp, JSNative constructor, uintN nargs,
--- a/js/src/jsproto.tbl
+++ b/js/src/jsproto.tbl
@@ -61,22 +61,16 @@
 #endif
 
 #if JS_HAS_GENERATORS
 # define GENERATOR_INIT                 js_InitIteratorClasses
 #else
 # define GENERATOR_INIT                 js_InitNullClass
 #endif
 
-#if JS_HAS_NO_SUCH_METHOD
-# define NO_SUCH_METHOD_INIT            js_InitNoSuchMethodClass
-#else
-# define NO_SUCH_METHOD_INIT            js_InitNullClass
-#endif
-
 #if JS_HAS_FILE_OBJECT
 # define FILE_INIT                      js_InitFileClass
 #else
 # define FILE_INIT                      js_InitNullClass
 #endif
 
 /*
  * Enumerator codes in the second column must not change -- they are part of
@@ -106,23 +100,18 @@ JS_PROTO(EvalError,             19,     
 JS_PROTO(RangeError,            20,     js_InitExceptionClasses)
 JS_PROTO(ReferenceError,        21,     js_InitExceptionClasses)
 JS_PROTO(SyntaxError,           22,     js_InitExceptionClasses)
 JS_PROTO(TypeError,             23,     js_InitExceptionClasses)
 JS_PROTO(URIError,              24,     js_InitExceptionClasses)
 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(File,                  28,     FILE_INIT)
 
 #undef SCRIPT_INIT
 #undef XML_INIT
 #undef NAMESPACE_INIT
 #undef QNAME_INIT
 #undef ANYNAME_INIT
 #undef ATTRIBUTE_INIT
 #undef GENERATOR_INIT
 #undef FILE_INIT
-#undef NO_SUCH_METHOD_INIT
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -4554,18 +4554,18 @@ regexp_call(JSContext *cx, JSObject *obj
     return regexp_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv,
                            JS_FALSE, rval);
 }
 
 #if JS_HAS_XDR
 
 #include "jsxdrapi.h"
 
-static JSBool
-regexp_xdrObject(JSXDRState *xdr, JSObject **objp)
+JSBool
+js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
 {
     JSRegExp *re;
     JSString *source;
     uint32 flagsword;
     JSObject *obj;
 
     if (xdr->mode == JSXDR_ENCODE) {
         re = (JSRegExp *) JS_GetPrivate(xdr->cx, *objp);
@@ -4594,17 +4594,17 @@ regexp_xdrObject(JSXDRState *xdr, JSObje
         }
         *objp = obj;
     }
     return JS_TRUE;
 }
 
 #else  /* !JS_HAS_XDR */
 
-#define regexp_xdrObject NULL
+#define js_XDRRegExpObject NULL
 
 #endif /* !JS_HAS_XDR */
 
 static void
 regexp_trace(JSTracer *trc, JSObject *obj)
 {
     JSRegExp *re;
 
@@ -4618,17 +4618,17 @@ JSClass js_RegExpClass = {
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
     JS_PropertyStub,    JS_PropertyStub,
     regexp_getProperty, regexp_setProperty,
     JS_EnumerateStub,   JS_ResolveStub,
     JS_ConvertStub,     regexp_finalize,
     NULL,               NULL,
     regexp_call,        NULL,
-    regexp_xdrObject,   NULL,
+    js_XDRRegExpObject, NULL,
     JS_CLASS_TRACE(regexp_trace), 0
 };
 
 static const jschar empty_regexp_ucstr[] = {'(', '?', ':', ')', 0};
 
 JSBool
 js_regexp_toString(JSContext *cx, JSObject *obj, jsval *vp)
 {
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -175,17 +175,17 @@ js_regexp_toString(JSContext *cx, JSObje
 /*
  * Create, serialize/deserialize, or clone a RegExp object.
  */
 extern JSObject *
 js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
                    jschar *chars, size_t length, uintN flags);
 
 extern JSBool
-js_XDRRegExp(JSXDRState *xdr, JSObject **objp);
+js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp);
 
 extern JSObject *
 js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *parent);
 
 /*
  * Get and set the per-object (clone or clone-parent) lastIndex slot.
  */
 extern JSBool
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -66,16 +66,22 @@ js_GetMutableScope(JSContext *cx, JSObje
     JSScope *scope, *newscope;
     JSClass *clasp;
     uint32 freeslot;
 
     scope = OBJ_SCOPE(obj);
     JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
     if (scope->object == obj)
         return scope;
+
+    /*
+     * Compile-time block objects each have their own scope, created at
+     * birth, and runtime clone of a block objects are never mutated.
+     */
+    JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass);
     newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj),
                            obj);
     if (!newscope)
         return NULL;
     JS_LOCK_SCOPE(cx, newscope);
     obj->map = js_HoldObjectMap(cx, &newscope->map);
     JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
     clasp = STOBJ_GET_CLASS(obj);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -588,29 +588,45 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     for (i = 0; i != natoms; ++i) {
         if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
             goto error;
     }
 
     /*
      * Here looping from 0-to-length to xdr objects is essential. It ensures
      * that block objects from the script->objects array will be written and
-     * restored in the outer-to-inner order. block_xdrObject relies on this to
-     * restore the parent chain.
+     * restored in the outer-to-inner order. js_XDRBlockObject relies on this
+     * to restore the parent chain.
      */
     for (i = 0; i != nobjects; ++i) {
-        if (!js_XDRObject(xdr, &JS_SCRIPT_OBJECTS(script)->vector[i]))
+        JSObject **objp = &JS_SCRIPT_OBJECTS(script)->vector[i];
+        uint32 isBlock;
+        if (xdr->mode == JSXDR_ENCODE) {
+            JSClass *clasp = STOBJ_GET_CLASS(*objp);
+            JS_ASSERT(clasp == &js_FunctionClass ||
+                      clasp == &js_BlockClass);
+            isBlock = (clasp == &js_BlockClass);
+         }
+        if (!JS_XDRUint32(xdr, &isBlock))
             goto error;
+        if (isBlock == 0) {
+            if (!js_XDRFunctionObject(xdr, objp))
+                goto error;
+        } else {
+            JS_ASSERT(isBlock == 1);
+            if (!js_XDRBlockObject(xdr, objp))
+                goto error;
+        }
     }
     for (i = 0; i != nupvars; ++i) {
         if (!JS_XDRUint32(xdr, &JS_SCRIPT_UPVARS(script)->vector[i]))
             goto error;
     }
     for (i = 0; i != nregexps; ++i) {
-        if (!js_XDRObject(xdr, &JS_SCRIPT_REGEXPS(script)->vector[i]))
+        if (!js_XDRRegExpObject(xdr, &JS_SCRIPT_REGEXPS(script)->vector[i]))
             goto error;
     }
 
     if (ntrynotes != 0) {
         /*
          * We combine tn->kind and tn->stackDepth when serializing as XDR is not
          * efficient when serializing small integer types.
          */
--- 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 - 47)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 48)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -7618,18 +7618,16 @@ js_InitXMLClasses(JSContext *cx, JSObjec
     if (!js_InitNamespaceClass(cx, obj))
         return NULL;
     if (!js_InitQNameClass(cx, obj))
         return NULL;
     if (!js_InitAttributeNameClass(cx, obj))
         return NULL;
     if (!js_InitAnyNameClass(cx, obj))
         return NULL;
-    if (!js_InitXMLFilterClass(cx, obj))
-        return NULL;
     return js_InitXMLClass(cx, obj);
 }
 
 JSBool
 js_GetFunctionNamespace(JSContext *cx, jsval *vp)
 {
     JSRuntime *rt;
     JSObject *obj;
@@ -8128,40 +8126,23 @@ xmlfilter_finalize(JSContext *cx, JSObje
         return;
 
     XMLArrayCursorFinish(&filter->cursor);
     JS_free(cx, filter);
 }
 
 JSClass js_XMLFilterClass = {
     "XMLFilter",
-    JSCLASS_HAS_PRIVATE |
-    JSCLASS_IS_ANONYMOUS |
-    JSCLASS_MARK_IS_TRACE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_XMLFilter),
+    JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_MARK_IS_TRACE,
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    xmlfilter_finalize,
     NULL,              NULL,            NULL,              NULL,
     NULL,              NULL,            JS_CLASS_TRACE(xmlfilter_trace), NULL
 };
 
-JSObject *
-js_InitXMLFilterClass(JSContext *cx, JSObject *obj)
-{
-    JSObject *proto;
-
-    proto = JS_InitClass(cx, obj, NULL, &js_XMLFilterClass, NULL, 0, NULL,
-                         NULL, NULL, NULL);
-    if (!proto)
-        return NULL;
-
-    OBJ_CLEAR_PROTO(cx, proto);
-    return proto;
-}
-
 JSBool
 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
 {
     jsval *sp;
     JSObject *obj, *filterobj, *resobj, *kidobj;
     JSXML *xml, *list;
     JSXMLFilter *filter;
 
@@ -8190,17 +8171,18 @@ js_StepXMLListFilter(JSContext *cx, JSBo
              * as it may be the only root holding xml.
              */
             sp[-1] = OBJECT_TO_JSVAL(obj);
             list = (JSXML *) JS_GetPrivate(cx, obj);
             if (!Append(cx, list, xml))
                 return JS_FALSE;
         }
 
-        filterobj = js_NewObject(cx, &js_XMLFilterClass, NULL, NULL, 0);
+        filterobj = js_NewObjectWithGivenProto(cx, &js_XMLFilterClass,
+                                               NULL, NULL, 0);
         if (!filterobj)
             return JS_FALSE;
 
         filter = (JSXMLFilter *) JS_malloc(cx, sizeof *filter);
         if (!filter)
             return JS_FALSE;
 
         /*
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -259,19 +259,16 @@ extern JSBool
 js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
 extern JSBool
 js_GetXMLDescendants(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
 extern JSBool
 js_DeleteXMLListElements(JSContext *cx, JSObject *listobj);
 
-extern JSObject *
-js_InitXMLFilterClass(JSContext *cx, JSObject* obj);
-
 extern JSBool
 js_StepXMLListFilter(JSContext *cx, JSBool initialized);
 
 extern JSObject *
 js_ValueToXMLObject(JSContext *cx, jsval v);
 
 extern JSObject *
 js_ValueToXMLListObject(JSContext *cx, jsval v);