Merge backout.
authorDavid Anderson <dvander@alliedmods.net>
Sun, 05 Sep 2010 18:36:26 -0700
changeset 74543 8a835a404d7094b0cba5e58f71544cbdb21c580c
parent 74542 a0d8de655f06531cd898dfd171cd574231b6e783 (current diff)
parent 74541 0548d99a060115c349be0109cca3c01713233234 (diff)
child 74545 96ddc3bc17b6df1ab5cac8485ba492af3f501dd6
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone2.0b6pre
Merge backout.
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -367,16 +367,17 @@ struct JSObject {
 
     const js::ObjectOps *getOps() const {
         return &getClass()->ops;
     }
 
     inline void trace(JSTracer *trc);
 
     static size_t flagsOffset();
+    uint32 flagsAndFreeslot();
 
     uint32 shape() const {
         JS_ASSERT(objShape != JSObjectMap::INVALID_SHAPE);
         return objShape;
     }
 
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -687,16 +687,24 @@ JSObject::flagsOffset()
             offset = testOffset;
             return offset;
         }
     }
     JS_NOT_REACHED("memory weirdness");
     return 0;
 }
 
+inline uint32
+JSObject::flagsAndFreeslot()
+{
+    size_t offset = flagsOffset();
+    char *ptr = offset + (char*) this;
+    return *(uint32*)ptr;
+}
+
 namespace js {
 
 class AutoPropDescArrayRooter : private AutoGCRooter
 {
   public:
     AutoPropDescArrayRooter(JSContext *cx)
       : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx)
     { }
--- a/js/src/jsregexpinlines.h
+++ b/js/src/jsregexpinlines.h
@@ -198,18 +198,29 @@ inline bool
 RegExp::compile(JSContext *cx)
 {
     if (!sticky())
         return compileHelper(cx, *source);
     /*
      * The sticky case we implement hackily by prepending a caret onto the front
      * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
      */
-    JSString *fakeySource = js_ConcatStringsZ(cx, "^", source);
-    AutoValueRooter rooter(cx, StringValue(fakeySource));
+    static const jschar prefix[] = {'^', '(', '?', ':'};
+    static const jschar postfix[] = {')'};
+
+    JSCharBuffer cb(cx);
+    if (!cb.reserve(JS_ARRAY_LENGTH(prefix) + source->length() + JS_ARRAY_LENGTH(postfix)))
+        return false;
+    JS_ALWAYS_TRUE(cb.append(prefix, JS_ARRAY_LENGTH(prefix)));
+    JS_ALWAYS_TRUE(cb.append(source->chars(), source->length()));
+    JS_ALWAYS_TRUE(cb.append(postfix, JS_ARRAY_LENGTH(postfix)));
+
+    JSString *fakeySource = js_NewStringFromCharBuffer(cx, cb);
+    if (!fakeySource)
+        return false;
     return compileHelper(cx, *fakeySource);
 }
 
 inline bool
 RegExp::hasMetaChars(const jschar *chars, size_t length)
 {
     for (size_t i = 0; i < length; ++i) {
         jschar c = chars[i];
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -484,28 +484,30 @@ JSCompartment::sweep(JSContext *cx)
             e.removeFront();
     }
 }
 
 void
 JSCompartment::purge(JSContext *cx)
 {
 #ifdef JS_METHODJIT
-    if (!cx->runtime->gcRegenShapes)
-        return;
-
     for (JSScript *script = (JSScript *)scripts.next;
          &script->links != &scripts;
          script = (JSScript *)script->links.next) {
         if (script->jit) {
 # if defined JS_POLYIC
             mjit::ic::PurgePICs(cx, script);
 # endif
 # if defined JS_MONOIC
-            mjit::ic::PurgeMICs(cx, script);
+            /*
+             * MICs do not refer to data which can be GC'ed, but are sensitive
+             * to shape regeneration.
+             */
+            if (cx->runtime->gcRegenShapes)
+                mjit::ic::PurgeMICs(cx, script);
 # endif
         }
     }
 #endif
 }
 
 AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target)
     : context(cx),
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -507,17 +507,18 @@ mjit::Compiler::finishThisUp()
         JS_ASSERT(script->pics[i].shapeGuard == masm.distanceOf(pics[i].shapeGuard) -
                                      masm.distanceOf(pics[i].fastPathStart));
         script->pics[i].shapeRegHasBaseShape = true;
 
 # if defined JS_CPU_X64
         memcpy(&script->pics[i].labels, &pics[i].labels, sizeof(PICLabels));
 # endif
 
-        if (pics[i].kind == ic::PICInfo::SET) {
+        if (pics[i].kind == ic::PICInfo::SET ||
+            pics[i].kind == ic::PICInfo::SETMETHOD) {
             script->pics[i].u.vr = pics[i].vr;
         } else if (pics[i].kind != ic::PICInfo::NAME) {
             if (pics[i].hasTypeCheck) {
                 int32 distance = stubcc.masm.distanceOf(pics[i].typeCheck) -
                                  stubcc.masm.distanceOf(pics[i].slowPathStart);
                 JS_ASSERT(distance <= 0);
                 script->pics[i].u.get.typeCheckOffset = distance;
             }
@@ -822,20 +823,17 @@ mjit::Compiler::generateMethod()
             jsop_bitop(op);
           END_CASE(JSOP_LSH)
 
           BEGIN_CASE(JSOP_RSH)
             jsop_rsh();
           END_CASE(JSOP_RSH)
 
           BEGIN_CASE(JSOP_URSH)
-            prepareStubCall(Uses(2));
-            stubCall(stubs::Ursh);
-            frame.popn(2);
-            frame.pushSynced();
+            jsop_bitop(op);
           END_CASE(JSOP_URSH)
 
           BEGIN_CASE(JSOP_ADD)
             jsop_binary(op, stubs::Add);
           END_CASE(JSOP_ADD)
 
           BEGIN_CASE(JSOP_SUB)
             jsop_binary(op, stubs::Sub);
@@ -2741,18 +2739,17 @@ mjit::Compiler::jsop_callprop_obj(JSAtom
     pic.shapeGuard = masm.label();
 
     DataLabel32 inlineShapeLabel;
     Jump j = masm.branch32WithPatch(Assembler::NotEqual, shapeReg,
                            Imm32(int32(JSObjectMap::INVALID_SHAPE)),
                            inlineShapeLabel);
     DBGLABEL(dbgInlineShapeJump);
 
-    pic.slowPathStart = stubcc.masm.label();
-    stubcc.linkExit(j, Uses(1));
+    pic.slowPathStart = stubcc.linkExit(j, Uses(1));
 
     stubcc.leave();
     stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1);
     pic.callReturn = stubcc.call(ic::CallProp);
 
     /* Load dslots. */
 #if defined JS_NUNBOX32
     DBGLABEL(dbgDslotsLoad);
@@ -2844,17 +2841,19 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
     FrameEntry *rhs = frame.peek(-1);
 
     /* If the incoming type will never PIC, take slow path. */
     if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
         jsop_setprop_slow(atom);
         return;
     }
 
-    PICGenInfo pic(ic::PICInfo::SET);
+    JSOp op = JSOp(*PC);
+
+    PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET);
     pic.atom = atom;
 
     /* Guard that the type is an object. */
     Jump typeCheck;
     if (!lhs->isTypeKnown()) {
         RegisterID reg = frame.tempRegForType(lhs);
         pic.typeReg = reg;
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -327,27 +327,34 @@ mjit::Compiler::jsop_bitop(JSOp op)
         stub = stubs::BitAnd;
         break;
       case JSOP_BITXOR:
         stub = stubs::BitXor;
         break;
       case JSOP_LSH:
         stub = stubs::Lsh;
         break;
+      case JSOP_URSH:
+        stub = stubs::Ursh;
+        break;
       default:
         JS_NOT_REACHED("wat");
         return;
     }
 
     /* We only want to handle integers here. */
-    if (rhs->isNotType(JSVAL_TYPE_INT32) || lhs->isNotType(JSVAL_TYPE_INT32)) {
+    if (rhs->isNotType(JSVAL_TYPE_INT32) || lhs->isNotType(JSVAL_TYPE_INT32) || 
+        (op == JSOP_URSH && rhs->isConstant() && rhs->getValue().toInt32() % 32 == 0)) {
         prepareStubCall(Uses(2));
         stubCall(stub);
         frame.popn(2);
-        frame.pushSyncedType(JSVAL_TYPE_INT32);
+        if (op == JSOP_URSH)
+            frame.pushSynced();
+        else
+            frame.pushSyncedType(JSVAL_TYPE_INT32);
         return;
     }
            
     /* Test the types. */
     bool stubNeeded = false;
     if (!rhs->isTypeKnown()) {
         Jump rhsFail = frame.testInt32(Assembler::NotEqual, rhs);
         stubcc.linkExit(rhsFail, Uses(2));
@@ -355,21 +362,16 @@ mjit::Compiler::jsop_bitop(JSOp op)
         stubNeeded = true;
     }
     if (!lhs->isTypeKnown() && !frame.haveSameBacking(lhs, rhs)) {
         Jump lhsFail = frame.testInt32(Assembler::NotEqual, lhs);
         stubcc.linkExit(lhsFail, Uses(2));
         stubNeeded = true;
     }
 
-    if (stubNeeded) {
-        stubcc.leave();
-        stubcc.call(stub);
-    }
-
     if (lhs->isConstant() && rhs->isConstant()) {
         int32 L = lhs->getValue().toInt32();
         int32 R = rhs->getValue().toInt32();
 
         frame.popn(2);
         switch (op) {
           case JSOP_BITOR:
             frame.push(Int32Value(L | R));
@@ -378,16 +380,25 @@ mjit::Compiler::jsop_bitop(JSOp op)
             frame.push(Int32Value(L ^ R));
             return;
           case JSOP_BITAND:
             frame.push(Int32Value(L & R));
             return;
           case JSOP_LSH:
             frame.push(Int32Value(L << R));
             return;
+          case JSOP_URSH: 
+          {
+            uint32 unsignedL;
+            if (ValueToECMAUint32(cx, lhs->getValue(), (uint32_t*)&unsignedL)) {
+                frame.push(NumberValue(uint32(unsignedL >> (R & 31))));
+                return;
+            }
+            break;
+          }
           default:
             JS_NOT_REACHED("say wat");
         }
     }
 
     RegisterID reg;
 
     switch (op) {
@@ -427,26 +438,37 @@ mjit::Compiler::jsop_bitop(JSOp op)
             else
                 masm.or32(rhsReg, reg);
         }
 
         break;
       }
 
       case JSOP_LSH:
+      case JSOP_URSH:
       {
         /* Not commutative. */
         if (rhs->isConstant()) {
             RegisterID reg = frame.ownRegForData(lhs);
             int shift = rhs->getValue().toInt32() & 0x1F;
 
-            if (shift)
-                masm.lshift32(Imm32(shift), reg);
-
+            if (shift) {
+                if (op == JSOP_LSH)
+                    masm.lshift32(Imm32(shift), reg);
+                else
+                    masm.urshift32(Imm32(shift), reg);
+            }
+            if (stubNeeded) {
+                stubcc.leave();
+                stubcc.call(stub);
+            }
             frame.popn(2);
+            
+            /* x >>> 0 may result in a double, handled above. */
+            JS_ASSERT_IF(op == JSOP_URSH, shift >= 1);
             frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
 
             if (stubNeeded)
                 stubcc.rejoin(Changes(1));
 
             return;
         }
 #if defined(JS_CPU_X86) || defined(JS_CPU_X64)
@@ -457,32 +479,49 @@ mjit::Compiler::jsop_bitop(JSOp op)
         RegisterID rr = frame.tempRegForData(rhs);
 #endif
 
         frame.pinReg(rr);
         if (lhs->isConstant()) {
             reg = frame.allocReg();
             masm.move(Imm32(lhs->getValue().toInt32()), reg);
         } else {
-            reg = frame.ownRegForData(lhs);
+            reg = frame.copyDataIntoReg(lhs);
         }
         frame.unpinReg(rr);
-
-        masm.lshift32(rr, reg);
+        
+        if (op == JSOP_LSH) {
+            masm.lshift32(rr, reg);
+        } else {
+            masm.urshift32(rr, reg);
+            
+            Jump isNegative = masm.branch32(Assembler::LessThan, reg, Imm32(0));
+            stubcc.linkExit(isNegative, Uses(2));
+            stubNeeded = true;
+        }
         break;
       }
 
       default:
         JS_NOT_REACHED("NYI");
         return;
     }
 
+    if (stubNeeded) {
+        stubcc.leave();
+        stubcc.call(stub);
+    }
+
     frame.pop();
     frame.pop();
-    frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
+
+    if (op == JSOP_URSH)
+        frame.pushNumber(reg, true);
+    else
+        frame.pushTypedPayload(JSVAL_TYPE_INT32, reg);
 
     if (stubNeeded)
         stubcc.rejoin(Changes(1));
 }
 
 void
 mjit::Compiler::jsop_globalinc(JSOp op, uint32 index)
 {
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -321,42 +321,135 @@ class SetPropCompiler : public PICStubCo
             if (pic.u.vr.u.s.isTypeKnown)
                 masm.storeTypeTag(ImmType(pic.u.vr.u.s.type.knownType), address);
             else
                 masm.storeTypeTag(pic.u.vr.u.s.type.reg, address);
             masm.storePayload(pic.u.vr.u.s.data, address);
         }
     }
 
-    bool generateStub(const Shape *shape)
+    bool generateStub(uint32 initialShape, uint32 initialFlagsAndFreeslot,
+                      const Shape *shape, bool adding)
     {
+        /* Exits to the slow path. */
+        Vector<Jump, 8> slowExits(f.cx);
+
         Assembler masm;
 
         // Shape guard.
         if (pic.shapeNeedsRemat()) {
             masm.loadShape(pic.objReg, pic.shapeReg);
             pic.shapeRegHasBaseShape = true;
         }
 
         Label start = masm.label();
-        Jump shapeMismatch = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
-                                                   Imm32(obj->shape()));
+        Jump shapeGuard = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
+                                                Imm32(initialShape));
+        if (!slowExits.append(shapeGuard))
+            return false;
 
 #if defined JS_NUNBOX32
         DBGLABEL(dbgStubShapeJump);
         JS_ASSERT(masm.differenceBetween(start, dbgStubShapeJump) == SETPROP_STUB_SHAPE_JUMP);
 #elif defined JS_PUNBOX64
         Label stubShapeJumpLabel = masm.label();
 #endif
 
         JS_ASSERT_IF(!shape->hasDefaultSetter(), obj->getClass() == &js_CallClass);
 
         Jump rebrand;
         Jump skipOver;
-        if (shape->hasDefaultSetter()) {
+
+        if (adding) {
+            JS_ASSERT(shape->hasSlot());
+            pic.shapeRegHasBaseShape = false;
+
+            Address flagsAndFreeslot(pic.objReg, JSObject::flagsOffset());
+
+            /*
+             * We need to always check the flags match as some object flags can
+             * vary between objects of the same shape (DELEGATE, SYSTEM).
+             * It would be nice if these bits did not vary, so that just the
+             * shape check is sufficient.
+             */
+            Jump flagsMismatch = masm.branch32(Assembler::NotEqual, flagsAndFreeslot,
+                                               Imm32(initialFlagsAndFreeslot));
+            if (!slowExits.append(flagsMismatch))
+                return false;
+
+            /* Emit shape guards for the object's prototype chain. */
+            size_t chainLength = 0;
+            JSObject *proto = obj->getProto();
+            while (proto) {
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, proto)), pic.shapeReg);
+                for (size_t i = 0; i < chainLength; i++)
+                    masm.loadPtr(Address(pic.shapeReg, offsetof(JSObject, proto)), pic.shapeReg);
+                masm.loadShape(pic.shapeReg, pic.shapeReg);
+
+                Jump protoGuard = masm.branch32(Assembler::NotEqual, pic.shapeReg,
+                                                Imm32(proto->shape()));
+                if (!slowExits.append(protoGuard))
+                    return false;
+
+                proto = proto->getProto();
+                chainLength++;
+            }
+
+            if (pic.kind == ic::PICInfo::SETMETHOD) {
+                /*
+                 * Guard that the value is equal to the shape's method.
+                 * We already know it is a function, so test the payload.
+                 */
+                JS_ASSERT(shape->isMethod());
+                JSObject *funobj = &shape->methodObject();
+                if (pic.u.vr.isConstant) {
+                    JS_ASSERT(funobj == &Valueify(pic.u.vr.u.v).toObject());
+                } else {
+                    Jump mismatchedFunction =
+                        masm.branchPtr(Assembler::NotEqual, pic.u.vr.u.s.data, ImmPtr(funobj));
+                    if (!slowExits.append(mismatchedFunction))
+                        return false;
+                }
+            }
+
+            if (shape->slot < JS_INITIAL_NSLOTS) {
+                Address address(pic.objReg,
+                                offsetof(JSObject, fslots) + shape->slot * sizeof(Value));
+                emitStore(masm, address);
+            } else {
+                /* Check dslots non-zero. */
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg);
+                Jump emptyDslots = masm.branchPtr(Assembler::Equal, pic.shapeReg, ImmPtr(0));
+                if (!slowExits.append(emptyDslots))
+                    return false;
+
+                /* Check capacity. */
+                Address capacity(pic.shapeReg, -sizeof(Value));
+                masm.load32(masm.payloadOf(capacity), pic.shapeReg);
+                Jump overCapacity = masm.branch32(Assembler::LessThanOrEqual, pic.shapeReg,
+                                                  Imm32(shape->slot));
+                if (!slowExits.append(overCapacity))
+                    return false;
+
+                masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.shapeReg);
+                Address address(pic.shapeReg,
+                                (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value));
+                emitStore(masm, address);
+            }
+
+            uint32 newShape = obj->shape();
+            JS_ASSERT(newShape != initialShape);
+
+            /* Write the object's new shape. */
+            masm.storePtr(ImmPtr(shape), Address(pic.objReg, offsetof(JSObject, lastProp)));
+            masm.store32(Imm32(newShape), Address(pic.objReg, offsetof(JSObject, objShape)));
+
+            /* Write both the object's flags and new freeslot. */
+            masm.store32(Imm32(obj->flagsAndFreeslot()), flagsAndFreeslot);
+        } else if (shape->hasDefaultSetter()) {
             Address address(pic.objReg, offsetof(JSObject, fslots) + shape->slot * sizeof(Value));
             if (shape->slot >= JS_INITIAL_NSLOTS) {
                 masm.loadPtr(Address(pic.objReg, offsetof(JSObject, dslots)), pic.objReg);
                 address = Address(pic.objReg, (shape->slot - JS_INITIAL_NSLOTS) * sizeof(Value));
             }
 
             // If the scope is branded, or has a method barrier. It's now necessary
             // to guard that we're not overwriting a function-valued property.
@@ -408,26 +501,27 @@ class SetPropCompiler : public PICStubCo
         if (!ep || !pic.execPools.append(ep)) {
             if (ep)
                 ep->release();
             js_ReportOutOfMemory(f.cx);
             return false;
         }
 
         JSC::LinkBuffer buffer(&masm, ep);
-        buffer.link(shapeMismatch, pic.slowPathStart);
+        for (Jump *pj = slowExits.begin(); pj != slowExits.end(); ++pj)
+            buffer.link(*pj, pic.slowPathStart);
         buffer.link(done, pic.storeBack);
-        if (shape->hasDefaultSetter() && (obj->brandedOrHasMethodBarrier()))
+        if (!adding && shape->hasDefaultSetter() && (obj->brandedOrHasMethodBarrier()))
             buffer.link(rebrand, pic.slowPathStart);
         if (!shape->hasDefaultSetter())
             buffer.link(skipOver, pic.storeBack);
         CodeLocationLabel cs = buffer.finalizeCodeAddendum();
         JaegerSpew(JSpew_PICs, "generate setprop stub %p %d %d at %p\n",
                    (void*)&pic,
-                   obj->shape(),
+                   initialShape,
                    pic.stubsGenerated,
                    cs.executableAddress());
 
         PICRepatchBuffer repatcher(pic, pic.lastPathStart());
 
         // This function can patch either the inline fast path for a generated
         // stub. The stub omits the prefix of the inline fast path that loads
         // the shape, so the offsets are different.
@@ -450,36 +544,125 @@ class SetPropCompiler : public PICStubCo
     bool update()
     {
         if (!pic.hit) {
             spew("first hit", "nop");
             pic.hit = true;
             return true;
         }
 
-        JSObject *aobj = js_GetProtoIfDenseArray(obj);
-        if (!aobj->isNative())
+        if (obj->isDenseArray())
+            return disable("dense array");
+        if (!obj->isNative())
             return disable("non-native");
+        if (obj->sealed())
+            return disable("sealed");
+
+        jsid id = ATOM_TO_JSID(atom);
 
         JSObject *holder;
         JSProperty *prop = NULL;
-        if (!aobj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
+        if (!obj->lookupProperty(f.cx, id, &holder, &prop))
             return false;
-        if (!prop)
-            return disable("property not found");
+
+        /* If the property exists but is on a prototype, treat as addprop. */
+        if (prop && holder != obj) {
+            AutoPropertyDropper dropper(f.cx, holder, prop);
+            const Shape *shape = (const Shape *) prop;
+
+            if (!holder->isNative())
+                return disable("non-native holder");
+            if (holder->sealed())
+                return disable("sealed holder");
+
+            if (!shape->writable())
+                return disable("readonly");
+            if (!shape->hasDefaultSetter() || !shape->hasDefaultGetter())
+                return disable("getter/setter in prototype");
+            if (shape->hasShortID())
+                return disable("short ID in prototype");
+            if (!shape->hasSlot())
+                return disable("missing slot");
+
+            prop = NULL;
+        }
+
+        if (!prop) {
+            /* Adding a property to the object. */
+            if (obj->isDelegate())
+                return disable("delegate");
+
+            Class *clasp = obj->getClass();
+
+            if (clasp->addProperty != PropertyStub)
+                return disable("add property hook");
+
+            uint32 index;
+            if (js_IdIsIndex(id, &index))
+                return disable("index");
+
+            uint32 initialShape = obj->shape();
+            uint32 initialFlagsAndFreeslot = obj->flagsAndFreeslot();
+
+            if (!obj->ensureClassReservedSlots(f.cx))
+                return false;
+
+            uint32 slots = obj->numSlots();
+            uintN flags = 0;
+            PropertyOp getter = clasp->getProperty;
+
+            if (pic.kind == ic::PICInfo::SETMETHOD) {
+                if (!obj->canHaveMethodBarrier())
+                    return disable("can't have method barrier");
+
+                JSObject *funobj = &f.regs.sp[-1].toObject();
+                if (funobj != GET_FUNCTION_PRIVATE(cx, funobj))
+                    return disable("mismatched function");
+
+                flags |= Shape::METHOD;
+                getter = CastAsPropertyOp(funobj);
+            }
+
+            const Shape *shape =
+                obj->putProperty(f.cx, id, getter, clasp->setProperty,
+                                 SHAPE_INVALID_SLOT, JSPROP_ENUMERATE, flags, 0);
+
+            if (!shape)
+                return false;
+
+            if (!shape->hasDefaultSetter())
+                return disable("adding non-default setter");
+            if (!shape->hasSlot())
+                return disable("adding invalid slot");
+
+            /*
+             * Watch for cases where the object reallocated its slots when
+             * adding the property, and disable the PIC.  Otherwise we will
+             * keep generating identical PICs as side exits are taken on the
+             * capacity checks.  Alternatively, we could avoid the disable
+             * and just not generate a stub in case there are multiple shapes
+             * that can flow here which don't all require reallocation.
+             * Doing this would cause us to walk down this same update path
+             * every time a reallocation is needed, however, which will
+             * usually be a slowdown even if there *are* other shapes that
+             * don't realloc.
+             */
+            if (obj->numSlots() != slots)
+                return disable("insufficient slot capacity");
+
+            return generateStub(initialShape, initialFlagsAndFreeslot, shape, true);
+        }
 
         AutoPropertyDropper dropper(f.cx, holder, prop);
-        if (holder != obj)
-            return disable("property not on object");
 
         const Shape *shape = (const Shape *) prop;
+        if (pic.kind == ic::PICInfo::SETMETHOD && !shape->isMethod())
+            return disable("set method on non-method shape");
         if (!shape->writable())
             return disable("readonly");
-        if (obj->sealed() && !shape->hasSlot())
-            return disable("what does this even mean");
 
         if (shape->hasDefaultSetter()) {
             if (!shape->hasSlot())
                 return disable("invalid slot");
         } else {
             if (shape->hasSetterValue())
                 return disable("scripted setter");
             if (shape->setterOp() != SetCallArg &&
@@ -491,17 +674,17 @@ class SetPropCompiler : public PICStubCo
         JS_ASSERT(obj == holder);
         if (!pic.inlinePathPatched &&
             !obj->brandedOrHasMethodBarrier() &&
             shape->hasDefaultSetter() &&
             !obj->isDenseArray()) {
             return patchInline(shape);
         } 
 
-        return generateStub(shape);
+        return generateStub(obj->shape(), 0, shape, false);
     }
 };
 
 class GetPropCompiler : public PICStubCompiler
 {
     JSObject *obj;
     JSAtom *atom;
     void   *stub;
@@ -1901,50 +2084,50 @@ ic::GetElem(VMFrame &f, uint32 picIndex)
     f.regs.sp[-2] = v;
 }
 
 void JS_FASTCALL
 ic::SetPropDumb(VMFrame &f, uint32 index)
 {
     JSScript *script = f.fp()->getScript();
     ic::PICInfo &pic = script->pics[index];
-    JS_ASSERT(pic.kind == ic::PICInfo::SET);
+    JS_ASSERT(pic.isSet());
     JSAtom *atom = pic.atom;
 
     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
     if (!obj)
         THROW();
     Value rval = f.regs.sp[-1];
     if (!obj->setProperty(f.cx, ATOM_TO_JSID(atom), &f.regs.sp[-1]))
         THROW();
     f.regs.sp[-2] = rval;
 }
 
 static void JS_FASTCALL
 SetPropSlow(VMFrame &f, uint32 index)
 {
     JSScript *script = f.fp()->getScript();
     ic::PICInfo &pic = script->pics[index];
-    JS_ASSERT(pic.kind == ic::PICInfo::SET);
+    JS_ASSERT(pic.isSet());
 
     JSAtom *atom = pic.atom;
     stubs::SetName(f, atom);
 }
 
 void JS_FASTCALL
 ic::SetProp(VMFrame &f, uint32 index)
 {
     JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
     if (!obj)
         THROW();
 
     JSScript *script = f.fp()->getScript();
     ic::PICInfo &pic = script->pics[index];
     JSAtom *atom = pic.atom;
-    JS_ASSERT(pic.kind == ic::PICInfo::SET);
+    JS_ASSERT(pic.isSet());
 
     //
     // Important: We update the PIC before looking up the property so that the
     // PIC is updated only if the property already exists. The PIC doesn't try
     // to optimize adding new properties; that is for the slow case.
     //
     // Also note, we can't use SetName for PROPINC PICs because the property
     // cache can't handle a GET and SET from the same scripted PC.
@@ -2195,16 +2378,17 @@ ic::BindName(VMFrame &f, uint32 index)
 void
 ic::PurgePICs(JSContext *cx, JSScript *script)
 {
     uint32 npics = script->jit->nPICs;
     for (uint32 i = 0; i < npics; i++) {
         ic::PICInfo &pic = script->pics[i];
         switch (pic.kind) {
           case ic::PICInfo::SET:
+          case ic::PICInfo::SETMETHOD:
             SetPropCompiler::reset(pic);
             break;
           case ic::PICInfo::NAME:
             ScopeNameCompiler::reset(pic);
             break;
           case ic::PICInfo::BIND:
             BindNameCompiler::reset(pic);
             break;
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -194,16 +194,17 @@ struct PICInfo {
     enum Kind
 #ifdef _MSC_VER
     : uint8_t
 #endif
     {
         GET,
         CALL,
         SET,
+        SETMETHOD,
         NAME,
         BIND,
         GETELEM
     };
 
     union {
         // This struct comes out to 93 bits with GCC.
         struct {
@@ -245,16 +246,19 @@ struct PICInfo {
     uint32 stubsGenerated : 5;
 
     // Offset from start of fast path to initial shape guard.
     uint32 shapeGuard;
     
     // Return address of slow path call, as an offset from slowPathStart.
     uint32 callReturn;
 
+    inline bool isSet() {
+        return kind == SET || kind == SETMETHOD;
+    }
     inline bool isGet() {
         return kind == GET || kind == CALL || kind == GETELEM;
     }
     inline RegisterID typeReg() {
         JS_ASSERT(isGet());
         return u.get.typeReg;
     }
     inline bool hasTypeCheck() {
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/bug524826-2.js
@@ -0,0 +1,11 @@
+var x = 42;
+
+function f() {
+    var a = [{}, {}, {}, {}, {}];
+    for (var i = 0; i < 5; i++)
+        a[i].m = function () {return x};
+    for (i = 0; i < 4; i++)
+        if (a[i].m == a[i+1].m)
+            throw "FAIL!";
+}
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/basic/bug576823-regexp.js
@@ -0,0 +1,3 @@
+// Sticky should work across disjunctions.
+
+assertEq(/A|B/y.exec("CB"), null);
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/jaeger/unsignedShiftZero.js
@@ -0,0 +1,8 @@
+
+function f(a) { 
+    return a >>> 0; 
+};
+
+assertEq(f(-2147483647), 2147483649);
+assertEq(f(-2147483648), 2147483648);
+assertEq(f(-2147483649), 2147483647);