diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 3cddd564f58..418a1e7d126 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -811,7 +811,8 @@ array_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) if (!obj->isDenseArray()) return js_SetProperty(cx, obj, id, vp); - if (!js_IdIsIndex(id, &i) || INDEX_TOO_SPARSE(obj, i)) { + if (!js_IdIsIndex(id, &i) || js_PrototypeHasIndexedProperties(cx, obj) || + INDEX_TOO_SPARSE(obj, i)) { if (!obj->makeDenseArraySlow(cx)) return JS_FALSE; return js_SetProperty(cx, obj, id, vp); diff --git a/js/src/jsscope.h b/js/src/jsscope.h index f5449683567..fa0ea9724fa 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -222,9 +222,7 @@ struct JSScope : public JSObjectMap #endif JSObject *object; /* object that owns this scope */ uint32 freeslot; /* index of next free slot in object */ - protected: uint8 flags; /* flags, see below */ - public: int8 hashShift; /* multiplicative hash shift */ uint16 spare; /* reserved */ diff --git a/js/src/methodjit/FastOps.cpp b/js/src/methodjit/FastOps.cpp index 4ee9e21f8ac..b3bc424af33 100644 --- a/js/src/methodjit/FastOps.cpp +++ b/js/src/methodjit/FastOps.cpp @@ -40,6 +40,7 @@ #include "jsbool.h" #include "jslibmath.h" #include "jsnum.h" +#include "jsscope.h" #include "methodjit/MethodJIT.h" #include "methodjit/Compiler.h" #include "methodjit/StubCalls.h" @@ -964,6 +965,14 @@ mjit::Compiler::jsop_setelem() } else { RegisterID idReg = maybeIdReg.reg(); + /* + * Register for use only in OOL hole path. TODO: would be nice + * for the frame to do any associated spilling in the OOL path. + */ + RegisterID T1 = frame.allocReg(); + + Label syncTarget = stubcc.syncExitAndJump(Uses(3)); + /* guard not a hole */ BaseIndex slot(objReg, idReg, Assembler::JSVAL_SCALE); #if defined JS_NUNBOX32 @@ -972,7 +981,57 @@ mjit::Compiler::jsop_setelem() masm.loadTypeTag(slot, Registers::ValueReg); Jump notHole = masm.branchPtr(Assembler::Equal, Registers::ValueReg, ImmType(JSVAL_TYPE_MAGIC)); #endif - stubcc.linkExit(notHole, Uses(3)); + + /* Make an OOL path for setting array holes. */ + Label lblHole = stubcc.masm.label(); + stubcc.linkExitDirect(notHole, lblHole); + + /* Need a new handle on the object, as objReg now holds the dslots. */ + RegisterID baseReg = frame.tempRegForData(obj, objReg, stubcc.masm); + + /* + * Check if the object has a prototype with indexed properties, + * in which case it might have a setter for this element. For dense + * arrays we only need to check Array.prototype and Object.prototype. + */ + + /* + * Test for indexed properties in Array.prototype. flags is a one byte + * quantity, but will be aligned on 4 bytes. + */ + stubcc.masm.loadPtr(Address(baseReg, offsetof(JSObject, proto)), T1); + stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, map)), T1); + stubcc.masm.load32(Address(T1, offsetof(JSScope, flags)), T1); + stubcc.masm.and32(Imm32(JSScope::INDEXED_PROPERTIES), T1); + Jump extendedArray = stubcc.masm.branchTest32(Assembler::NonZero, T1, T1); + extendedArray.linkTo(syncTarget, &stubcc.masm); + + /* Test for indexed properties in Object.prototype. */ + stubcc.masm.loadPtr(Address(baseReg, offsetof(JSObject, proto)), T1); + stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, proto)), T1); + stubcc.masm.loadPtr(Address(T1, offsetof(JSObject, map)), T1); + stubcc.masm.load32(Address(T1, offsetof(JSScope, flags)), T1); + stubcc.masm.and32(Imm32(JSScope::INDEXED_PROPERTIES), T1); + Jump extendedObject = stubcc.masm.branchTest32(Assembler::NonZero, T1, T1); + extendedObject.linkTo(syncTarget, &stubcc.masm); + + /* Update the array length if needed. Don't worry about overflow. */ + Address arrayLength(baseReg, offsetof(JSObject, fslots[JSObject::JSSLOT_ARRAY_LENGTH])); + stubcc.masm.loadPayload(arrayLength, T1); + Jump underLength = stubcc.masm.branch32(Assembler::LessThan, idReg, T1); + stubcc.masm.move(idReg, T1); + stubcc.masm.add32(Imm32(1), T1); + stubcc.masm.storePayload(T1, arrayLength); + underLength.linkTo(stubcc.masm.label(), &stubcc.masm); + + /* Restore the dslots register if we clobbered it with the object. */ + if (baseReg == objReg) + stubcc.masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg); + + /* Rejoin OOL path with inline path to do the store itself. */ + Jump jmpHoleExit = stubcc.masm.jump(); + Label lblRejoin = masm.label(); + stubcc.crossJump(jmpHoleExit, lblRejoin); stubcc.leave(); stubcc.call(stubs::SetElem); @@ -993,6 +1052,7 @@ mjit::Compiler::jsop_setelem() } frame.freeReg(idReg); + frame.freeReg(T1); } frame.freeReg(objReg); diff --git a/js/src/methodjit/FrameState-inl.h b/js/src/methodjit/FrameState-inl.h index 205d3587165..477f0f95204 100644 --- a/js/src/methodjit/FrameState-inl.h +++ b/js/src/methodjit/FrameState-inl.h @@ -442,6 +442,23 @@ FrameState::tempRegInMaskForData(FrameEntry *fe, uint32 mask) return reg; } +inline JSC::MacroAssembler::RegisterID +FrameState::tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const +{ + JS_ASSERT(!fe->data.isConstant()); + + if (fe->isCopy()) + fe = fe->copyOf(); + + if (fe->data.inRegister()) { + JS_ASSERT(fe->data.reg() != reg); + return fe->data.reg(); + } else { + masm.loadPayload(addressOf(fe), reg); + return reg; + } +} + inline bool FrameState::shouldAvoidTypeRemat(FrameEntry *fe) { diff --git a/js/src/methodjit/FrameState.h b/js/src/methodjit/FrameState.h index 10512a8ecdd..2549e0153f9 100644 --- a/js/src/methodjit/FrameState.h +++ b/js/src/methodjit/FrameState.h @@ -320,6 +320,12 @@ class FrameState */ inline RegisterID tempRegInMaskForData(FrameEntry *fe, uint32 mask); + /* + * Same as above, except loads into reg (using masm) if the entry does not + * already have a register, and does not change the frame state in doing so. + */ + inline RegisterID tempRegForData(FrameEntry *fe, RegisterID reg, Assembler &masm) const; + /* * Forcibly loads the type tag for the specified FrameEntry * into a register already marked as owning the type.