Bug 848743 - Change SetElem_DenseAdd stub to check all shapes on the proto chain. r=djvj

This commit is contained in:
Jan de Mooij 2013-03-25 17:54:29 +01:00
parent 7f0a88535b
commit 50722320c8
7 changed files with 272 additions and 110 deletions

View File

@ -805,8 +805,7 @@ InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
// Calculate and write out return address.
// The icEntry in question MUST have a ICCall_Fallback as its fallback stub.
ICEntry &icEntry = baselineScript->icEntryFromPCOffset(pcOff);
ICFallbackStub *fallbackStub = icEntry.firstStub()->getChainFallback();
JS_ASSERT(fallbackStub->isCall_Fallback());
JS_ASSERT(icEntry.firstStub()->getChainFallback()->isCall_Fallback());
if (!builder.writePtr(baselineScript->returnAddressForIC(icEntry), "ReturnAddr"))
return false;

View File

@ -212,8 +212,17 @@ ICStub::trace(JSTracer *trc)
ICSetElem_DenseAdd *setElemStub = toSetElem_DenseAdd();
MarkShape(trc, &setElemStub->shape(), "baseline-getelem-denseadd-shape");
MarkTypeObject(trc, &setElemStub->type(), "baseline-setelem-denseadd-type");
MarkObject(trc, &setElemStub->lastProto(), "baseline-setelem-denseadd-lastproto");
MarkShape(trc, &setElemStub->lastProtoShape(), "baseline-setelem-denseadd-lastprotoshape");
JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4);
switch (setElemStub->protoChainDepth()) {
case 0: setElemStub->toImpl<0>()->traceProtoShapes(trc); break;
case 1: setElemStub->toImpl<1>()->traceProtoShapes(trc); break;
case 2: setElemStub->toImpl<2>()->traceProtoShapes(trc); break;
case 3: setElemStub->toImpl<3>()->traceProtoShapes(trc); break;
case 4: setElemStub->toImpl<4>()->traceProtoShapes(trc); break;
default: JS_NOT_REACHED("Invalid proto stub.");
}
break;
}
case ICStub::SetElem_TypedArray: {
@ -896,7 +905,7 @@ DoProfilerFallback(JSContext *cx, BaselineFrame *frame, ICProfiler_Fallback *stu
{
RootedScript script(cx, frame->script());
RootedFunction func(cx, frame->maybeFun());
ICEntry *icEntry = stub->icEntry();
mozilla::DebugOnly<ICEntry *> icEntry = stub->icEntry();
FallbackICSpew(cx, stub, "Profiler");
@ -3593,13 +3602,13 @@ RemoveExistingTypedArraySetElemStub(JSContext *cx, ICSetElem_Fallback *stub, Han
static bool
CanOptimizeDenseSetElem(JSContext *cx, HandleObject obj, uint32_t index,
HandleShape oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
bool *isAddingCaseOut, MutableHandleObject lastProtoOut)
bool *isAddingCaseOut, size_t *protoDepthOut)
{
uint32_t initLength = obj->getDenseInitializedLength();
uint32_t capacity = obj->getDenseCapacity();
*isAddingCaseOut = false;
lastProtoOut.set(NULL);
*protoDepthOut = 0;
// Some initial sanity checks.
if (initLength < oldInitLength || capacity < oldCapacity)
@ -3640,7 +3649,6 @@ CanOptimizeDenseSetElem(JSContext *cx, HandleObject obj, uint32_t index,
// which is a proxy, that handles a particular integer write.
// Scan the prototype and shape chain to make sure that this is not the case.
RootedObject curObj(cx, obj);
RootedObject lastObj(cx);
while (curObj) {
// Ensure object is native.
if (!curObj->isNative())
@ -3650,14 +3658,15 @@ CanOptimizeDenseSetElem(JSContext *cx, HandleObject obj, uint32_t index,
if (curObj->isIndexed())
return false;
if (!curObj->getProto())
lastObj = curObj;
curObj = curObj->getProto();
if (curObj)
++*protoDepthOut;
}
if (*protoDepthOut > ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH)
return false;
*isAddingCaseOut = true;
lastProtoOut.set(lastObj);
return true;
}
@ -3695,7 +3704,7 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
if (!InitElemOperation(cx, obj, &nindex, rhs))
return false;
} else if (op == JSOP_INITELEM_ARRAY) {
JS_ASSERT(index.toInt32() == GET_UINT24(pc));
JS_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
return false;
} else {
@ -3721,21 +3730,20 @@ DoSetElemFallback(JSContext *cx, BaselineFrame *frame, ICSetElem_Fallback *stub,
JS_ASSERT(!obj->isTypedArray());
bool addingCase;
RootedObject lastProto(cx);
size_t protoDepth;
if (CanOptimizeDenseSetElem(cx, obj, index.toInt32(), oldShape, oldCapacity, oldInitLength,
&addingCase, &lastProto))
&addingCase, &protoDepth))
{
RootedTypeObject type(cx, obj->getType(cx));
RootedShape shape(cx, obj->lastProperty());
if (addingCase && !DenseSetElemStubExists(cx, ICStub::SetElem_DenseAdd, stub, obj)) {
RootedShape lastProtoShape(cx, lastProto->lastProperty());
IonSpew(IonSpew_BaselineIC,
" Generating SetElem_DenseAdd stub "
"(shape=%p, type=%p, lastProto=%p, lastProtoShape=%p)",
obj->lastProperty(), type.get(), lastProto.get(), lastProtoShape.get());
ICSetElem_DenseAdd::Compiler compiler(cx, shape, type, lastProto, lastProtoShape);
"(shape=%p, type=%p, protoDepth=%u)",
obj->lastProperty(), type.get(), protoDepth);
ICSetElemDenseAddCompiler compiler(cx, obj, protoDepth);
ICUpdatedStub *denseStub = compiler.getStub(compiler.getStubSpace(script));
if (!denseStub)
return false;
@ -3850,7 +3858,7 @@ ICSetElem_Dense::Compiler::generateStubCode(MacroAssembler &masm)
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
// Unbox R0 and guard it's a dense array.
// Unbox R0 and guard on its shape.
Register obj = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICSetElem_Dense::offsetOfShape()), scratchReg);
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
@ -3931,12 +3939,49 @@ ICSetElem_Dense::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
static bool
GetProtoShapes(JSObject *obj, size_t protoChainDepth, AutoShapeVector *protoShapes)
{
JS_ASSERT(protoShapes->empty());
JSObject *curProto = obj->getProto();
for (size_t i = 0; i < protoChainDepth; i++) {
if (!protoShapes->append(curProto->lastProperty()))
return false;
curProto = curProto->getProto();
}
JS_ASSERT(!curProto);
return true;
}
//
// SetElem_DenseAdd
//
ICUpdatedStub *
ICSetElemDenseAddCompiler::getStub(ICStubSpace *space)
{
AutoShapeVector protoShapes(cx);
if (!GetProtoShapes(obj_, protoChainDepth_, &protoShapes))
return NULL;
JS_STATIC_ASSERT(ICSetElem_DenseAdd::MAX_PROTO_CHAIN_DEPTH == 4);
ICUpdatedStub *stub = NULL;
switch (protoChainDepth_) {
case 0: stub = getStubSpecific<0>(space, &protoShapes); break;
case 1: stub = getStubSpecific<1>(space, &protoShapes); break;
case 2: stub = getStubSpecific<2>(space, &protoShapes); break;
case 3: stub = getStubSpecific<3>(space, &protoShapes); break;
case 4: stub = getStubSpecific<4>(space, &protoShapes); break;
default: JS_NOT_REACHED("ProtoChainDepth too high.");
}
if (!stub || !stub->initUpdatingChain(cx, space))
return NULL;
return stub;
}
bool
ICSetElem_DenseAdd::Compiler::generateStubCode(MacroAssembler &masm)
ICSetElemDenseAddCompiler::generateStubCode(MacroAssembler &masm)
{
// R0 = object
// R1 = key
@ -3949,13 +3994,7 @@ ICSetElem_DenseAdd::Compiler::generateStubCode(MacroAssembler &masm)
GeneralRegisterSet regs(availableGeneralRegs(2));
Register scratchReg = regs.takeAny();
// Guard on the shape of the last prototype object.
masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfLastProto()), scratchReg);
masm.loadPtr(Address(scratchReg, JSObject::offsetOfShape()), scratchReg);
Address lastProtoShape(BaselineStubReg, ICSetElem_DenseAdd::offsetOfLastProtoShape());
masm.branchPtr(Assembler::NotEqual, lastProtoShape, scratchReg, &failure);
// Unbox R0 and guard it's a dense array.
// Unbox R0 and guard on its shape.
Register obj = masm.extractObject(R0, ExtractTemp0);
masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAdd::offsetOfShape()), scratchReg);
masm.branchTestObjShape(Assembler::NotEqual, obj, scratchReg, &failure);
@ -3975,6 +4014,18 @@ ICSetElem_DenseAdd::Compiler::generateStubCode(MacroAssembler &masm)
&failureUnstow);
regs.add(typeReg);
// Shape guard objects on the proto chain.
scratchReg = regs.takeAny();
Register protoReg = regs.takeAny();
for (size_t i = 0; i < protoChainDepth_; i++) {
masm.loadObjProto(i == 0 ? obj : protoReg, protoReg);
masm.loadPtr(Address(BaselineStubReg, ICSetElem_DenseAddImpl<0>::offsetOfProtoShape(i)),
scratchReg);
masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratchReg, &failureUnstow);
}
regs.add(protoReg);
regs.add(scratchReg);
// Stack is now: { ..., rhs-value, object-value, key-value, maybe?-RET-ADDR }
// Load rhs-value in to R0
masm.loadValue(Address(BaselineStackReg, 2 * sizeof(Value) + ICStackValueOffset), R0);
@ -4412,6 +4463,9 @@ ICGetName_Scope<NumHops>::Compiler::generateStubCode(MacroAssembler &masm)
Register walker = regs.takeAny();
Register scratch = regs.takeAny();
// Use a local to silence Clang tautological-compare warning if NumHops is 0.
size_t numHops = NumHops;
for (size_t index = 0; index < NumHops + 1; index++) {
Register scope = index ? walker : obj;
@ -4419,7 +4473,7 @@ ICGetName_Scope<NumHops>::Compiler::generateStubCode(MacroAssembler &masm)
masm.loadPtr(Address(BaselineStubReg, ICGetName_Scope::offsetOfShape(index)), scratch);
masm.branchTestObjShape(Assembler::NotEqual, scope, scratch, &failure);
if (index < NumHops)
if (index < numHops)
masm.extractObject(Address(scope, ScopeObject::offsetOfEnclosingScope()), walker);
}
@ -5282,6 +5336,29 @@ ICSetProp_Native::Compiler::generateStubCode(MacroAssembler &masm)
return true;
}
ICUpdatedStub *
ICSetPropNativeAddCompiler::getStub(ICStubSpace *space)
{
AutoShapeVector protoShapes(cx);
if (!GetProtoShapes(obj_, protoChainDepth_, &protoShapes))
return NULL;
JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4);
ICUpdatedStub *stub = NULL;
switch(protoChainDepth_) {
case 0: stub = getStubSpecific<0>(space, &protoShapes); break;
case 1: stub = getStubSpecific<1>(space, &protoShapes); break;
case 2: stub = getStubSpecific<2>(space, &protoShapes); break;
case 3: stub = getStubSpecific<3>(space, &protoShapes); break;
case 4: stub = getStubSpecific<4>(space, &protoShapes); break;
default: JS_NOT_REACHED("ProtoChainDepth too high.");
}
if (!stub || !stub->initUpdatingChain(cx, space))
return NULL;
return stub;
}
bool
ICSetPropNativeAddCompiler::generateStubCode(MacroAssembler &masm)
{

View File

@ -1129,6 +1129,7 @@ class ICProfiler_PushFunction : public ICStub
{
friend class ICStubSpace;
protected:
const char *str_;
HeapPtrScript script_;
@ -3155,46 +3156,36 @@ class ICSetElem_Dense : public ICUpdatedStub
};
};
template <size_t ProtoChainDepth> class ICSetElem_DenseAddImpl;
class ICSetElem_DenseAdd : public ICUpdatedStub
{
friend class ICStubSpace;
public:
static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
protected:
HeapPtrShape shape_;
HeapPtrTypeObject type_;
HeapPtrObject lastProto_;
HeapPtrShape lastProtoShape_;
ICSetElem_DenseAdd(IonCode *stubCode, HandleShape shape, HandleTypeObject type,
HandleObject lastProto, HandleShape lastProtoShape)
ICSetElem_DenseAdd(IonCode *stubCode, RawShape shape, types::TypeObject *type,
size_t protoChainDepth)
: ICUpdatedStub(SetElem_DenseAdd, stubCode),
shape_(shape),
type_(type),
lastProto_(lastProto),
lastProtoShape_(lastProtoShape)
{}
public:
static inline ICSetElem_DenseAdd *New(ICStubSpace *space, IonCode *code,
HandleShape shape, HandleTypeObject type,
HandleObject lastProto, HandleShape lastProtoShape)
type_(type)
{
if (!code)
return NULL;
return space->allocate<ICSetElem_DenseAdd>(code, shape, type, lastProto, lastProtoShape);
JS_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
extra_ = protoChainDepth;
}
public:
static size_t offsetOfShape() {
return offsetof(ICSetElem_DenseAdd, shape_);
}
static size_t offsetOfType() {
return offsetof(ICSetElem_DenseAdd, type_);
}
static size_t offsetOfLastProto() {
return offsetof(ICSetElem_DenseAdd, lastProto_);
}
static size_t offsetOfLastProtoShape() {
return offsetof(ICSetElem_DenseAdd, lastProtoShape_);
}
HeapPtrShape &shape() {
return shape_;
@ -3202,44 +3193,86 @@ class ICSetElem_DenseAdd : public ICUpdatedStub
HeapPtrTypeObject &type() {
return type_;
}
HeapPtrObject &lastProto() {
return lastProto_;
}
HeapPtrShape &lastProtoShape() {
return lastProtoShape_;
size_t protoChainDepth() const {
return extra_;
}
class Compiler : public ICStubCompiler {
RootedShape shape_;
template <size_t ProtoChainDepth>
ICSetElem_DenseAddImpl<ProtoChainDepth> *toImpl() {
JS_ASSERT(ProtoChainDepth == protoChainDepth());
return static_cast<ICSetElem_DenseAddImpl<ProtoChainDepth> *>(this);
}
};
// Compiler is only live on stack during compilation, it should
// outlive any RootedTypeObject it's passed. So it can just
// use the handle.
HandleTypeObject type_;
template <size_t ProtoChainDepth>
class ICSetElem_DenseAddImpl : public ICSetElem_DenseAdd
{
friend class ICStubSpace;
RootedObject lastProto_;
RootedShape lastProtoShape_;
HeapPtrShape protoShapes_[ProtoChainDepth];
bool generateStubCode(MacroAssembler &masm);
ICSetElem_DenseAddImpl(IonCode *stubCode, RawShape shape, types::TypeObject *type,
const AutoShapeVector *protoShapes)
: ICSetElem_DenseAdd(stubCode, shape, type, ProtoChainDepth)
{
JS_ASSERT(protoShapes->length() == ProtoChainDepth);
for (size_t i = 0; i < protoChainDepth(); i++)
protoShapes_[i].init((*protoShapes)[i]);
}
public:
Compiler(JSContext *cx, RawShape shape, HandleTypeObject type,
RawObject lastProto, RawShape lastProtoShape)
: ICStubCompiler(cx, ICStub::SetElem_DenseAdd),
shape_(cx, shape),
type_(type),
lastProto_(cx, lastProto),
lastProtoShape_(cx, lastProtoShape)
{}
// Used to silence Clang tautological-compare warning for
// ProtoChainDepth == 0.
size_t protoChainDepth() const {
return ProtoChainDepth;
}
ICUpdatedStub *getStub(ICStubSpace *space) {
ICSetElem_DenseAdd *stub = ICSetElem_DenseAdd::New(space, getStubCode(), shape_, type_,
lastProto_, lastProtoShape_);
if (!stub || !stub->initUpdatingChain(cx, space))
return NULL;
return stub;
}
};
public:
static inline ICSetElem_DenseAddImpl *New(ICStubSpace *space, IonCode *code, RawShape shape,
types::TypeObject *type,
const AutoShapeVector *protoShapes)
{
if (!code)
return NULL;
return space->allocate<ICSetElem_DenseAddImpl<ProtoChainDepth> >(code, shape, type,
protoShapes);
}
void traceProtoShapes(JSTracer *trc) {
for (size_t i = 0; i < protoChainDepth(); i++)
MarkShape(trc, &protoShapes_[i], "baseline-setelem-denseadd-stub-protoshape");
}
static size_t offsetOfProtoShape(size_t idx) {
return offsetof(ICSetElem_DenseAddImpl, protoShapes_) + idx * sizeof(HeapPtrShape);
}
};
class ICSetElemDenseAddCompiler : public ICStubCompiler {
RootedObject obj_;
size_t protoChainDepth_;
bool generateStubCode(MacroAssembler &masm);
protected:
virtual int32_t getKey() const {
return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
}
public:
ICSetElemDenseAddCompiler(JSContext *cx, HandleObject obj, size_t protoChainDepth)
: ICStubCompiler(cx, ICStub::SetElem_DenseAdd),
obj_(cx, obj),
protoChainDepth_(protoChainDepth)
{}
template <size_t ProtoChainDepth>
ICUpdatedStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *protoShapes) {
return ICSetElem_DenseAddImpl<ProtoChainDepth>::New(space, getStubCode(),
obj_->lastProperty(), obj_->getType(cx),
protoShapes);
}
ICUpdatedStub *getStub(ICStubSpace *space);
};
class ICSetElem_TypedArray : public ICStub
@ -3480,7 +3513,7 @@ class ICGetName_Scope : public ICMonitoredStub
}
void traceScopes(JSTracer *trc) {
for (size_t i = 0; i <= NumHops; i++)
for (size_t i = 0; i < NumHops + 1; i++)
MarkShape(trc, &shapes_[i], "baseline-scope-stub-shape");
}
@ -3980,6 +4013,7 @@ class ICGetProp_CallScripted : public ICMonitoredStub
{
friend class ICStubSpace;
protected:
// Object shape (lastProperty).
HeapPtrShape shape_;
@ -4266,9 +4300,16 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
: ICSetProp_NativeAdd(stubCode, type, oldShape, ProtoChainDepth, newShape, offset)
{
JS_ASSERT(protoShapes->length() == ProtoChainDepth);
for (size_t i = 0; i < ProtoChainDepth; i++)
for (size_t i = 0; i < protoChainDepth(); i++)
protoShapes_[i].init((*protoShapes)[i]);
}
// Used to silence Clang tautological-compare warning for
// ProtoChainDepth == 0.
size_t protoChainDepth() const {
return ProtoChainDepth;
}
public:
static inline ICSetProp_NativeAddImpl *New(
ICStubSpace *space, IonCode *code, HandleTypeObject type, HandleShape oldShape,
@ -4281,7 +4322,7 @@ class ICSetProp_NativeAddImpl : public ICSetProp_NativeAdd
}
void traceProtoShapes(JSTracer *trc) {
for (size_t i = 0; i < ProtoChainDepth; i++)
for (size_t i = 0; i < protoChainDepth(); i++)
MarkShape(trc, &protoShapes_[i], "baseline-setpropnativeadd-stub-protoshape");
}
@ -4328,28 +4369,7 @@ class ICSetPropNativeAddCompiler : public ICStubCompiler {
space, getStubCode(), type, oldShape_, protoShapes, newShape, offset_);
}
ICUpdatedStub *getStub(ICStubSpace *space) {
AutoShapeVector protoShapes(cx);
RootedObject curProto(cx, obj_->getProto());
for (size_t i = 0; i < protoChainDepth_; i++) {
JS_ASSERT(curProto);
protoShapes.append(curProto->lastProperty());
curProto = curProto->getProto();
}
JS_STATIC_ASSERT(ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH == 4);
ICUpdatedStub *stub = NULL;
switch(protoChainDepth_) {
case 0: stub = getStubSpecific<0>(space, &protoShapes); break;
case 1: stub = getStubSpecific<1>(space, &protoShapes); break;
case 2: stub = getStubSpecific<2>(space, &protoShapes); break;
case 3: stub = getStubSpecific<3>(space, &protoShapes); break;
case 4: stub = getStubSpecific<4>(space, &protoShapes); break;
default: JS_NOT_REACHED("ProtoChainDepth too high.");
}
if (!stub || !stub->initUpdatingChain(cx, space))
return NULL;
return stub;
}
ICUpdatedStub *getStub(ICStubSpace *space);
};
// Stub for calling a scripted setter on a native object.
@ -4357,6 +4377,7 @@ class ICSetProp_CallScripted : public ICStub
{
friend class ICStubSpace;
protected:
// Object shape (lastProperty).
HeapPtrShape shape_;
@ -4538,6 +4559,7 @@ class ICCall_Scripted : public ICMonitoredStub
{
friend class ICStubSpace;
protected:
HeapPtrScript calleeScript_;
uint32_t pcOffset_;
@ -4574,6 +4596,7 @@ class ICCall_AnyScripted : public ICMonitoredStub
{
friend class ICStubSpace;
protected:
uint32_t pcOffset_;
ICCall_AnyScripted(IonCode *stubCode, ICStub *firstMonitorStub, uint32_t pcOffset)
@ -4628,7 +4651,6 @@ class ICCallScriptedCompiler : public ICCallStubCompiler {
{ }
ICStub *getStub(ICStubSpace *space) {
ICStub *stub = NULL;
if (calleeScript_) {
return ICCall_Scripted::New(space, getStubCode(), firstMonitorStub_, calleeScript_,
pcOffset_);
@ -4641,6 +4663,7 @@ class ICCall_Native : public ICMonitoredStub
{
friend class ICStubSpace;
protected:
HeapPtrFunction callee_;
uint32_t pcOffset_;

View File

@ -1282,8 +1282,7 @@ SetPropertyIC::attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj,
masm.loadPtr(Address(protoReg, JSObject::offsetOfType()), protoReg);
masm.loadPtr(Address(protoReg, offsetof(types::TypeObject, proto)), protoReg);
// ensure that the prototype is not NULL and that its shape matches
masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &protoFailures);
// Ensure that its shape matches.
masm.branchTestObjShape(Assembler::NotEqual, protoReg, protoShape, &protoFailures);
proto = proto->getProto();

View File

@ -1410,6 +1410,14 @@ IonFrameIterator::dump() const
fprintf(stderr, " Entry frame\n");
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
break;
case IonFrame_BaselineJS:
dumpBaseline();
break;
case IonFrame_BaselineStub:
case IonFrame_Unwound_BaselineStub:
fprintf(stderr, " Baseline stub frame\n");
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
break;
case IonFrame_OptimizedJS:
{
InlineFrameIterator frames(GetIonContext()->cx, this);

View File

@ -0,0 +1,42 @@
function A() {};
A.prototype = [];
function B() {};
B.prototype = new A();
function C() {};
C.prototype = new B();
function D() {};
D.prototype = new C();
function E() {};
E.prototype = new D();
function f() {
var o = new B();
for (var i=0; i<10; i++)
o[i] = i;
var expected = '{"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}';
assertEq(JSON.stringify(o), expected);
var o = new A();
for (var i=0; i<10; i++)
o[i] = i;
assertEq(JSON.stringify(o), expected);
var o = new D();
for (var i=0; i<10; i++)
o[i] = i;
assertEq(JSON.stringify(o), expected);
var o = new E();
for (var i=0; i<10; i++)
o[i] = i;
assertEq(JSON.stringify(o), expected);
}
f();

View File

@ -0,0 +1,14 @@
var gTestcases = new Array();
var gTc = gTestcases.length;
var setterCalled = false;
function TestCase() {
gTestcases[gTc++] = this;
}
for(var i = 0; i < 13; ++i) {
var testcase = new TestCase();
}
Array.prototype.__defineSetter__(32, function() { setterCalled = true; });
for(var i = 0; i < 20; ++i) {
var testcase = new TestCase();
}
assertEq(setterCalled, true);