Bug 559653 - TM: Record assignment before the interpreter goes; remove record_SetPropHit. r=brendan.
☠☠ backed out by 8d256e784695 ☠ ☠
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 28 Apr 2010 11:12:19 -0500
changeset 41823 ae857d81879311f67ff49e4f4e677e2279cc1bdd
parent 41804 b27741421347f8ca108a2f60abc93902750f5f0f
child 41824 ec52e62fa3e9ed520e4af0a70345475ebb327833
child 41827 8d256e7846959ca614b1da225d2786396888d770
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs559653
milestone1.9.3a5pre
Bug 559653 - TM: Record assignment before the interpreter goes; remove record_SetPropHit. r=brendan.
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsobj.cpp
js/src/jsops.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -543,31 +543,31 @@ js_SweepWatchPoints(JSContext *cx)
 }
 
 
 
 /*
  * NB: FindWatchPoint must be called with rt->debuggerLock acquired.
  */
 static JSWatchPoint *
-FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
+FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id)
 {
     JSWatchPoint *wp;
 
     for (wp = (JSWatchPoint *)rt->watchPointList.next;
          &wp->links != &rt->watchPointList;
          wp = (JSWatchPoint *)wp->links.next) {
         if (wp->object->scope() == scope && wp->sprop->id == id)
             return wp;
     }
     return NULL;
 }
 
 JSScopeProperty *
-js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id)
+js_FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id)
 {
     JSWatchPoint *wp;
     JSScopeProperty *sprop;
 
     DBG_LOCK(rt);
     wp = FindWatchPoint(rt, scope, id);
     sprop = wp ? wp->sprop : NULL;
     DBG_UNLOCK(rt);
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -106,17 +106,17 @@ JS_ClearAllWatchPoints(JSContext *cx);
  */
 extern void
 js_TraceWatchPoints(JSTracer *trc, JSObject *obj);
 
 extern void
 js_SweepWatchPoints(JSContext *cx);
 
 extern JSScopeProperty *
-js_FindWatchPoint(JSRuntime *rt, JSScope *scope, jsid id);
+js_FindWatchPoint(JSRuntime *rt, const JSScope *scope, jsid id);
 
 /*
  * NB: callers outside of jsdbgapi.c must pass non-null scope.
  */
 extern JSPropertyOp
 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
                     const JSScopeProperty *sprop);
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4291,19 +4291,17 @@ js_DefineNativeProperty(JSContext *cx, J
     /* XXXbe called with lock held */
     if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &value)) {
         scope->removeProperty(cx, id);
         goto error;
     }
 
     if (defineHow & JSDNP_CACHE_RESULT) {
         JS_ASSERT_NOT_ON_TRACE(cx);
-        PropertyCacheEntry *entry =
-            JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
-        TRACE_2(SetPropHit, entry, sprop);
+        JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
     }
     if (propp)
         *propp = (JSProperty *) sprop;
     else
         JS_UNLOCK_OBJ(cx, obj);
     return JS_TRUE;
 
 error: // TRACE_2 jumps here on error, as well.
@@ -4937,21 +4935,16 @@ ReportReadOnly(JSContext* cx, jsid id, u
 {
     return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
                                     JSDVG_IGNORE_STACK, ID_TO_VALUE(id), NULL,
                                     NULL, NULL);
 }
 
 }
 
-/*
- * Note: all non-error exits in this function must notify the tracer using
- * SetPropHit when called from the interpreter, which is detected by testing
- * (defineHow & JSDNP_CACHE_RESULT).
- */
 JSBool
 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
                      jsval *vp)
 {
     int protoIndex;
     JSObject *pobj;
     JSProperty *prop;
     JSScopeProperty *sprop;
@@ -5020,41 +5013,30 @@ js_SetPropertyHelper(JSContext *cx, JSOb
          * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
          */
         scope = pobj->scope();
 
         /* ES5 8.12.4 [[Put]] step 2. */
         if (sprop->isAccessorDescriptor()) {
             if (sprop->hasDefaultSetter()) {
                 JS_UNLOCK_SCOPE(cx, scope);
-                if (defineHow & JSDNP_CACHE_RESULT)
-                    TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
                 return js_ReportGetterOnlyAssignment(cx);
             }
         } else {
             JS_ASSERT(sprop->isDataDescriptor());
 
             if (!sprop->writable()) {
                 JS_UNLOCK_SCOPE(cx, scope);
 
                 PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
-                if (defineHow & JSDNP_CACHE_RESULT) {
-                    JS_ASSERT_NOT_ON_TRACE(cx);
-                    TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
-                }
 
                 /* Warn in strict mode, otherwise do nothing. */
                 if (JS_HAS_STRICT_OPTION(cx))
                     return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
                 return JS_TRUE;
-
-#ifdef JS_TRACER
-              error: // TRACE_2 jumps here in case of error.
-                return JS_FALSE;
-#endif
             }
         }
         if (scope->sealed() && !sprop->hasSlot()) {
             JS_UNLOCK_SCOPE(cx, scope);
             return ReportReadOnly(cx, id, JSREPORT_ERROR);
         }
 
         attrs = sprop->attributes();
@@ -5067,19 +5049,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
              * sprop going away behind our back after we've unlocked scope.
              */
             JS_UNLOCK_SCOPE(cx, scope);
 
             /* Don't clone a prototype property that doesn't have a slot. */
             if (!sprop->hasSlot()) {
                 if (defineHow & JSDNP_CACHE_RESULT) {
                     JS_ASSERT_NOT_ON_TRACE(cx);
-                    PropertyCacheEntry *entry =
-                        JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
-                    TRACE_2(SetPropHit, entry, sprop);
+                    JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
                 }
 
                 if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
                     return JS_TRUE;
 
                 return sprop->set(cx, obj, vp);
             }
 
@@ -5127,18 +5107,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
             JS_UNLOCK_OBJ(cx, obj);
             return JS_FALSE;
         }
 
         /*
          * Check for Object class here to avoid defining a method on a class
          * with magic resolve, addProperty, getProperty, etc. hooks.
          */
-        if ((defineHow & JSDNP_SET_METHOD) &&
-            obj->getClass() == &js_ObjectClass) {
+        if ((defineHow & JSDNP_SET_METHOD) && obj->getClass() == &js_ObjectClass) {
             JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
             JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
 
             JSObject *funobj = JSVAL_TO_OBJECT(*vp);
             if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
                 flags |= JSScopeProperty::METHOD;
                 getter = js_CastAsPropertyOp(funobj);
             }
@@ -5165,18 +5144,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
             JS_UNLOCK_SCOPE(cx, scope);
             return JS_FALSE;
         }
         added = true;
     }
 
     if (defineHow & JSDNP_CACHE_RESULT) {
         JS_ASSERT_NOT_ON_TRACE(cx);
-        PropertyCacheEntry *entry = JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
-        TRACE_2(SetPropHit, entry, sprop);
+        JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
     }
 
     if (!js_NativeSet(cx, obj, sprop, added, vp))
         return NULL;
 
     JS_UNLOCK_SCOPE(cx, scope);
     return JS_TRUE;
 }
--- a/js/src/jsops.cpp
+++ b/js/src/jsops.cpp
@@ -614,17 +614,16 @@ END_CASE(JSOP_PICK)
         } else {                                                              \
             if (!js_NativeGet(cx, obj, pobj, sprop, getHow, vp))              \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define NATIVE_SET(cx,obj,sprop,entry,vp)                                     \
     JS_BEGIN_MACRO                                                            \
-        TRACE_2(SetPropHit, entry, sprop);                                    \
         if (sprop->hasDefaultSetter() &&                                      \
             (sprop)->slot != SPROP_INVALID_SLOT &&                            \
             !(obj)->scope()->brandedOrHasMethodBarrier()) {                   \
             /* Fast path for, e.g., plain Object instance properties. */      \
             (obj)->lockedSetSlot((sprop)->slot, *vp);                         \
         } else {                                                              \
             if (!js_NativeSet(cx, obj, sprop, false, vp))                     \
                 goto error;                                                   \
@@ -1778,17 +1777,16 @@ BEGIN_CASE(JSOP_SETMETHOD)
                 }
 
                 /*
                  * No method change check here because here we are
                  * adding a new property, not updating an existing
                  * slot's value that might contain a method of a
                  * branded scope.
                  */
-                TRACE_2(SetPropHit, entry, sprop);
                 obj->lockedSetSlot(slot, rval);
 
                 /*
                  * Purge the property cache of the id we may have just
                  * shadowed in obj's scope and proto chains. We do this
                  * after unlocking obj's scope to avoid lock nesting.
                  */
                 js_PurgeScopeChain(cx, obj, sprop->id);
@@ -3346,16 +3344,17 @@ BEGIN_CASE(JSOP_INITMETHOD)
     /* Load the property's initial value into rval. */
     JS_ASSERT(regs.sp - StackBase(fp) >= 2);
     rval = FETCH_OPND(-1);
 
     /* Load the object being initialized into lval/obj. */
     lval = FETCH_OPND(-2);
     obj = JSVAL_TO_OBJECT(lval);
     JS_ASSERT(obj->isNative());
+    JS_ASSERT(obj->getClass() == &js_ObjectClass);
     JS_ASSERT(!obj->getClass()->reserveSlots);
 
     JSScope *scope = obj->scope();
     PropertyCacheEntry *entry;
 
     /*
      * Probe the property cache. 
      *
@@ -3400,17 +3399,16 @@ BEGIN_CASE(JSOP_INITMETHOD)
             scope->extend(cx, sprop);
         }
 
         /*
          * No method change check here because here we are adding a new
          * property, not updating an existing slot's value that might
          * contain a method of a branded scope.
          */
-        TRACE_2(SetPropHit, entry, sprop);
         obj->lockedSetSlot(slot, rval);
     } else {
         PCMETER(JS_PROPERTY_CACHE(cx).inipcmisses++);
 
         /* Get the immediate property name into id. */
         LOAD_ATOM(0);
         id = ATOM_TO_JSID(atom);
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -758,17 +758,17 @@ JSScope::addProperty(JSContext *cx, jsid
     return addPropertyHelper(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
 }
 
 /*
  * Normalize stub getter and setter values for faster is-stub testing in the
  * SPROP_CALL_[GS]ETTER macros.
  */
 static inline bool
-NormalizeGetterAndSetter(JSContext *cx, JSScope *scope,
+NormalizeGetterAndSetter(JSContext *cx, const JSScope *scope,
                          jsid id, uintN attrs, uintN flags,
                          JSPropertyOp &getter,
                          JSPropertyOp &setter)
 {
     if (setter == JS_PropertyStub)
         setter = NULL;
     if (flags & JSScopeProperty::METHOD) {
         /* Here, getter is the method, a function object reference. */
@@ -851,16 +851,35 @@ JSScope::addPropertyHelper(JSContext *cx
         return sprop;
     }
 
     METER(addFails);
     return NULL;
 }
 
 JSScopeProperty *
+JSScope::prepareAddProperty(JSContext *cx, jsid id, JSPropertyOp getter, JSPropertyOp setter,
+                            uint32 slot, uintN attrs, uintN flags, intN shortid) const
+{
+    NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
+
+    // Find or create a property tree node labeled by our arguments. Do not use
+    // getChildProperty because it also effectively adds the property to this
+    // JSScope.
+    if (inDictionaryMode()) {
+        JSScopeProperty *sprop = JS_PROPERTY_TREE(cx).newScopeProperty(cx, true);
+        if (sprop)
+            new (sprop) JSScopeProperty(id, getter, setter, slot, attrs, flags, shortid);
+        return sprop;
+    }
+    JSScopeProperty child(id, getter, setter, slot, attrs, flags, shortid);
+    return JS_PROPERTY_TREE(cx).getChild(cx, lastProp, shape, child);
+}
+
+JSScopeProperty *
 JSScope::putProperty(JSContext *cx, jsid id,
                      JSPropertyOp getter, JSPropertyOp setter,
                      uint32 slot, uintN attrs,
                      uintN flags, intN shortid)
 {
     JSScopeProperty **spp, *sprop, *overwriting;
 
     JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this));
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -238,16 +238,17 @@ struct JSScope : public JSObjectMap
      * a tagged pointer again.
      */
     inline JSScopeProperty *lastProperty() const;
 
   private:
     JSScopeProperty *getChildProperty(JSContext *cx, JSScopeProperty *parent,
                                       JSScopeProperty &child);
 
+  private:
     JSScopeProperty *newDictionaryProperty(JSContext *cx, const JSScopeProperty &child,
                                            JSScopeProperty **childp);
 
     bool toDictionaryMode(JSContext *cx, JSScopeProperty *&aprop);
 
     /*
      * Private pointer to the last added property and methods to manipulate the
      * list it links among properties in this scope. The {remove,insert} pair
@@ -322,16 +323,27 @@ struct JSScope : public JSObjectMap
     inline bool hasProperty(JSScopeProperty *sprop);
 
     /* Add a property whose id is not yet in this scope. */
     JSScopeProperty *addProperty(JSContext *cx, jsid id,
                                  JSPropertyOp getter, JSPropertyOp setter,
                                  uint32 slot, uintN attrs,
                                  uintN flags, intN shortid);
 
+    /*
+     * Like addProperty, but just return the new (or cached) JSScopeProperty
+     * without adding it. This is for use with js_AddProperty.
+     *
+     * Note that the return value is not necessarily rooted.
+     */
+    JSScopeProperty *prepareAddProperty(JSContext *cx, jsid id,
+                                        JSPropertyOp getter, JSPropertyOp setter,
+                                        uint32 slot, uintN attrs,
+                                        uintN flags, intN shortid) const;
+
     /* Add a data property whose id is not yet in this scope. */
     JSScopeProperty *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
         return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0);
     }
 
     /* Add or overwrite a property for id in this scope. */
     JSScopeProperty *putProperty(JSContext *cx, jsid id,
@@ -395,59 +407,59 @@ struct JSScope : public JSObjectMap
          * See JSRuntime::gcRegenShapesScopeFlag.
          */
         SHAPE_REGEN             = 0x0040,
 
         /* The anti-branded flag, to avoid overspecializing. */
         GENERIC                 = 0x0080
     };
 
-    bool inDictionaryMode()     { return flags & DICTIONARY_MODE; }
-    void setDictionaryMode()    { flags |= DICTIONARY_MODE; }
-    void clearDictionaryMode()  { flags &= ~DICTIONARY_MODE; }
+    bool inDictionaryMode() const   { return flags & DICTIONARY_MODE; }
+    void setDictionaryMode()        { flags |= DICTIONARY_MODE; }
+    void clearDictionaryMode()      { flags &= ~DICTIONARY_MODE; }
 
     /*
      * Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
      * will avoid taking the lock if the object owns its scope and the scope is
      * sealed.
      */
-    bool sealed()               { return flags & SEALED; }
+    bool sealed() const             { return flags & SEALED; }
 
     void seal(JSContext *cx) {
         JS_ASSERT(!isSharedEmpty());
         JS_ASSERT(!sealed());
         generateOwnShape(cx);
         flags |= SEALED;
     }
 
     /*
      * A branded scope's object contains plain old methods (function-valued
      * properties without magic getters and setters), and its scope->shape
      * evolves whenever a function value changes.
      */
-    bool branded()              { JS_ASSERT(!generic()); return flags & BRANDED; }
+    bool branded() const            { JS_ASSERT(!generic()); return flags & BRANDED; }
 
     bool brand(JSContext *cx, uint32 slot, jsval v) {
         JS_ASSERT(!branded());
         generateOwnShape(cx);
         if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
             return false;
         flags |= BRANDED;
         return true;
     }
 
-    bool generic()              { return flags & GENERIC; }
-    void setGeneric()           { flags |= GENERIC; }
+    bool generic() const            { return flags & GENERIC; }
+    void setGeneric()               { flags |= GENERIC; }
 
-    bool hadIndexedProperties() { return flags & INDEXED_PROPERTIES; }
-    void setIndexedProperties() { flags |= INDEXED_PROPERTIES; }
+    bool hadIndexedProperties() const { return flags & INDEXED_PROPERTIES; }
+    void setIndexedProperties()     { flags |= INDEXED_PROPERTIES; }
 
-    bool hasOwnShape()          { return flags & OWN_SHAPE; }
+    bool hasOwnShape() const        { return flags & OWN_SHAPE; }
 
-    bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; }
+    bool hasRegenFlag(uint8 regenFlag) const { return (flags & SHAPE_REGEN) == regenFlag; }
 
     /*
      * A scope has a method barrier when some compiler-created "null closure"
      * function objects (functions that do not use lexical bindings above their
      * scope, only free variable names) that have a correct JSSLOT_PARENT value
      * thanks to the COMPILE_N_GO optimization are stored as newly added direct
      * property values of the scope's object.
      *
@@ -479,29 +491,28 @@ struct JSScope : public JSObjectMap
      * such ops do not present an identity or mutation hazard. The compiler
      * performs this optimization only for null closures that do not use their
      * own name or equivalent built-in references (arguments.callee).
      *
      * The BRANDED write barrier, JSScope::methodWriteBarrer, must check for
      * METHOD_BARRIER too, and regenerate this scope's shape if the method's
      * value is in fact changing.
      */
-    bool hasMethodBarrier()     { return flags & METHOD_BARRIER; }
-    void setMethodBarrier()     { flags |= METHOD_BARRIER; }
+    bool hasMethodBarrier() const   { return flags & METHOD_BARRIER; }
+    void setMethodBarrier()         { flags |= METHOD_BARRIER; }
 
     /*
      * Test whether this scope may be branded due to method calls, which means
      * any assignment to a function-valued property must regenerate shape; else
      * test whether this scope has method properties, which require a method
      * write barrier.
      */
-    bool
-    brandedOrHasMethodBarrier() { return flags & (BRANDED | METHOD_BARRIER); }
+    bool brandedOrHasMethodBarrier() const { return flags & (BRANDED | METHOD_BARRIER); }
 
-    bool isSharedEmpty() const  { return !object; }
+    bool isSharedEmpty() const      { return !object; }
 
     static bool initRuntimeState(JSContext *cx);
     static void finishRuntimeState(JSContext *cx);
 };
 
 struct JSEmptyScope : public JSScope
 {
     JSClass * const clasp;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2440,37 +2440,41 @@ TraceRecorder::insImmVal(jsval val)
     if (JSVAL_IS_TRACEABLE(val))
         tree->gcthings.addUnique(val);
     return lir->insImmWord(val);
 }
 
 inline LIns*
 TraceRecorder::insImmObj(JSObject* obj)
 {
+    JS_ASSERT(obj);
     tree->gcthings.addUnique(OBJECT_TO_JSVAL(obj));
     return lir->insImmP((void*)obj);
 }
 
 inline LIns*
 TraceRecorder::insImmFun(JSFunction* fun)
 {
+    JS_ASSERT(fun);
     tree->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun)));
     return lir->insImmP((void*)fun);
 }
 
 inline LIns*
 TraceRecorder::insImmStr(JSString* str)
 {
+    JS_ASSERT(str);
     tree->gcthings.addUnique(STRING_TO_JSVAL(str));
     return lir->insImmP((void*)str);
 }
 
 inline LIns*
 TraceRecorder::insImmSprop(JSScopeProperty* sprop)
 {
+    JS_ASSERT(sprop);
     tree->sprops.addUnique(sprop);
     return lir->insImmP((void*)sprop);
 }
 
 inline LIns*
 TraceRecorder::p2i(nanojit::LIns* ins)
 {
 #ifdef NANOJIT_64BIT
@@ -11240,166 +11244,254 @@ TraceRecorder::record_JSOP_ELEMDEC()
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETPROP()
 {
     return getProp(stackval(-1));
 }
 
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_SETPROP()
-{
-    jsval& l = stackval(-2);
-    if (JSVAL_IS_PRIMITIVE(l))
-        RETURN_STOP_A("primitive this for SETPROP");
-
-    JSObject* obj = JSVAL_TO_OBJECT(l);
-    if (obj->map->ops->setProperty != js_SetProperty)
-        RETURN_STOP_A("non-native JSObjectOps::setProperty");
-    return ARECORD_CONTINUE;
-}
-
-/* Emit a specialized, inlined copy of js_NativeSet. */
+/*
+ * If possible, lookup obj[id] without calling any resolve hooks or touching
+ * any non-native objects or objects that have been shared across contexts,
+ * store the results in *pobjp and *spropp (NULL if no such property exists),
+ * and return true. This does not take any locks. That is safe because we
+ * avoid shared objects.
+ *
+ * If a safe lookup is not possible, return false; *pobjp and *spropp are
+ * undefined.
+ */
+static bool
+SafeLookup(JSContext *cx, JSObject* obj, jsid id, JSObject** pobjp, JSScopeProperty** spropp)
+{
+    for (; obj; obj = obj->getProto()) {
+        // Avoid non-native lookupProperty hooks.
+        if (obj->map->ops->lookupProperty != js_LookupProperty)
+            return false;
+
+        // Avoid shared objects.
+        JSScope *scope = obj->scope();
+        if (!CX_OWNS_SCOPE_TITLE(cx, scope))
+            return false;
+
+        if (JSScopeProperty *sprop = scope->lookup(id)) {
+            *pobjp = obj;
+            *spropp = sprop;
+            return true;
+        }
+
+        // Avoid resolve hooks.
+        if (obj->getClass()->resolve != JS_ResolveStub)
+            return false;
+    }
+    *pobjp = NULL;
+    *spropp = NULL;
+    return true;
+}
+
+/*
+ * Lookup the property for the SETPROP/SETNAME/SETMETHOD instruction at pc.
+ * Emit guards to ensure that the result at run time is the same.
+ */
+JS_REQUIRES_STACK RecordingStatus
+TraceRecorder::lookupForSetPropertyOp(JSObject* obj, LIns* obj_ins, jsid id,
+                                      bool* safep, JSObject** pobjp, JSScopeProperty** spropp)
+{
+    // We could consult the property cache here, but the contract for
+    // PropertyCache::testForSet is intricate enough that it's a lot less code
+    // to do a SafeLookup.
+    *safep = SafeLookup(cx, obj, id, pobjp, spropp);
+    if (!*safep)
+        return RECORD_CONTINUE;
+
+    VMSideExit *exit = snapshot(BRANCH_EXIT);
+    if (*spropp) {
+        if (obj != globalObj)
+            CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_kshape", exit));
+        if (obj != *pobjp && *pobjp != globalObj) {
+            CHECK_STATUS(guardShape(INS_CONSTOBJ(*pobjp), *pobjp, (*pobjp)->shape(),
+                                    "guard_vshape", exit));
+        }
+    } else {
+        for (;;) {
+            if (obj != globalObj)
+                CHECK_STATUS(guardShape(obj_ins, obj, obj->shape(), "guard_proto_chain", exit));
+            obj = obj->getProto();
+            if (!obj)
+                break;
+            obj_ins = INS_CONSTOBJ(obj);
+        }
+    }
+    return RECORD_CONTINUE;
+}
+
+static JSBool FASTCALL
+MethodWriteBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj)
+{
+    AutoValueRooter tvr(cx, funobj);
+
+    return obj->scope()->methodWriteBarrier(cx, sprop, tvr.value());
+}
+JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
+                     0, ACC_STORE_ANY)
+
+/*
+ * Emit a specialized, inlined copy of js_NativeSet.
+ *
+ * Note that since this runs before the interpreter opcode, obj->scope() may
+ * not actually have the property sprop yet.
+ */
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::nativeSet(JSObject* obj, LIns* obj_ins, JSScopeProperty* sprop,
                          jsval v, LIns* v_ins)
 {
     JSScope* scope = obj->scope();
     uint32 slot = sprop->slot;
 
-    /*
-     * We do not trace assignment to properties that have both a nonstub setter
-     * and a slot, for several reasons.
+    // If obj is the global, it must already have sprop. Otherwise we would
+    // have gone through addDataProperty, which would have stopped recording.
+    JS_ASSERT_IF(obj == globalObj, scope->hasProperty(sprop));
+
+    /*
+     * We do not trace assignment to properties that have both a non-default
+     * setter and a slot, for several reasons.
      *
      * First, that would require sampling rt->propertyRemovals before and after
      * (see js_NativeSet), and even more code to handle the case where the two
      * samples differ. A mere guard is not enough, because you can't just bail
      * off trace in the middle of a property assignment without storing the
      * value and making the stack right.
      *
      * If obj is the global object, there are two additional problems. We would
      * have to emit still more code to store the result in the object (not the
      * native global frame) if the setter returned successfully after
      * deep-bailing.  And we would have to cope if the run-time type of the
      * setter's return value differed from the record-time type of v, in which
      * case unboxing would fail and, having called a native setter, we could
      * not just retry the instruction in the interpreter.
-     */
-    JS_ASSERT(sprop->hasDefaultSetter() || slot == SPROP_INVALID_SLOT);
+     *
+     * If obj is branded, we would have a similar problem recovering from a
+     * failed call to MethodWriteBarrier.
+     */
+    if (!sprop->hasDefaultSetter() && slot != SPROP_INVALID_SLOT)
+        RETURN_STOP("can't trace set of property with setter and slot");
+
+    // These two cases are strict-mode errors and can't be traced.
+    if (sprop->hasGetterValue() && sprop->hasDefaultSetter())
+        RETURN_STOP("can't set a property that has only a getter");
+    if (sprop->isDataDescriptor() && !sprop->writable())
+        RETURN_STOP("can't assign to readonly property");
 
     // Box the value to be stored, if necessary.
     LIns* boxed_ins = NULL;
     if (!sprop->hasDefaultSetter() || (slot != SPROP_INVALID_SLOT && obj != globalObj))
         boxed_ins = box_jsval(v, v_ins);
 
     // Call the setter, if any.
-    if (!sprop->hasDefaultSetter())
+    if (!sprop->hasDefaultSetter()) {
+        if (sprop->hasSetterValue())
+            RETURN_STOP("can't trace JavaScript function setter yet");
         emitNativePropertyOp(scope, sprop, obj_ins, true, boxed_ins);
-
-    // Store the value, if this property has a slot.
+    }
+
     if (slot != SPROP_INVALID_SLOT) {
-        JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
+        if (scope->brandedOrHasMethodBarrier()) {
+            if (obj == globalObj) {
+                // Because the trace is type-specialized to the global object's
+                // slots, no run-time check is needed. Avoid recording a global
+                // shape change, though.
+                if (VALUE_IS_FUNCTION(cx, obj->getSlot(slot)))
+                    RETURN_STOP("can't trace set of function-valued property in branded global");
+            } else {
+                // Setting a function-valued property might need to rebrand the
+                // object. Call the method write barrier. Note that even if the
+                // property is not function-valued now, it might be on trace.
+                enterDeepBailCall();
+                LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
+                LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
+                guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
+                leaveDeepBailCall();
+            }
+        }
+
+        // Store the value.
         JS_ASSERT(sprop->hasSlot());
         if (obj == globalObj) {
+            JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
             if (!lazilyImportGlobalSlot(slot))
                 RETURN_STOP("lazy import of global slot failed");
             set(&obj->getSlotRef(slot), v_ins);
         } else {
             LIns* dslots_ins = NULL;
             stobj_set_slot(obj_ins, slot, dslots_ins, boxed_ins);
         }
     }
 
     return RECORD_CONTINUE;
 }
 
-static JSBool FASTCALL
-MethodWriteBarrier(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, JSObject* funobj)
-{
-    AutoValueRooter tvr(cx, funobj);
-
-    return obj->scope()->methodWriteBarrier(cx, sprop, tvr.value());
-}
-JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
-                     0, ACC_STORE_ANY)
-
 JS_REQUIRES_STACK RecordingStatus
-TraceRecorder::setProp(jsval &l, PropertyCacheEntry* entry, JSScopeProperty* sprop,
-                       jsval &v, LIns*& v_ins)
-{
-    if (entry == JS_NO_PROP_CACHE_FILL)
-        RETURN_STOP("can't trace uncacheable property set");
-    JS_ASSERT_IF(entry->vcapTag() >= 1, !sprop->hasSlot());
-    if (!sprop->hasDefaultSetter() && sprop->slot != SPROP_INVALID_SLOT)
-        RETURN_STOP("can't trace set of property with setter and slot");
-    if (sprop->hasSetterValue())
-        RETURN_STOP("can't trace JavaScript function setter");
-
-    // These two cases are errors and can't be traced.
-    if (sprop->hasGetterValue())
-        RETURN_STOP("can't assign to property with script getter but no setter");
-    if (!sprop->writable())
-        RETURN_STOP("can't assign to readonly property");
-
-    JS_ASSERT(!JSVAL_IS_PRIMITIVE(l));
-    JSObject* obj = JSVAL_TO_OBJECT(l);
-    LIns* obj_ins = get(&l);
-
-    JS_ASSERT_IF(entry->directHit(), obj->scope()->hasProperty(sprop));
-
-    // Fast path for CallClass. This is about 20% faster than the general case.
-    v_ins = get(&v);
-    if (obj->getClass() == &js_CallClass)
-        return setCallProp(obj, obj_ins, sprop, v_ins, v);
-
-    // Find obj2. If entry->adding(), the TAG bits are all 0.
-    JSObject* obj2 = obj;
-    for (jsuword i = entry->scopeIndex(); i; i--)
-        obj2 = obj2->getParent();
-    for (jsuword j = entry->protoIndex(); j; j--)
-        obj2 = obj2->getProto();
-    JSScope *scope = obj2->scope();
-    JS_ASSERT_IF(entry->adding(), obj2 == obj);
-
-    // Guard before anything else.
-    PCVal pcval;
-    CHECK_STATUS(guardPropertyCacheHit(obj_ins, obj, obj2, entry, pcval));
-    JS_ASSERT(scope->object == obj2);
-    JS_ASSERT(scope->hasProperty(sprop));
-    JS_ASSERT_IF(obj2 != obj, !sprop->hasSlot());
-
-    /*
-     * Setting a function-valued property might need to rebrand the object, so
-     * we emit a call to the method write barrier. There's no need to guard on
-     * this, because functions have distinct trace-type from other values and
-     * branded-ness is implied by the shape, which we've already guarded on.
-     */
-    if (scope->brandedOrHasMethodBarrier() && VALUE_IS_FUNCTION(cx, v) && entry->directHit()) {
-        if (obj == globalObj)
-            RETURN_STOP("can't trace function-valued property set in branded global scope");
-
-        enterDeepBailCall();
-        LIns* args[] = { v_ins, INS_CONSTSPROP(sprop), obj_ins, cx_ins };
-        LIns* ok_ins = lir->insCall(&MethodWriteBarrier_ci, args);
-        guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
-        leaveDeepBailCall();
-    }
-
-    // Add a property to the object if necessary.
-    if (entry->adding()) {
-        JS_ASSERT(sprop->hasSlot());
-        if (obj == globalObj)
-            RETURN_STOP("adding a property to the global object");
-
-        LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
-        LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
-        guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
-    }
-
+TraceRecorder::addDataProperty(JSObject* obj, LIns* obj_ins,
+                               jsid id, JSPropertyOp getter, JSPropertyOp setter,
+                               uintN flags, intN shortid, jsval v, LIns* v_ins)
+{
+    // If obj is the global, the global shape is about to change. Note also
+    // that since we do not record this case, SETNAME and SETPROP are identical
+    // as far as the tracer is concerned. (js_CheckUndeclaredVarAssignment
+    // distinguishes the two, in the interpreter.)
+    if (obj == globalObj)
+        RETURN_STOP("set new property of global object");  // global shape change
+
+    // js_AddProperty does not call the addProperty hook.
+    if (obj->getClass()->addProperty != JS_PropertyStub)
+        RETURN_STOP("set new property of object with addProperty hook");
+
+    // See comment in TR::nativeSet about why we do not support setting a
+    // property that has both a setter and a slot.
+    if (setter != JS_PropertyStub)
+        RETURN_STOP("set new property with setter and slot");
+
+    // Methods can be defined only on plain Objects.
+    jsbytecode op = *cx->fp->regs->pc;
+    if ((op == JSOP_INITMETHOD || op == JSOP_SETMETHOD) && obj->getClass() == &js_ObjectClass) {
+        JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
+
+        JSObject *funobj = JSVAL_TO_OBJECT(v);
+        if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
+            flags |= JSScopeProperty::METHOD;
+            getter = js_CastAsPropertyOp(funobj);
+        }
+    }
+
+    // Determine what slot the interpreter will assign this property.
+    JSScope *scope;
+    uint32 slot;
+    JS_LOCK_OBJ(cx, obj);
+    scope = js_GetMutableScope(cx, obj);
+    if (!scope)
+        RETURN_STOP("js_GetMutableScope failed");
+    if (!js_AllocSlot(cx, obj, &slot))
+        RETURN_STOP("js_AllocSlot failed");
+    js_FreeSlot(cx, obj, slot);
+    JS_UNLOCK_OBJ(cx, obj);
+
+    // Find or create an sprop for the new property but avoid adding it to
+    // obj's scope. The sprop returned by prepareAddProperty may not be rooted,
+    // but we immediately pass it to INS_CONSTSPROP, which roots it.
+    JSScopeProperty* sprop = obj->scope()->prepareAddProperty(cx, id, getter, setter, slot,
+                                                              JSPROP_ENUMERATE, flags, shortid);
+    if (!sprop)
+        RETURN_ERROR("JSScope::getScopeProperty failed");
+
+    // On trace, call js_AddProperty to do the dirty work.
+    LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
+    LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
+    guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
+
+    // Box the value and store it in the new slot.
     return nativeSet(obj, obj_ins, sprop, v, v_ins);
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::setUpwardTrackedVar(jsval* stackVp, jsval v, LIns* v_ins)
 {
     TraceType stackT = determineSlotType(stackVp);
     TraceType otherT = getCoercedType(v);
@@ -11540,37 +11632,171 @@ TraceRecorder::setCallProp(JSObject *cal
     guard(false, addName(lir->insEqI_0(call_ins), "guard(set upvar)"), STATUS_EXIT);
 
     LIns *label2 = lir->ins0(LIR_label);
     br2->setTarget(label2);
 
     return RECORD_CONTINUE;
 }
 
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_SetPropHit(PropertyCacheEntry* entry, JSScopeProperty* sprop)
-{
-    jsval& r = stackval(-1);
+/*
+ * Emit a specialized, inlined copy of js_SetPropertyHelper for the current
+ * instruction.
+ */
+JS_REQUIRES_STACK RecordingStatus
+TraceRecorder::setProperty(JSObject* obj, LIns* obj_ins, jsval v, LIns* v_ins)
+{
+    JSAtom *atom;
+    GET_ATOM_FROM_BYTECODE(cx->fp->script, cx->fp->regs->pc, 0, atom);
+    jsid id = ATOM_TO_JSID(atom);
+
+    if (obj->map->ops->setProperty != js_SetProperty)
+        RETURN_STOP("non-native object");  // TODO - fall back on slow path
+    if (obj->scope()->sealed())
+        RETURN_STOP("setting property of sealed object");  // this will throw
+
+    bool safe;
+    JSObject* pobj;
+    JSScopeProperty* sprop;
+    CHECK_STATUS(lookupForSetPropertyOp(obj, obj_ins, id, &safe, &pobj, &sprop));
+    if (!safe)
+        RETURN_STOP("setprop: lookup fail"); // TODO - fall back on slow path
+
+    // Handle Call objects specially. The Call objects we create on trace are
+    // bogus. Calling the setter on such an object wouldn't work.
+    if (obj->getClass() == &js_CallClass)
+        return setCallProp(obj, obj_ins, sprop, v_ins, v);
+
+    // Handle setting a property that is not found on obj or anywhere on its
+    // the prototype chain.
+    if (!sprop) {
+        JSClass *cls = obj->getClass();
+        return addDataProperty(obj, obj_ins, id, cls->getProperty, cls->setProperty, 0, 0,
+                               v, v_ins);
+    }
+
+    // Check whether we can assign to/over the existing property.
+    if (sprop->isAccessorDescriptor()) {
+        if (sprop->hasDefaultSetter())
+            RETURN_STOP("setting accessor property with no setter");
+    } else if (!sprop->writable()) {
+        RETURN_STOP("setting readonly data property");
+    }
+
+    // Handle setting an existing own property.
+    if (pobj == obj)
+        return nativeSet(obj, obj_ins, sprop, v, v_ins);
+
+    // Handle setting an inherited non-SHARED property.
+    if (sprop->hasSlot()) {
+        if (sprop->hasShortID()) {
+            return addDataProperty(obj, obj_ins, id, sprop->getter(), sprop->setter(),
+                                   JSScopeProperty::HAS_SHORTID, sprop->shortid,
+                                   v, v_ins);
+        } else {
+            JSClass *cls = obj->getClass();
+            return addDataProperty(obj, obj_ins, id, cls->getProperty, cls->setProperty,
+                                   0, 0, v, v_ins);
+        }
+    }
+
+    // Handle setting an inherited SHARED property.
+    if (pobj->scope()->sealed())
+        RETURN_STOP("setting SHARED property inherited from sealed object");
+    if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
+        return RECORD_CONTINUE; // nothing happens
+
+    // Handle setting an inherited SHARED property with a non-default setter.
+    return nativeSet(obj, obj_ins, sprop, v, v_ins);
+}
+
+/*
+ * Record a JSOP_SET{PROP,NAME,METHOD} instruction or a JSOP_INITPROP
+ * instruction initializing __proto__.
+ */
+JS_REQUIRES_STACK RecordingStatus
+TraceRecorder::recordSetPropertyOp()
+{
     jsval& l = stackval(-2);
-    LIns* v_ins;
-    CHECK_STATUS_A(setProp(l, entry, sprop, r, v_ins));
-
-    jsbytecode* pc = cx->fp->regs->pc;
-    switch (*pc) {
-      case JSOP_SETPROP:
-      case JSOP_SETNAME:
-      case JSOP_SETMETHOD:
-        if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
-            set(&l, v_ins);
-        break;
-
-      default:;
-    }
-
-    return ARECORD_CONTINUE;
+    if (JSVAL_IS_PRIMITIVE(l))
+        RETURN_STOP("primitive operand object");
+    JSObject* obj = JSVAL_TO_OBJECT(l);
+    LIns* obj_ins = get(&l);
+
+    jsval& r = stackval(-1);
+    LIns* r_ins = get(&r);
+
+    CHECK_STATUS(setProperty(obj, obj_ins, r, r_ins));
+    set(&l, r_ins);
+    return RECORD_CONTINUE;
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_SETPROP()
+{
+    return InjectStatus(recordSetPropertyOp());
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_SETMETHOD()
+{
+    return InjectStatus(recordSetPropertyOp());
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_SETNAME()
+{
+    return InjectStatus(recordSetPropertyOp());
+}
+
+JS_REQUIRES_STACK RecordingStatus
+TraceRecorder::recordInitPropertyOp()
+{
+    jsval& l = stackval(-2);
+    JSObject* obj = JSVAL_TO_OBJECT(l);
+    LIns* obj_ins = get(&l);
+    JS_ASSERT(obj->getClass() == &js_ObjectClass);
+
+    jsval& v = stackval(-1);
+    LIns* v_ins = get(&v);
+
+    JSAtom *atom;
+    GET_ATOM_FROM_BYTECODE(cx->fp->script, cx->fp->regs->pc, 0, atom);
+    jsid id = ATOM_TO_JSID(atom);
+
+    // If obj already has this property (because the id appears more than once
+    // in the initializer), just set it.
+    JSScope* scope = obj->scope();
+    if (!CX_OWNS_SCOPE_TITLE(cx, scope))
+        RETURN_STOP("object being initialized is shared among contexts");
+    if (JSScopeProperty* sprop = scope->lookup(id)) {
+        JS_ASSERT(sprop->isDataDescriptor());
+        JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, scope));
+        JS_ASSERT(sprop->hasDefaultSetter());
+        return nativeSet(obj, obj_ins, sprop, v, v_ins);
+    }
+
+    // Duplicate the interpreter's special treatment of __proto__.
+    if (atom == cx->runtime->atomState.protoAtom)
+        return recordSetPropertyOp();
+
+    // Define a new property.
+    return addDataProperty(obj, obj_ins, id, JS_PropertyStub, JS_PropertyStub, 0, 0, v, v_ins);
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_INITPROP()
+{
+    return InjectStatus(recordInitPropertyOp());
+}
+
+JS_REQUIRES_STACK AbortableRecordingStatus
+TraceRecorder::record_JSOP_INITMETHOD()
+{
+    return InjectStatus(recordInitPropertyOp());
 }
 
 JS_REQUIRES_STACK VMSideExit*
 TraceRecorder::enterDeepBailCall()
 {
     // Take snapshot for DeepBail and store it in cx->bailExit.
     VMSideExit* exit = snapshot(DEEP_BAIL_EXIT);
     lir->insStore(INS_CONSTPTR(exit), cx_ins, offsetof(JSContext, bailExit), ACC_OTHER);
@@ -13525,23 +13751,16 @@ TraceRecorder::record_JSOP_ENDINIT()
 #ifdef DEBUG
     jsval& v = stackval(-1);
     JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
 #endif
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_INITPROP()
-{
-    // All the action is in record_SetPropHit.
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_INITELEM()
 {
     return setElem(-3, -2, -1);
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_DEFSHARP()
 {
@@ -13873,23 +14092,16 @@ TraceRecorder::record_JSOP_BINDNAME()
 
     // If |obj2| is the global object, we can refer to it directly instead of walking up
     // the scope chain. There may still be guards on intervening call objects.
     stack(0, obj2 == globalObj ? INS_CONSTOBJ(obj2) : obj2_ins);
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_SETNAME()
-{
-    // record_SetPropHit does all the work.
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_THROW()
 {
     return ARECORD_STOP;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_IN()
 {
@@ -15276,28 +15488,16 @@ TraceRecorder::record_JSOP_CONCATN()
     LIns *result_ins = lir->insCall(&js_ConcatN_ci, args);
     guard(false, lir->insEqP_0(result_ins), OOM_EXIT);
 
     /* Update tracker with result. */
     set(argBase, result_ins);
     return ARECORD_CONTINUE;
 }
 
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_SETMETHOD()
-{
-    return record_JSOP_SETPROP();
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_INITMETHOD()
-{
-    return record_JSOP_INITPROP();
-}
-
 JSBool FASTCALL
 js_Unbrand(JSContext *cx, JSObject *obj)
 {
     return obj->unbrand(cx);
 }
 
 JS_DEFINE_CALLINFO_2(extern, BOOL, js_Unbrand, CONTEXT, OBJECT, 0, ACC_STORE_ANY)
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1299,34 +1299,47 @@ class TraceRecorder
     JS_REQUIRES_STACK RecordingStatus getPropertyById(nanojit::LIns* obj_ins, jsval* outp);
     JS_REQUIRES_STACK RecordingStatus getPropertyWithNativeGetter(nanojit::LIns* obj_ins,
                                                                   JSScopeProperty* sprop,
                                                                   jsval* outp);
     JS_REQUIRES_STACK RecordingStatus getPropertyWithScriptGetter(JSObject *obj,
                                                                   nanojit::LIns* obj_ins,
                                                                   JSScopeProperty* sprop);
 
-    JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
-                                                  JSScopeProperty* sprop,
-                                                  jsval v, nanojit::LIns* v_ins);
-    JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, PropertyCacheEntry* entry,
-                                                JSScopeProperty* sprop,
-                                                jsval &v, nanojit::LIns*& v_ins);
-    JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
-                                                    JSScopeProperty *sprop, nanojit::LIns *v_ins,
-                                                    jsval v);
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
-                                                                jsval* idvalp, jsval* rvalp,
-                                                                bool init);
+                                                              jsval* idvalp, jsval* rvalp,
+                                                              bool init);
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
-                                                                 nanojit::LIns* index_ins,
-                                                                 jsval* rvalp, bool init);
+                                                               nanojit::LIns* index_ins,
+                                                               jsval* rvalp, bool init);
     JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
                                                        int v_spindex);
 
+    JS_REQUIRES_STACK RecordingStatus lookupForSetPropertyOp(JSObject* obj, nanojit::LIns* obj_ins,
+                                                             jsid id, bool* safep,
+                                                             JSObject** pobjp,
+                                                             JSScopeProperty** spropp);
+    JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
+                                                JSScopeProperty* sprop,
+                                                jsval v, nanojit::LIns* v_ins);
+    JS_REQUIRES_STACK RecordingStatus addDataProperty(JSObject* obj, nanojit::LIns* obj_ins,
+                                                      jsid id,
+                                                      JSPropertyOp getter, JSPropertyOp setter,
+                                                      uintN flags, intN shortid,
+                                                      jsval v, nanojit::LIns* v_ins);
+    JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
+                                                  JSScopeProperty *sprop, nanojit::LIns *v_ins,
+                                                  jsval v);
+    JS_REQUIRES_STACK RecordingStatus setProperty(JSObject* obj, nanojit::LIns* obj_ins,
+                                                  jsval v, nanojit::LIns* v_ins);
+    JS_REQUIRES_STACK RecordingStatus recordSetPropertyOp();
+
+    JS_REQUIRES_STACK RecordingStatus recordInitPropertyOp();
+
+
     JS_REQUIRES_STACK nanojit::LIns* box_jsval(jsval v, nanojit::LIns* v_ins);
     JS_REQUIRES_STACK nanojit::LIns* unbox_jsval(jsval v, nanojit::LIns* v_ins, VMSideExit* exit);
     JS_REQUIRES_STACK bool guardClass(JSObject* obj, nanojit::LIns* obj_ins, JSClass* clasp,
                                       VMSideExit* exit, nanojit::AccSet accSet);
     JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
                                            ExitType exitType);
     JS_REQUIRES_STACK bool guardDenseArray(JSObject* obj, nanojit::LIns* obj_ins,
                                            VMSideExit* exit);
@@ -1455,18 +1468,16 @@ public:
     VMFragment*         getFragment() const { return fragment; }
     TreeFragment*       getTree() const { return tree; }
     bool                outOfMemory() const { return traceMonitor->outOfMemory(); }
 
     /* Entry points / callbacks from the interpreter. */
     JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
     JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame(uintN& inlineCallCount);
     JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame();
-    JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(PropertyCacheEntry* entry,
-                                                                 JSScopeProperty* sprop);
     JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot, JSObject* obj);
     JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete();
     void forgetGuardedShapesForObject(JSObject* obj);
 
 #ifdef DEBUG
     /* Debug printing functionality to emit printf() on trace. */
     JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]);
     JS_REQUIRES_STACK void tprint(const char *format);