--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -826,18 +826,16 @@ array_defineProperty(JSContext *cx, JSOb
if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom))
return JS_TRUE;
isIndex = js_IdIsIndex(ID_TO_VALUE(id), &i);
if (!isIndex || attrs != JSPROP_ENUMERATE) {
if (!ENSURE_SLOW_ARRAY(cx, obj))
return JS_FALSE;
- if (isIndex && STOBJ_IS_DELEGATE(obj))
- cx->runtime->anyArrayProtoHasElement = JS_TRUE;
return js_DefineProperty(cx, obj, id, value, getter, setter, attrs, propp);
}
return array_setProperty(cx, obj, id, &value);
}
static JSBool
array_getAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1700,17 +1700,16 @@ EmitIndexOp(JSContext *cx, JSOp op, uint
* caller's lexical environment, and embedding a false return on error.
*/
#define EMIT_INDEX_OP(op, index) \
JS_BEGIN_MACRO \
if (!EmitIndexOp(cx, op, index, cg)) \
return JS_FALSE; \
JS_END_MACRO
-
static JSBool
EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
{
JSAtomListElement *ale;
JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
if (op == JSOP_GETPROP &&
pn->pn_atom == cx->runtime->atomState.lengthAtom) {
@@ -2328,22 +2327,34 @@ EmitXMLName(JSContext *cx, JSParseNode *
return JS_FALSE;
}
return js_Emit1(cx, cg, op) >= 0;
}
#endif
static JSBool
+EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg);
+
+static JSBool
EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg,
JSBool callContext)
{
JSParseNode *pn2, *pndot, *pnup, *pndown;
ptrdiff_t top;
+ /*
+ * Special case obj.__proto__ to deoptimize away from fast paths in the
+ * interpreter and trace recorder, which skip dense array instances by
+ * skipping over the directly referenced object to Array.prototype before
+ * looking up the property name. See bug 450274.
+ */
+ if (pn->pn_atom == cx->runtime->atomState.protoAtom)
+ return EmitElemOp(cx, pn, callContext ? JSOP_CALLELEM : JSOP_GETELEM, cg);
+
pn2 = pn->pn_expr;
if (callContext) {
JS_ASSERT(pn->pn_type == TOK_DOT);
JS_ASSERT(op == JSOP_GETPROP);
op = JSOP_CALLPROP;
} else if (op == JSOP_GETPROP && pn->pn_type == TOK_DOT) {
if (pn2->pn_op == JSOP_THIS) {
if (pn->pn_atom != cx->runtime->atomState.lengthAtom) {
@@ -5324,16 +5335,21 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
break;
case TOK_DOT:
if (js_Emit1(cx, cg, JSOP_DUP) < 0)
return JS_FALSE;
if (pn2->pn_atom == cx->runtime->atomState.lengthAtom) {
if (js_Emit1(cx, cg, JSOP_LENGTH) < 0)
return JS_FALSE;
+ } else if (pn2->pn_atom == cx->runtime->atomState.protoAtom) {
+ if (!EmitIndexOp(cx, JSOP_QNAMEPART, atomIndex, cg))
+ return JS_FALSE;
+ if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
+ return JS_FALSE;
} else {
EMIT_INDEX_OP(JSOP_GETPROP, atomIndex);
}
break;
case TOK_LB:
#if JS_HAS_LVALUE_RETURN
case TOK_LP:
#endif
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -321,20 +321,30 @@ js_SetProtoOrParent(JSContext *cx, JSObj
(slot == JSSLOT_PROTO) ? js_proto_str
: js_parent_str
#endif
);
}
return JS_FALSE;
}
- // Maintain the "any Array prototype has indexed properties hazard" flag.
+ /*
+ * Maintain the "any Array prototype has indexed properties hazard" flag by
+ * conservatively setting it. We simply don't know what pobj has in the way
+ * of indexed properties, either directly or along its prototype chain, and
+ * we won't expend effort here to find out. We do know that if obj is not
+ * an array or a prototype (delegate), then we're ok. And, of course, pobj
+ * must be non-null.
+ *
+ * This pessimistic approach could be improved, but setting __proto__ is
+ * quite rare and arguably deserving of deoptimization.
+ */
if (slot == JSSLOT_PROTO &&
- OBJ_IS_ARRAY(cx, pobj) &&
- pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) {
+ pobj &&
+ (OBJ_IS_ARRAY(cx, obj) || OBJ_IS_DELEGATE(cx, obj))) {
rt->anyArrayProtoHasElement = JS_TRUE;
}
return JS_TRUE;
}
static JSHashNumber
js_hash_object(const void *key)
{
@@ -3180,29 +3190,34 @@ js_DefineProperty(JSContext *cx, JSObjec
0, 0, propp);
}
/*
* Backward compatibility requires allowing addProperty hooks to mutate the
* nominal initial value of a slot-full property, while GC safety wants that
* value to be stored before the call-out through the hook. Optimize to do
* both while saving cycles for classes that stub their addProperty hook.
+ *
+ * As in js_SetProtoOrParent (see above), we maintain the "any Array prototype
+ * has indexed properties hazard" flag by conservatively setting it.
*/
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \
JS_BEGIN_MACRO \
if ((clasp)->addProperty != JS_PropertyStub) { \
jsval nominal_ = *(vp); \
if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \
cleanup; \
} \
if (*(vp) != nominal_) { \
if (SPROP_HAS_VALID_SLOT(sprop, scope)) \
LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *(vp)); \
} \
} \
+ if (STOBJ_IS_DELEGATE(obj) && JSID_IS_INT(sprop->id)) \
+ cx->runtime->anyArrayProtoHasElement = JS_TRUE; \
JS_END_MACRO
JSBool
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
uintN flags, intN shortid, JSProperty **propp)
{
JSClass *clasp;
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -536,16 +536,17 @@ OPDEF(JSOP_UNUSED219, 219,"unused219
*/
OPDEF(JSOP_INDEXBASE1, 220,"atombase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE2, 221,"atombase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE3, 222,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_CALLGVAR, 223, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLLOCAL, 224, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 225, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
+
OPDEF(JSOP_UNUSED226, 226, "unused226", NULL, 1, 0, 1, 1, JOF_BYTE)
/*
* Opcodes to hold 8-bit and 32-bit immediate integer operands.
*/
OPDEF(JSOP_INT8, 227, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -7758,17 +7758,16 @@ JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_GOTOX()
{
return true;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_IFEQX()
{
- trackCfgMerges(cx->fp->regs->pc);
return record_JSOP_IFEQ();
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_IFNEX()
{
return record_JSOP_IFNE();
}
@@ -8452,17 +8451,17 @@ TraceRecorder::record_JSOP_INT32()
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_LENGTH()
{
jsval& l = stackval(-1);
if (JSVAL_IS_PRIMITIVE(l)) {
if (!JSVAL_IS_STRING(l))
- ABORT_TRACE("non-string primitives unsupported");
+ ABORT_TRACE("non-string primitive JSOP_LENGTH unsupported");
LIns* str_ins = get(&l);
LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, length));
LIns* masked_len_ins = lir->ins2(LIR_piand,
len_ins,
INS_CONSTPTR(JSSTRING_LENGTH_MASK));
LIns *choose_len_ins =