mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1129382 - Add Ion ICs for scripted getters/setters. r=efaust,nbp,djvj
This commit is contained in:
parent
0fcdc6c58e
commit
c62c6dfd2a
59
js/src/jit-test/tests/ion/scripted-getter-setter.js
Normal file
59
js/src/jit-test/tests/ion/scripted-getter-setter.js
Normal file
@ -0,0 +1,59 @@
|
||||
if (getJitCompilerOptions()["ion.warmup.trigger"] > 50)
|
||||
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||
|
||||
function getObjects() {
|
||||
var objs = [];
|
||||
|
||||
// Own scripted getter/setter.
|
||||
objs.push({x: 0, get prop() {
|
||||
assertJitStackInvariants();
|
||||
return ++this.x;
|
||||
}, set prop(v) {
|
||||
assertJitStackInvariants();
|
||||
this.x += v;
|
||||
}});
|
||||
|
||||
// Scripted getter/setter on prototype. Also verify extra formal args are
|
||||
// handled correctly.
|
||||
function getter(a, b, c) {
|
||||
assertEq(arguments.length, 0);
|
||||
assertEq(a, undefined);
|
||||
assertEq(b, undefined);
|
||||
assertEq(c, undefined);
|
||||
assertJitStackInvariants();
|
||||
bailout();
|
||||
return ++this.y;
|
||||
}
|
||||
function setter1(a, b) {
|
||||
assertEq(arguments.length, 1);
|
||||
assertEq(b, undefined);
|
||||
assertJitStackInvariants();
|
||||
this.y = a;
|
||||
bailout();
|
||||
return "unused";
|
||||
}
|
||||
var proto = {};
|
||||
Object.defineProperty(proto, "prop", {get: getter, set: setter1});
|
||||
objs.push(Object.create(proto));
|
||||
|
||||
function setter2() {
|
||||
assertEq(arguments.length, 1);
|
||||
assertJitStackInvariants();
|
||||
this.y = arguments[0];
|
||||
}
|
||||
proto = {};
|
||||
Object.defineProperty(proto, "prop", {get: getter, set: setter2});
|
||||
objs.push(Object.create(proto));
|
||||
return objs;
|
||||
}
|
||||
function f() {
|
||||
var objs = getObjects();
|
||||
var res = 0;
|
||||
for (var i=0; i<200; i++) {
|
||||
var o = objs[i % objs.length];
|
||||
o.prop = 2;
|
||||
res += o.prop;
|
||||
}
|
||||
assertEq(res, 7233);
|
||||
}
|
||||
f();
|
32
js/src/jit-test/tests/profiler/getter-setter-ic.js
Normal file
32
js/src/jit-test/tests/profiler/getter-setter-ic.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Ensure readSPSProfilingStack() doesn't crash with Ion
|
||||
// getter/setter ICs on the stack.
|
||||
function getObjects() {
|
||||
var objs = [];
|
||||
objs.push({x: 0, get prop() {
|
||||
readSPSProfilingStack();
|
||||
return ++this.x;
|
||||
}, set prop(v) {
|
||||
readSPSProfilingStack();
|
||||
this.x = v + 2;
|
||||
}});
|
||||
objs.push({x: 0, y: 0, get prop() {
|
||||
readSPSProfilingStack();
|
||||
return this.y;
|
||||
}, set prop(v) {
|
||||
readSPSProfilingStack();
|
||||
this.y = v;
|
||||
}});
|
||||
return objs;
|
||||
}
|
||||
function f() {
|
||||
var objs = getObjects();
|
||||
var res = 0;
|
||||
for (var i=0; i<100; i++) {
|
||||
var o = objs[i % objs.length];
|
||||
res += o.prop;
|
||||
o.prop = i;
|
||||
}
|
||||
assertEq(res, 4901);
|
||||
}
|
||||
enableSPSProfiling();
|
||||
f();
|
@ -329,10 +329,11 @@ struct BaselineStackBuilder
|
||||
BufferPointer<JitFrameLayout> topFrame = topFrameAddress();
|
||||
FrameType type = topFrame->prevType();
|
||||
|
||||
// For IonJS and Entry frames, the "saved" frame pointer in the baseline
|
||||
// frame is meaningless, since Ion saves all registers before calling other ion
|
||||
// frames, and the entry frame saves all registers too.
|
||||
if (type == JitFrame_IonJS || type == JitFrame_Entry)
|
||||
// For IonJS, IonAccessorIC and Entry frames, the "saved" frame pointer
|
||||
// in the baseline frame is meaningless, since Ion saves all registers
|
||||
// before calling other ion frames, and the entry frame saves all
|
||||
// registers too.
|
||||
if (type == JitFrame_IonJS || type == JitFrame_Entry || type == JitFrame_IonAccessorIC)
|
||||
return nullptr;
|
||||
|
||||
// BaselineStub - Baseline calling into Ion.
|
||||
@ -1348,7 +1349,8 @@ jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIter
|
||||
MOZ_ASSERT(prevFrameType == JitFrame_IonJS ||
|
||||
prevFrameType == JitFrame_BaselineStub ||
|
||||
prevFrameType == JitFrame_Entry ||
|
||||
prevFrameType == JitFrame_Rectifier);
|
||||
prevFrameType == JitFrame_Rectifier ||
|
||||
prevFrameType == JitFrame_IonAccessorIC);
|
||||
|
||||
// All incoming frames are going to look like this:
|
||||
//
|
||||
|
@ -2507,10 +2507,14 @@ InvalidateActivation(FreeOp *fop, const JitActivationIterator &activations, bool
|
||||
case JitFrame_Unwound_IonJS:
|
||||
case JitFrame_Unwound_BaselineJS:
|
||||
case JitFrame_Unwound_BaselineStub:
|
||||
case JitFrame_Unwound_IonAccessorIC:
|
||||
MOZ_CRASH("invalid");
|
||||
case JitFrame_Unwound_Rectifier:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%d unwound rectifier frame @ %p", frameno, it.fp());
|
||||
break;
|
||||
case JitFrame_IonAccessorIC:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%d ion IC getter/setter frame @ %p", frameno, it.fp());
|
||||
break;
|
||||
case JitFrame_Entry:
|
||||
JitSpew(JitSpew_IonInvalidate, "#%d entry frame @ %p", frameno, it.fp());
|
||||
break;
|
||||
|
@ -652,6 +652,26 @@ IsCacheableGetPropCallNative(JSObject *obj, JSObject *holder, Shape *shape)
|
||||
return !obj->getClass()->ext.outerObject;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableGetPropCallScripted(JSObject *obj, JSObject *holder, Shape *shape)
|
||||
{
|
||||
if (!shape || !IsCacheableProtoChainForIon(obj, holder))
|
||||
return false;
|
||||
|
||||
if (!shape->hasGetterValue() || !shape->getterValue().isObject())
|
||||
return false;
|
||||
|
||||
if (!shape->getterValue().toObject().is<JSFunction>())
|
||||
return false;
|
||||
|
||||
JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
|
||||
if (!getter.hasJITCode())
|
||||
return false;
|
||||
|
||||
// See IsCacheableGetPropCallNative.
|
||||
return !obj->getClass()->ext.outerObject;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableGetPropCallPropertyOp(JSObject *obj, JSObject *holder, Shape *shape)
|
||||
{
|
||||
@ -920,20 +940,15 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
|
||||
// to try so hard to not use the stack. Scratch regs are just taken from the register
|
||||
// set not including the input, current value saved on the stack, and restored when
|
||||
// we're done with it.
|
||||
Register scratchReg = regSet.takeGeneral();
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argUintNReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
Register scratchReg = regSet.takeGeneral();
|
||||
|
||||
// Shape has a getter function.
|
||||
bool callNative = IsCacheableGetPropCallNative(obj, holder, shape);
|
||||
MOZ_ASSERT_IF(!callNative, IsCacheableGetPropCallPropertyOp(obj, holder, shape));
|
||||
// Shape has a JSNative, PropertyOp or scripted getter function.
|
||||
if (IsCacheableGetPropCallNative(obj, holder, shape)) {
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argUintNReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
|
||||
if (callNative) {
|
||||
MOZ_ASSERT(shape->hasGetterValue() && shape->getterValue().isObject() &&
|
||||
shape->getterValue().toObject().is<JSFunction>());
|
||||
JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
|
||||
|
||||
MOZ_ASSERT(target);
|
||||
MOZ_ASSERT(target->isNative());
|
||||
|
||||
@ -977,7 +992,10 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
|
||||
|
||||
// masm.leaveExitFrame & pop locals
|
||||
masm.adjustStack(IonOOLNativeExitFrameLayout::Size(0));
|
||||
} else {
|
||||
} else if (IsCacheableGetPropCallPropertyOp(obj, holder, shape)) {
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argUintNReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
Register argObjReg = argUintNReg;
|
||||
Register argIdReg = regSet.takeGeneral();
|
||||
|
||||
@ -1023,6 +1041,51 @@ EmitGetterCall(JSContext *cx, MacroAssembler &masm,
|
||||
|
||||
// masm.leaveExitFrame & pop locals.
|
||||
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
|
||||
} else {
|
||||
MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
|
||||
|
||||
JSFunction *target = &shape->getterValue().toObject().as<JSFunction>();
|
||||
uint32_t framePushedBefore = masm.framePushed();
|
||||
|
||||
// Construct IonAccessorICFrameLayout.
|
||||
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
|
||||
attacher.pushStubCodePointer(masm);
|
||||
masm.Push(Imm32(descriptor));
|
||||
masm.Push(ImmPtr(returnAddr));
|
||||
|
||||
// The JitFrameLayout pushed below will be aligned to JitStackAlignment,
|
||||
// so we just have to make sure the stack is aligned after we push the
|
||||
// |this| + argument Values.
|
||||
uint32_t argSize = (target->nargs() + 1) * sizeof(Value);
|
||||
uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
|
||||
MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
|
||||
MOZ_ASSERT(padding < JitStackAlignment);
|
||||
masm.reserveStack(padding);
|
||||
|
||||
for (size_t i = 0; i < target->nargs(); i++)
|
||||
masm.Push(UndefinedValue());
|
||||
masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
|
||||
|
||||
masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
|
||||
|
||||
descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
|
||||
masm.Push(Imm32(0)); // argc
|
||||
masm.Push(scratchReg);
|
||||
masm.Push(Imm32(descriptor));
|
||||
|
||||
// Check stack alignment. Add sizeof(uintptr_t) for the return address.
|
||||
MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
|
||||
|
||||
// The getter has JIT code now and we will only discard the getter's JIT
|
||||
// code when discarding all JIT code in the Zone, so we can assume it'll
|
||||
// still have JIT code.
|
||||
MOZ_ASSERT(target->hasJITCode());
|
||||
masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
|
||||
masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
|
||||
masm.callJit(scratchReg);
|
||||
masm.storeCallResultValue(output);
|
||||
|
||||
masm.freeStack(masm.framePushed() - framePushedBefore);
|
||||
}
|
||||
|
||||
masm.icRestoreLive(liveRegs, aic);
|
||||
@ -1240,12 +1303,13 @@ CanAttachNativeGetProp(typename GetPropCache::Context cx, const GetPropCache &ca
|
||||
//
|
||||
// Be careful when adding support for other getters here: for outer window
|
||||
// proxies, IonBuilder can innerize and pass us the inner window (the global),
|
||||
// see IonBuilder::getPropTryInnerize. This is fine for native getters because
|
||||
// IsCacheableGetPropCallNative checks they can handle both the inner and
|
||||
// outer object, but scripted getters would need a similar mechanism.
|
||||
// see IonBuilder::getPropTryInnerize. This is fine for native/scripted getters
|
||||
// because IsCacheableGetPropCallNative and IsCacheableGetPropCallScripted
|
||||
// handle this.
|
||||
if (cache.allowGetters() &&
|
||||
(IsCacheableGetPropCallNative(obj, holder, shape) ||
|
||||
IsCacheableGetPropCallPropertyOp(obj, holder, shape)))
|
||||
IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
|
||||
IsCacheableGetPropCallScripted(obj, holder, shape)))
|
||||
{
|
||||
// Don't enable getter call if cache is idempotent, since they can be
|
||||
// effectful. This is handled by allowGetters()
|
||||
@ -1995,6 +2059,19 @@ IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape
|
||||
shape->setterObject()->as<JSFunction>().isNative();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableSetPropCallScripted(HandleObject obj, HandleObject holder, HandleShape shape)
|
||||
{
|
||||
MOZ_ASSERT(obj->isNative());
|
||||
|
||||
if (!shape || !IsCacheableProtoChainForIon(obj, holder))
|
||||
return false;
|
||||
|
||||
return shape->hasSetterValue() && shape->setterObject() &&
|
||||
shape->setterObject()->is<JSFunction>() &&
|
||||
shape->setterObject()->as<JSFunction>().hasJITCode();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
|
||||
{
|
||||
@ -2238,14 +2315,12 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
||||
//
|
||||
// Be very careful not to use any of these before value is pushed, since they
|
||||
// might shadow.
|
||||
Register scratchReg = regSet.takeGeneral();
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
Register scratchReg = regSet.takeGeneral();
|
||||
|
||||
bool callNative = IsCacheableSetPropCallNative(obj, holder, shape);
|
||||
MOZ_ASSERT_IF(!callNative, IsCacheableSetPropCallPropertyOp(obj, holder, shape));
|
||||
if (IsCacheableSetPropCallNative(obj, holder, shape)) {
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
|
||||
if (callNative) {
|
||||
MOZ_ASSERT(shape->hasSetterValue() && shape->setterObject() &&
|
||||
shape->setterObject()->is<JSFunction>());
|
||||
JSFunction *target = &shape->setterObject()->as<JSFunction>();
|
||||
@ -2290,7 +2365,9 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
||||
|
||||
// masm.leaveExitFrame & pop locals.
|
||||
masm.adjustStack(IonOOLNativeExitFrameLayout::Size(1));
|
||||
} else {
|
||||
} else if (IsCacheableSetPropCallPropertyOp(obj, holder, shape)) {
|
||||
Register argJSContextReg = regSet.takeGeneral();
|
||||
Register argVpReg = regSet.takeGeneral();
|
||||
Register argObjReg = regSet.takeGeneral();
|
||||
Register argIdReg = regSet.takeGeneral();
|
||||
Register argStrictReg = regSet.takeGeneral();
|
||||
@ -2338,6 +2415,52 @@ GenerateCallSetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
|
||||
|
||||
// masm.leaveExitFrame & pop locals.
|
||||
masm.adjustStack(IonOOLPropertyOpExitFrameLayout::Size());
|
||||
} else {
|
||||
MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
|
||||
|
||||
JSFunction *target = &shape->setterValue().toObject().as<JSFunction>();
|
||||
uint32_t framePushedBefore = masm.framePushed();
|
||||
|
||||
// Construct IonAccessorICFrameLayout.
|
||||
uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_IonJS);
|
||||
attacher.pushStubCodePointer(masm);
|
||||
masm.Push(Imm32(descriptor));
|
||||
masm.Push(ImmPtr(returnAddr));
|
||||
|
||||
// The JitFrameLayout pushed below will be aligned to JitStackAlignment,
|
||||
// so we just have to make sure the stack is aligned after we push the
|
||||
// |this| + argument Values.
|
||||
uint32_t numArgs = Max(size_t(1), target->nargs());
|
||||
uint32_t argSize = (numArgs + 1) * sizeof(Value);
|
||||
uint32_t padding = ComputeByteAlignment(masm.framePushed() + argSize, JitStackAlignment);
|
||||
MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
|
||||
MOZ_ASSERT(padding < JitStackAlignment);
|
||||
masm.reserveStack(padding);
|
||||
|
||||
for (size_t i = 1; i < target->nargs(); i++)
|
||||
masm.Push(UndefinedValue());
|
||||
masm.Push(value);
|
||||
masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
|
||||
|
||||
masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
|
||||
|
||||
descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
|
||||
masm.Push(Imm32(1)); // argc
|
||||
masm.Push(scratchReg);
|
||||
masm.Push(Imm32(descriptor));
|
||||
|
||||
// Check stack alignment. Add sizeof(uintptr_t) for the return address.
|
||||
MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
|
||||
|
||||
// The setter has JIT code now and we will only discard the setter's JIT
|
||||
// code when discarding all JIT code in the Zone, so we can assume it'll
|
||||
// still have JIT code.
|
||||
MOZ_ASSERT(target->hasJITCode());
|
||||
masm.loadPtr(Address(scratchReg, JSFunction::offsetOfNativeOrScript()), scratchReg);
|
||||
masm.loadBaselineOrIonRaw(scratchReg, scratchReg, nullptr);
|
||||
masm.callJit(scratchReg);
|
||||
|
||||
masm.freeStack(masm.framePushed() - framePushedBefore);
|
||||
}
|
||||
|
||||
masm.icRestoreLive(liveRegs, aic);
|
||||
@ -2364,7 +2487,8 @@ IsCacheableDOMProxyUnshadowedSetterCall(JSContext *cx, HandleObject obj, HandleP
|
||||
return true;
|
||||
|
||||
if (!IsCacheableSetPropCallNative(checkObj, holder, shape) &&
|
||||
!IsCacheableSetPropCallPropertyOp(checkObj, holder, shape))
|
||||
!IsCacheableSetPropCallPropertyOp(checkObj, holder, shape) &&
|
||||
!IsCacheableSetPropCallScripted(checkObj, holder, shape))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -2729,7 +2853,8 @@ CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrR
|
||||
return SetPropertyIC::MaybeCanAttachAddSlot;
|
||||
|
||||
if (IsCacheableSetPropCallPropertyOp(obj, holder, shape) ||
|
||||
IsCacheableSetPropCallNative(obj, holder, shape))
|
||||
IsCacheableSetPropCallNative(obj, holder, shape) ||
|
||||
IsCacheableSetPropCallScripted(obj, holder, shape))
|
||||
{
|
||||
return SetPropertyIC::CanAttachCallSetter;
|
||||
}
|
||||
@ -4095,7 +4220,8 @@ IsCacheableNameCallGetter(HandleObject scopeChain, HandleObject obj, HandleObjec
|
||||
return false;
|
||||
|
||||
return IsCacheableGetPropCallNative(obj, holder, shape) ||
|
||||
IsCacheableGetPropCallPropertyOp(obj, holder, shape);
|
||||
IsCacheableGetPropCallPropertyOp(obj, holder, shape) ||
|
||||
IsCacheableGetPropCallScripted(obj, holder, shape);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -44,18 +44,17 @@ enum FrameType
|
||||
// mismatches in calls.
|
||||
JitFrame_Rectifier,
|
||||
|
||||
// Ion IC calling a scripted getter/setter.
|
||||
JitFrame_IonAccessorIC,
|
||||
|
||||
// An unwound JS frame is a JS frame signalling that its callee frame has been
|
||||
// turned into an exit frame (see EnsureExitFrame). Used by Ion bailouts and
|
||||
// Baseline exception unwinding.
|
||||
JitFrame_Unwound_BaselineJS,
|
||||
JitFrame_Unwound_IonJS,
|
||||
|
||||
// Like Unwound_IonJS, but the caller is a baseline stub frame.
|
||||
JitFrame_Unwound_BaselineStub,
|
||||
|
||||
// An unwound rectifier frame is a rectifier frame signalling that its callee
|
||||
// frame has been turned into an exit frame (see EnsureExitFrame).
|
||||
JitFrame_Unwound_Rectifier,
|
||||
JitFrame_Unwound_IonAccessorIC,
|
||||
|
||||
// An exit frame is necessary for transitioning from a JS frame into C++.
|
||||
// From within C++, an exit frame is always the last frame in any
|
||||
|
@ -249,6 +249,7 @@ SizeOfFramePrefix(FrameType type)
|
||||
case JitFrame_Unwound_IonJS:
|
||||
return JitFrameLayout::Size();
|
||||
case JitFrame_BaselineStub:
|
||||
case JitFrame_Unwound_BaselineStub:
|
||||
return BaselineStubFrameLayout::Size();
|
||||
case JitFrame_Rectifier:
|
||||
return RectifierFrameLayout::Size();
|
||||
@ -256,9 +257,12 @@ SizeOfFramePrefix(FrameType type)
|
||||
return IonUnwoundRectifierFrameLayout::Size();
|
||||
case JitFrame_Exit:
|
||||
return ExitFrameLayout::Size();
|
||||
default:
|
||||
MOZ_CRASH("unknown frame type");
|
||||
case JitFrame_IonAccessorIC:
|
||||
case JitFrame_Unwound_IonAccessorIC:
|
||||
return IonAccessorICFrameLayout::Size();
|
||||
}
|
||||
|
||||
MOZ_CRASH("unknown frame type");
|
||||
}
|
||||
|
||||
uint8_t *
|
||||
@ -302,6 +306,8 @@ JitFrameIterator::operator++()
|
||||
type_ = JitFrame_BaselineJS;
|
||||
else if (type_ == JitFrame_Unwound_BaselineStub)
|
||||
type_ = JitFrame_BaselineStub;
|
||||
else if (type_ == JitFrame_Unwound_IonAccessorIC)
|
||||
type_ = JitFrame_IonAccessorIC;
|
||||
returnAddressToFp_ = current()->returnAddress();
|
||||
current_ = prev;
|
||||
|
||||
@ -889,43 +895,51 @@ HandleException(ResumeFromException *rfe)
|
||||
void
|
||||
EnsureExitFrame(CommonFrameLayout *frame)
|
||||
{
|
||||
if (frame->prevType() == JitFrame_Unwound_IonJS ||
|
||||
frame->prevType() == JitFrame_Unwound_BaselineJS ||
|
||||
frame->prevType() == JitFrame_Unwound_BaselineStub ||
|
||||
frame->prevType() == JitFrame_Unwound_Rectifier)
|
||||
{
|
||||
switch (frame->prevType()) {
|
||||
case JitFrame_Unwound_IonJS:
|
||||
case JitFrame_Unwound_BaselineJS:
|
||||
case JitFrame_Unwound_BaselineStub:
|
||||
case JitFrame_Unwound_Rectifier:
|
||||
case JitFrame_Unwound_IonAccessorIC:
|
||||
// Already an exit frame, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->prevType() == JitFrame_Entry) {
|
||||
case JitFrame_Entry:
|
||||
// The previous frame type is the entry frame, so there's no actual
|
||||
// need for an exit frame.
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->prevType() == JitFrame_Rectifier) {
|
||||
case JitFrame_Rectifier:
|
||||
// The rectifier code uses the frame descriptor to discard its stack,
|
||||
// so modifying its descriptor size here would be dangerous. Instead,
|
||||
// we change the frame type, and teach the stack walking code how to
|
||||
// deal with this edge case. bug 717297 would obviate the need
|
||||
frame->changePrevType(JitFrame_Unwound_Rectifier);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame->prevType() == JitFrame_BaselineStub) {
|
||||
case JitFrame_BaselineStub:
|
||||
frame->changePrevType(JitFrame_Unwound_BaselineStub);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (frame->prevType() == JitFrame_BaselineJS) {
|
||||
case JitFrame_BaselineJS:
|
||||
frame->changePrevType(JitFrame_Unwound_BaselineJS);
|
||||
return;
|
||||
|
||||
case JitFrame_IonJS:
|
||||
frame->changePrevType(JitFrame_Unwound_IonJS);
|
||||
return;
|
||||
|
||||
case JitFrame_IonAccessorIC:
|
||||
frame->changePrevType(JitFrame_Unwound_IonAccessorIC);
|
||||
return;
|
||||
|
||||
case JitFrame_Exit:
|
||||
case JitFrame_Bailout:
|
||||
// Fall-through to MOZ_CRASH below.
|
||||
break;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(frame->prevType() == JitFrame_IonJS);
|
||||
frame->changePrevType(JitFrame_Unwound_IonJS);
|
||||
MOZ_CRASH("Unexpected frame type");
|
||||
}
|
||||
|
||||
CalleeToken
|
||||
@ -1169,6 +1183,14 @@ MarkBaselineStubFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MarkIonAccessorICFrame(JSTracer *trc, const JitFrameIterator &frame)
|
||||
{
|
||||
MOZ_ASSERT(frame.type() == JitFrame_IonAccessorIC);
|
||||
IonAccessorICFrameLayout *layout = (IonAccessorICFrameLayout *)frame.fp();
|
||||
gc::MarkJitCodeRoot(trc, layout->stubCode(), "ion-ic-accessor-code");
|
||||
}
|
||||
|
||||
void
|
||||
JitActivationIterator::jitStackRange(uintptr_t *&min, uintptr_t *&end)
|
||||
{
|
||||
@ -1441,12 +1463,17 @@ MarkJitActivation(JSTracer *trc, const JitActivationIterator &activations)
|
||||
break;
|
||||
case JitFrame_Unwound_IonJS:
|
||||
case JitFrame_Unwound_BaselineJS:
|
||||
case JitFrame_Unwound_BaselineStub:
|
||||
case JitFrame_Unwound_IonAccessorIC:
|
||||
MOZ_CRASH("invalid");
|
||||
case JitFrame_Rectifier:
|
||||
MarkRectifierFrame(trc, frames);
|
||||
break;
|
||||
case JitFrame_Unwound_Rectifier:
|
||||
break;
|
||||
case JitFrame_IonAccessorIC:
|
||||
MarkIonAccessorICFrame(trc, frames);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected frame type");
|
||||
}
|
||||
@ -2660,6 +2687,11 @@ JitFrameIterator::dump() const
|
||||
fprintf(stderr, " Rectifier frame\n");
|
||||
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
|
||||
break;
|
||||
case JitFrame_IonAccessorIC:
|
||||
case JitFrame_Unwound_IonAccessorIC:
|
||||
fprintf(stderr, " Ion scripted accessor IC\n");
|
||||
fprintf(stderr, " Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
|
||||
break;
|
||||
case JitFrame_Unwound_IonJS:
|
||||
case JitFrame_Unwound_BaselineJS:
|
||||
fprintf(stderr, "Warning! Unwound JS frames are not observable.\n");
|
||||
@ -3012,6 +3044,18 @@ JitProfilingFrameIterator::operator++()
|
||||
MOZ_CRASH("Bad frame type prior to rectifier frame.");
|
||||
}
|
||||
|
||||
if (prevType == JitFrame_IonAccessorIC || prevType == JitFrame_Unwound_IonAccessorIC) {
|
||||
IonAccessorICFrameLayout *accessorFrame =
|
||||
GetPreviousRawFrame<JitFrameLayout, IonAccessorICFrameLayout *>(frame);
|
||||
|
||||
MOZ_ASSERT(accessorFrame->prevType() == JitFrame_IonJS);
|
||||
|
||||
returnAddressToFp_ = accessorFrame->returnAddress();
|
||||
fp_ = GetPreviousRawFrame<IonAccessorICFrameLayout, uint8_t *>(accessorFrame);
|
||||
type_ = JitFrame_IonJS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevType == JitFrame_Entry) {
|
||||
// No previous frame, set to null to indicate that JitFrameIterator is done()
|
||||
returnAddressToFp_ = nullptr;
|
||||
|
@ -428,6 +428,21 @@ class RectifierFrameLayout : public JitFrameLayout
|
||||
}
|
||||
};
|
||||
|
||||
class IonAccessorICFrameLayout : public CommonFrameLayout
|
||||
{
|
||||
protected:
|
||||
// Pointer to root the stub's JitCode.
|
||||
JitCode *stubCode_;
|
||||
|
||||
public:
|
||||
JitCode **stubCode() {
|
||||
return &stubCode_;
|
||||
}
|
||||
static size_t Size() {
|
||||
return sizeof(IonAccessorICFrameLayout);
|
||||
}
|
||||
};
|
||||
|
||||
// The callee token is now dead.
|
||||
class IonUnwoundRectifierFrameLayout : public RectifierFrameLayout
|
||||
{
|
||||
|
@ -1130,6 +1130,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
Label handle_IonJS;
|
||||
Label handle_BaselineStub;
|
||||
Label handle_Rectifier;
|
||||
Label handle_IonAccessorIC;
|
||||
Label handle_Entry;
|
||||
Label end;
|
||||
|
||||
@ -1137,6 +1138,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
@ -1307,6 +1309,54 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
// JitFrame_IonAccessorIC
|
||||
//
|
||||
// The caller is always an IonJS frame.
|
||||
//
|
||||
// Ion-Descriptor
|
||||
// Ion-ReturnAddr
|
||||
// ... ion frame data ... |- AccFrame-Descriptor.Size
|
||||
// StubCode |
|
||||
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
|
||||
// AccFrame-ReturnAddr |
|
||||
// ... accessor frame data & args ... |- Descriptor.Size
|
||||
// ActualArgc |
|
||||
// CalleeToken |- JitFrameLayout::Size()
|
||||
// Descriptor |
|
||||
// FP -----> ReturnAddr |
|
||||
masm.bind(&handle_IonAccessorIC);
|
||||
{
|
||||
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
|
||||
masm.ma_add(StackPointer, scratch1, scratch2);
|
||||
masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
|
||||
|
||||
// scratch3 := AccFrame-Descriptor.Size
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
|
||||
#ifdef DEBUG
|
||||
// Assert previous frame is an IonJS frame.
|
||||
masm.movePtr(scratch3, scratch1);
|
||||
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
|
||||
{
|
||||
Label checkOk;
|
||||
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
|
||||
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
|
||||
masm.bind(&checkOk);
|
||||
}
|
||||
#endif
|
||||
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
|
||||
|
||||
// lastProfilingCallSite := AccFrame-ReturnAddr
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingCallSite);
|
||||
|
||||
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
|
||||
// IonAccessorICFrameLayout::Size()
|
||||
masm.ma_add(scratch2, scratch3, scratch1);
|
||||
masm.addPtr(Imm32(IonAccessorICFrameLayout::Size()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingFrame);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
//
|
||||
|
@ -1125,6 +1125,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
Label handle_IonJS;
|
||||
Label handle_BaselineStub;
|
||||
Label handle_Rectifier;
|
||||
Label handle_IonAccessorIC;
|
||||
Label handle_Entry;
|
||||
Label end;
|
||||
|
||||
@ -1132,6 +1133,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
@ -1302,6 +1304,54 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
// JitFrame_IonAccessorIC
|
||||
//
|
||||
// The caller is always an IonJS frame.
|
||||
//
|
||||
// Ion-Descriptor
|
||||
// Ion-ReturnAddr
|
||||
// ... ion frame data ... |- AccFrame-Descriptor.Size
|
||||
// StubCode |
|
||||
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
|
||||
// AccFrame-ReturnAddr |
|
||||
// ... accessor frame data & args ... |- Descriptor.Size
|
||||
// ActualArgc |
|
||||
// CalleeToken |- JitFrameLayout::Size()
|
||||
// Descriptor |
|
||||
// FP -----> ReturnAddr |
|
||||
masm.bind(&handle_IonAccessorIC);
|
||||
{
|
||||
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
|
||||
masm.ma_addu(StackPointer, scratch1, scratch2);
|
||||
masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
|
||||
|
||||
// scratch3 := AccFrame-Descriptor.Size
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
|
||||
#ifdef DEBUG
|
||||
// Assert previous frame is an IonJS frame.
|
||||
masm.movePtr(scratch3, scratch1);
|
||||
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
|
||||
{
|
||||
Label checkOk;
|
||||
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
|
||||
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
|
||||
masm.bind(&checkOk);
|
||||
}
|
||||
#endif
|
||||
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
|
||||
|
||||
// lastProfilingCallSite := AccFrame-ReturnAddr
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingCallSite);
|
||||
|
||||
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
|
||||
// IonAccessorICFrameLayout::Size()
|
||||
masm.ma_addu(scratch2, scratch3, scratch1);
|
||||
masm.addPtr(Imm32(IonAccessorICFrameLayout::Size()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingFrame);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
//
|
||||
|
@ -981,6 +981,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
Label handle_IonJS;
|
||||
Label handle_BaselineStub;
|
||||
Label handle_Rectifier;
|
||||
Label handle_IonAccessorIC;
|
||||
Label handle_Entry;
|
||||
Label end;
|
||||
|
||||
@ -988,6 +989,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
@ -1153,6 +1155,52 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
// JitFrame_IonAccessorIC
|
||||
//
|
||||
// The caller is always an IonJS frame.
|
||||
//
|
||||
// Ion-Descriptor
|
||||
// Ion-ReturnAddr
|
||||
// ... ion frame data ... |- AccFrame-Descriptor.Size
|
||||
// StubCode |
|
||||
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
|
||||
// AccFrame-ReturnAddr |
|
||||
// ... accessor frame data & args ... |- Descriptor.Size
|
||||
// ActualArgc |
|
||||
// CalleeToken |- JitFrameLayout::Size()
|
||||
// Descriptor |
|
||||
// FP -----> ReturnAddr |
|
||||
masm.bind(&handle_IonAccessorIC);
|
||||
{
|
||||
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
|
||||
masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2);
|
||||
|
||||
// scratch3 := AccFrame-Descriptor.Size
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
|
||||
#ifdef DEBUG
|
||||
// Assert previous frame is an IonJS frame.
|
||||
masm.movePtr(scratch3, scratch1);
|
||||
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
|
||||
{
|
||||
Label checkOk;
|
||||
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
|
||||
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
|
||||
masm.bind(&checkOk);
|
||||
}
|
||||
#endif
|
||||
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
|
||||
|
||||
// lastProfilingCallSite := AccFrame-ReturnAddr
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingCallSite);
|
||||
|
||||
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
|
||||
// IonAccessorICFrameLayout::Size()
|
||||
masm.lea(Operand(scratch2, scratch3, TimesOne, IonAccessorICFrameLayout::Size()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingFrame);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
//
|
||||
|
@ -1008,6 +1008,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
Label handle_IonJS;
|
||||
Label handle_BaselineStub;
|
||||
Label handle_Rectifier;
|
||||
Label handle_IonAccessorIC;
|
||||
Label handle_Entry;
|
||||
Label end;
|
||||
|
||||
@ -1015,6 +1016,7 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineJS), &handle_IonJS);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_BaselineStub), &handle_BaselineStub);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Rectifier), &handle_Rectifier);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_IonAccessorIC), &handle_IonAccessorIC);
|
||||
masm.branch32(Assembler::Equal, scratch2, Imm32(JitFrame_Entry), &handle_Entry);
|
||||
|
||||
masm.assumeUnreachable("Invalid caller frame type when exiting from Ion frame.");
|
||||
@ -1182,6 +1184,52 @@ JitRuntime::generateProfilerExitFrameTailStub(JSContext *cx)
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
// JitFrame_IonAccessorIC
|
||||
//
|
||||
// The caller is always an IonJS frame.
|
||||
//
|
||||
// Ion-Descriptor
|
||||
// Ion-ReturnAddr
|
||||
// ... ion frame data ... |- AccFrame-Descriptor.Size
|
||||
// StubCode |
|
||||
// AccFrame-Descriptor |- IonAccessorICFrameLayout::Size()
|
||||
// AccFrame-ReturnAddr |
|
||||
// ... accessor frame data & args ... |- Descriptor.Size
|
||||
// ActualArgc |
|
||||
// CalleeToken |- JitFrameLayout::Size()
|
||||
// Descriptor |
|
||||
// FP -----> ReturnAddr |
|
||||
masm.bind(&handle_IonAccessorIC);
|
||||
{
|
||||
// scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
|
||||
masm.lea(Operand(StackPointer, scratch1, TimesOne, JitFrameLayout::Size()), scratch2);
|
||||
|
||||
// scratch3 := AccFrame-Descriptor.Size
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
|
||||
#ifdef DEBUG
|
||||
// Assert previous frame is an IonJS frame.
|
||||
masm.movePtr(scratch3, scratch1);
|
||||
masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
|
||||
{
|
||||
Label checkOk;
|
||||
masm.branch32(Assembler::Equal, scratch1, Imm32(JitFrame_IonJS), &checkOk);
|
||||
masm.assumeUnreachable("IonAccessorIC frame must be preceded by IonJS frame");
|
||||
masm.bind(&checkOk);
|
||||
}
|
||||
#endif
|
||||
masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
|
||||
|
||||
// lastProfilingCallSite := AccFrame-ReturnAddr
|
||||
masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingCallSite);
|
||||
|
||||
// lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
|
||||
// IonAccessorICFrameLayout::Size()
|
||||
masm.lea(Operand(scratch2, scratch3, TimesOne, IonAccessorICFrameLayout::Size()), scratch1);
|
||||
masm.storePtr(scratch1, lastProfilingFrame);
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
//
|
||||
// JitFrame_Entry
|
||||
//
|
||||
|
21
js/src/tests/ecma_5/misc/getter-setter-outerize-this.js
Normal file
21
js/src/tests/ecma_5/misc/getter-setter-outerize-this.js
Normal file
@ -0,0 +1,21 @@
|
||||
if (typeof window === "undefined") {
|
||||
// This test is meant to run in the browser, but it's easy to
|
||||
// run it in the shell as well, even though it has no inner/outer
|
||||
// windows.
|
||||
window = this;
|
||||
}
|
||||
|
||||
var res = false;
|
||||
Object.defineProperty(this, "foo", {configurable: true,
|
||||
get: function() { return this === window; },
|
||||
set: function(v) { res = this === window; }});
|
||||
|
||||
(function() {
|
||||
for (var i = 0; i < 3000; ++i) {
|
||||
window.foo = i;
|
||||
assertEq(res, true, "setter");
|
||||
assertEq(window.foo, true, "getter");
|
||||
}
|
||||
})();
|
||||
|
||||
reportCompare(true, true);
|
Loading…
Reference in New Issue
Block a user