Bug 909764 - Fix IonBuilder to properly propagate exceptions raised during compilation. Make getTypeOrSingleObject (and any of its infallible callers) into fallible methods. r=h4writer
authorKannan Vijayan <kvijayan@mozilla.com>
Fri, 06 Sep 2013 14:27:07 -0400
changeset 158920 1699c4e7afdc3cc6e347412c59ace7a6f8ab17c3
parent 158919 94e1942ba37883e0bc20dd512b48cafb2ed45489
child 158921 5b1ca7188e8477f5b7007b27c448feea73e8ddee
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersh4writer
bugs909764
milestone26.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
Bug 909764 - Fix IonBuilder to properly propagate exceptions raised during compilation. Make getTypeOrSingleObject (and any of its infallible callers) into fallible methods. r=h4writer
js/src/jit/Ion.cpp
js/src/jit/Ion.h
js/src/jit/IonBuilder.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsinfer.h
js/src/jsinferinlines.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1647,16 +1647,21 @@ IonCompile(JSContext *cx, JSScript *scri
 
     JS_ASSERT(!GetIonScript(builder->script(), executionMode));
     JS_ASSERT(CanIonCompile(builder->script(), executionMode));
 
     RootedScript builderScript(cx, builder->script());
     IonSpewNewFunction(graph, builderScript);
 
     if (!builder->build()) {
+        if (cx->isExceptionPending()) {
+            IonSpew(IonSpew_Abort, "Builder raised exception.");
+            return AbortReason_Error;
+        }
+
         IonSpew(IonSpew_Abort, "Builder failed to build.");
         return builder->abortReason();
     }
     builder->clearForBackEnd();
 
     // If possible, compile the script off thread.
     if (OffThreadCompilationAvailable(cx)) {
         SetIonScript(builder->script(), executionMode, ION_COMPILING_SCRIPT);
@@ -1827,16 +1832,19 @@ Compile(JSContext *cx, HandleScript scri
     if (executionMode == SequentialExecution) {
         // Use getUseCount instead of incUseCount to avoid bumping the
         // use count twice.
         if (script->getUseCount() < js_IonOptions.usesBeforeCompile)
             return Method_Skipped;
     }
 
     AbortReason reason = IonCompile(cx, script, osrFrame, osrPc, constructing, executionMode);
+    if (reason == AbortReason_Error)
+        return Method_Error;
+
     if (reason == AbortReason_Disable)
         return Method_CantCompile;
 
     // Compilation succeeded or we invalidated right away or an inlining/alloc abort
     return HasIonScript(script, executionMode) ? Method_Compiled : Method_Skipped;
 }
 
 } // namespace jit
--- a/js/src/jit/Ion.h
+++ b/js/src/jit/Ion.h
@@ -250,16 +250,17 @@ enum MethodStatus
     Method_Skipped,
     Method_Compiled
 };
 
 enum AbortReason {
     AbortReason_Alloc,
     AbortReason_Inlining,
     AbortReason_Disable,
+    AbortReason_Error,
     AbortReason_NoAbort
 };
 
 // An Ion context is needed to enter into either an Ion method or an instance
 // of the Ion compiler. It points to a temporary allocator and the active
 // JSContext, either of which may be NULL, and the active compartment, which
 // will not be NULL.
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -3767,24 +3767,33 @@ IonBuilder::inlineScriptedCall(CallInfo 
                                                  this->info().executionMode());
     if (!info)
         return false;
 
     MIRGraphExits saveExits;
     AutoAccumulateExits aae(graph(), saveExits);
 
     // Build the graph.
+    JS_ASSERT(!cx->isExceptionPending());
     IonBuilder inlineBuilder(cx, &temp(), &graph(), &inspector, info, NULL,
                              inliningDepth_ + 1, loopDepth_);
     if (!inlineBuilder.buildInline(this, outerResumePoint, callInfo)) {
         JS_ASSERT(calleeScript->hasAnalysis());
+        if (cx->isExceptionPending()) {
+            IonSpew(IonSpew_Abort, "Inline builder raised exception.");
+            abortReason_ = AbortReason_Error;
+            return false;
+        }
 
         // Inlining the callee failed. Disable inlining the function
-        if (inlineBuilder.abortReason_ == AbortReason_Disable)
+        if (inlineBuilder.abortReason_ == AbortReason_Disable) {
+            // Only mark callee as un-inlineable only if the inlining was aborted
+            // for a non-exception reason.
             calleeScript->analysis()->setIonUninlineable();
+        }
 
         abortReason_ = AbortReason_Inlining;
         return false;
     }
 
     // Create return block.
     jsbytecode *postCall = GetNextPc(pc);
     MBasicBlock *returnBlock = newBlock(NULL, postCall);
@@ -5560,19 +5569,30 @@ IonBuilder::jsop_initprop(HandleProperty
     if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, id))
         return abort("INITPROP template object is special");
 
     bool res = LookupPropertyWithFlags(cx, templateObject, id,
                                        0, &holder, &shape);
     if (!res)
         return false;
 
-    if (!shape || holder != templateObject ||
-        PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value))
+    if (!shape || holder != templateObject) {
+        // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
+        MInitProp *init = MInitProp::New(obj, name, value);
+        current->add(init);
+        return resumeAfter(init);
+    }
+
+    bool writeNeedsBarrier = false;
+    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value, /* canModify = */ true,
+                                       &writeNeedsBarrier))
     {
+        return false;
+    }
+    if (writeNeedsBarrier) {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
@@ -6262,17 +6282,22 @@ IonBuilder::getStaticName(HandleObject s
     if (propertyTypes && propertyTypes->isOwnProperty(cx, staticType, true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         *psucceeded = false;
         return true;
     }
 
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, staticType, name, types);
+    bool barrier;
+    if (!PropertyReadNeedsTypeBarrier(cx, staticType, name, types, /* updateObserved = */ true,
+                                      &barrier))
+    {
+        return false;
+    }
 
     // If the property is permanent, a shape guard isn't necessary.
 
     JSObject *singleton = types->getSingleton();
 
     JSValueType knownType = types->getKnownTypeTag();
     if (!barrier) {
         if (singleton) {
@@ -6786,17 +6811,19 @@ IonBuilder::getElemTryCache(bool *emitte
     // of this getelem.
     bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
     if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         return true;
 
     // Emit GetElementCache.
 
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, obj, NULL, types);
+    bool barrier;
+    if (!PropertyReadNeedsTypeBarrier(cx, obj, NULL, types, &barrier))
+        return false;
 
     // Always add a barrier if the index might be a string, so that the cache
     // can attach stubs for particular properties.
     if (index->mightBeType(MIRType_String))
         barrier = true;
 
     // See note about always needing a barrier in jsop_getprop.
     if (needsToMonitorMissingProperties(types))
@@ -6830,20 +6857,24 @@ bool
 IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
 {
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
 
     if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String) && types->noConstraints()) {
         // Indexed call on an element of an array. Populate the observed types
         // with any objects that could be in the array, to avoid extraneous
         // type barriers.
-        AddObjectsForPropertyRead(cx, obj, NULL, types);
-    }
-
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, obj, NULL, types);
+        if (!AddObjectsForPropertyRead(cx, obj, NULL, types))
+            return false;
+    }
+
+    bool barrier;
+    if (!PropertyReadNeedsTypeBarrier(cx, obj, NULL, types, &barrier))
+        return false;
+
     bool needsHoleCheck = !ElementAccessIsPacked(cx, obj);
 
     // Reads which are on holes in the object do not have to bail out if
     // undefined values have been observed at this access site and the access
     // cannot hit another indexed property on the object or its prototypes.
     bool readOutOfBounds =
         types->hasType(types::Type::UndefinedType()) &&
         !ElementAccessHasExtraIndexedProperty(cx, obj);
@@ -7198,17 +7229,23 @@ IonBuilder::setElemTryTyped(bool *emitte
 bool
 IonBuilder::setElemTryDense(bool *emitted, MDefinition *object,
                             MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     if (!ElementAccessIsDenseNative(object, index))
         return true;
-    if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value))
+    bool needsBarrier;
+    if (!PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value, /* canModify = */ true,
+                                       &needsBarrier))
+    {
+        return false;
+    }
+    if (needsBarrier)
         return true;
     if (!object->resultTypeSet())
         return true;
 
     types::StackTypeSet::DoubleConversion conversion =
         object->resultTypeSet()->convertDoubleElements(cx);
 
     // If AmbiguousDoubleConversion, only handle int32 values for now.
@@ -7258,17 +7295,24 @@ IonBuilder::setElemTryCache(bool *emitte
 
     // TODO: Bug 876650: remove this check:
     // Temporary disable the cache if non dense native,
     // untill the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     if (!icInspect.sawDenseWrite())
         return true;
 
-    if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value))
+    bool needsBarrier;
+    if (PropertyWriteNeedsTypeBarrier(cx, current, &object, NULL, &value, /* canModify = */ true,
+                                      &needsBarrier))
+    {
+        return false;
+    }
+
+    if (needsBarrier)
         return true;
 
     // Emit SetElementCache.
     MInstruction *ins = MSetElementCache::New(object, index, value, script()->strict);
     current->add(ins);
     current->push(value);
 
     if (!resumeAfter(ins))
@@ -7278,17 +7322,20 @@ IonBuilder::setElemTryCache(bool *emitte
     return true;
 }
 
 bool
 IonBuilder::jsop_setelem_dense(types::StackTypeSet::DoubleConversion conversion,
                                SetElemSafety safety,
                                MDefinition *obj, MDefinition *id, MDefinition *value)
 {
-    MIRType elementType = DenseNativeElementType(cx, obj);
+    MIRType elementType;
+    if (!DenseNativeElementType(cx, obj, &elementType))
+        return false;
+
     bool packed = ElementAccessIsPacked(cx, obj);
 
     // Writes which are on holes in the object do not have to bail out if they
     // cannot hit another indexed property on the object or its prototypes.
     bool writeOutOfBounds = !ElementAccessHasExtraIndexedProperty(cx, obj);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
@@ -8095,17 +8142,19 @@ IonBuilder::jsop_getprop(HandlePropertyN
     bool emitted = false;
 
     types::StackTypeSet *types = types::TypeScript::BytecodeTypes(script(), pc);
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted) || emitted)
         return emitted;
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types);
+    bool barrier;
+    if (!PropertyReadNeedsTypeBarrier(cx, current->peek(-1), name, types, &barrier))
+        return false;
 
     // Try to hardcode known constants.
     if (!getPropTryConstant(&emitted, id, types) || emitted)
         return emitted;
 
     // Except when loading constants above, always use a call if we are doing
     // the definite properties analysis and not actually emitting code, to
     // simplify later analysis.
@@ -8400,17 +8449,21 @@ IonBuilder::getPropTryCache(bool *emitte
     //
     // In parallel execution, idempotency of caches is ignored, since we
     // repeat the entire ForkJoin workload if we bail out. Note that it's
     // overly restrictive to mark everything as idempotent, because we can
     // treat non-idempotent caches in parallel as repeatable.
     if (obj->type() == MIRType_Object && !invalidatedIdempotentCache() &&
         info().executionMode() != ParallelExecution)
     {
-        if (PropertyReadIsIdempotent(cx, obj, name))
+        bool idempotent;
+        if (!PropertyReadIsIdempotent(cx, obj, name, &idempotent))
+            return false;
+
+        if (idempotent)
             load->setIdempotent();
     }
 
     if (JSOp(*pc) == JSOP_CALLPROP) {
         if (!annotateGetPropertyCache(cx, obj, load, obj->resultTypeSet(), types))
             return false;
     }
 
@@ -8475,17 +8528,22 @@ IonBuilder::jsop_setprop(HandlePropertyN
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // Try to inline a common property setter, or make a call.
     if (!setPropTryCommonSetter(&emitted, obj, name, id, value) || emitted)
         return emitted;
 
     types::StackTypeSet *objTypes = obj->resultTypeSet();
-    bool barrier = PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value);
+    bool barrier;
+    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, name, &value,
+                                       /* canModify = */ true, &barrier))
+    {
+        return false;
+    }
 
     // Try to emit store from definite slots.
     if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
         return emitted;
 
     // Try to emit a monomorphic/polymorphic store based on baseline caches.
     if (!setPropTryInlineAccess(&emitted, obj, name, id, value, barrier, objTypes) || emitted)
         return emitted;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -318,17 +318,20 @@ IonBuilder::inlineArrayPopShift(CallInfo
         return InliningStatus_NotInlined;
 
     callInfo.unwrapArgs();
 
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(types::Type::UndefinedType());
 
-    bool barrier = PropertyReadNeedsTypeBarrier(cx, callInfo.thisArg(), NULL, returnTypes);
+    bool barrier;
+    if (!PropertyReadNeedsTypeBarrier(cx, callInfo.thisArg(), NULL, returnTypes, &barrier))
+        return InliningStatus_Error;
+
     if (barrier)
         returnType = MIRType_Value;
 
     MArrayPopShift *ins = MArrayPopShift::New(callInfo.thisArg(), mode,
                                               needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
     ins->setResultType(returnType);
@@ -345,17 +348,23 @@ IonBuilder::inlineArrayPopShift(CallInfo
 IonBuilder::InliningStatus
 IonBuilder::inlineArrayPush(CallInfo &callInfo)
 {
     if (callInfo.argc() != 1 || callInfo.constructing())
         return InliningStatus_NotInlined;
 
     MDefinition *obj = callInfo.thisArg();
     MDefinition *value = callInfo.getArg(0);
-    if (PropertyWriteNeedsTypeBarrier(cx, current, &obj, NULL, &value, /* canModify = */ false))
+    bool writeNeedsBarrier;
+    if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, NULL, &value, /* canModify = */ false,
+                                       &writeNeedsBarrier))
+    {
+        return InliningStatus_Error;
+    }
+    if (writeNeedsBarrier)
         return InliningStatus_NotInlined;
     JS_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
@@ -997,22 +1006,32 @@ IonBuilder::inlineUnsafePutElements(Call
         uint32_t arri = base + 0;
         uint32_t idxi = base + 1;
         uint32_t elemi = base + 2;
 
         MDefinition *obj = callInfo.getArg(arri);
         MDefinition *id = callInfo.getArg(idxi);
         MDefinition *elem = callInfo.getArg(elemi);
 
+        bool isDenseNative = ElementAccessIsDenseNative(obj, id);
+
+        bool writeNeedsBarrier = false;
+        if (isDenseNative) {
+            if (!PropertyWriteNeedsTypeBarrier(cx, current, &obj, NULL, &elem,
+                                               /* canModify = */ false,
+                                               &writeNeedsBarrier))
+            {
+                return InliningStatus_Error;
+            }
+        }
+
         // We can only inline setelem on dense arrays that do not need type
         // barriers and on typed arrays.
         ScalarTypeRepresentation::Type arrayType;
-        if ((!ElementAccessIsDenseNative(obj, id) ||
-             PropertyWriteNeedsTypeBarrier(cx, current, &obj, NULL,
-                                           &elem, /* canModify = */ false)) &&
+        if ((!isDenseNative || writeNeedsBarrier) &&
             !ElementAccessIsTypedArray(obj, id, &arrayType))
         {
             return InliningStatus_NotInlined;
         }
     }
 
     callInfo.unwrapArgs();
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2525,58 +2525,71 @@ jit::ElementAccessHasExtraIndexedPropert
     types::StackTypeSet *types = obj->resultTypeSet();
 
     if (!types || types->hasObjectFlags(cx, types::OBJECT_FLAG_LENGTH_OVERFLOW))
         return true;
 
     return types::TypeCanHaveExtraIndexedProperties(cx, types);
 }
 
-MIRType
-jit::DenseNativeElementType(JSContext *cx, MDefinition *obj)
+bool
+jit::DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result)
 {
+    JS_ASSERT(result);
+    *result = MIRType_None;
+
     types::StackTypeSet *types = obj->resultTypeSet();
     MIRType elementType = MIRType_None;
     unsigned count = types->getObjectCount();
 
     for (unsigned i = 0; i < count; i++) {
-        if (types::TypeObject *object = types->getTypeOrSingleObject(cx, i)) {
-            if (object->unknownProperties())
-                return MIRType_None;
-
-            types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
-            if (!elementTypes)
-                return MIRType_None;
-
-            MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx));
-            if (type == MIRType_None)
-                return MIRType_None;
-
-            if (elementType == MIRType_None)
-                elementType = type;
-            else if (elementType != type)
-                return MIRType_None;
-        }
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
+        if (!object)
+            continue;
+
+        if (object->unknownProperties())
+            return true;
+
+        types::HeapTypeSet *elementTypes = object->getProperty(cx, JSID_VOID, false);
+        if (!elementTypes)
+            return true;
+
+        MIRType type = MIRTypeFromValueType(elementTypes->getKnownTypeTag(cx));
+        if (type == MIRType_None)
+            return true;
+
+        if (elementType == MIRType_None)
+            elementType = type;
+        else if (elementType != type)
+            return true;
     }
 
-    return elementType;
+    *result = elementType;
+    return true;
 }
 
 bool
 jit::PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name,
-                                  types::StackTypeSet *observed, bool updateObserved)
+                                  types::StackTypeSet *observed, bool updateObserved, bool *result)
 {
     // If the object being read from has types for the property which haven't
     // been observed at this access site, the read could produce a new type and
     // a barrier is needed. Note that this only covers reads from properties
     // which are accounted for by type information, i.e. native data properties
     // and elements.
-
-    if (object->unknownProperties())
+    JS_ASSERT(result);
+    *result = false;
+
+    if (object->unknownProperties()) {
+        *result = true;
         return true;
+    }
 
     jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
 
     // If this access has never executed, try to add types to the observed set
     // according to any property which exists on the object or its prototype.
     if (updateObserved && observed->empty() && observed->noConstraints() && !JSID_IS_VOID(id)) {
         JSObject *obj = object->singleton ? object->singleton : object->proto;
 
@@ -2591,151 +2604,186 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
                 observed->addType(cx, types::GetValueType(v));
             }
 
             obj = obj->getProto();
         }
     }
 
     types::HeapTypeSet *property = object->getProperty(cx, id, false);
-    if (!property)
+    if (!property) {
+        *result = true;
         return true;
+    }
 
     // We need to consider possible types for the property both as an 'own'
     // property on the object and as inherited from any prototype. Type sets
     // for a property do not, however, reflect inherited types until a
     // getFromPrototypes() call has been performed.
     if (!property->hasPropagatedProperty())
         object->getFromPrototypes(cx, id, property);
 
-    if (!TypeSetIncludes(observed, MIRType_Value, property))
+    if (!TypeSetIncludes(observed, MIRType_Value, property)) {
+        *result = true;
         return true;
+    }
 
     // Type information for singleton objects is not required to reflect the
     // initial 'undefined' value for native properties, in particular global
     // variables declared with 'var'. Until the property is assigned a value
     // other than undefined, a barrier is required.
     if (name && object->singleton && object->singleton->isNative()) {
         Shape *shape = object->singleton->nativeLookup(cx, name);
         if (shape &&
             shape->hasDefaultGetter() &&
             object->singleton->nativeGetSlot(shape->slot()).isUndefined())
         {
+            *result = true;
             return true;
         }
     }
 
     property->addFreeze(cx);
-    return false;
+    *result = false;
+    return true;
 }
 
 bool
 jit::PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                  types::StackTypeSet *observed)
+                                  types::StackTypeSet *observed, bool *result)
 {
+    JS_ASSERT(result);
+    *result = false;
+
     if (observed->unknown())
-        return false;
+        return true;
 
     types::TypeSet *types = obj->resultTypeSet();
-    if (!types || types->unknownObject())
+    if (!types || types->unknownObject()) {
+        *result = true;
         return true;
+    }
 
     bool updateObserved = types->getObjectCount() == 1;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object = types->getTypeOrSingleObject(cx, i);
-        if (object && PropertyReadNeedsTypeBarrier(cx, object, name, observed, updateObserved))
-            return true;
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
+        if (object) {
+            if (!PropertyReadNeedsTypeBarrier(cx, object, name, observed, updateObserved, result))
+                return false;
+
+            if (*result)
+                return true;
+        }
     }
 
-    return false;
+    *result = false;
+    return true;
 }
 
 bool
-jit::PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name)
+jit::PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result)
 {
+    JS_ASSERT(result);
+    *result = false;
     // Determine if reading a property from obj is likely to be idempotent.
 
     jsid id = types::IdToTypeId(NameToId(name));
 
     types::TypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject())
-        return false;
+        return true;
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        if (types::TypeObject *object = types->getTypeOrSingleObject(cx, i)) {
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
+        if (object) {
             if (object->unknownProperties())
-                return false;
+                return true;
 
             // Check if the property has been reconfigured or is a getter.
             types::HeapTypeSet *property = object->getProperty(cx, id, false);
             if (!property || property->isOwnProperty(cx, object, true))
-                return false;
+                return true;
         }
     }
 
+    *result = true;
     return true;
 }
 
-void
+bool
 jit::AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
                                types::StackTypeSet *observed)
 {
     // Add objects to observed which *could* be observed by reading name from obj,
     // to hopefully avoid unnecessary type barriers and code invalidations.
 
     JS_ASSERT(observed->noConstraints());
 
     types::StackTypeSet *types = obj->resultTypeSet();
     if (!types || types->unknownObject()) {
         observed->addType(cx, types::Type::AnyObjectType());
-        return;
+        return true;
     }
 
     jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
 
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object = types->getTypeOrSingleObject(cx, i);
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
         if (!object)
             continue;
 
         if (object->unknownProperties()) {
             observed->addType(cx, types::Type::AnyObjectType());
-            return;
+            return true;
         }
 
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
         if (property->unknownObject()) {
             observed->addType(cx, types::Type::AnyObjectType());
-            return;
+            return true;
         }
 
         for (size_t i = 0; i < property->getObjectCount(); i++) {
             if (types::TypeObject *object = property->getTypeObject(i))
                 observed->addType(cx, types::Type::ObjectType(object));
             else if (JSObject *object = property->getSingleObject(i))
                 observed->addType(cx, types::Type::ObjectType(object));
         }
     }
+
+    return true;
 }
 
 static bool
 TryAddTypeBarrierForWrite(JSContext *cx, MBasicBlock *current, types::StackTypeSet *objTypes,
                           jsid id, MDefinition **pvalue)
 {
     // Return whether pvalue was modified to include a type barrier ensuring
     // that writing the value to objTypes/id will not require changing type
     // information.
 
     // All objects in the set must have the same types for id. Otherwise, we
     // could bail out without subsequently triggering a type change that
     // invalidates the compiled code.
     types::HeapTypeSet *aggregateProperty = NULL;
 
     for (size_t i = 0; i < objTypes->getObjectCount(); i++) {
-        types::TypeObject *object = objTypes->getTypeOrSingleObject(cx, i);
+        types::TypeObject *object;
+        if (!objTypes->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
         if (!object)
             continue;
 
         if (object->unknownProperties())
             return false;
 
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
         if (!property)
@@ -2803,83 +2851,103 @@ AddTypeGuard(MBasicBlock *current, MDefi
     // For now, never move type object guards.
     guard->setNotMovable();
 
     return guard;
 }
 
 bool
 jit::PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj,
-                                   PropertyName *name, MDefinition **pvalue, bool canModify)
+                                   PropertyName *name, MDefinition **pvalue, bool canModify,
+                                   bool *result)
 {
+    JS_ASSERT(result);
+    *result = false;
+
     // If any value being written is not reflected in the type information for
     // objects which obj could represent, a type barrier is needed when writing
     // the value. As for propertyReadNeedsTypeBarrier, this only applies for
     // properties that are accounted for by type information, i.e. normal data
     // properties and elements.
 
     types::StackTypeSet *types = (*pobj)->resultTypeSet();
-    if (!types || types->unknownObject())
+    if (!types || types->unknownObject()) {
+        *result = true;
         return true;
+    }
 
     jsid id = name ? types::IdToTypeId(NameToId(name)) : JSID_VOID;
 
     // If all of the objects being written to have property types which already
     // reflect the value, no barrier at all is needed. Additionally, if all
     // objects being written to have the same types for the property, and those
     // types do *not* reflect the value, add a type barrier for the value.
 
     bool success = true;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object = types->getTypeOrSingleObject(cx, i);
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
         if (!object || object->unknownProperties())
             continue;
 
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
         if (!property) {
             success = false;
             break;
         }
         if (!TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
-            if (!canModify)
+            if (!canModify) {
+                *result = true;
                 return true;
+            }
             success = TryAddTypeBarrierForWrite(cx, current, types, id, pvalue);
             break;
         }
     }
 
     if (success)
-        return false;
+        return true;
 
     // If all of the objects except one have property types which reflect the
     // value, and the remaining object has no types at all for the property,
     // add a guard that the object does not have that remaining object's type.
 
-    if (types->getObjectCount() <= 1)
+    if (types->getObjectCount() <= 1) {
+        *result = true;
         return true;
+    }
 
     types::TypeObject *excluded = NULL;
     for (size_t i = 0; i < types->getObjectCount(); i++) {
-        types::TypeObject *object = types->getTypeOrSingleObject(cx, i);
+        types::TypeObject *object;
+        if (!types->getTypeOrSingleObject(cx, i, &object))
+            return false;
+
         if (!object || object->unknownProperties())
             continue;
 
         types::HeapTypeSet *property = object->getProperty(cx, id, false);
-        if (!property)
+        if (!property) {
+            *result = true;
             return true;
+        }
 
         if (TypeSetIncludes(property, (*pvalue)->type(), (*pvalue)->resultTypeSet()))
             continue;
 
-        if (!property->empty() || excluded)
+        if (!property->empty() || excluded) {
+            *result = true;
             return true;
+        }
         excluded = object;
     }
 
     JS_ASSERT(excluded);
 
     *pobj = AddTypeGuard(current, *pobj, excluded, /* bailOnEquality = */ true);
-    return false;
+    return true;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -8505,24 +8505,24 @@ typedef Vector<MDefinition *, 8, IonAllo
 
 // Helper functions used to decide how to build MIR.
 
 bool ElementAccessIsDenseNative(MDefinition *obj, MDefinition *id);
 bool ElementAccessIsTypedArray(MDefinition *obj, MDefinition *id,
                                ScalarTypeRepresentation::Type *arrayType);
 bool ElementAccessIsPacked(JSContext *cx, MDefinition *obj);
 bool ElementAccessHasExtraIndexedProperty(JSContext *cx, MDefinition *obj);
-MIRType DenseNativeElementType(JSContext *cx, MDefinition *obj);
+bool DenseNativeElementType(JSContext *cx, MDefinition *obj, MIRType *result);
 bool PropertyReadNeedsTypeBarrier(JSContext *cx, types::TypeObject *object, PropertyName *name,
-                                  types::StackTypeSet *observed, bool updateObserved = true);
+                                  types::StackTypeSet *observed, bool updateObserved, bool *result);
 bool PropertyReadNeedsTypeBarrier(JSContext *cx, MDefinition *obj, PropertyName *name,
-                                  types::StackTypeSet *observed);
-bool PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name);
-void AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
+                                  types::StackTypeSet *observed, bool *result);
+bool PropertyReadIsIdempotent(JSContext *cx, MDefinition *obj, PropertyName *name, bool *result);
+bool AddObjectsForPropertyRead(JSContext *cx, MDefinition *obj, PropertyName *name,
                                types::StackTypeSet *observed);
 bool PropertyWriteNeedsTypeBarrier(JSContext *cx, MBasicBlock *current, MDefinition **pobj,
                                    PropertyName *name, MDefinition **pvalue,
-                                   bool canModify = true);
+                                   bool canModify, bool *result);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MIR_h */
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -506,17 +506,17 @@ class TypeSet
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return NULL.
      */
     inline unsigned getObjectCount() const;
     inline TypeObjectKey *getObject(unsigned i) const;
     inline JSObject *getSingleObject(unsigned i) const;
     inline TypeObject *getTypeObject(unsigned i) const;
-    inline TypeObject *getTypeOrSingleObject(JSContext *cx, unsigned i) const;
+    inline bool getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **obj) const;
 
     void setOwnProperty(bool configurable) {
         flags |= TYPE_FLAG_OWN_PROPERTY;
         if (configurable)
             flags |= TYPE_FLAG_CONFIGURED_PROPERTY;
     }
     void setDefinite(unsigned slot) {
         JS_ASSERT(slot <= (TYPE_FLAG_DEFINITE_MASK >> TYPE_FLAG_DEFINITE_SHIFT));
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1402,30 +1402,38 @@ TypeSet::getSingleObject(unsigned i) con
 
 inline TypeObject *
 TypeSet::getTypeObject(unsigned i) const
 {
     TypeObjectKey *key = getObject(i);
     return (key && !(uintptr_t(key) & 1)) ? (TypeObject *) key : NULL;
 }
 
-inline TypeObject *
-TypeSet::getTypeOrSingleObject(JSContext *cx, unsigned i) const
+inline bool
+TypeSet::getTypeOrSingleObject(JSContext *cx, unsigned i, TypeObject **result) const
 {
+    JS_ASSERT(result);
     JS_ASSERT(cx->compartment()->activeAnalysis);
+
+    *result = NULL;
+
     TypeObject *type = getTypeObject(i);
     if (!type) {
         JSObject *singleton = getSingleObject(i);
         if (!singleton)
-            return NULL;
+            return true;
+
         type = singleton->uninlinedGetType(cx);
-        if (!type)
+        if (!type) {
             cx->compartment()->types.setPendingNukeTypes(cx);
+            return false;
+        }
     }
-    return type;
+    *result = type;
+    return true;
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeCallsite
 /////////////////////////////////////////////////////////////////////
 
 inline
 TypeCallsite::TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,