[INFER] Allow the same shape to be shared by objects with different types, bug 674621.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 28 Jul 2011 14:17:42 -0700
changeset 77392 d37f88fa371317e0c890e508225e62091ae7f021
parent 77391 65c33bba9d01dc819ff0c68d8a0c057aaeb42598
child 77393 aea5b967c21b2fa615d8698dd5aba5cccb69e9a4
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs674621
milestone8.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[INFER] Allow the same shape to be shared by objects with different types, bug 674621.
js/src/jit-test/tests/jaeger/setPropTypeGuard.js
js/src/jsarray.cpp
js/src/jsemit.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscopeinlines.h
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/setPropTypeGuard.js
@@ -0,0 +1,23 @@
+
+/*
+ * Get a SETPROP site which is monitored (unknown lhs) and is repeatedly
+ * invoked on objects with the same shape but different types (and without
+ * triggering a recompile of the function). The SETPROP PIC needs a type guard
+ * when the object is being monitored.
+ */
+var x = {g:0};
+var y = {g:0,f:"fubar"};
+x.f = 10;
+
+function foo(x) {
+  for (var i = 0; i < 30; i++)
+    x.f = 10;
+}
+function access(x) {
+  return x.f + 10;
+}
+foo(Object.create({}));
+eval("foo(x)");
+assertEq(access(y), "fubar10");
+eval("foo(y)");
+assertEq(access(y), 20);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1024,17 +1024,17 @@ JSObject::makeDenseArraySlow(JSContext *
      * Save old map now, before calling InitScopeForObject. We'll have to undo
      * on error. This is gross, but a better way is not obvious. Note: the
      * exact contents of the array are not preserved on error.
      */
     js::Shape *oldMap = lastProp;
 
     /* Create a native scope. */
     js::gc::FinalizeKind kind = js::gc::FinalizeKind(arenaHeader()->getThingKind());
-    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getType(cx), kind))
+    if (!InitScopeForObject(cx, this, &js_SlowArrayClass, getProto()->getNewType(cx), kind))
         return false;
 
     backfillDenseArrayHoles(cx);
 
     uint32 arrayCapacity = getDenseArrayCapacity();
     uint32 arrayInitialized = getDenseArrayInitializedLength();
 
     /*
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -7037,27 +7037,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
          * JSOP_NEWOBJECT with the final shape instead.
          */
         JSObject *obj = NULL;
         if (!cg->hasSharps() && cg->compileAndGo()) {
             gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false);
             obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
             if (!obj)
                 return JS_FALSE;
-
-            /*
-             * Generate a unique empty shape for obj, to distinguish it from
-             * initializers with the same fields created at other points.
-             * Each initialization site has a unique type, but as the script
-             * hasn't been created yet we don't know what that type is.
-             */
-            Shape *newshape = EmptyShape::create(cx, &js_ObjectClass);
-            if (!newshape)
-                return JS_FALSE;
-            obj->setMap(newshape);
         }
 
         uintN methodInits = 0, slowMethodInits = 0;
         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
             /* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
             pn3 = pn2->pn_left;
             if (pn3->pn_type == TOK_NUMBER) {
                 if (!EmitNumberOp(cx, pn3->pn_dval, cg))
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2540,17 +2540,17 @@ js_CloneFunctionObject(JSContext *cx, JS
         /*
          * We can use the same type as the original function provided that (a)
          * its prototype is correct, and (b) its type is not a singleton. The
          * first case will hold in all compileAndGo code, and the second case
          * will have been caught by CloneFunctionObject coming from function
          * definitions or read barriers, so will not get here.
          */
         if (fun->getProto() == proto && !fun->hasSingletonType())
-            clone->setTypeAndShape(fun->type(), fun->lastProperty());
+            clone->setType(fun->type());
 
         clone->setPrivate(fun);
     } else {
         /*
          * Across compartments we have to deep copy JSFunction and clone the
          * script (for interpreted functions).
          */
         clone = NewFunction(cx, parent);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2434,17 +2434,16 @@ struct types::ObjectTableKey
         }
         return true;
     }
 };
 
 struct types::ObjectTableEntry
 {
     TypeObject *object;
-    Shape *newShape;
     Type *types;
 };
 
 void
 TypeCompartment::fixObjectType(JSContext *cx, JSObject *obj)
 {
     AutoEnterTypeInference enter(cx);
 
@@ -2494,44 +2493,31 @@ TypeCompartment::fixObjectType(JSContext
                         }
                     }
                 } else {
                     return;
                 }
             }
         }
 
-        obj->setTypeAndShape(p->value.object, p->value.newShape);
+        obj->setType(p->value.object);
     } else {
-        /*
-         * Make a new type to use, and regenerate a new shape to go with it.
-         * Shapes are rooted at the empty shape for the object's type, so we
-         * can't change the type without changing the shape.
-         */
-        JSObject *xobj = NewBuiltinClassInstance(cx, &js_ObjectClass,
-                                                 (gc::FinalizeKind) obj->finalizeKind());
-        if (!xobj) {
-            cx->compartment->types.setPendingNukeTypes(cx);
-            return;
-        }
-        AutoObjectRooter xvr(cx, xobj);
-
+        /* Make a new type to use for the object and similar future ones. */
         char *name = NULL;
 #ifdef DEBUG
         static unsigned count = 0;
         name = (char *) alloca(20);
         JS_snprintf(name, 20, "TableObject:%u", ++count);
 #endif
 
         TypeObject *objType = newTypeObject(cx, NULL, name, "", JSProto_Object, obj->getProto());
-        if (!objType) {
+        if (!objType || !objType->addDefiniteProperties(cx, obj)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
-        xobj->setType(objType);
 
         jsid *ids = (jsid *) cx->calloc_(obj->slotSpan() * sizeof(jsid));
         if (!ids) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         Type *types = (Type *) cx->calloc_(obj->slotSpan() * sizeof(Type));
@@ -2546,49 +2532,34 @@ TypeCompartment::fixObjectType(JSContext
             types[shape->slot] = GetValueType(cx, obj->getSlot(shape->slot));
             if (!objType->unknownProperties()) {
                 jsid id = MakeTypeId(cx, shape->propid);
                 objType->addPropertyType(cx, id, types[shape->slot]);
             }
             shape = shape->previous();
         }
 
-        /* Construct the new shape. */
-        for (unsigned i = 0; i < obj->slotSpan(); i++) {
-            if (!DefineNativeProperty(cx, xobj, ids[i], UndefinedValue(), NULL, NULL,
-                                      JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
-                cx->compartment->types.setPendingNukeTypes(cx);
-                return;
-            }
-        }
-        JS_ASSERT(!xobj->inDictionaryMode());
-        const Shape *newShape = xobj->lastProperty();
-
-        if (!objType->addDefiniteProperties(cx, xobj))
-            return;
-
         ObjectTableKey key;
         key.ids = ids;
         key.nslots = obj->slotSpan();
         key.nfixed = obj->numFixedSlots();
         key.proto = obj->getProto();
         JS_ASSERT(ObjectTableKey::match(key, obj));
 
         ObjectTableEntry entry;
         entry.object = objType;
-        entry.newShape = (Shape *) newShape;
         entry.types = types;
 
         p = objectTypeTable->lookupForAdd(obj);
         if (!objectTypeTable->add(p, key, entry)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
-        obj->setTypeAndShape(objType, newShape);
+        obj->setType(objType);
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeObject
 /////////////////////////////////////////////////////////////////////
 
 void
@@ -4404,19 +4375,19 @@ CheckNewScriptProperties(JSContext *cx, 
     gc::FinalizeKind kind = gc::GetGCObjectKind(baseobj->slotSpan());
 
     /* We should not have overflowed the maximum number of fixed slots for an object. */
     JS_ASSERT(gc::GetGCKindSlots(kind) >= baseobj->slotSpan());
 
     TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
 
     /*
-     * The base object was created with a different type and
-     * finalize kind than we will use for subsequent new objects.
-     * Generate an object with the appropriate final shape.
+     * The base object may have been created with a different finalize kind
+     * than we will use for subsequent new objects. Generate an object with the
+     * appropriate final shape.
      */
     baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
                                 baseobj->lastProperty());
     if (!baseobj ||
         !type->addDefiniteProperties(cx, baseobj) ||
         !initializerList.append(done)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
@@ -4957,23 +4928,17 @@ JSScript::typeSetFunction(JSContext *cx,
             return false;
     } else {
         TypeObject *type = cx->compartment->types.newTypeObject(cx, this, name, "",
                                                                 JSProto_Function, fun->getProto());
         if (!type)
             return false;
         AutoTypeRooter root(cx, type);
 
-        js::Shape *shape = js::EmptyShape::create(cx, fun->getClass());
-        if (!shape)
-            return false;
-
         fun->setType(type);
-        fun->setMap(shape);
-
         type->functionScript = this;
     }
 
     return true;
 }
 
 #ifdef DEBUG
 
@@ -5154,21 +5119,21 @@ JSObject::makeNewType(JSContext *cx, JSS
     name = TypeString(Type::ObjectType(this));
 #endif
 
     TypeObject *type = cx->compartment->types.newTypeObject(cx, NULL, name, "new",
                                                             JSProto_Object, this, unknown);
     if (!type)
         return;
 
-    if (!cx->typeInferenceEnabled()) {
-        newType = type;
-        setDelegate();
+    newType = type;
+    setDelegate();
+
+    if (!cx->typeInferenceEnabled())
         return;
-    }
 
     AutoEnterTypeInference enter(cx);
 
     /*
      * Set the special equality flag for types whose prototype also has the
      * flag set. This is a hack, :XXX: need a real correspondence between
      * types and the possible js::Class of objects with that type.
      */
@@ -5192,19 +5157,16 @@ JSObject::makeNewType(JSContext *cx, JSS
      * unknown in all type sets it appears in. This allows the prototype of
      * such objects to mutate freely without triggering an expensive walk of
      * the compartment's type sets. (While scripts normally don't mutate
      * __proto__, the browser will for proxies and such, and we need to
      * accommodate this behavior).
      */
     if (type->unknownProperties())
         type->flags |= OBJECT_FLAG_SETS_MARKED_UNKNOWN;
-
-    newType = type;
-    setDelegate();
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
 void
 TypeSet::sweep(JSContext *cx, JSCompartment *compartment)
@@ -5416,17 +5378,17 @@ TypeCompartment::sweep(JSContext *cx)
 
     if (objectTypeTable) {
         for (ObjectTypeTable::Enum e(*objectTypeTable); !e.empty(); e.popFront()) {
             const ObjectTableKey &key = e.front().key;
             const ObjectTableEntry &entry = e.front().value;
             JS_ASSERT(entry.object->proto == key.proto);
 
             bool remove = false;
-            if (!entry.object->isMarked() || !entry.newShape->isMarked())
+            if (!entry.object->isMarked())
                 remove = true;
             for (unsigned i = 0; !remove && i < key.nslots; i++) {
                 if (JSID_IS_STRING(key.ids[i])) {
                     JSString *str = JSID_TO_STRING(key.ids[i]);
                     if (!str->isStaticAtom() && !str->isMarked())
                         remove = true;
                 }
                 JS_ASSERT(!entry.types[i].isSingleObject());
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -5197,22 +5197,17 @@ BEGIN_CASE(JSOP_NEWINIT)
     }
 
     if (!obj)
         goto error;
 
     TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i);
     if (!type)
         goto error;
-    if (i == JSProto_Array) {
-        obj->setType(type);
-    } else {
-        if (!obj->setTypeAndEmptyShape(cx, type))
-            goto error;
-    }
+    obj->setType(type);
 
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWINIT)
 
 BEGIN_CASE(JSOP_NEWARRAY)
 {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2833,18 +2833,19 @@ js_Object(JSContext *cx, uintN argc, Val
     if (!obj) {
         /* Make an object whether this was called with 'new' or not. */
         JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
         gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
         obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
         if (!obj)
             return JS_FALSE;
         TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object);
-        if (!type || !obj->setTypeAndEmptyShape(cx, type))
+        if (!type)
             return JS_FALSE;
+        obj->setType(type);
     }
     vp->setObject(*obj);
     return JS_TRUE;
 }
 
 JSObject *
 js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent,
                       gc::FinalizeKind kind, const Shape *shape)
@@ -4264,17 +4265,18 @@ JSObject::allocSlots(JSContext *cx, size
      * type to give these objects a larger number of fixed slots when future
      * objects are constructed.
      */
     if (!hasLazyType() && type()->newScript) {
         gc::FinalizeKind kind = gc::FinalizeKind(type()->newScript->finalizeKind);
         unsigned newScriptSlots = gc::GetGCKindSlots(kind);
         if (newScriptSlots == numFixedSlots() && gc::CanBumpFinalizeKind(kind)) {
             kind = gc::BumpFinalizeKind(kind);
-            JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind, type()->newScript->shape);
+            JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind,
+                                              type()->newScript->shape);
             if (!obj)
                 return false;
 
             type()->newScript->finalizeKind = kind;
             type()->newScript->shape = obj->lastProperty();
         }
     }
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -801,22 +801,22 @@ struct JSObject : js::gc::Cell {
         /* Direct field access for use by GC. */
         return type_;
     }
 
     static inline size_t offsetOfType() { return offsetof(JSObject, type_); }
 
     inline void clearType();
     inline void setType(js::types::TypeObject *newType);
-    inline bool setTypeAndEmptyShape(JSContext *cx, js::types::TypeObject *newType);
-    inline void setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape);
 
     inline js::types::TypeObject *getNewType(JSContext *cx, JSScript *script = NULL,
                                              bool markUnknown = false);
+  private:
     void makeNewType(JSContext *cx, JSScript *script, bool markUnknown);
+  public:
 
     /* Set a new prototype for an object with a singleton type. */
     bool splicePrototype(JSContext *cx, JSObject *proto);
 
     /*
      * For bootstrapping, whether to splice a prototype for Function.prototype
      * or the global object.
      */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -793,20 +793,16 @@ JSObject::setSingletonType(JSContext *cx
         return true;
 
     JS_ASSERT(!lastProp->previous());
     JS_ASSERT(!hasLazyType());
     JS_ASSERT_IF(getProto(), type() == getProto()->getNewType(cx, NULL));
 
     flags |= SINGLETON_TYPE | LAZY_TYPE;
 
-    js::Shape *shape = js::EmptyShape::create(cx, getClass());
-    if (!shape)
-        return false;
-    setMap(shape);
     return true;
 }
 
 inline js::types::TypeObject *
 JSObject::getType(JSContext *cx)
 {
     if (hasLazyType())
         makeLazyType(cx);
@@ -855,37 +851,16 @@ JSObject::setType(js::types::TypeObject 
     for (JSObject *obj = newType->proto; obj; obj = obj->getProto())
         JS_ASSERT(obj != this);
 #endif
     JS_ASSERT_IF(hasSpecialEquality(), newType->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY));
     JS_ASSERT(!hasSingletonType());
     type_ = newType;
 }
 
-inline bool
-JSObject::setTypeAndEmptyShape(JSContext *cx, js::types::TypeObject *newType)
-{
-    JS_ASSERT(nativeEmpty() && newType->canProvideEmptyShape(getClass()));
-    setType(newType);
-
-    js::Shape *shape = type()->getEmptyShape(cx, getClass(), finalizeKind());
-    if (!shape)
-        return false;
-    setMap(shape);
-    return true;
-}
-
-inline void
-JSObject::setTypeAndShape(js::types::TypeObject *newType, const js::Shape *newShape)
-{
-    JS_ASSERT(newShape->slot == lastProperty()->slot);
-    setType(newType);
-    setLastProperty(newShape);
-}
-
 inline void
 JSObject::init(JSContext *cx, js::Class *aclasp, js::types::TypeObject *type,
                JSObject *parent, void *priv, bool denseArray)
 {
     clasp = aclasp;
     flags = capacity << FIXED_SLOTS_SHIFT;
 
     JS_ASSERT(denseArray == (aclasp == &js_ArrayClass));
@@ -1214,17 +1189,16 @@ class AutoPropertyDescriptorRooter : pri
     friend void AutoGCRooter::trace(JSTracer *trc);
 };
 
 static inline bool
 InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, js::types::TypeObject *type,
                    gc::FinalizeKind kind)
 {
     JS_ASSERT(clasp->isNative());
-    JS_ASSERT(type == obj->type());
 
     /* Share proto's emptyShape only if obj is similar to proto. */
     js::EmptyShape *empty = NULL;
 
     uint32 freeslot = JSSLOT_FREE(clasp);
     if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot))
         goto bad;
 
@@ -1535,16 +1509,18 @@ NewObject(JSContext *cx, js::Class *clas
 
 /*
  * Create a plain object with the specified type. This bypasses getNewType to
  * avoid losing creation site information for objects made by scripted 'new'.
  */
 static JS_ALWAYS_INLINE JSObject *
 NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::FinalizeKind kind)
 {
+    JS_ASSERT(type == type->proto->newType);
+
     JSObject* obj = js_NewGCObject(cx, kind);
     if (!obj)
         goto out;
 
     /*
      * Default parent to the parent of the prototype, which was set from
      * the parent of the prototype's constructor.
      */
@@ -1558,17 +1534,17 @@ NewObjectWithType(JSContext *cx, types::
     }
 
 out:
     Probes::createObject(cx, obj);
     return obj;
 }
 
 extern JSObject *
-NewReshapedObject(JSContext *cx, types::TypeObject *type, JSObject *parent,
+NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent,
                   gc::FinalizeKind kind, const Shape *shape);
 
 /*
  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
  * the object, zero if the final size is unknown. This should only be used for
  * objects that do not require any fixed slots.
  */
 static inline gc::FinalizeKind
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -67,16 +67,23 @@ js::Shape::freeTable(JSContext *cx)
 }
 
 inline js::EmptyShape *
 js::types::TypeObject::getEmptyShape(JSContext *cx, js::Class *aclasp,
                                      /* gc::FinalizeKind */ unsigned kind)
 {
     JS_ASSERT(!singleton);
 
+    /*
+     * Empty shapes can only be on the default 'new' type for a prototype.
+     * Objects with a common prototype use the same shape lineage, even if
+     * their prototypes differ.
+     */
+    JS_ASSERT(this == proto->newType);
+
     JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     int i = kind - js::gc::FINALIZE_OBJECT0;
 
     if (!emptyShapes) {
         emptyShapes = (js::EmptyShape**)
             cx->calloc_(sizeof(js::EmptyShape*) * js::gc::FINALIZE_FUNCTION_AND_OBJECT_LAST);
         if (!emptyShapes)
             return NULL;
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -300,16 +300,34 @@ class SetPropCompiler : public PICStubCo
         Label start = masm.label();
         Jump shapeGuard = masm.branch32FixedLength(Assembler::NotEqual, pic.shapeReg,
                                                    Imm32(initialShape));
 
         Label stubShapeJumpLabel = masm.label();
 
         pic.setPropLabels().setStubShapeJump(masm, start, stubShapeJumpLabel);
 
+        if (pic.typeMonitored) {
+            /*
+             * Inference does not know the type of the object being updated,
+             * and we need to make sure that the updateMonitoredTypes() call
+             * covers this stub, i.e. we will be writing to an object with the
+             * same type. Add a type guard in addition to the shape guard.
+             * Note: it is possible that this test gets a spurious hit if the
+             * object has a lazy type, but in such cases no analyzed scripts
+             * depend on the object and we will reconstruct its type from the
+             * value being written here.
+             */
+            Jump typeGuard = masm.branchPtr(Assembler::NotEqual,
+                                            Address(pic.objReg, JSObject::offsetOfType()),
+                                            ImmPtr(obj->getType(cx)));
+            if (!otherGuards.append(typeGuard))
+                return error();
+        }
+
         JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->getClass() == &js_CallClass);
 
         MaybeJump skipOver;
 
         if (adding) {
             JS_ASSERT(shape->hasSlot());
             pic.shapeRegHasBaseShape = false;
 
@@ -681,16 +699,17 @@ class SetPropCompiler : public PICStubCo
                     return Lookup_Uncacheable;
             }
         }
 
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
             shape->hasDefaultSetter() &&
+            !pic.typeMonitored &&
             !obj->isDenseArray()) {
             return patchInline(shape);
         }
 
         return generateStub(obj->shape(), shape, false);
     }
 };
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1339,18 +1339,18 @@ stubs::NewInitObject(VMFrame &f, JSObjec
     JSContext *cx = f.cx;
     TypeObject *type = (TypeObject *) f.scratch;
 
     if (!baseobj) {
         gc::FinalizeKind kind = GuessObjectGCKind(0, false);
         JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
         if (!obj)
             THROWV(NULL);
-        if (type && !obj->setTypeAndEmptyShape(cx, type))
-            THROWV(NULL);
+        if (type)
+            obj->setType(type);
         return obj;
     }
 
     JS_ASSERT(type);
     JSObject *obj = CopyInitializerObject(cx, baseobj, type);
 
     if (!obj)
         THROWV(NULL);