Bug 912303 - Added noSuchMethod support to baseline CALLPROP/CALLELEM stubs. r=efaust
authorKannan Vijayan <kvijayan@mozilla.com>
Tue, 12 Nov 2013 14:20:34 -0500
changeset 154604 58605e9a6ea172088f4d14d7b5d6e887769a0c3a
parent 154603 57882166c5d4c3ae806b019f8c3e15b407500657
child 154605 38ec7516cc7bc10437c46133b433a8d1fc231644
push id36138
push userkvijayan@mozilla.com
push dateTue, 12 Nov 2013 19:21:26 +0000
treeherdermozilla-inbound@58605e9a6ea1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs912303
milestone28.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 912303 - Added noSuchMethod support to baseline CALLPROP/CALLELEM stubs. r=efaust
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/arm/BaselineHelpers-arm.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/x64/BaselineHelpers-x64.h
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/BaselineHelpers-x86.h
js/src/jit/x86/MacroAssembler-x86.h
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3483,16 +3483,28 @@ IsCacheableSetPropCall(JSContext *cx, JS
     if (!func->hasJITCode())
         return false;
 
     *isScripted = true;
     return true;
 }
 
 static bool
+LookupNoSuchMethodHandler(JSContext *cx, HandleObject obj, HandleValue id,
+                          MutableHandleValue result)
+{
+    return OnUnknownMethod(cx, obj, id, result);
+}
+
+typedef bool (*LookupNoSuchMethodHandlerFn)(JSContext *, HandleObject, HandleValue,
+                                            MutableHandleValue);
+static const VMFunction LookupNoSuchMethodHandlerInfo =
+    FunctionInfo<LookupNoSuchMethodHandlerFn>(LookupNoSuchMethodHandler);
+
+static bool
 GetElemNativeStubExists(ICGetElem_Fallback *stub, HandleObject obj, HandleObject holder,
                         HandlePropertyName propName, bool needsAtomize)
 {
     bool indirect = (obj.get() != holder.get());
 
     for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->kind() != ICStub::GetElem_NativeSlot &&
             iter->kind() != ICStub::GetElem_NativePrototypeSlot &&
@@ -3663,16 +3675,17 @@ static bool TryAttachNativeGetElemStub(J
         return false;
 
     uint32_t dummy;
     if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&dummy))
         return true;
 
     RootedPropertyName propName(cx, JSID_TO_ATOM(id)->asPropertyName());
     bool needsAtomize = !key.toString()->isAtom();
+    bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
 
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!EffectlesslyLookupProperty(cx, obj, propName, &holder, &shape))
         return false;
 
     if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
         // If a suitable stub already exists, nothing else to do.
@@ -3693,30 +3706,37 @@ static bool TryAttachNativeGetElemStub(J
         IonSpew(IonSpew_BaselineIC, "  Generating GetElem(Native %s%s slot) stub "
                                     "(obj=%p, shape=%p, holder=%p, holderShape=%p)",
                     (obj == holder) ? "direct" : "prototype",
                     needsAtomize ? " atomizing" : "",
                     obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
 
         ICGetElemNativeStub::AccessType acctype = isFixedSlot ? ICGetElemNativeStub::FixedSlot
                                                               : ICGetElemNativeStub::DynamicSlot;
-        ICGetElemNativeCompiler compiler(cx, kind, monitorStub, obj, holder, propName, acctype,
-                                         needsAtomize, offset);
+        ICGetElemNativeCompiler compiler(cx, kind, isCallElem, monitorStub, obj, holder, propName,
+                                         acctype, needsAtomize, offset);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         return true;
     }
 
     bool getterIsScripted = false;
     if (IsCacheableGetPropCall(cx, obj, holder, shape, &getterIsScripted, /*isDOMProxy=*/false)) {
         RootedFunction getter(cx, &shape->getterObject()->as<JSFunction>());
 
+#if JS_HAS_NO_SUCH_METHOD
+        // It's unlikely that a getter function will be used in callelem locations.
+        // Just don't attach stubs in that case to avoid issues with __noSuchMethod__ handling.
+        if (isCallElem)
+            return true;
+#endif
+
         // If a suitable stub already exists, nothing else to do.
         if (GetElemNativeStubExists(stub, obj, holder, propName, needsAtomize))
             return true;
 
         // Remove any existing stubs that may interfere with the new stub being added.
         RemoveExistingGetElemNativeStubs(cx, stub, obj, holder, propName, needsAtomize);
 
         ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
@@ -3739,17 +3759,17 @@ static bool TryAttachNativeGetElemStub(J
                         needsAtomize ? " atomizing" : "",
                         obj.get(), obj->lastProperty(), holder.get(), holder->lastProperty());
         }
 
         ICGetElemNativeStub::AccessType acctype = getterIsScripted
                                                            ? ICGetElemNativeStub::ScriptedGetter
                                                            : ICGetElemNativeStub::NativeGetter;
         ICGetElemNativeCompiler compiler(cx, kind, monitorStub, obj, holder, propName, acctype,
-                                         needsAtomize, getter, pc - script->code);
+                                         needsAtomize, getter, pc - script->code, isCallElem);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         return true;
     }
 
@@ -3764,36 +3784,44 @@ TypedArrayRequiresFloatingPoint(TypedArr
             type == ScalarTypeRepresentation::TYPE_FLOAT32 ||
             type == ScalarTypeRepresentation::TYPE_FLOAT64);
 }
 
 static bool
 TryAttachGetElemStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICGetElem_Fallback *stub,
                      HandleValue lhs, HandleValue rhs, HandleValue res)
 {
+    bool isCallElem = (JSOp(*pc) == JSOP_CALLELEM);
+
     // Check for String[i] => Char accesses.
     if (lhs.isString() && rhs.isInt32() && res.isString() &&
         !stub->hasStub(ICStub::GetElem_String))
     {
+        // NoSuchMethod handling doesn't apply to string targets.
+
         IonSpew(IonSpew_BaselineIC, "  Generating GetElem(String[Int32]) stub");
         ICGetElem_String::Compiler compiler(cx);
         ICStub *stringStub = compiler.getStub(compiler.getStubSpace(script));
         if (!stringStub)
             return false;
 
         stub->addNewStub(stringStub);
         return true;
     }
 
     if (lhs.isMagic(JS_OPTIMIZED_ARGUMENTS) && rhs.isInt32() &&
         !ArgumentsGetElemStubExists(stub, ICGetElem_Arguments::Magic))
     {
+        // Any script with a CALLPROP on arguments (arguments.foo())
+        // should not have optimized arguments.
+        JS_ASSERT(!isCallElem);
+
         IonSpew(IonSpew_BaselineIC, "  Generating GetElem(MagicArgs[Int32]) stub");
         ICGetElem_Arguments::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                               ICGetElem_Arguments::Magic);
+                                               ICGetElem_Arguments::Magic, false);
         ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
         if (!argsStub)
             return false;
 
         stub->addNewStub(argsStub);
         return true;
     }
 
@@ -3805,32 +3833,32 @@ TryAttachGetElemStub(JSContext *cx, Hand
     // Check for ArgumentsObj[int] accesses
     if (obj->is<ArgumentsObject>() && rhs.isInt32()) {
         ICGetElem_Arguments::Which which = ICGetElem_Arguments::Normal;
         if (obj->is<StrictArgumentsObject>())
             which = ICGetElem_Arguments::Strict;
         if (!ArgumentsGetElemStubExists(stub, which)) {
             IonSpew(IonSpew_BaselineIC, "  Generating GetElem(ArgsObj[Int32]) stub");
             ICGetElem_Arguments::Compiler compiler(
-                cx, stub->fallbackMonitorStub()->firstMonitorStub(), which);
+                cx, stub->fallbackMonitorStub()->firstMonitorStub(), which, isCallElem);
             ICStub *argsStub = compiler.getStub(compiler.getStubSpace(script));
             if (!argsStub)
                 return false;
 
             stub->addNewStub(argsStub);
             return true;
         }
     }
 
     if (obj->isNative()) {
         // Check for NativeObject[int] dense accesses.
         if (rhs.isInt32() && rhs.toInt32() >= 0) {
             IonSpew(IonSpew_BaselineIC, "  Generating GetElem(Native[Int32] dense) stub");
             ICGetElem_Dense::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
-                                               obj->lastProperty());
+                                               obj->lastProperty(), isCallElem);
             ICStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
             if (!denseStub)
                 return false;
 
             stub->addNewStub(denseStub);
             return true;
         }
 
@@ -3840,16 +3868,22 @@ TryAttachGetElemStub(JSContext *cx, Hand
                 return false;
         }
     }
 
     // Check for TypedArray[int] => Number accesses.
     if (obj->is<TypedArrayObject>() && rhs.isInt32() && res.isNumber() &&
         !TypedArrayGetElemStubExists(stub, obj))
     {
+        // Don't attach CALLELEM stubs for accesses on typed array expected to yield numbers.
+#if JS_HAS_NO_SUCH_METHOD
+        if (isCallElem)
+            return true;
+#endif
+
         Rooted<TypedArrayObject*> tarr(cx, &obj->as<TypedArrayObject>());
         if (!cx->runtime()->jitSupportsFloatingPoint && TypedArrayRequiresFloatingPoint(tarr))
             return true;
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetElem(TypedArray[Int32]) stub");
         ICGetElem_TypedArray::Compiler compiler(cx, tarr->lastProperty(), tarr->type());
         ICStub *typedArrayStub = compiler.getStub(compiler.getStubSpace(script));
         if (!typedArrayStub)
@@ -4206,26 +4240,68 @@ ICGetElemNativeCompiler::generateStubCod
         }
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratchReg,
                                 popR1 ? &failurePopR1 : &failure);
     }
 
     if (acctype_ == ICGetElemNativeStub::DynamicSlot ||
         acctype_ == ICGetElemNativeStub::FixedSlot)
     {
+        masm.load32(Address(BaselineStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
+                    scratchReg);
+
         // Load from object.
         if (acctype_ == ICGetElemNativeStub::DynamicSlot)
-            masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), holderReg);
-
-        masm.load32(Address(BaselineStubReg, ICGetElemNativeSlotStub::offsetOfOffset()),
-                    scratchReg);
-        masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0);
-
+            masm.addPtr(Address(holderReg, JSObject::offsetOfSlots()), scratchReg);
+        else
+            masm.addPtr(holderReg, scratchReg);
+
+        Address valAddr(scratchReg, 0);
+
+        // Check if __noSuchMethod__ needs to be called.
+#if JS_HAS_NO_SUCH_METHOD
+        if (isCallElem_) {
+            Label afterNoSuchMethod;
+            Label skipNoSuchMethod;
+
+            masm.branchTestUndefined(Assembler::NotEqual, valAddr, &skipNoSuchMethod);
+
+            GeneralRegisterSet regs = availableGeneralRegs(0);
+            regs.takeUnchecked(objReg);
+            regs.take(R1);
+            Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
+            if (popR1)
+                masm.pop(R1.scratchReg());
+            enterStubFrame(masm, scratch);
+
+            masm.pushValue(R1);
+            masm.push(objReg);
+            if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
+                return false;
+
+            leaveStubFrame(masm);
+            // Result is already in R0
+            masm.jump(&afterNoSuchMethod);
+            masm.bind(&skipNoSuchMethod);
+
+            if (popR1)
+                masm.pop(R1.scratchReg());
+            masm.loadValue(valAddr, R0);
+            masm.bind(&afterNoSuchMethod);
+        } else {
+            masm.loadValue(valAddr, R0);
+            if (popR1)
+                masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg);
+        }
+#else
+        masm.loadValue(valAddr, R0);
         if (popR1)
             masm.addPtr(ImmWord(sizeof(size_t)), BaselineStackReg);
+#endif
+
     } else {
         JS_ASSERT(acctype_ == ICGetElemNativeStub::NativeGetter ||
                   acctype_ == ICGetElemNativeStub::ScriptedGetter);
         JS_ASSERT(kind == ICStub::GetElem_NativePrototypeCallNative ||
                   kind == ICStub::GetElem_NativePrototypeCallScripted);
 
         if (acctype_ == ICGetElemNativeStub::NativeGetter) {
             // If calling a native getter, there is no chance of failure now.
@@ -4350,17 +4426,55 @@ ICGetElem_Dense::Compiler::generateStubC
     // Bounds check.
     Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
     masm.branch32(Assembler::BelowOrEqual, initLength, key, &failure);
 
     // Hole check and load value.
     JS_STATIC_ASSERT(sizeof(Value) == 8);
     BaseIndex element(scratchReg, key, TimesEight);
     masm.branchTestMagic(Assembler::Equal, element, &failure);
+
+    // Check if __noSuchMethod__ should be called.
+#if JS_HAS_NO_SUCH_METHOD
+    entersStubFrame_ = true;
+    if (isCallElem_) {
+        Label afterNoSuchMethod;
+        Label skipNoSuchMethod;
+        regs = availableGeneralRegs(0);
+        regs.takeUnchecked(obj);
+        regs.take(R1);
+
+        masm.pushValue(R1);
+        masm.loadValue(element, R1);
+        masm.branchTestUndefined(Assembler::NotEqual, R1, &skipNoSuchMethod);
+
+        // Call __noSuchMethod__ checker.  Object pointer is in objReg.
+        scratchReg = regs.takeAnyExcluding(BaselineTailCallReg);
+        enterStubFrame(masm, scratchReg);
+
+        // propName (R1) already pushed above.
+        masm.push(obj);
+        if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
+            return false;
+
+        leaveStubFrame(masm);
+        // Result is already in R0
+        masm.jump(&afterNoSuchMethod);
+        masm.bind(&skipNoSuchMethod);
+
+        masm.moveValue(R1, R0);
+        masm.addPtr(Imm32(sizeof(Value)), BaselineStackReg); // pop previously pushed propName (R1)
+        masm.bind(&afterNoSuchMethod);
+    } else {
+        masm.loadValue(element, R0);
+    }
+#else
+    // Load value from element location.
     masm.loadValue(element, R0);
+#endif
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
@@ -4409,18 +4523,26 @@ ICGetElem_TypedArray::Compiler::generate
 }
 
 //
 // GetEelem_Arguments
 //
 bool
 ICGetElem_Arguments::Compiler::generateStubCode(MacroAssembler &masm)
 {
+    // Variatns of GetElem_Arguments can enter stub frames if entered in CallProp
+    // context when noSuchMethod support is on.
+#if JS_HAS_NO_SUCH_METHOD
+    entersStubFrame_ = true;
+#endif
+
     Label failure;
     if (which_ == ICGetElem_Arguments::Magic) {
+        JS_ASSERT(!isCallElem_);
+
         // Ensure that this is a magic arguments value.
         masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
 
         // Ensure that frame has not loaded different arguments object since.
         masm.branchTest32(Assembler::NonZero,
                           Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags()),
                           Imm32(BaselineFrame::HAS_ARGS_OBJ),
                           &failure);
@@ -4511,25 +4633,57 @@ ICGetElem_Arguments::Compiler::generateS
 
     // Don't bother testing specific bit, if any bit is set in the word, fail.
     masm.branchPtr(Assembler::NotEqual, scratchReg, ImmPtr(nullptr), &failureReconstructInputs);
 
     // Load the value.  use scratchReg and tempReg to form a ValueOperand to load into.
     masm.addPtr(Imm32(ArgumentsData::offsetOfArgs()), argData);
     regs.add(scratchReg);
     regs.add(tempReg);
-    regs.add(argData);
     ValueOperand tempVal = regs.takeAnyValue();
     masm.loadValue(BaseIndex(argData, idxReg, ScaleFromElemWidth(sizeof(Value))), tempVal);
 
     // Makesure that this is not a FORWARD_TO_CALL_SLOT magic value.
     masm.branchTestMagic(Assembler::Equal, tempVal, &failureReconstructInputs);
 
-    // Everything checked out, return value.
+#if JS_HAS_NO_SUCH_METHOD
+    if (isCallElem_) {
+        Label afterNoSuchMethod;
+        Label skipNoSuchMethod;
+
+        masm.branchTestUndefined(Assembler::NotEqual, tempVal, &skipNoSuchMethod);
+
+        // Call __noSuchMethod__ checker.  Object pointer is in objReg.
+        regs = availableGeneralRegs(0);
+        // R1 and objReg are guaranteed not to overlap.
+        regs.takeUnchecked(objReg);
+        regs.take(R1);
+        masm.tagValue(JSVAL_TYPE_INT32, idxReg, R1);
+        scratchReg = regs.takeAnyExcluding(BaselineTailCallReg);
+        enterStubFrame(masm, scratchReg);
+
+        masm.pushValue(R1);
+        masm.push(objReg);
+        if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
+            return false;
+
+        leaveStubFrame(masm);
+        // Result is already in R0
+        masm.jump(&afterNoSuchMethod);
+        masm.bind(&skipNoSuchMethod);
+
+        masm.moveValue(tempVal, R0);
+        masm.bind(&afterNoSuchMethod);
+    } else {
+        masm.moveValue(tempVal, R0);
+    }
+#else
+    // Copy value from temp to R0.
     masm.moveValue(tempVal, R0);
+#endif
 
     // Type-check result
     EmitEnterTypeMonitorIC(masm);
 
     // Failed, but inputs are deconstructed into object and int, and need to be
     // reconstructed into values.
     masm.bind(&failureReconstructInputs);
     masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
@@ -5813,47 +5967,58 @@ TryAttachNativeGetPropStub(JSContext *cx
                                     &domProxyShadowsResult, &domProxyHasGeneration))
     {
         return false;
     }
 
     if (!isDOMProxy && !obj->isNative())
         return true;
 
+    bool isCallProp = (JSOp(*pc) == JSOP_CALLPROP);
+
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (!isDOMProxy && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(holder, shape->slot(), &isFixedSlot, &offset);
 
         // Instantiate this property for singleton holders, for use during Ion compilation.
         if (IsIonEnabled(cx))
             types::EnsureTrackPropertyTypes(cx, holder, NameToId(name));
 
         ICStub::Kind kind = (obj == holder) ? ICStub::GetProp_Native
                                             : ICStub::GetProp_NativePrototype;
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s %s) stub",
                     isDOMProxy ? "DOMProxy" : "Native",
                     (obj == holder) ? "direct" : "prototype");
-        ICGetPropNativeCompiler compiler(cx, kind, monitorStub, obj, holder, isFixedSlot, offset);
+        ICGetPropNativeCompiler compiler(cx, kind, isCallProp, monitorStub, obj, holder,
+                                         name, isFixedSlot, offset);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     bool isScripted = false;
     bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted, isDOMProxy);
 
     // Try handling scripted getters.
     if (cacheableCall && isScripted && !isDOMProxy) {
+#if JS_HAS_NO_SUCH_METHOD
+        // It's hard to keep the original object alive through a call, and it's unlikely
+        // that a getter will be used to generate functions for calling in CALLPROP locations.
+        // Just don't attach stubs in that case.
+        if (isCallProp)
+            return true;
+#endif
+
         RootedFunction callee(cx, &shape->getterObject()->as<JSFunction>());
         JS_ASSERT(obj != holder);
         JS_ASSERT(callee->hasScript());
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(NativeObj/ScriptedGetter %s:%d) stub",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno);
 
         ICGetProp_CallScripted::Compiler compiler(cx, monitorStub, obj, holder, callee,
@@ -5864,16 +6029,24 @@ TryAttachNativeGetPropStub(JSContext *cx
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     // Try handling JSNative getters.
     if (cacheableCall && !isScripted) {
+#if JS_HAS_NO_SUCH_METHOD
+        // It's unlikely that a getter function will be used to generate functions for calling
+        // in CALLPROP locations.  Just don't attach stubs in that case to avoid issues with
+        // __noSuchMethod__ handling.
+        if (isCallProp)
+            return true;
+#endif
+
         RootedFunction callee(cx, &shape->getterObject()->as<JSFunction>());
         JS_ASSERT(obj != holder);
         JS_ASSERT(callee->isNative());
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s%s/NativeGetter %p) stub",
                 isDOMProxy ? "DOMProxyObj" : "NativeObj",
                 isDOMProxy && domProxyHasGeneration ? "WithGeneration" : "",
                 callee->native());
@@ -5904,16 +6077,20 @@ TryAttachNativeGetPropStub(JSContext *cx
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     // If it's a shadowed listbase proxy property, attach stub to call Proxy::get instead.
     if (isDOMProxy && domProxyShadowsResult == Shadows) {
         JS_ASSERT(obj == holder);
+#if JS_HAS_NO_SUCH_METHOD
+        if (isCallProp)
+            return true;
+#endif
 
         IonSpew(IonSpew_BaselineIC, "  Generating GetProp(DOMProxyProxy) stub");
         Rooted<ProxyObject*> proxy(cx, &obj->as<ProxyObject>());
         ICGetProp_DOMProxyShadowed::Compiler compiler(cx, monitorStub, proxy, name,
                                                       pc - script->code);
         ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
@@ -5921,19 +6098,19 @@ TryAttachNativeGetPropStub(JSContext *cx
         *attached = true;
         return true;
     }
 
     return true;
 }
 
 static bool
-TryAttachStringGetPropStub(JSContext *cx, HandleScript script, ICGetProp_Fallback *stub,
-                           HandlePropertyName name, HandleValue val, HandleValue res,
-                           bool *attached)
+TryAttachStringGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
+                           ICGetProp_Fallback *stub, HandlePropertyName name, HandleValue val,
+                           HandleValue res, bool *attached)
 {
     JS_ASSERT(!*attached);
     JS_ASSERT(val.isString());
 
     RootedObject stringProto(cx, script->global().getOrCreateStringPrototype(cx));
     if (!stringProto)
         return false;
 
@@ -6033,17 +6210,17 @@ DoGetPropFallback(JSContext *cx, Baselin
     }
 
     if (!TryAttachNativeGetPropStub(cx, script, pc, stub, name, val, res, &attached))
         return false;
     if (attached)
         return true;
 
     if (val.isString()) {
-        if (!TryAttachStringGetPropStub(cx, script, stub, name, val, res, &attached))
+        if (!TryAttachStringGetPropStub(cx, script, pc, stub, name, val, res, &attached))
             return false;
         if (attached)
             return true;
     }
 
     JS_ASSERT(!attached);
     stub->noteUnoptimizableAccess();
 
@@ -6214,17 +6391,17 @@ bool
 ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
     GeneralRegisterSet regs(availableGeneralRegs(1));
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
 
-    Register scratch = regs.takeAny();
+    Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
     masm.loadPtr(Address(BaselineStubReg, ICGetPropNativeStub::offsetOfShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
 
     Register holderReg;
     if (obj_ == holder_) {
@@ -6234,21 +6411,61 @@ ICGetPropNativeCompiler::generateStubCod
         holderReg = regs.takeAny();
         masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativePrototype::offsetOfHolder()),
                      holderReg);
         masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativePrototype::offsetOfHolderShape()),
                      scratch);
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
     }
 
-    if (!isFixedSlot_)
-        masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), holderReg);
+    if (!isFixedSlot_) {
+        // Don't overwrite actual holderReg if we need to load a dynamic slots object.
+        // May need to preserve object for noSuchMethod check later.
+        Register nextHolder = regs.takeAny();
+        masm.loadPtr(Address(holderReg, JSObject::offsetOfSlots()), nextHolder);
+        holderReg = nextHolder;
+    }
 
     masm.load32(Address(BaselineStubReg, ICGetPropNativeStub::offsetOfOffset()), scratch);
-    masm.loadValue(BaseIndex(holderReg, scratch, TimesOne), R0);
+    BaseIndex result(holderReg, scratch, TimesOne);
+
+#if JS_HAS_NO_SUCH_METHOD
+    entersStubFrame_ = true;
+    if (isCallProp_) {
+        // Check for __noSuchMethod__ invocation.
+        Label afterNoSuchMethod;
+        Label skipNoSuchMethod;
+
+        masm.push(objReg);
+        masm.loadValue(result, R0);
+        masm.branchTestUndefined(Assembler::NotEqual, R0, &skipNoSuchMethod);
+
+        masm.pop(objReg);
+        enterStubFrame(masm, scratch);
+
+        masm.movePtr(ImmGCPtr(propName_.get()), R1.scratchReg());
+        masm.tagValue(JSVAL_TYPE_STRING, R1.scratchReg(), R1);
+        masm.pushValue(R1);
+        masm.push(objReg);
+        if (!callVM(LookupNoSuchMethodHandlerInfo, masm))
+            return false;
+
+        leaveStubFrame(masm);
+        masm.jump(&afterNoSuchMethod);
+        masm.bind(&skipNoSuchMethod);
+
+        // Pop pushed objReg.
+        masm.addPtr(Imm32(sizeof(void *)), BaselineStackReg);
+        masm.bind(&afterNoSuchMethod);
+    } else {
+        masm.loadValue(result, R0);
+    }
+#else
+    masm.loadValue(result, R0);
+#endif
 
     // Enter type monitor IC to type-check result.
     EmitEnterTypeMonitorIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -749,16 +749,22 @@ class ICStub
           case SetProp_CallScripted:
           case SetProp_CallNative:
           case RetSub_Fallback:
           // These two fallback stubs don't actually make non-tail calls,
           // but the fallback code for the bailout path needs to pop the stub frame
           // pushed during the bailout.
           case GetProp_Fallback:
           case SetProp_Fallback:
+#if JS_HAS_NO_SUCH_METHOD
+          case GetElem_Dense:
+          case GetElem_Arguments:
+          case GetProp_NativePrototype:
+          case GetProp_Native:
+#endif
             return true;
           default:
             return false;
         }
     }
 
     // Optimized stubs get purged on GC.  But some stubs can be active on the
     // stack during GC - specifically the ones that can make calls.  To ensure
@@ -3105,58 +3111,68 @@ class ICGetElem_NativePrototypeCallScrip
                         code, firstMonitorStub, shape, name, acctype, needsAtomize, getter,
                         pcOffset, holder, holderShape);
     }
 };
 
 // Compiler for GetElem_NativeSlot and GetElem_NativePrototypeSlot stubs.
 class ICGetElemNativeCompiler : public ICStubCompiler
 {
+    bool isCallElem_;
     ICStub *firstMonitorStub_;
     HandleObject obj_;
     HandleObject holder_;
     HandlePropertyName name_;
     ICGetElemNativeStub::AccessType acctype_;
     bool needsAtomize_;
     uint32_t offset_;
     HandleFunction getter_;
     uint32_t pcOffset_;
 
     bool emitCallNative(MacroAssembler &masm, Register objReg);
     bool emitCallScripted(MacroAssembler &masm, Register objReg);
     bool generateStubCode(MacroAssembler &masm);
 
   protected:
     virtual int32_t getKey() const {
+#if JS_HAS_NO_SUCH_METHOD
+        return static_cast<int32_t>(kind) |
+               (static_cast<int32_t>(isCallElem_) << 16) |
+               (static_cast<int32_t>(needsAtomize_) << 17) |
+               (static_cast<int32_t>(acctype_) << 18);
+#else
         return static_cast<int32_t>(kind) | (static_cast<int32_t>(needsAtomize_) << 16) |
                (static_cast<int32_t>(acctype_) << 17);
+#endif
     }
 
   public:
-    ICGetElemNativeCompiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
-                            HandleObject obj, HandleObject holder, HandlePropertyName name,
-                            ICGetElemNativeStub::AccessType acctype, bool needsAtomize,
-                            uint32_t offset)
+    ICGetElemNativeCompiler(JSContext *cx, ICStub::Kind kind, bool isCallElem,
+                            ICStub *firstMonitorStub, HandleObject obj, HandleObject holder,
+                            HandlePropertyName name, ICGetElemNativeStub::AccessType acctype,
+                            bool needsAtomize, uint32_t offset)
       : ICStubCompiler(cx, kind),
+        isCallElem_(isCallElem),
         firstMonitorStub_(firstMonitorStub),
         obj_(obj),
         holder_(holder),
         name_(name),
         acctype_(acctype),
         needsAtomize_(needsAtomize),
         offset_(offset),
         getter_(js::NullPtr()),
         pcOffset_(0)
     {}
 
     ICGetElemNativeCompiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
                             HandleObject obj, HandleObject holder, HandlePropertyName name,
                             ICGetElemNativeStub::AccessType acctype, bool needsAtomize,
-                            HandleFunction getter, uint32_t pcOffset)
+                            HandleFunction getter, uint32_t pcOffset, bool isCallElem)
       : ICStubCompiler(cx, kind),
+        isCallElem_(false),
         firstMonitorStub_(firstMonitorStub),
         obj_(obj),
         holder_(holder),
         name_(name),
         acctype_(acctype),
         needsAtomize_(needsAtomize),
         offset_(0),
         getter_(getter),
@@ -3250,25 +3266,35 @@ class ICGetElem_Dense : public ICMonitor
 
     HeapPtrShape &shape() {
         return shape_;
     }
 
     class Compiler : public ICStubCompiler {
       ICStub *firstMonitorStub_;
       RootedShape shape_;
+      bool isCallElem_;
 
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
+        virtual int32_t getKey() const {
+#if JS_HAS_NO_SUCH_METHOD
+            return static_cast<int32_t>(kind) | (static_cast<int32_t>(isCallElem_) << 16);
+#else
+            return static_cast<int32_t>(kind);
+#endif
+        }
+
       public:
-        Compiler(JSContext *cx, ICStub *firstMonitorStub, Shape *shape)
+        Compiler(JSContext *cx, ICStub *firstMonitorStub, Shape *shape, bool isCallElem)
           : ICStubCompiler(cx, ICStub::GetElem_Dense),
             firstMonitorStub_(firstMonitorStub),
-            shape_(cx, shape)
+            shape_(cx, shape),
+            isCallElem_(isCallElem)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICGetElem_Dense::New(space, getStubCode(), firstMonitorStub_, shape_);
         }
     };
 };
 
@@ -3346,29 +3372,37 @@ class ICGetElem_Arguments : public ICMon
 
     Which which() const {
         return static_cast<Which>(extra_);
     }
 
     class Compiler : public ICStubCompiler {
       ICStub *firstMonitorStub_;
       Which which_;
+      bool isCallElem_;
 
       protected:
         bool generateStubCode(MacroAssembler &masm);
 
         virtual int32_t getKey() const {
+#if JS_HAS_NO_SUCH_METHOD
+            return static_cast<int32_t>(kind) |
+                   static_cast<int32_t>(isCallElem_ << 16) |
+                   (static_cast<int32_t>(which_) << 17);
+#else
             return static_cast<int32_t>(kind) | (static_cast<int32_t>(which_) << 16);
+#endif
         }
 
       public:
-        Compiler(JSContext *cx, ICStub *firstMonitorStub, Which which)
+        Compiler(JSContext *cx, ICStub *firstMonitorStub, Which which, bool isCallElem)
           : ICStubCompiler(cx, ICStub::GetElem_Arguments),
             firstMonitorStub_(firstMonitorStub),
-            which_(which)
+            which_(which),
+            isCallElem_(isCallElem)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             return ICGetElem_Arguments::New(space, getStubCode(), firstMonitorStub_, which_);
         }
     };
 };
 
@@ -4248,37 +4282,47 @@ class ICGetProp_NativePrototype : public
         return offsetof(ICGetProp_NativePrototype, holderShape_);
     }
 };
 
 
 // Compiler for GetProp_Native and GetProp_NativePrototype stubs.
 class ICGetPropNativeCompiler : public ICStubCompiler
 {
+    bool isCallProp_;
     ICStub *firstMonitorStub_;
     HandleObject obj_;
     HandleObject holder_;
+    HandlePropertyName propName_;
     bool isFixedSlot_;
     uint32_t offset_;
 
     bool generateStubCode(MacroAssembler &masm);
 
   protected:
     virtual int32_t getKey() const {
+#if JS_HAS_NO_SUCH_METHOD
+        return static_cast<int32_t>(kind) |
+               (static_cast<int32_t>(isCallProp_) << 16) |
+               (static_cast<int32_t>(isFixedSlot_) << 17);
+#else
         return static_cast<int32_t>(kind) | (static_cast<int32_t>(isFixedSlot_) << 16);
+#endif
     }
 
   public:
-    ICGetPropNativeCompiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
-                            HandleObject obj, HandleObject holder, bool isFixedSlot,
-                            uint32_t offset)
+    ICGetPropNativeCompiler(JSContext *cx, ICStub::Kind kind, bool isCallProp,
+                            ICStub *firstMonitorStub, HandleObject obj, HandleObject holder,
+                            HandlePropertyName propName, bool isFixedSlot, uint32_t offset)
       : ICStubCompiler(cx, kind),
+        isCallProp_(isCallProp),
         firstMonitorStub_(firstMonitorStub),
         obj_(obj),
         holder_(holder),
+        propName_(propName),
         isFixedSlot_(isFixedSlot),
         offset_(offset)
     {}
 
     ICStub *getStub(ICStubSpace *space) {
         RootedShape shape(cx, obj_->lastProperty());
         if (kind == ICStub::GetProp_Native) {
             JS_ASSERT(obj_ == holder_);
@@ -4350,18 +4394,18 @@ class ICGetPropCallGetter : public ICMon
       protected:
         ICStub *firstMonitorStub_;
         RootedObject obj_;
         RootedObject holder_;
         RootedFunction getter_;
         uint32_t pcOffset_;
 
       public:
-        Compiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub, HandleObject obj,
-                 HandleObject holder, HandleFunction getter, uint32_t pcOffset)
+        Compiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
+                 HandleObject obj, HandleObject holder, HandleFunction getter, uint32_t pcOffset)
           : ICStubCompiler(cx, kind),
             firstMonitorStub_(firstMonitorStub),
             obj_(cx, obj),
             holder_(cx, holder),
             getter_(cx, getter),
             pcOffset_(pcOffset)
         {
             JS_ASSERT(kind == ICStub::GetProp_CallScripted || kind == ICStub::GetProp_CallNative);
--- a/js/src/jit/arm/BaselineHelpers-arm.h
+++ b/js/src/jit/arm/BaselineHelpers-arm.h
@@ -200,17 +200,17 @@ EmitUnstowICValues(MacroAssembler &masm,
 {
     JS_ASSERT(values >= 0 && values <= 2);
     switch(values) {
       case 1:
         // Unstow R0
         masm.popValue(R0);
         break;
       case 2:
-        // Untow R0 and R1
+        // Unstow R0 and R1
         masm.popValue(R1);
         masm.popValue(R0);
         break;
     }
 }
 
 inline void
 EmitCallTypeUpdateIC(MacroAssembler &masm, IonCode *code, uint32_t objectOffset)
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2684,24 +2684,72 @@ MacroAssemblerARMCompat::testInt32(Assem
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(address, ScratchRegister);
     ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_INT32));
     return cond;
 }
 
 Assembler::Condition
-MacroAssemblerARMCompat::testDouble(Assembler::Condition cond, const Address &address)
+MacroAssemblerARMCompat::testDouble(Condition cond, const Address &address)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(address, ScratchRegister);
     return testDouble(cond, ScratchRegister);
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testBoolean(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testBoolean(cond, ScratchRegister);
+}
+
+Assembler::Condition
+MacroAssemblerARMCompat::testNull(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testNull(cond, ScratchRegister);
+}
+
+Assembler::Condition
+MacroAssemblerARMCompat::testUndefined(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testUndefined(cond, ScratchRegister);
+}
+
+Assembler::Condition
+MacroAssemblerARMCompat::testString(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testString(cond, ScratchRegister);
+}
+
+Assembler::Condition
+MacroAssemblerARMCompat::testObject(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testObject(cond, ScratchRegister);
+}
+
+Assembler::Condition
+MacroAssemblerARMCompat::testNumber(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testNumber(cond, ScratchRegister);
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testDouble(Condition cond, const Register &tag)
 {
     JS_ASSERT(cond == Assembler::Equal || cond == Assembler::NotEqual);
     Condition actual = (cond == Equal) ? Below : AboveOrEqual;
     ma_cmp(tag, ImmTag(JSVAL_TAG_CLEAR));
     return actual;
 }
 
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -723,16 +723,22 @@ class MacroAssemblerARMCompat : public M
     Condition testNumber(Condition cond, const Register &tag);
     Condition testMagic(Condition cond, const Register &tag);
     Condition testPrimitive(Condition cond, const Register &tag);
 
     Condition testGCThing(Condition cond, const Address &address);
     Condition testMagic(Condition cond, const Address &address);
     Condition testInt32(Condition cond, const Address &address);
     Condition testDouble(Condition cond, const Address &address);
+    Condition testBoolean(Condition cond, const Address &address);
+    Condition testNull(Condition cond, const Address &address);
+    Condition testUndefined(Condition cond, const Address &address);
+    Condition testString(Condition cond, const Address &address);
+    Condition testObject(Condition cond, const Address &address);
+    Condition testNumber(Condition cond, const Address &address);
 
     Condition testUndefined(Condition cond, const BaseIndex &src);
     Condition testNull(Condition cond, const BaseIndex &src);
     Condition testBoolean(Condition cond, const BaseIndex &src);
     Condition testString(Condition cond, const BaseIndex &src);
     Condition testInt32(Condition cond, const BaseIndex &src);
     Condition testObject(Condition cond, const BaseIndex &src);
     Condition testDouble(Condition cond, const BaseIndex &src);
--- a/js/src/jit/x64/BaselineHelpers-x64.h
+++ b/js/src/jit/x64/BaselineHelpers-x64.h
@@ -188,17 +188,17 @@ EmitUnstowICValues(MacroAssembler &masm,
     switch(values) {
       case 1:
         // Unstow R0
         masm.pop(BaselineTailCallReg);
         masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
       case 2:
-        // Untow R0 and R1
+        // Unstow R0 and R1
         masm.pop(BaselineTailCallReg);
         masm.popValue(R1);
         masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -351,28 +351,66 @@ class MacroAssemblerX64 : public MacroAs
     Condition testObject(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testObject(cond, ScratchReg);
     }
     Condition testGCThing(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testGCThing(cond, ScratchReg);
     }
+    Condition testPrimitive(Condition cond, const ValueOperand &src) {
+        splitTag(src, ScratchReg);
+        return testPrimitive(cond, ScratchReg);
+    }
+
+
+    Condition testUndefined(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testUndefined(cond, ScratchReg);
+    }
+    Condition testInt32(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testInt32(cond, ScratchReg);
+    }
+    Condition testBoolean(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testBoolean(cond, ScratchReg);
+    }
+    Condition testDouble(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testDouble(cond, ScratchReg);
+    }
+    Condition testNumber(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testNumber(cond, ScratchReg);
+    }
+    Condition testNull(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testNull(cond, ScratchReg);
+    }
+    Condition testString(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testString(cond, ScratchReg);
+    }
+    Condition testObject(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testObject(cond, ScratchReg);
+    }
+    Condition testPrimitive(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testPrimitive(cond, ScratchReg);
+    }
     Condition testGCThing(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testGCThing(cond, ScratchReg);
     }
     Condition testMagic(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testMagic(cond, ScratchReg);
     }
-    Condition testPrimitive(Condition cond, const ValueOperand &src) {
-        splitTag(src, ScratchReg);
-        return testPrimitive(cond, ScratchReg);
-    }
 
 
     Condition testUndefined(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
         return testUndefined(cond, ScratchReg);
     }
     Condition testNull(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
@@ -768,16 +806,20 @@ class MacroAssemblerX64 : public MacroAs
     // x64 can test for certain types directly from memory when the payload
     // of the type is limited to 32 bits. This avoids loading into a register,
     // accesses half as much memory, and removes a right-shift.
     void branchTestUndefined(Condition cond, const Operand &operand, Label *label) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_UNDEFINED))));
         j(cond, label);
     }
+    void branchTestUndefined(Condition cond, const Address &address, Label *label) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        branchTestUndefined(cond, Operand(address), label);
+    }
     void branchTestInt32(Condition cond, const Operand &operand, Label *label) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_INT32))));
         j(cond, label);
     }
     void branchTestInt32(Condition cond, const Address &address, Label *label) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         branchTestInt32(cond, Operand(address), label);
--- a/js/src/jit/x86/BaselineHelpers-x86.h
+++ b/js/src/jit/x86/BaselineHelpers-x86.h
@@ -194,17 +194,17 @@ EmitUnstowICValues(MacroAssembler &masm,
     switch(values) {
       case 1:
         // Unstow R0
         masm.pop(BaselineTailCallReg);
         masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
       case 2:
-        // Untow R0 and R1
+        // Unstow R0 and R1
         masm.pop(BaselineTailCallReg);
         masm.popValue(R1);
         masm.popValue(R0);
         masm.push(BaselineTailCallReg);
         break;
     }
 }
 
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -352,16 +352,28 @@ class MacroAssemblerX86 : public MacroAs
         Condition actual = (cond == Equal) ? Below : AboveOrEqual;
         cmpl(ToType(operand), ImmTag(JSVAL_TAG_CLEAR));
         return actual;
     }
     Condition testDouble(Condition cond, const Address &address) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         return testDouble(cond, Operand(address));
     }
+
+
+    Condition testUndefined(Condition cond, const Operand &operand) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        cmpl(ToType(operand), ImmTag(JSVAL_TAG_UNDEFINED));
+        return cond;
+    }
+    Condition testUndefined(Condition cond, const Address &addr) {
+        return testUndefined(cond, Operand(addr));
+    }
+
+
     Condition testUndefined(Condition cond, const ValueOperand &value) {
         return testUndefined(cond, value.typeReg());
     }
     Condition testBoolean(Condition cond, const ValueOperand &value) {
         return testBoolean(cond, value.typeReg());
     }
     Condition testInt32(Condition cond, const ValueOperand &value) {
         return testInt32(cond, value.typeReg());