Use singleton types for global object initializers, bug 731398. r=dvander
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 21 Mar 2012 07:37:43 -0600
changeset 93235 149eff9b7b92cc43d53212aa2a8a4a7e3f255d8c
parent 93224 d9491b6074a43d05fd2962d1b3f77e475c19b988
child 93236 34526f45d863381297e3a9d72044b77704d77617
push idunknown
push userunknown
push dateunknown
reviewersdvander
bugs731398
milestone14.0a1
Use singleton types for global object initializers, bug 731398. r=dvander
js/src/jsanalyze.cpp
js/src/jsanalyze.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsobjinlines.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -66,17 +66,17 @@ PrintBytecode(JSContext *cx, JSScript *s
 #endif
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
-                        unsigned *currentOffset, unsigned *forwardJump,
+                        unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth)
 {
     JS_ASSERT(offset < script->length);
 
     Bytecode *&code = codeArray[offset];
     if (!code) {
         code = cx->typeLifoAlloc().new_<Bytecode>();
         if (!code) {
@@ -88,20 +88,36 @@ ScriptAnalysis::addJump(JSContext *cx, u
     JS_ASSERT(code->stackDepth == stackDepth);
 
     code->jumpTarget = true;
 
     if (offset < *currentOffset) {
         /* Scripts containing loops are never inlined. */
         isInlineable = false;
 
-        /* Don't follow back edges to bytecode which has already been analyzed. */
-        if (!code->analyzed) {
+        if (code->analyzed) {
+            /*
+             * Backedge in a do-while loop, the body has been analyzed. Rewalk
+             * the body to set inLoop bits.
+             */
+            for (unsigned i = offset; i <= *currentOffset; i++) {
+                Bytecode *code = maybeCode(i);
+                if (code)
+                    code->inLoop = true;
+            }
+        } else {
+            /*
+             * Backedge in a while/for loop, whose body has not been analyzed
+             * due to a lack of fallthrough at the loop head. Roll back the
+             * offset to analyze the body.
+             */
             if (*forwardJump == 0)
                 *forwardJump = *currentOffset;
+            if (*forwardLoop == 0)
+                *forwardLoop = *currentOffset;
             *currentOffset = offset;
         }
     } else if (offset > *forwardJump) {
         *forwardJump = offset;
     }
 
     return true;
 }
@@ -215,16 +231,19 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     /*
      * If we are in the middle of one or more jumps, the offset of the highest
      * target jumping over this bytecode.  Includes implicit jumps from
      * try/catch/finally blocks.
      */
     unsigned forwardJump = 0;
 
+    /* If we are in the middle of a loop, the offset of the highest backedge. */
+    unsigned forwardLoop = 0;
+
     /*
      * If we are in the middle of a try block, the offset of the highest
      * catch/finally/enditer.
      */
     unsigned forwardCatch = 0;
 
     /* Fill in stack depth and definitions at initial bytecode. */
     Bytecode *startcode = tla.new_<Bytecode>();
@@ -267,16 +286,26 @@ ScriptAnalysis::analyzeBytecode(JSContex
          */
         nextOffset = successorOffset;
 
         if (!code) {
             /* Haven't found a path by which this bytecode is reachable. */
             continue;
         }
 
+        /*
+         * Update info about bytecodes inside loops, which may have been
+         * analyzed before the backedge was seen.
+         */
+        if (forwardLoop) {
+            code->inLoop = true;
+            if (forwardLoop <= offset)
+                forwardLoop = 0;
+        }
+
         if (code->analyzed) {
             /* No need to reanalyze, see Bytecode::mergeDefines. */
             continue;
         }
 
         code->analyzed = true;
 
         if (forwardCatch)
@@ -388,50 +417,50 @@ ScriptAnalysis::analyzeBytecode(JSContex
             isInlineable = false;
             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
             int32_t low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
             int32_t high = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
 
-            if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth))
+            if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                 return;
             getCode(defaultOffset).switchTarget = true;
             getCode(defaultOffset).safePoint = true;
 
             for (int32_t i = low; i <= high; i++) {
                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
                 if (targetOffset != offset) {
-                    if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth))
+                    if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                         return;
                 }
                 getCode(targetOffset).switchTarget = true;
                 getCode(targetOffset).safePoint = true;
                 pc2 += JUMP_OFFSET_LEN;
             }
             break;
           }
 
           case JSOP_LOOKUPSWITCH: {
             isInlineable = false;
             unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
             jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
             unsigned npairs = GET_UINT16(pc2);
             pc2 += UINT16_LEN;
 
-            if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, stackDepth))
+            if (!addJump(cx, defaultOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                 return;
             getCode(defaultOffset).switchTarget = true;
             getCode(defaultOffset).safePoint = true;
 
             while (npairs) {
                 pc2 += UINT32_INDEX_LEN;
                 unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
-                if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, stackDepth))
+                if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                     return;
                 getCode(targetOffset).switchTarget = true;
                 getCode(targetOffset).safePoint = true;
                 pc2 += JUMP_OFFSET_LEN;
                 npairs--;
             }
             break;
           }
@@ -451,17 +480,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
                     if (catchOffset > forwardCatch)
                         forwardCatch = catchOffset;
 
                     if (tn->kind != JSTRY_ITER) {
-                        if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, stackDepth))
+                        if (!addJump(cx, catchOffset, &nextOffset, &forwardJump, &forwardLoop, stackDepth))
                             return;
                         getCode(catchOffset).exceptionEntry = true;
                         getCode(catchOffset).safePoint = true;
                     }
                 }
             }
             break;
           }
@@ -633,17 +662,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
                 /* Case instructions do not push the lvalue back when branching. */
                 newStackDepth--;
                 break;
 
               default:;
             }
 
             unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
-            if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, newStackDepth))
+            if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, newStackDepth))
                 return;
         }
 
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
             JS_ASSERT(successorOffset < script->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
@@ -665,17 +694,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
             if (type == JOF_JUMP)
                 nextcode->jumpTarget = true;
             else
                 nextcode->fallthrough = true;
         }
     }
 
     JS_ASSERT(!failed());
-    JS_ASSERT(forwardJump == 0 && forwardCatch == 0);
+    JS_ASSERT(forwardJump == 0 && forwardLoop == 0 && forwardCatch == 0);
 
     ranBytecode_ = true;
 
     /*
      * Always ensure that a script's arguments usage has been analyzed before
      * entering the script. This allows the functionPrologue to ensure that
      * arguments are always created eagerly which simplifies interp logic.
      */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -118,16 +118,19 @@ class Bytecode
     bool analyzed : 1;
 
     /* Whether this is a catch/finally entry point. */
     bool exceptionEntry : 1;
 
     /* Whether this is in a try block. */
     bool inTryBlock : 1;
 
+    /* Whether this is in a loop. */
+    bool inLoop : 1;
+
     /* Method JIT safe point. */
     bool safePoint : 1;
 
     /*
      * Side effects of this bytecode were not determined by type inference.
      * Either a property set with unknown lvalue, or call with unknown callee.
      */
     bool monitoredTypes : 1;
@@ -1168,17 +1171,17 @@ class ScriptAnalysis
         if (!outOfMemory)
             js_ReportOutOfMemory(cx);
         outOfMemory = true;
         hadFailure = true;
     }
 
     /* Bytecode helpers */
     inline bool addJump(JSContext *cx, unsigned offset,
-                        unsigned *currentOffset, unsigned *forwardJump,
+                        unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth);
     void checkAliasedName(JSContext *cx, jsbytecode *pc);
 
     /* Lifetime helpers */
     inline void addVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
                             LifetimeVariable **&saved, unsigned &savedCount);
     inline void killVariable(JSContext *cx, LifetimeVariable &var, unsigned offset,
                              LifetimeVariable **&saved, unsigned &savedCount);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2011,16 +2011,40 @@ types::UseNewType(JSContext *cx, JSScrip
         if (id == id_prototype(cx))
             return true;
     }
 
     return false;
 }
 
 bool
+types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+    /*
+     * Objects created outside loops in global and eval scripts should have
+     * singleton types. For now this is only done for plain objects, not arrays.
+     */
+
+    if (!cx->typeInferenceEnabled() || script->function())
+        return false;
+
+    JSOp op = JSOp(*pc);
+    if (op == JSOP_NEWOBJECT || op == JSOP_NEWINIT && (JSProtoKey)pc[1] == JSProto_Object) {
+        AutoEnterTypeInference enter(cx);
+
+        if (!script->ensureRanAnalysis(cx, NULL))
+            return false;
+
+        return !script->analysis()->getCode(pc).inLoop;
+    }
+
+    return false;
+}
+
+bool
 types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script)
 {
     if (!cx->typeInferenceEnabled() || !script->hasGlobal())
         return true;
 
     JSObject *proto = script->global()->getOrCreateArrayPrototype(cx);
     if (!proto)
         return true;
@@ -3159,16 +3183,19 @@ CheckNextTest(jsbytecode *pc)
 }
 
 static inline TypeObject *
 GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     if (!script->hasGlobal())
         return NULL;
 
+    if (UseNewTypeForInitializer(cx, script, pc))
+        return NULL;
+
     JSOp op = JSOp(*pc);
     JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
 
     bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
     return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
 }
 
 /*
@@ -3790,27 +3817,33 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
         poppedTypes(pc, argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT: {
+        TypeSet *types = script->analysis()->bytecodeTypes(pc);
+        types->addSubset(cx, &pushed[0]);
+
+        if (UseNewTypeForInitializer(cx, script, pc)) {
+            /* Defer types pushed by this bytecode until runtime. */
+            break;
+        }
+
         TypeObject *initializer = GetInitializerType(cx, script, pc);
-        TypeSet *types = script->analysis()->bytecodeTypes(pc);
         if (script->hasGlobal()) {
             if (!initializer)
                 return false;
             types->addType(cx, Type::ObjectType(initializer));
         } else {
             JS_ASSERT(!initializer);
             types->addType(cx, Type::UnknownType());
         }
-        types->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_ENDINIT:
         break;
 
       case JSOP_INITELEM: {
         const SSAValue &objv = poppedValue(pc, 2);
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -904,16 +904,20 @@ struct TypeObjectEntry
     static inline bool match(TypeObject *key, JSObject *lookup);
 };
 typedef HashSet<ReadBarriered<TypeObject>, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
 
 /* Whether to use a new type object when calling 'new' at script/pc. */
 bool
 UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc);
 
+/* Whether to use a new type object for an initializer opcode at script/pc. */
+bool
+UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc);
+
 /*
  * Whether Array.prototype, or an object on its proto chain, has an
  * indexed property.
  */
 bool
 ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script);
 
 /*
@@ -1075,17 +1079,17 @@ class TypeScript
     /* Check that correct types were inferred for the values pushed by this bytecode. */
     static void CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp);
 #endif
 
     /* Get the default 'new' object for a given standard class, per the script's global. */
     static inline TypeObject *StandardType(JSContext *cx, JSScript *script, JSProtoKey kind);
 
     /* Get a type object for an allocation site in this script. */
-    static inline TypeObject *InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind);
+    static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind);
 
     /*
      * Monitor a bytecode pushing a value which is not accounted for by the
      * inference type constraints, such as integer overflow.
      */
     static inline void MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc);
     static inline void MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc);
     static inline void MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -539,18 +539,20 @@ struct AllocationSiteKey {
     }
 
     static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
         return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
     }
 };
 
 /* static */ inline TypeObject *
-TypeScript::InitObject(JSContext *cx, JSScript *script, const jsbytecode *pc, JSProtoKey kind)
+TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
 {
+    JS_ASSERT(!UseNewTypeForInitializer(cx, script, pc));
+
     /* :XXX: Limit script->length so we don't need to check the offset up front? */
     uint32_t offset = pc - script->code;
 
     if (!cx->typeInferenceEnabled() || !script->hasGlobal() || offset >= AllocationSiteKey::OFFSET_LIMIT)
         return GetTypeNewObject(cx, kind);
 
     AllocationSiteKey key;
     key.script = script;
@@ -562,16 +564,44 @@ TypeScript::InitObject(JSContext *cx, JS
 
     AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(key);
 
     if (p)
         return p->value;
     return cx->compartment->types.newAllocationSiteTypeObject(cx, key);
 }
 
+/* Set the type to use for obj according to the site it was allocated at. */
+static inline bool
+SetInitializerObjectType(JSContext *cx, JSScript *script, jsbytecode *pc, JSObject *obj)
+{
+    if (!cx->typeInferenceEnabled())
+        return true;
+
+    if (UseNewTypeForInitializer(cx, script, pc)) {
+        if (!obj->setSingletonType(cx))
+            return false;
+
+        /*
+         * Inference does not account for types of run-once initializer
+         * objects, as these may not be created until after the script
+         * has been analyzed.
+         */
+        TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
+    } else {
+        JSProtoKey key = obj->isDenseArray() ? JSProto_Array : JSProto_Object;
+        types::TypeObject *type = TypeScript::InitObject(cx, script, pc, key);
+        if (!type)
+            return false;
+        obj->setType(type);
+    }
+
+    return true;
+}
+
 /* static */ inline void
 TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
 {
     if (cx->typeInferenceEnabled())
         TypeMonitorResult(cx, script, pc, rval);
 }
 
 /* static */ inline void
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3387,56 +3387,42 @@ BEGIN_CASE(JSOP_NEWINIT)
 
     JSObject *obj;
     if (i == JSProto_Array) {
         obj = NewDenseEmptyArray(cx);
     } else {
         gc::AllocKind kind = GuessObjectGCKind(0);
         obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
     }
-    if (!obj)
+    if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj))
         goto error;
 
-    TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i);
-    if (!type)
-        goto error;
-    obj->setType(type);
-
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWINIT)
 
 BEGIN_CASE(JSOP_NEWARRAY)
 {
     unsigned count = GET_UINT24(regs.pc);
     JSObject *obj = NewDenseAllocatedArray(cx, count);
-    if (!obj)
+    if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj))
         goto error;
 
-    TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Array);
-    if (!type)
-        goto error;
-    obj->setType(type);
-
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWARRAY)
 
 BEGIN_CASE(JSOP_NEWOBJECT)
 {
     JSObject *baseobj = script->getObject(GET_UINT32_INDEX(regs.pc));
 
-    TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Object);
-    if (!type)
-        goto error;
-
-    JSObject *obj = CopyInitializerObject(cx, baseobj, type);
-    if (!obj)
+    JSObject *obj = CopyInitializerObject(cx, baseobj);
+    if (!obj || !SetInitializerObjectType(cx, script, regs.pc, obj))
         goto error;
 
     PUSH_OBJECT(*obj);
     CHECK_INTERRUPT_HANDLER();
 }
 END_CASE(JSOP_NEWOBJECT)
 
 BEGIN_CASE(JSOP_ENDINIT)
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -785,17 +785,16 @@ JSObject::setQNameLocalName(JSAtom *name
 }
 
 inline bool
 JSObject::setSingletonType(JSContext *cx)
 {
     if (!cx->typeInferenceEnabled())
         return true;
 
-    JS_ASSERT(!lastProperty()->previous());
     JS_ASSERT(!hasLazyType());
     JS_ASSERT_IF(getProto(), type() == getProto()->getNewType(cx, NULL));
 
     js::types::TypeObject *type = cx->compartment->getLazyType(cx, getProto());
     if (!type)
         return false;
 
     type_ = type;
@@ -1770,33 +1769,31 @@ FindClassPrototype(JSContext *cx, JSObje
  * Create a plain object with the specified type. This bypasses getNewType to
  * avoid losing creation site information for objects made by scripted 'new'.
  */
 JSObject *
 NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind);
 
 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
 static inline JSObject *
-CopyInitializerObject(JSContext *cx, JSObject *baseobj, types::TypeObject *type)
+CopyInitializerObject(JSContext *cx, JSObject *baseobj)
 {
     JS_ASSERT(baseobj->getClass() == &ObjectClass);
     JS_ASSERT(!baseobj->inDictionaryMode());
 
     gc::AllocKind kind = gc::GetGCObjectFixedSlotsKind(baseobj->numFixedSlots());
 #ifdef JS_THREADSAFE
     kind = gc::GetBackgroundAllocKind(kind);
 #endif
     JS_ASSERT(kind == baseobj->getAllocKind());
     JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
 
     if (!obj)
         return NULL;
 
-    obj->setType(type);
-
     if (!obj->setLastProperty(cx, baseobj->lastProperty()))
         return NULL;
 
     return obj;
 }
 
 JSObject *
 NewReshapedObject(JSContext *cx, js::types::TypeObject *type, JSObject *parent,
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -6782,56 +6782,56 @@ mjit::Compiler::jsop_newinit()
     if (isArray) {
         stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitArray);
         stubArg = (void *) uintptr_t(count);
     } else {
         stub = JS_FUNC_TO_DATA_PTR(void *, stubs::NewInitObject);
         stubArg = (void *) baseobj;
     }
 
-    /* Don't bake in types for non-compileAndGo scripts. */
+    /*
+     * Don't bake in types for non-compileAndGo scripts, or at initializers
+     * producing objects with singleton types.
+     */
     types::TypeObject *type = NULL;
-    if (globalObj) {
+    if (globalObj && !types::UseNewTypeForInitializer(cx, script, PC)) {
         type = types::TypeScript::InitObject(cx, script, PC,
                                              isArray ? JSProto_Array : JSProto_Object);
         if (!type)
             return false;
     }
 
     size_t maxArraySlots =
         gc::GetGCKindSlots(gc::FINALIZE_OBJECT_LAST) - ObjectElements::VALUES_PER_HEADER;
 
     if (!cx->typeInferenceEnabled() ||
-        !globalObj ||
+        !type ||
         (isArray && count > maxArraySlots) ||
         (!isArray && !baseobj) ||
         (!isArray && baseobj->hasDynamicSlots())) {
         prepareStubCall(Uses(0));
         masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
         masm.move(ImmPtr(stubArg), Registers::ArgReg1);
         INLINE_STUBCALL(stub, REJOIN_FALLTHROUGH);
         frame.pushSynced(JSVAL_TYPE_OBJECT);
 
         frame.extra(frame.peek(-1)).initArray = (*PC == JSOP_NEWARRAY);
         frame.extra(frame.peek(-1)).initObject = baseobj;
 
         return true;
     }
 
     JSObject *templateObject;
-    if (isArray) {
+    if (isArray)
         templateObject = NewDenseUnallocatedArray(cx, count);
-        if (!templateObject)
-            return false;
-        templateObject->setType(type);
-    } else {
-        templateObject = CopyInitializerObject(cx, baseobj, type);
-        if (!templateObject)
-            return false;
-    }
+    else
+        templateObject = CopyInitializerObject(cx, baseobj);
+    if (!templateObject)
+        return false;
+    templateObject->setType(type);
 
     RegisterID result = frame.allocReg();
     Jump emptyFreeList = masm.getNewObject(cx, result, templateObject);
 
     stubcc.linkExit(emptyFreeList, Uses(0));
     stubcc.leave();
 
     stubcc.masm.storePtr(ImmPtr(type), FrameAddress(offsetof(VMFrame, scratch)));
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -900,44 +900,51 @@ stubs::Neg(VMFrame &f)
 void JS_FASTCALL
 stubs::NewInitArray(VMFrame &f, uint32_t count)
 {
     JSObject *obj = NewDenseAllocatedArray(f.cx, count);
     if (!obj)
         THROW();
 
     TypeObject *type = (TypeObject *) f.scratch;
-    if (type)
+    if (type) {
         obj->setType(type);
+    } else {
+        if (!SetInitializerObjectType(f.cx, f.script(), f.pc(), obj))
+            THROW();
+    }
 
     f.regs.sp[0].setObject(*obj);
 }
 
 void JS_FASTCALL
 stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
 {
     JSContext *cx = f.cx;
     TypeObject *type = (TypeObject *) f.scratch;
 
-    if (!baseobj) {
+    JSObject *obj;
+
+    if (baseobj) {
+        obj = CopyInitializerObject(cx, baseobj);
+    } else {
         gc::AllocKind kind = GuessObjectGCKind(0);
-        JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
-        if (!obj)
-            THROW();
-        if (type)
-            obj->setType(type);
-        f.regs.sp[0].setObject(*obj);
-        return;
+        obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
     }
 
-    JS_ASSERT(type);
-    JSObject *obj = CopyInitializerObject(cx, baseobj, type);
-
     if (!obj)
         THROW();
+
+    if (type) {
+        obj->setType(type);
+    } else {
+        if (!SetInitializerObjectType(cx, f.script(), f.pc(), obj))
+            THROW();
+    }
+
     f.regs.sp[0].setObject(*obj);
 }
 
 void JS_FASTCALL
 stubs::InitElem(VMFrame &f, uint32_t last)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;