Bug 475761 - TM: js_Any_GetProp and friends can reenter. r=brendan. Note that this patch alone does not fix the bug. The rest of the fix comes in bug 462027.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 29 Jan 2009 00:13:03 -0600
changeset 24489 76a7344912ae6279e994f6b22fa69c7b67d998cd
parent 24488 6a6d6d76ebc8fad447f77cf8d537dea380361855
child 24490 b224b8b053ff86f896b9db39513b86ed192d9755
push id5074
push userrsayre@mozilla.com
push dateSat, 31 Jan 2009 19:45:42 +0000
treeherdermozilla-central@f1cade532f6f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs475761, 462027
milestone1.9.2a1pre
Bug 475761 - TM: js_Any_GetProp and friends can reenter. r=brendan. Note that this patch alone does not fix the bug. The rest of the fix comes in bug 462027.
js/src/builtins.tbl
js/src/imacros.c.out
js/src/imacros.jsasm
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jscntxt.h
js/src/jsopcode.tbl
js/src/jstracer.cpp
js/src/trace-test.js
--- a/js/src/builtins.tbl
+++ b/js/src/builtins.tbl
@@ -72,20 +72,16 @@ BUILTIN1(extern, DOUBLE,    js_UnboxDoub
 BUILTIN1(extern, INT32,     js_UnboxInt32, JSVAL,                               1, 1)
 BUILTIN2(extern, DOUBLE,    js_dmod, DOUBLE, DOUBLE,                            1, 1)
 BUILTIN2(extern, INT32,     js_imod, INT32, INT32,                              1, 1)
 BUILTIN1(extern, INT32,     js_DoubleToInt32, DOUBLE,                           1, 1)
 BUILTIN1(extern, UINT32,    js_DoubleToUint32, DOUBLE,                          1, 1)
 
 BUILTIN2(extern, DOUBLE,    js_StringToNumber, CONTEXT, STRING,                 1, 1)
 BUILTIN2(extern, INT32,     js_StringToInt32, CONTEXT, STRING,                  1, 1)
-BUILTIN3(extern, JSVAL,     js_Any_getprop, CONTEXT, OBJECT, STRING,            0, 0)
-BUILTIN4(extern, BOOL,      js_Any_setprop, CONTEXT, OBJECT, STRING, JSVAL,     0, 0)
-BUILTIN3(extern, JSVAL,     js_Any_getelem, CONTEXT, OBJECT, INT32,             0, 0)
-BUILTIN4(extern, BOOL,      js_Any_setelem, CONTEXT, OBJECT, INT32, JSVAL,      0, 0)
 BUILTIN2(FRIEND, BOOL,      js_CloseIterator, CONTEXT, JSVAL,                   0, 0)
 BUILTIN2(extern, SIDEEXIT,  js_CallTree, INTERPSTATE, FRAGMENT,                 0, 0)
 BUILTIN2(extern, OBJECT,    js_FastNewObject, CONTEXT, OBJECT,                  0, 0)
 BUILTIN3(extern, BOOL,      js_AddProperty, CONTEXT, OBJECT, SCOPEPROP,         0, 0)
 BUILTIN3(extern, BOOL,      js_HasNamedProperty, CONTEXT, OBJECT, STRING,       0, 0)
 BUILTIN3(extern, BOOL,      js_HasNamedPropertyInt32, CONTEXT, OBJECT, INT32,   0, 0)
 BUILTIN3(extern, JSVAL,     js_CallGetter, CONTEXT, OBJECT, SCOPEPROP,          0, 0)
 BUILTIN2(extern, STRING,    js_TypeOfObject, CONTEXT, OBJECT,                   1, 1)
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -589,16 +589,74 @@ static struct {
 /* 2*/  JSOP_CALLBUILTIN, ((JSBUILTIN_CallIteratorNext) & 0xff00) >> 8, ((JSBUILTIN_CallIteratorNext) & 0xff),
 /* 5*/  JSOP_CALL, 0, 0,
 /* 8*/  JSOP_DUP,
 /* 9*/  JSOP_HOLE,
 /*10*/  JSOP_STRICTNE,
 /*11*/  JSOP_STOP,
     },
 };
+static struct {
+    jsbytecode getprop[10];
+    jsbytecode getelem[10];
+} getelem_imacros = {
+    {
+/* 0*/  JSOP_SWAP,
+/* 1*/  JSOP_CALLBUILTIN, ((JSBUILTIN_GetProperty) & 0xff00) >> 8, ((JSBUILTIN_GetProperty) & 0xff),
+/* 4*/  JSOP_PICK, 2,
+/* 6*/  JSOP_CALL, 0, 1,
+/* 9*/  JSOP_STOP,
+    },
+    {
+/* 0*/  JSOP_SWAP,
+/* 1*/  JSOP_CALLBUILTIN, ((JSBUILTIN_GetElement) & 0xff00) >> 8, ((JSBUILTIN_GetElement) & 0xff),
+/* 4*/  JSOP_PICK, 2,
+/* 6*/  JSOP_CALL, 0, 1,
+/* 9*/  JSOP_STOP,
+    },
+};
+static struct {
+    jsbytecode setprop[15];
+    jsbytecode setelem[15];
+} setelem_imacros = {
+    {
+/* 0*/  JSOP_DUP,
+/* 1*/  JSOP_PICK, 3,
+/* 3*/  JSOP_CALLBUILTIN, ((JSBUILTIN_SetProperty) & 0xff00) >> 8, ((JSBUILTIN_SetProperty) & 0xff),
+/* 6*/  JSOP_PICK, 4,
+/* 8*/  JSOP_PICK, 4,
+/*10*/  JSOP_CALL, 0, 2,
+/*13*/  JSOP_POP,
+/*14*/  JSOP_STOP,
+    },
+    {
+/* 0*/  JSOP_DUP,
+/* 1*/  JSOP_PICK, 3,
+/* 3*/  JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
+/* 6*/  JSOP_PICK, 4,
+/* 8*/  JSOP_PICK, 4,
+/*10*/  JSOP_CALL, 0, 2,
+/*13*/  JSOP_POP,
+/*14*/  JSOP_STOP,
+    },
+};
+static struct {
+    jsbytecode initelem[15];
+} initelem_imacros = {
+    {
+/* 0*/  JSOP_PICK, 2,
+/* 2*/  JSOP_DUP,
+/* 3*/  JSOP_CALLBUILTIN, ((JSBUILTIN_SetElement) & 0xff00) >> 8, ((JSBUILTIN_SetElement) & 0xff),
+/* 6*/  JSOP_PICK, 4,
+/* 8*/  JSOP_PICK, 4,
+/*10*/  JSOP_CALL, 0, 2,
+/*13*/  JSOP_POP,
+/*14*/  JSOP_STOP,
+    },
+};
 uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_NOP */
     0,  /* JSOP_PUSH */
     0,  /* JSOP_POPV */
     0,  /* JSOP_ENTERWITH */
     0,  /* JSOP_LEAVEWITH */
     0,  /* JSOP_RETURN */
     0,  /* JSOP_GOTO */
@@ -645,18 +703,18 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_NAMEINC */
     0,  /* JSOP_PROPINC */
     0,  /* JSOP_ELEMINC */
     0,  /* JSOP_NAMEDEC */
     0,  /* JSOP_PROPDEC */
     0,  /* JSOP_ELEMDEC */
     0,  /* JSOP_GETPROP */
     0,  /* JSOP_SETPROP */
-    0,  /* JSOP_GETELEM */
-    0,  /* JSOP_SETELEM */
+    2,  /* JSOP_GETELEM */
+    2,  /* JSOP_SETELEM */
     0,  /* JSOP_CALLNAME */
     0,  /* JSOP_CALL */
     0,  /* JSOP_NAME */
     0,  /* JSOP_DOUBLE */
     0,  /* JSOP_STRING */
     0,  /* JSOP_ZERO */
     0,  /* JSOP_ONE */
     0,  /* JSOP_NULL */
@@ -682,17 +740,17 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_GETARG */
     0,  /* JSOP_SETARG */
     0,  /* JSOP_GETLOCAL */
     0,  /* JSOP_SETLOCAL */
     0,  /* JSOP_UINT16 */
     0,  /* JSOP_NEWINIT */
     0,  /* JSOP_ENDINIT */
     0,  /* JSOP_INITPROP */
-    0,  /* JSOP_INITELEM */
+    2,  /* JSOP_INITELEM */
     0,  /* JSOP_DEFSHARP */
     0,  /* JSOP_USESHARP */
     0,  /* JSOP_INCARG */
     0,  /* JSOP_DECARG */
     0,  /* JSOP_ARGINC */
     0,  /* JSOP_ARGDEC */
     0,  /* JSOP_INCLOCAL */
     0,  /* JSOP_DECLOCAL */
--- a/js/src/imacros.jsasm
+++ b/js/src/imacros.jsasm
@@ -1,8 +1,9 @@
+# -*- indent-tabs-mode: nil; -*-
 # vim: set sw=4 ts=8 et tw=78 ft=asm:
 # ***** BEGIN LICENSE BLOCK *****
 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
 #
 # The contents of this file are subject to the Mozilla Public License Version
 # 1.1 (the "License"); you may not use this file except in compliance with
 # the License. You may obtain a copy of the License at
 # http://www.mozilla.org/MPL/
@@ -630,8 +631,69 @@ 4:      imacop                          
         call 0                                      # iterobj nextval?
         dup                                         # iterobj nextval? nextval?
         hole                                        # iterobj nextval? nextval? hole
         strictne                                    # iterobj nextval? boolean
         stop
     .end
 
 .end
+
+.igroup getelem JSOP_GETELEM
+
+    .imacro getprop                                 # obj name
+        swap                                        # name obj
+        callbuiltin (JSBUILTIN_GetProperty)         # name fun obj
+        pick 2                                      # fun obj name
+        call 1                                      # propval
+        stop
+    .end
+
+    .imacro getelem                                 # obj i
+        swap                                        # i obj
+        callbuiltin (JSBUILTIN_GetElement)          # i fun obj
+        pick 2                                      # fun obj i
+        call 1                                      # propval
+        stop
+    .end
+
+.end
+
+.igroup setelem JSOP_SETELEM
+
+    .imacro setprop                                 # obj name val
+        dup                                         # obj name val val
+        pick 3                                      # name val val obj
+        callbuiltin (JSBUILTIN_SetProperty)         # name val val fun obj
+        pick 4                                      # val val fun obj name
+        pick 4                                      # val fun obj name val
+        call 2                                      # val junk
+        pop                                         # val
+        stop
+    .end
+
+    .imacro setelem                                 # obj i val
+        dup                                         # obj i val val
+        pick 3                                      # i val val obj
+        callbuiltin (JSBUILTIN_SetElement)          # i val val fun obj
+        pick 4                                      # val val fun obj i
+        pick 4                                      # val fun obj i val
+        call 2                                      # val junk
+        pop                                         # val
+        stop
+    .end
+
+.end
+
+.igroup initelem JSOP_INITELEM
+
+    .imacro initelem                                # obj i val
+        pick 2                                      # i val obj
+        dup                                         # i val obj obj
+        callbuiltin (JSBUILTIN_SetElement)          # i val obj fun obj
+        pick 4                                      # val obj fun obj i
+        pick 4                                      # obj fun obj i val
+        call 2                                      # obj junk
+        pop                                         # obj
+        stop
+    .end
+
+.end
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -182,72 +182,16 @@ js_StringToInt32(JSContext* cx, JSString
     jsdouble d;
 
     JSSTRING_CHARS_AND_END(str, bp, end);
     if (!js_strtod(cx, bp, end, &ep, &d) || js_SkipWhiteSpace(ep, end) != end)
         return 0;
     return js_DoubleToECMAInt32(d);
 }
 
-static inline JSBool
-js_Int32ToId(JSContext* cx, int32 index, jsid* id)
-{
-    if (index <= JSVAL_INT_MAX) {
-        *id = INT_TO_JSID(index);
-        return JS_TRUE;
-    }
-    JSString* str = js_NumberToString(cx, index);
-    if (!str)
-        return JS_FALSE;
-    return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
-}
-
-jsval FASTCALL
-js_Any_getprop(JSContext* cx, JSObject* obj, JSString* idstr)
-{
-    jsval v;
-    jsid id;
-
-    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
-        return JSVAL_ERROR_COOKIE;
-    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
-        return JSVAL_ERROR_COOKIE;
-    return v;
-}
-
-JSBool FASTCALL
-js_Any_setprop(JSContext* cx, JSObject* obj, JSString* idstr, jsval v)
-{
-    jsid id;
-    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id))
-        return JS_FALSE;
-    return OBJ_SET_PROPERTY(cx, obj, id, &v);
-}
-
-jsval FASTCALL
-js_Any_getelem(JSContext* cx, JSObject* obj, int32 index)
-{
-    jsval v;
-    jsid id;
-    if (!js_Int32ToId(cx, index, &id))
-        return JSVAL_ERROR_COOKIE;
-    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
-        return JSVAL_ERROR_COOKIE;
-    return v;
-}
-
-JSBool FASTCALL
-js_Any_setelem(JSContext* cx, JSObject* obj, int32 index, jsval v)
-{
-    jsid id;
-    if (!js_Int32ToId(cx, index, &id))
-        return JSVAL_ERROR_COOKIE;
-    return OBJ_SET_PROPERTY(cx, obj, id, &v);
-}
-
 SideExit* FASTCALL
 js_CallTree(InterpState* state, Fragment* f)
 {
     union { NIns *code; GuardRecord* (FASTCALL *func)(InterpState*, Fragment*); } u;
 
     u.code = f->code();
     JS_ASSERT(u.code);
 
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -317,16 +317,29 @@ struct JSTraceableNative {
 #define _JS_DEFINE_CALLINFO_n(n, args)  JS_DEFINE_CALLINFO_##n args
 
 jsdouble FASTCALL
 js_StringToNumber(JSContext* cx, JSString* str);
 
 jsdouble FASTCALL
 js_BooleanOrUndefinedToNumber(JSContext* cx, int32 unboxed);
 
+static JS_INLINE JSBool
+js_Int32ToId(JSContext* cx, int32 index, jsid* id)
+{
+    if (index <= JSVAL_INT_MAX) {
+        *id = INT_TO_JSID(index);
+        return JS_TRUE;
+    }
+    JSString* str = js_NumberToString(cx, index);
+    if (!str)
+        return JS_FALSE;
+    return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
+}
+
 #else
 
 #define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, cse, fold)
 #define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, cse, fold)
 #define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, cse, fold)
 #define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, cse, fold)
 #define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, cse, fold)
 #define JS_DECLARE_CALLINFO(name)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -252,16 +252,20 @@ typedef enum JSRuntimeState {
     JSRTS_LAUNCHING,
     JSRTS_UP,
     JSRTS_LANDING
 } JSRuntimeState;
 
 typedef enum JSBuiltinFunctionId {
     JSBUILTIN_ObjectToIterator,
     JSBUILTIN_CallIteratorNext,
+    JSBUILTIN_GetProperty,
+    JSBUILTIN_GetElement,
+    JSBUILTIN_SetProperty,
+    JSBUILTIN_SetElement,
     JSBUILTIN_LIMIT
 } JSBuiltinFunctionId;
 
 typedef struct JSPropertyTreeEntry {
     JSDHashEntryHdr     hdr;
     JSScopeProperty     *child;
 } JSPropertyTreeEntry;
 
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -484,18 +484,17 @@ OPDEF(JSOP_TYPEOFEXPR,    198,"typeofexp
  * Block-local scope support.
  */
 OPDEF(JSOP_ENTERBLOCK,    199,"enterblock",  NULL,    3,  0, -1,  0,  JOF_OBJECT)
 OPDEF(JSOP_LEAVEBLOCK,    200,"leaveblock",  NULL,    3, -1,  0,  0,  JOF_UINT16)
 
 /*
  * Pick an element from the stack.
  */
-OPDEF(JSOP_PICK,          201,"pick",        NULL,    2,  1,  0,  0,  JOF_UINT8)
-
+OPDEF(JSOP_PICK,          201,"pick",        NULL,    2,  0,  0,  0,  JOF_UINT8)
 
 /* Throws a TypeError if the value at the top of the stack is not primitive. */
 OPDEF(JSOP_PRIMTOP,       202, "primtop",    NULL,    1,  1,  1,  0,  JOF_BYTE)
 
 OPDEF(JSOP_UNUSED203,     203,"unused203",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED204,     204,"unused204",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED205,     205,"unused205",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_UNUSED206,     206,"unused206",   NULL,    1,  0,  0,  0,  JOF_BYTE)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -4223,16 +4223,17 @@ TraceRecorder::monitorRecording(JSContex
                             (cx->fp->imacpc)                                  \
                             ? 0                                               \
                             : PTRDIFF(cx->fp->regs->pc,                       \
                                       cx->fp->script->code,                   \
                                       jsbytecode),                            \
                             !cx->fp->imacpc, stdout);)                        \
         flag = tr->record_##x();                                              \
         if (x == JSOP_ITER || x == JSOP_NEXTITER || x == JSOP_APPLY ||        \
+            x == JSOP_GETELEM || x == JSOP_SETELEM || x== JSOP_INITELEM ||    \
             JSOP_IS_BINARY(x) || JSOP_IS_UNARY(x) ||                          \
             JSOP_IS_EQUALITY(x)) {                                            \
             goto imacro;                                                      \
         }                                                                     \
         break;
 # include "jsopcode.tbl"
 # undef OPDEF
     }
@@ -6804,16 +6805,79 @@ TraceRecorder::record_SetPropMiss(JSProp
     JS_ASSERT(scope->object == obj);
     JS_ASSERT(scope->shape == PCVCAP_SHAPE(entry->vcap));
     JS_ASSERT(SCOPE_HAS_PROPERTY(scope, sprop));
 #endif
 
     return record_SetPropHit(entry, sprop);
 }
 
+/* Functions used by JSOP_GETELEM. */
+
+static JSBool
+GetProperty(JSContext *cx, uintN argc, jsval *vp)
+{
+    jsval *argv;
+    jsid id;
+
+    JS_ASSERT(argc == 1);
+    argv = JS_ARGV(cx, vp);
+    JS_ASSERT(JSVAL_IS_STRING(argv[0]));
+    if (!js_ValueToStringId(cx, argv[0], &id))
+        return JS_FALSE;
+    argv[0] = ID_TO_VALUE(id);
+    return OBJ_GET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &JS_RVAL(cx, vp));
+}
+
+static jsval FASTCALL
+GetProperty_tn(JSContext *cx, JSObject *obj, JSString *name)
+{
+    jsid id;
+    jsval v;
+
+    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(name), &id) ||
+        !OBJ_GET_PROPERTY(cx, obj, id, &v)) {
+        return JSVAL_ERROR_COOKIE;
+    }
+    return v;
+}
+
+static JSBool
+GetElement(JSContext *cx, uintN argc, jsval *vp)
+{
+    jsval *argv;
+    jsid id;
+
+    JS_ASSERT(argc == 1);
+    argv = JS_ARGV(cx, vp);
+    JS_ASSERT(JSVAL_IS_NUMBER(argv[0]));
+    if (!JS_ValueToId(cx, argv[0], &id))
+        return JS_FALSE;
+    argv[0] = ID_TO_VALUE(id);
+    return OBJ_GET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &JS_RVAL(cx, vp));
+}
+
+static jsval FASTCALL
+GetElement_tn(JSContext* cx, JSObject* obj, int32 index)
+{
+    jsval v;
+    jsid id;
+
+    if (!js_Int32ToId(cx, index, &id))
+        return JSVAL_ERROR_COOKIE;
+    if (!OBJ_GET_PROPERTY(cx, obj, id, &v))
+        return JSVAL_ERROR_COOKIE;
+    return v;
+}
+
+JS_DEFINE_TRCINFO_1(GetProperty,
+    (3, (static, JSVAL_FAIL,    GetProperty_tn, CONTEXT, THIS, STRING,          0, 0)))
+JS_DEFINE_TRCINFO_1(GetElement,
+    (3, (extern, JSVAL_FAIL,    GetElement_tn,  CONTEXT, THIS, INT32,           0, 0)))
+
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_GETELEM()
 {
     jsval& idx = stackval(-1);
     jsval& lval = stackval(-2);
 
     LIns* obj_ins = get(&lval);
     LIns* idx_ins = get(&idx);
@@ -6848,54 +6912,108 @@ TraceRecorder::record_JSOP_GETELEM()
         if (js_IdIsIndex(idx, &index) && guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
             v = (index >= ARRAY_DENSE_LENGTH(obj)) ? JSVAL_HOLE : obj->dslots[index];
             if (v == JSVAL_HOLE)
                 ABORT_TRACE("can't see through hole in dense array");
         } else {
             if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
                 return false;
         }
-        LIns* args[] = { idx_ins, obj_ins, cx_ins };
-        v_ins = lir->insCall(&js_Any_getprop_ci, args);
-        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
-        unbox_jsval(v, v_ins);
-        set(&lval, v_ins);
-        return true;
+        return call_imacro(getelem_imacros.getprop);
     }
 
     /* At this point we expect a whole number or we bail. */
     if (!JSVAL_IS_INT(idx))
         ABORT_TRACE("non-string, non-int JSOP_GETELEM index");
     if (JSVAL_TO_INT(idx) < 0)
         ABORT_TRACE("negative JSOP_GETELEM index");
 
     /* Accessing an object using integer index but not a dense array. */
     if (!OBJ_IS_DENSE_ARRAY(cx, obj)) {
         idx_ins = makeNumberInt32(idx_ins);
-        LIns* args[] = { idx_ins, obj_ins, cx_ins };
         if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
             return false;
-        idx = ID_TO_VALUE(id);
         if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, getProperty), &v))
             return false;
-        LIns* v_ins = lir->insCall(&js_Any_getelem_ci, args);
-        guard(false, lir->ins2(LIR_eq, v_ins, INS_CONST(JSVAL_ERROR_COOKIE)), MISMATCH_EXIT);
-        unbox_jsval(v, v_ins);
-        set(&lval, v_ins);
-        return true;
+        return call_imacro(getelem_imacros.getelem);
     }
 
     jsval* vp;
     LIns* addr_ins;
     if (!elem(lval, idx, vp, v_ins, addr_ins))
         return false;
     set(&lval, v_ins);
     return true;
 }
 
+/* Functions used by JSOP_SETELEM */
+
+static JSBool
+SetProperty(JSContext *cx, uintN argc, jsval *vp)
+{
+    jsval *argv;
+    jsid id;
+
+    JS_ASSERT(argc == 2);
+    argv = JS_ARGV(cx, vp);
+    JS_ASSERT(JSVAL_IS_STRING(argv[0]));
+    if (!js_ValueToStringId(cx, argv[0], &id))
+        return JS_FALSE;
+    argv[0] = ID_TO_VALUE(id);
+    if (!OBJ_SET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &argv[1]))
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    return JS_TRUE;
+}
+
+static int32 FASTCALL
+SetProperty_tn(JSContext* cx, JSObject* obj, JSString* idstr, jsval v)
+{
+    jsid id;
+
+    if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), &id) ||
+        !OBJ_SET_PROPERTY(cx, obj, id, &v)) {
+        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
+    }
+    return JSVAL_TRUE;
+}
+
+static JSBool
+SetElement(JSContext *cx, uintN argc, jsval *vp)
+{
+    jsval *argv;
+    jsid id;
+
+    JS_ASSERT(argc == 2);
+    argv = JS_ARGV(cx, vp);
+    JS_ASSERT(JSVAL_IS_NUMBER(argv[0]));
+    if (!JS_ValueToId(cx, argv[0], &id))
+        return JS_FALSE;
+    argv[0] = ID_TO_VALUE(id);
+    if (!OBJ_SET_PROPERTY(cx, JS_THIS_OBJECT(cx, vp), id, &argv[1]))
+        return JS_FALSE;
+    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+    return JS_TRUE;
+}
+
+static int32 FASTCALL
+SetElement_tn(JSContext* cx, JSObject* obj, int32 index, jsval v)
+{
+    jsid id;
+
+    if (!js_Int32ToId(cx, index, &id) || !OBJ_SET_PROPERTY(cx, obj, id, &v))
+        return JSVAL_TO_BOOLEAN(JSVAL_VOID);
+    return JSVAL_TRUE;
+}
+
+JS_DEFINE_TRCINFO_1(SetProperty,
+    (4, (extern, BOOL_FAIL,     SetProperty_tn, CONTEXT, THIS, STRING, JSVAL,   0, 0)))
+JS_DEFINE_TRCINFO_1(SetElement,
+    (4, (extern, BOOL_FAIL,     SetElement_tn,  CONTEXT, THIS, INT32, JSVAL,    0, 0)))
+
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_SETELEM()
 {
     jsval& v = stackval(-1);
     jsval& idx = stackval(-2);
     jsval& lval = stackval(-3);
 
     /* no guards for type checks, trace specialized this already */
@@ -6913,45 +7031,46 @@ TraceRecorder::record_JSOP_SETELEM()
 
     if (JSVAL_IS_STRING(idx)) {
         if (!js_ValueToStringId(cx, idx, &id))
             return false;
         // Store the interned string to the stack to save the interpreter from redoing this work.
         idx = ID_TO_VALUE(id);
         if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
             return false;
-        LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
-        LIns* ok_ins = lir->insCall(&js_Any_setprop_ci, args);
-        guard(false, lir->ins_eq0(ok_ins), MISMATCH_EXIT);    
-    } else if (JSVAL_IS_INT(idx)) {
+        return call_imacro(setelem_imacros.setprop);
+    }
+    if (JSVAL_IS_INT(idx)) {
         if (JSVAL_TO_INT(idx) < 0)
             ABORT_TRACE("negative JSOP_SETELEM index");
         idx_ins = makeNumberInt32(idx_ins);
-        LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
-        LIns* res_ins;
-        if (guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
-            res_ins = lir->insCall(&js_Array_dense_setelem_ci, args);
-        } else {
+
+        if (!guardDenseArray(obj, obj_ins, BRANCH_EXIT)) {
             if (!js_IndexToId(cx, JSVAL_TO_INT(idx), &id))
                 return false;
             idx = ID_TO_VALUE(id);
             if (!guardElemOp(obj, obj_ins, id, offsetof(JSObjectOps, setProperty), NULL))
                 return false;
-            res_ins = lir->insCall(&js_Any_setelem_ci, args);
+            jsbytecode* pc = cx->fp->regs->pc;
+            return call_imacro((*pc == JSOP_INITELEM)
+                               ? initelem_imacros.initelem
+                               : setelem_imacros.setelem);
         }
+
+        LIns* args[] = { boxed_v_ins, idx_ins, obj_ins, cx_ins };
+        LIns* res_ins = lir->insCall(&js_Array_dense_setelem_ci, args);
         guard(false, lir->ins_eq0(res_ins), MISMATCH_EXIT);
-    } else {
-        ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
-    }
-
-    jsbytecode* pc = cx->fp->regs->pc;
-    if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
-        set(&lval, v_ins);
-
-    return true;
+
+        jsbytecode* pc = cx->fp->regs->pc;
+        if (*pc == JSOP_SETELEM && pc[JSOP_SETELEM_LENGTH] != JSOP_POP)
+            set(&lval, v_ins);
+
+        return true;
+    }
+    ABORT_TRACE("non-string, non-int JSOP_SETELEM index");
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_CALLNAME()
 {
     JSObject* obj = cx->fp->scopeChain;
     if (obj != globalObj) {
         jsval* vp;
@@ -8806,17 +8925,21 @@ JS_DEFINE_TRCINFO_1(ObjectToIterator,
 JS_DEFINE_TRCINFO_1(CallIteratorNext,
     (2, (static, JSVAL_FAIL,       CallIteratorNext_tn, CONTEXT, THIS,          0, 0)))
 
 static const struct BuiltinFunctionInfo {
     JSTraceableNative *tn;
     int nargs;
 } builtinFunctionInfo[JSBUILTIN_LIMIT] = {
     {ObjectToIterator_trcinfo,   1},
-    {CallIteratorNext_trcinfo,   0}
+    {CallIteratorNext_trcinfo,   0},
+    {GetProperty_trcinfo,        1},
+    {GetElement_trcinfo,         1},
+    {SetProperty_trcinfo,        2},
+    {SetElement_trcinfo,         2}
 };
 
 JSObject *
 js_GetBuiltinFunction(JSContext *cx, uintN index)
 {
     JSRuntime *rt = cx->runtime;
     JSObject *funobj = rt->builtinFunctions[index];
 
@@ -9022,16 +9145,19 @@ InitIMacroCode()
     for (uintN op = JSOP_BITOR; op <= JSOP_MOD; op++)
         imacro_code[op] = (jsbytecode*)&binary_imacros - 1;
 
     // NB: above loop mis-set JSOP_ADD's entry, so order here is crucial.
     imacro_code[JSOP_ADD] = (jsbytecode*)&add_imacros - 1;
 
     imacro_code[JSOP_ITER] = (jsbytecode*)&iter_imacros - 1;
     imacro_code[JSOP_NEXTITER] = (jsbytecode*)&nextiter_imacros - 1;
+    imacro_code[JSOP_GETELEM] = (jsbytecode*)&getelem_imacros - 1;
+    imacro_code[JSOP_SETELEM] = (jsbytecode*)&setelem_imacros - 1;
+    imacro_code[JSOP_INITELEM] = (jsbytecode*)&initelem_imacros - 1;
     imacro_code[JSOP_APPLY] = (jsbytecode*)&apply_imacros - 1;
 
     imacro_code[JSOP_NEG] = (jsbytecode*)&unary_imacros - 1;
     imacro_code[JSOP_POS] = (jsbytecode*)&unary_imacros - 1;
 
     imacro_code[JSOP_EQ] = (jsbytecode*)&equality_imacros - 1;
     imacro_code[JSOP_NE] = (jsbytecode*)&equality_imacros - 1;
 }
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -4102,16 +4102,57 @@ function testInterpreterReentry3() {
     for (let i=0;i<5;++i) this["y" + i] = function(){};
     this.__defineGetter__('e', function (x2) { yield; });
     [1 for each (a in this) for (b in {})];
     return 1;
 }
 testInterpreterReentry3.expected = 1;
 test(testInterpreterReentry3);
 
+function testInterpreterReentry4() {
+    var obj = {a:1, b:1, c:1, d:1, get e() 1000 };
+    for (var p in obj)
+        obj[p];
+}
+test(testInterpreterReentry4);
+
+function testInterpreterReentry5() {
+    var arr = [0, 1, 2, 3, 4];
+    arr.__defineGetter__("4", function() 1000);
+    for (var i = 0; i < 5; i++)
+        arr[i];
+    for (var p in arr)
+        arr[p];
+}
+test(testInterpreterReentry5);
+
+/* // These tests should pass but currently crash, pending bug 462027.
+function testInterpreterReentry6() {
+    var obj = {a:1, b:1, c:1, d:1, set e(x) { this._e = x; }};
+    for (var p in obj)
+        obj[p] = "grue";
+    return obj._e;
+}
+testInterpreterReentry6.expected = "grue";
+test(testInterpreterReentry6);
+
+function testInterpreterReentry7() {
+    var arr = [0, 1, 2, 3, 4];
+    arr.__defineSetter__("4", function(x) { this._4 = x; });
+    for (var i = 0; i < 5; i++)
+        arr[i] = "grue";
+    var tmp = arr._4;
+    for (var p in arr)
+        arr[p] = "bleen";
+    return tmp + " " + arr._4;
+}
+testInterpreterReentry7.expected = "grue bleen";
+test(testInterpreterReentry7);
+*/
+
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *
  * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *