зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1375505 part 9 - Optimize for-in/JSOP_ITER with a Baseline/Ion IC. r=evilpie
This commit is contained in:
Родитель
f79c5fa84d
Коммит
2be2d91c31
|
@ -1837,6 +1837,54 @@ BaselineCacheIRCompiler::emitLoadStackValue()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitGuardAndGetIterator()
|
||||
{
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
AutoScratchRegister niScratch(allocator, masm);
|
||||
|
||||
Address iterAddr(stubAddress(reader.stubOffset()));
|
||||
Address enumeratorsAddr(stubAddress(reader.stubOffset()));
|
||||
|
||||
Register output = allocator.defineRegister(masm, reader.objOperandId());
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
// Load our PropertyIteratorObject* and its NativeIterator.
|
||||
masm.loadPtr(iterAddr, output);
|
||||
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niScratch);
|
||||
|
||||
// Ensure the |active| and |unreusable| bits are not set.
|
||||
masm.branchTest32(Assembler::NonZero, Address(niScratch, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), failure->label());
|
||||
|
||||
// Pre-write barrier for store to 'obj'.
|
||||
Address iterObjAddr(niScratch, offsetof(NativeIterator, obj));
|
||||
EmitPreBarrier(masm, iterObjAddr, MIRType::Object);
|
||||
|
||||
// Mark iterator as active.
|
||||
Address iterFlagsAddr(niScratch, offsetof(NativeIterator, flags));
|
||||
masm.storePtr(obj, iterObjAddr);
|
||||
masm.or32(Imm32(JSITER_ACTIVE), iterFlagsAddr);
|
||||
|
||||
// Post-write barrier for stores to 'obj'.
|
||||
emitPostBarrierSlot(output, TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), scratch1);
|
||||
|
||||
// Chain onto the active iterator stack. Note that Baseline CacheIR stub
|
||||
// code is shared across compartments within a Zone, so we can't bake in
|
||||
// compartment->enumerators here.
|
||||
masm.loadPtr(enumeratorsAddr, scratch1);
|
||||
masm.loadPtr(Address(scratch1, 0), scratch1);
|
||||
emitRegisterEnumerator(scratch1, niScratch, scratch2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitGuardDOMExpandoMissingOrGuardShape()
|
||||
{
|
||||
|
@ -1913,6 +1961,7 @@ BaselineCacheIRCompiler::init(CacheKind kind)
|
|||
switch (kind) {
|
||||
case CacheKind::GetProp:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::GetIterator:
|
||||
MOZ_ASSERT(numInputs == 1);
|
||||
allocator.initInputLocation(0, R0);
|
||||
break;
|
||||
|
@ -1992,6 +2041,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
|||
case CacheKind::HasOwn:
|
||||
case CacheKind::BindName:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::GetIterator:
|
||||
stubDataOffset = sizeof(ICCacheIR_Regular);
|
||||
stubKind = CacheIRStubKind::Regular;
|
||||
break;
|
||||
|
|
|
@ -4201,7 +4201,7 @@ BaselineCompiler::emit_JSOP_ITER()
|
|||
{
|
||||
frame.popRegsAndSync(1);
|
||||
|
||||
ICIteratorNew_Fallback::Compiler compiler(cx);
|
||||
ICGetIterator_Fallback::Compiler compiler(cx);
|
||||
if (!emitOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -4148,33 +4148,53 @@ ICTableSwitch::fixupJumpTable(JSScript* script, BaselineScript* baseline)
|
|||
}
|
||||
|
||||
//
|
||||
// IteratorNew_Fallback
|
||||
// GetIterator_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoIteratorNewFallback(JSContext* cx, BaselineFrame* frame, ICIteratorNew_Fallback* stub,
|
||||
DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, ICGetIterator_Fallback* stub,
|
||||
HandleValue value, MutableHandleValue res)
|
||||
{
|
||||
jsbytecode* pc = stub->icEntry()->pc(frame->script());
|
||||
FallbackICSpew(cx, stub, "IteratorNew");
|
||||
FallbackICSpew(cx, stub, "GetIterator");
|
||||
|
||||
if (stub->state().maybeTransition())
|
||||
stub->discardStubs(cx);
|
||||
|
||||
if (stub->state().canAttachStub()) {
|
||||
RootedScript script(cx, frame->script());
|
||||
jsbytecode* pc = stub->icEntry()->pc(script);
|
||||
|
||||
ICStubEngine engine = ICStubEngine::Baseline;
|
||||
GetIteratorIRGenerator gen(cx, script, pc, stub->state().mode(), value);
|
||||
bool attached = false;
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
engine, script, stub, &attached);
|
||||
if (newStub)
|
||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||
}
|
||||
if (!attached)
|
||||
stub->state().trackNotAttached();
|
||||
}
|
||||
|
||||
uint8_t flags = GET_UINT8(pc);
|
||||
res.set(value);
|
||||
RootedObject iterobj(cx, ValueToIterator(cx, flags, res));
|
||||
JSObject* iterobj = ValueToIterator(cx, flags, value);
|
||||
if (!iterobj)
|
||||
return false;
|
||||
|
||||
res.setObject(*iterobj);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoIteratorNewFallbackFn)(JSContext*, BaselineFrame*, ICIteratorNew_Fallback*,
|
||||
typedef bool (*DoGetIteratorFallbackFn)(JSContext*, BaselineFrame*, ICGetIterator_Fallback*,
|
||||
HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoIteratorNewFallbackInfo =
|
||||
FunctionInfo<DoIteratorNewFallbackFn>(DoIteratorNewFallback, "DoIteratorNewFallback",
|
||||
static const VMFunction DoGetIteratorFallbackInfo =
|
||||
FunctionInfo<DoGetIteratorFallbackFn>(DoGetIteratorFallback, "DoGetIteratorFallback",
|
||||
TailCall, PopValues(1));
|
||||
|
||||
bool
|
||||
ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
ICGetIterator_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
|
||||
|
@ -4187,7 +4207,7 @@ ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
masm.push(ICStubReg);
|
||||
pushStubPayload(masm, R0.scratchReg());
|
||||
|
||||
return tailCallVM(DoIteratorNewFallbackInfo, masm);
|
||||
return tailCallVM(DoGetIteratorFallbackInfo, masm);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -1359,12 +1359,12 @@ class ICTableSwitch : public ICStub
|
|||
};
|
||||
|
||||
// IC for constructing an iterator from an input value.
|
||||
class ICIteratorNew_Fallback : public ICFallbackStub
|
||||
class ICGetIterator_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICIteratorNew_Fallback(JitCode* stubCode)
|
||||
: ICFallbackStub(ICStub::IteratorNew_Fallback, stubCode)
|
||||
explicit ICGetIterator_Fallback(JitCode* stubCode)
|
||||
: ICFallbackStub(ICStub::GetIterator_Fallback, stubCode)
|
||||
{ }
|
||||
|
||||
public:
|
||||
|
@ -1374,11 +1374,11 @@ class ICIteratorNew_Fallback : public ICFallbackStub
|
|||
|
||||
public:
|
||||
explicit Compiler(JSContext* cx)
|
||||
: ICStubCompiler(cx, ICStub::IteratorNew_Fallback, Engine::Baseline)
|
||||
: ICStubCompiler(cx, ICStub::GetIterator_Fallback, Engine::Baseline)
|
||||
{ }
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) {
|
||||
return newStub<ICIteratorNew_Fallback>(space, getStubCode());
|
||||
return newStub<ICGetIterator_Fallback>(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace jit {
|
|||
\
|
||||
_(TableSwitch) \
|
||||
\
|
||||
_(IteratorNew_Fallback) \
|
||||
_(GetIterator_Fallback) \
|
||||
_(IteratorMore_Fallback) \
|
||||
_(IteratorMore_Native) \
|
||||
_(IteratorClose_Fallback) \
|
||||
|
|
|
@ -3571,6 +3571,69 @@ TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
|
|||
return true;
|
||||
}
|
||||
|
||||
GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
ICState::Mode mode, HandleValue value)
|
||||
: IRGenerator(cx, script, pc, CacheKind::GetIterator, mode),
|
||||
val_(value)
|
||||
{ }
|
||||
|
||||
bool
|
||||
GetIteratorIRGenerator::tryAttachStub()
|
||||
{
|
||||
MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
|
||||
|
||||
AutoAssertNoPendingException aanpe(cx_);
|
||||
|
||||
if (mode_ == ICState::Mode::Megamorphic)
|
||||
return false;
|
||||
|
||||
ValOperandId valId(writer.setInputOperandId(0));
|
||||
if (!val_.isObject())
|
||||
return false;
|
||||
|
||||
RootedObject obj(cx_, &val_.toObject());
|
||||
|
||||
ObjOperandId objId = writer.guardIsObject(valId);
|
||||
if (tryAttachNativeIterator(objId, obj))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject obj)
|
||||
{
|
||||
MOZ_ASSERT(JSOp(*pc_) == JSOP_ITER);
|
||||
|
||||
if (GET_UINT8(pc_) != JSITER_ENUMERATE)
|
||||
return false;
|
||||
|
||||
PropertyIteratorObject* iterobj = LookupInIteratorCache(cx_, obj);
|
||||
if (!iterobj)
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
|
||||
|
||||
// Guard on the receiver's shape/group.
|
||||
Maybe<ObjOperandId> expandoId;
|
||||
TestMatchingReceiver(writer, obj, objId, &expandoId);
|
||||
|
||||
// Ensure the receiver or its expando object has no dense elements.
|
||||
if (obj->isNative())
|
||||
writer.guardNoDenseElements(objId);
|
||||
else if (expandoId)
|
||||
writer.guardNoDenseElements(*expandoId);
|
||||
|
||||
// Do the same for the objects on the proto chain.
|
||||
GeneratePrototypeHoleGuards(writer, obj, objId);
|
||||
|
||||
ObjOperandId iterId =
|
||||
writer.guardAndGetIterator(objId, iterobj, &cx_->compartment()->enumerators);
|
||||
writer.loadObjectResult(iterId);
|
||||
writer.returnFromIC();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||
ICState::Mode mode, uint32_t argc,
|
||||
|
|
|
@ -145,6 +145,7 @@ class TypedOperandId : public OperandId
|
|||
_(In) \
|
||||
_(HasOwn) \
|
||||
_(TypeOf) \
|
||||
_(GetIterator) \
|
||||
_(Compare) \
|
||||
_(Call)
|
||||
|
||||
|
@ -184,6 +185,7 @@ extern const char* CacheKindNames[];
|
|||
_(GuardNoUnboxedExpando) \
|
||||
_(GuardAndLoadUnboxedExpando) \
|
||||
_(GuardAndGetIndexFromString) \
|
||||
_(GuardAndGetIterator) \
|
||||
_(GuardHasGetterSetter) \
|
||||
_(GuardGroupHasUnanalyzedNewScript) \
|
||||
_(LoadStackValue) \
|
||||
|
@ -583,6 +585,16 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
|||
writeOperandId(res);
|
||||
return res;
|
||||
}
|
||||
ObjOperandId guardAndGetIterator(ObjOperandId obj, PropertyIteratorObject* iter,
|
||||
NativeIterator** enumeratorsAddr)
|
||||
{
|
||||
ObjOperandId res(nextOperandId_++);
|
||||
writeOpWithOperandId(CacheOp::GuardAndGetIterator, obj);
|
||||
addStubField(uintptr_t(iter), StubField::Type::JSObject);
|
||||
addStubField(uintptr_t(enumeratorsAddr), StubField::Type::RawWord);
|
||||
writeOperandId(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void guardHasGetterSetter(ObjOperandId obj, Shape* shape) {
|
||||
writeOpWithOperandId(CacheOp::GuardHasGetterSetter, obj);
|
||||
|
@ -1408,6 +1420,19 @@ class MOZ_RAII TypeOfIRGenerator : public IRGenerator
|
|||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
class MOZ_RAII GetIteratorIRGenerator : public IRGenerator
|
||||
{
|
||||
HandleValue val_;
|
||||
|
||||
bool tryAttachNativeIterator(ObjOperandId objId, HandleObject obj);
|
||||
|
||||
public:
|
||||
GetIteratorIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
|
||||
HandleValue value);
|
||||
|
||||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
class MOZ_RAII CallIRGenerator : public IRGenerator
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -2330,6 +2330,23 @@ CacheIRCompiler::emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTy
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
CacheIRCompiler::emitRegisterEnumerator(Register enumeratorsList, Register iter, Register scratch)
|
||||
{
|
||||
// iter->next = list
|
||||
masm.storePtr(enumeratorsList, Address(iter, NativeIterator::offsetOfNext()));
|
||||
|
||||
// iter->prev = list->prev
|
||||
masm.loadPtr(Address(enumeratorsList, NativeIterator::offsetOfPrev()), scratch);
|
||||
masm.storePtr(scratch, Address(iter, NativeIterator::offsetOfPrev()));
|
||||
|
||||
// list->prev->next = iter
|
||||
masm.storePtr(iter, Address(scratch, NativeIterator::offsetOfNext()));
|
||||
|
||||
// list->prev = ni
|
||||
masm.storePtr(iter, Address(enumeratorsList, NativeIterator::offsetOfPrev()));
|
||||
}
|
||||
|
||||
void
|
||||
CacheIRCompiler::emitPostBarrierShared(Register obj, const ConstantOrRegister& val,
|
||||
Register scratch, Register maybeIndex)
|
||||
|
|
|
@ -597,6 +597,8 @@ class MOZ_RAII CacheIRCompiler
|
|||
void emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
|
||||
const Address& dest, Register scratch);
|
||||
|
||||
void emitRegisterEnumerator(Register enumeratorsList, Register iter, Register scratch);
|
||||
|
||||
private:
|
||||
void emitPostBarrierShared(Register obj, const ConstantOrRegister& val, Register scratch,
|
||||
Register maybeIndex);
|
||||
|
|
|
@ -160,6 +160,10 @@ typedef JSObject* (*IonBindNameICFn)(JSContext*, HandleScript, IonBindNameIC*, H
|
|||
static const VMFunction IonBindNameICInfo =
|
||||
FunctionInfo<IonBindNameICFn>(IonBindNameIC::update, "IonBindNameIC::update");
|
||||
|
||||
typedef JSObject* (*IonGetIteratorICFn)(JSContext*, HandleScript, IonGetIteratorIC*, HandleValue);
|
||||
static const VMFunction IonGetIteratorICInfo =
|
||||
FunctionInfo<IonGetIteratorICFn>(IonGetIteratorIC::update, "IonGetIteratorIC::update");
|
||||
|
||||
typedef bool (*IonInICFn)(JSContext*, HandleScript, IonInIC*, HandleValue, HandleObject, bool*);
|
||||
static const VMFunction IonInICInfo =
|
||||
FunctionInfo<IonInICFn>(IonInIC::update, "IonInIC::update");
|
||||
|
@ -249,6 +253,23 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
|||
masm.jump(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
case CacheKind::GetIterator: {
|
||||
IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
|
||||
|
||||
saveLive(lir);
|
||||
|
||||
pushArg(getIteratorIC->value());
|
||||
icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
|
||||
pushArg(ImmGCPtr(gen->info().script()));
|
||||
|
||||
callVM(IonGetIteratorICInfo, lir);
|
||||
|
||||
StoreRegisterTo(getIteratorIC->output()).generate(this);
|
||||
restoreLiveIgnore(lir, StoreRegisterTo(getIteratorIC->output()).clobbered());
|
||||
|
||||
masm.jump(ool->rejoin());
|
||||
return;
|
||||
}
|
||||
case CacheKind::In: {
|
||||
IonInIC* inIC = ic->asInIC();
|
||||
|
||||
|
@ -9244,163 +9265,18 @@ CodeGenerator::visitArrayJoin(LArrayJoin* lir)
|
|||
callVM(ArrayJoinInfo, lir);
|
||||
}
|
||||
|
||||
typedef JSObject* (*ValueToIteratorFn)(JSContext*, uint32_t, HandleValue);
|
||||
static const VMFunction ValueToIteratorInfo =
|
||||
FunctionInfo<ValueToIteratorFn>(ValueToIterator, "ValueToIterator");
|
||||
|
||||
void
|
||||
CodeGenerator::visitCallIteratorStartV(LCallIteratorStartV* lir)
|
||||
CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir)
|
||||
{
|
||||
pushArg(ToValue(lir, LCallIteratorStartV::Value));
|
||||
pushArg(Imm32(lir->mir()->flags()));
|
||||
callVM(ValueToIteratorInfo, lir);
|
||||
}
|
||||
LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
|
||||
TypedOrValueRegister val =
|
||||
toConstantOrRegister(lir, LGetIteratorCache::Value, lir->mir()->value()->type()).reg();
|
||||
Register output = ToRegister(lir->output());
|
||||
Register temp1 = ToRegister(lir->temp1());
|
||||
Register temp2 = ToRegister(lir->temp2());
|
||||
|
||||
typedef JSObject* (*GetIteratorFn)(JSContext*, HandleObject, uint32_t);
|
||||
static const VMFunction GetIteratorInfo =
|
||||
FunctionInfo<GetIteratorFn>(GetIterator, "GetIterator");
|
||||
|
||||
void
|
||||
CodeGenerator::visitCallIteratorStartO(LCallIteratorStartO* lir)
|
||||
{
|
||||
pushArg(Imm32(lir->mir()->flags()));
|
||||
pushArg(ToRegister(lir->object()));
|
||||
callVM(GetIteratorInfo, lir);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::branchIfNotEmptyObjectElements(Register obj, Label* target)
|
||||
{
|
||||
Label emptyObj;
|
||||
masm.branchPtr(Assembler::Equal,
|
||||
Address(obj, NativeObject::offsetOfElements()),
|
||||
ImmPtr(js::emptyObjectElements),
|
||||
&emptyObj);
|
||||
masm.branchPtr(Assembler::NotEqual,
|
||||
Address(obj, NativeObject::offsetOfElements()),
|
||||
ImmPtr(js::emptyObjectElementsShared),
|
||||
target);
|
||||
masm.bind(&emptyObj);
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
|
||||
{
|
||||
const Register obj = ToRegister(lir->object());
|
||||
const Register output = ToRegister(lir->output());
|
||||
|
||||
uint32_t flags = lir->mir()->flags();
|
||||
|
||||
OutOfLineCode* ool = oolCallVM(GetIteratorInfo, lir,
|
||||
ArgList(obj, Imm32(flags)), StoreRegisterTo(output));
|
||||
|
||||
const Register temp1 = ToRegister(lir->temp1());
|
||||
const Register temp2 = ToRegister(lir->temp2());
|
||||
const Register niTemp = ToRegister(lir->temp3()); // Holds the NativeIterator object.
|
||||
|
||||
// Iterators other than for-in should use LCallIteratorStart.
|
||||
MOZ_ASSERT(flags == JSITER_ENUMERATE);
|
||||
|
||||
// Fetch the most recent iterator and ensure it's not nullptr.
|
||||
masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfLastCachedNativeIterator()), output);
|
||||
masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
|
||||
|
||||
// Load NativeIterator.
|
||||
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niTemp);
|
||||
|
||||
// Ensure the |active| and |unreusable| bits are not set.
|
||||
masm.branchTest32(Assembler::NonZero, Address(niTemp, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), ool->entry());
|
||||
|
||||
// Load the iterator's receiver guard array.
|
||||
masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2);
|
||||
|
||||
// Compare object with the first receiver guard. The last iterator can only
|
||||
// match for native objects and unboxed objects.
|
||||
{
|
||||
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
|
||||
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
|
||||
Label guardDone, unboxedObject, noExpando;
|
||||
// This is a guard for an unboxed object.
|
||||
masm.branchPtr(Assembler::NotEqual, groupAddr, ImmWord(0), &unboxedObject);
|
||||
|
||||
// Guard for a normal object, make sure the shape matches.
|
||||
masm.loadObjShape(obj, temp1);
|
||||
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
|
||||
|
||||
// Ensure the object does not have any elements. The presence of dense
|
||||
// elements is not captured by the shape tests above.
|
||||
branchIfNotEmptyObjectElements(obj, ool->entry());
|
||||
masm.jump(&guardDone);
|
||||
|
||||
masm.bind(&unboxedObject);
|
||||
masm.loadObjGroup(obj, temp1);
|
||||
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
|
||||
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1);
|
||||
masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando);
|
||||
branchIfNotEmptyObjectElements(temp1, ool->entry());
|
||||
masm.loadObjShape(temp1, temp1);
|
||||
masm.bind(&noExpando);
|
||||
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
|
||||
masm.bind(&guardDone);
|
||||
}
|
||||
|
||||
// Compare shape of object's prototype with the second shape. The prototype
|
||||
// must be native, as unboxed objects cannot be prototypes (they cannot
|
||||
// have the delegate flag set). Also check for the absence of dense elements.
|
||||
Address prototypeShapeAddr(temp2, sizeof(ReceiverGuard) + offsetof(ReceiverGuard, shape));
|
||||
masm.loadObjProto(obj, temp1);
|
||||
branchIfNotEmptyObjectElements(temp1, ool->entry());
|
||||
masm.loadObjShape(temp1, temp1);
|
||||
masm.branchPtr(Assembler::NotEqual, prototypeShapeAddr, temp1, ool->entry());
|
||||
|
||||
// Ensure the object's prototype's prototype is nullptr. The last native
|
||||
// iterator will always have a prototype chain length of one (i.e. it must
|
||||
// be a plain object), so we do not need to generate a loop here.
|
||||
masm.loadObjProto(obj, temp1);
|
||||
masm.loadObjProto(temp1, temp1);
|
||||
masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
|
||||
|
||||
// Pre-write barrier for store to 'obj'.
|
||||
masm.guardedCallPreBarrier(Address(niTemp, offsetof(NativeIterator, obj)), MIRType::Object);
|
||||
|
||||
// Mark iterator as active.
|
||||
masm.storePtr(obj, Address(niTemp, offsetof(NativeIterator, obj)));
|
||||
masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags)));
|
||||
|
||||
// Post-write barrier for stores to 'obj'. The iterator JSObject is never
|
||||
// nursery allocated. Put this in the whole cell buffer is we wrote a
|
||||
// nursery pointer into it.
|
||||
{
|
||||
Label skipBarrier;
|
||||
masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp1, &skipBarrier);
|
||||
|
||||
LiveRegisterSet temps;
|
||||
temps.add(temp1);
|
||||
temps.add(temp2);
|
||||
saveVolatile(temps);
|
||||
emitPostWriteBarrier(output);
|
||||
restoreVolatile(temps);
|
||||
masm.bind(&skipBarrier);
|
||||
}
|
||||
|
||||
// Chain onto the active iterator stack.
|
||||
masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfEnumerators()), temp1);
|
||||
|
||||
// ni->next = list
|
||||
masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext()));
|
||||
|
||||
// ni->prev = list->prev
|
||||
masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2);
|
||||
masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev()));
|
||||
|
||||
// list->prev->next = ni
|
||||
masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext()));
|
||||
|
||||
// list->prev = ni
|
||||
masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev()));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
IonGetIteratorIC ic(liveRegs, val, output, temp1, temp2);
|
||||
addIC(lir, allocateIC(ic));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -336,9 +336,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
void visitClampIToUint8(LClampIToUint8* lir);
|
||||
void visitClampDToUint8(LClampDToUint8* lir);
|
||||
void visitClampVToUint8(LClampVToUint8* lir);
|
||||
void visitCallIteratorStartV(LCallIteratorStartV* lir);
|
||||
void visitCallIteratorStartO(LCallIteratorStartO* lir);
|
||||
void visitIteratorStartO(LIteratorStartO* lir);
|
||||
void visitGetIteratorCache(LGetIteratorCache* lir);
|
||||
void visitIteratorMore(LIteratorMore* lir);
|
||||
void visitIsNoIterAndBranch(LIsNoIterAndBranch* lir);
|
||||
void visitIteratorEnd(LIteratorEnd* lir);
|
||||
|
@ -537,10 +535,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
|||
Label* ifDoesntEmulateUndefined,
|
||||
Register scratch, OutOfLineTestObject* ool);
|
||||
|
||||
// Branch to target unless obj has an emptyObjectElements or emptyObjectElementsShared
|
||||
// elements pointer.
|
||||
void branchIfNotEmptyObjectElements(Register obj, Label* target);
|
||||
|
||||
void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
|
||||
Register elements, const LAllocation* index,
|
||||
int32_t offsetAdjustment);
|
||||
|
|
|
@ -223,18 +223,6 @@ CompileCompartment::runtime()
|
|||
return CompileRuntime::get(compartment()->runtimeFromAnyThread());
|
||||
}
|
||||
|
||||
const void*
|
||||
CompileCompartment::addressOfEnumerators()
|
||||
{
|
||||
return &compartment()->enumerators;
|
||||
}
|
||||
|
||||
const void*
|
||||
CompileCompartment::addressOfLastCachedNativeIterator()
|
||||
{
|
||||
return &compartment()->lastCachedNativeIterator;
|
||||
}
|
||||
|
||||
const void*
|
||||
CompileCompartment::addressOfRandomNumberGenerator()
|
||||
{
|
||||
|
|
|
@ -94,9 +94,7 @@ class CompileCompartment
|
|||
CompileZone* zone();
|
||||
CompileRuntime* runtime();
|
||||
|
||||
const void* addressOfEnumerators();
|
||||
const void* addressOfRandomNumberGenerator();
|
||||
const void* addressOfLastCachedNativeIterator();
|
||||
|
||||
const JitCompartment* jitCompartment();
|
||||
|
||||
|
|
|
@ -12497,7 +12497,7 @@ IonBuilder::jsop_iter(uint8_t flags)
|
|||
nonStringIteration_ = true;
|
||||
|
||||
MDefinition* obj = current->pop();
|
||||
MInstruction* ins = MIteratorStart::New(alloc(), obj, flags);
|
||||
MInstruction* ins = MGetIteratorCache::New(alloc(), obj);
|
||||
|
||||
if (!outermostBuilder()->iterators_.append(ins))
|
||||
return abort(AbortReason::Alloc);
|
||||
|
|
|
@ -440,6 +440,21 @@ IonCacheIRCompiler::init()
|
|||
allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
|
||||
break;
|
||||
}
|
||||
case CacheKind::GetIterator: {
|
||||
IonGetIteratorIC* ic = ic_->asGetIteratorIC();
|
||||
Register output = ic->output();
|
||||
|
||||
available.add(output);
|
||||
available.add(ic->temp1());
|
||||
available.add(ic->temp2());
|
||||
|
||||
liveRegs_.emplace(ic->liveRegs());
|
||||
outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Object, AnyRegister(output)));
|
||||
|
||||
MOZ_ASSERT(numInputs == 1);
|
||||
allocator.initInputLocation(0, ic->value());
|
||||
break;
|
||||
}
|
||||
case CacheKind::In: {
|
||||
IonInIC* ic = ic_->asInIC();
|
||||
Register output = ic->output();
|
||||
|
@ -2006,6 +2021,52 @@ IonCacheIRCompiler::emitLoadStackValue()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitGuardAndGetIterator()
|
||||
{
|
||||
Register obj = allocator.useRegister(masm, reader.objOperandId());
|
||||
|
||||
AutoScratchRegister scratch1(allocator, masm);
|
||||
AutoScratchRegister scratch2(allocator, masm);
|
||||
AutoScratchRegister niScratch(allocator, masm);
|
||||
|
||||
PropertyIteratorObject* iterobj =
|
||||
&objectStubField(reader.stubOffset())->as<PropertyIteratorObject>();
|
||||
NativeIterator** enumerators = rawWordStubField<NativeIterator**>(reader.stubOffset());
|
||||
|
||||
Register output = allocator.defineRegister(masm, reader.objOperandId());
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
// Load our PropertyIteratorObject* and its NativeIterator.
|
||||
masm.movePtr(ImmGCPtr(iterobj), output);
|
||||
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niScratch);
|
||||
|
||||
// Ensure the |active| and |unreusable| bits are not set.
|
||||
masm.branchTest32(Assembler::NonZero, Address(niScratch, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), failure->label());
|
||||
|
||||
// Pre-write barrier for store to 'obj'.
|
||||
Address iterObjAddr(niScratch, offsetof(NativeIterator, obj));
|
||||
EmitPreBarrier(masm, iterObjAddr, MIRType::Object);
|
||||
|
||||
// Mark iterator as active.
|
||||
Address iterFlagsAddr(niScratch, offsetof(NativeIterator, flags));
|
||||
masm.storePtr(obj, iterObjAddr);
|
||||
masm.or32(Imm32(JSITER_ACTIVE), iterFlagsAddr);
|
||||
|
||||
// Post-write barrier for stores to 'obj'.
|
||||
emitPostBarrierSlot(output, TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), scratch1);
|
||||
|
||||
// Chain onto the active iterator stack.
|
||||
masm.loadPtr(AbsoluteAddress(enumerators), scratch1);
|
||||
emitRegisterEnumerator(scratch1, niScratch, scratch2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitGuardDOMExpandoMissingOrGuardShape()
|
||||
{
|
||||
|
|
|
@ -52,6 +52,8 @@ IonIC::scratchRegisterForEntryJump()
|
|||
return asInIC()->temp();
|
||||
case CacheKind::HasOwn:
|
||||
return asHasOwnIC()->output();
|
||||
case CacheKind::GetIterator:
|
||||
return asGetIteratorIC()->temp1();
|
||||
case CacheKind::Call:
|
||||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
|
@ -346,6 +348,31 @@ IonBindNameIC::update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic
|
|||
return holder;
|
||||
}
|
||||
|
||||
/* static */ JSObject*
|
||||
IonGetIteratorIC::update(JSContext* cx, HandleScript outerScript, IonGetIteratorIC* ic,
|
||||
HandleValue value)
|
||||
{
|
||||
IonScript* ionScript = outerScript->ionScript();
|
||||
jsbytecode* pc = ic->pc();
|
||||
|
||||
if (ic->state().maybeTransition())
|
||||
ic->discardStubs(cx->zone());
|
||||
|
||||
if (ic->state().canAttachStub()) {
|
||||
bool attached = false;
|
||||
RootedScript script(cx, ic->script());
|
||||
GetIteratorIRGenerator gen(cx, script, pc, ic->state().mode(), value);
|
||||
if (gen.tryAttachStub())
|
||||
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
|
||||
|
||||
if (!attached)
|
||||
ic->state().trackNotAttached();
|
||||
}
|
||||
|
||||
uint8_t flags = GET_UINT8(pc);
|
||||
return ValueToIterator(cx, flags, value);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
|
||||
HandleValue val, HandleValue idVal, int32_t* res)
|
||||
|
|
|
@ -60,6 +60,7 @@ class IonGetPropertyIC;
|
|||
class IonSetPropertyIC;
|
||||
class IonGetNameIC;
|
||||
class IonBindNameIC;
|
||||
class IonGetIteratorIC;
|
||||
class IonHasOwnIC;
|
||||
class IonInIC;
|
||||
|
||||
|
@ -149,6 +150,10 @@ class IonIC
|
|||
MOZ_ASSERT(kind_ == CacheKind::BindName);
|
||||
return (IonBindNameIC*)this;
|
||||
}
|
||||
IonGetIteratorIC* asGetIteratorIC() {
|
||||
MOZ_ASSERT(kind_ == CacheKind::GetIterator);
|
||||
return (IonGetIteratorIC*)this;
|
||||
}
|
||||
IonHasOwnIC* asHasOwnIC() {
|
||||
MOZ_ASSERT(kind_ == CacheKind::HasOwn);
|
||||
return (IonHasOwnIC*)this;
|
||||
|
@ -314,6 +319,35 @@ class IonBindNameIC : public IonIC
|
|||
HandleObject envChain);
|
||||
};
|
||||
|
||||
class IonGetIteratorIC : public IonIC
|
||||
{
|
||||
LiveRegisterSet liveRegs_;
|
||||
TypedOrValueRegister value_;
|
||||
Register output_;
|
||||
Register temp1_;
|
||||
Register temp2_;
|
||||
|
||||
public:
|
||||
IonGetIteratorIC(LiveRegisterSet liveRegs, TypedOrValueRegister value, Register output,
|
||||
Register temp1, Register temp2)
|
||||
: IonIC(CacheKind::GetIterator),
|
||||
liveRegs_(liveRegs),
|
||||
value_(value),
|
||||
output_(output),
|
||||
temp1_(temp1),
|
||||
temp2_(temp2)
|
||||
{ }
|
||||
|
||||
TypedOrValueRegister value() const { return value_; }
|
||||
Register output() const { return output_; }
|
||||
Register temp1() const { return temp1_; }
|
||||
Register temp2() const { return temp2_; }
|
||||
LiveRegisterSet liveRegs() const { return liveRegs_; }
|
||||
|
||||
static JSObject* update(JSContext* cx, HandleScript outerScript, IonGetIteratorIC* ic,
|
||||
HandleValue value);
|
||||
};
|
||||
|
||||
class IonHasOwnIC : public IonIC
|
||||
{
|
||||
LiveRegisterSet liveRegs_;
|
||||
|
|
|
@ -4057,27 +4057,14 @@ LIRGenerator::visitCallInitElementArray(MCallInitElementArray* ins)
|
|||
}
|
||||
|
||||
void
|
||||
LIRGenerator::visitIteratorStart(MIteratorStart* ins)
|
||||
LIRGenerator::visitGetIteratorCache(MGetIteratorCache* ins)
|
||||
{
|
||||
if (ins->object()->type() == MIRType::Value) {
|
||||
LCallIteratorStartV* lir = new(alloc()) LCallIteratorStartV(useBoxAtStart(ins->object()));
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
return;
|
||||
}
|
||||
MDefinition* value = ins->value();
|
||||
MOZ_ASSERT(value->type() == MIRType::Object || value->type() == MIRType::Value);
|
||||
|
||||
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
|
||||
|
||||
// Call a stub if this is not a simple for-in loop.
|
||||
if (ins->flags() != JSITER_ENUMERATE) {
|
||||
LCallIteratorStartO* lir = new(alloc()) LCallIteratorStartO(useRegisterAtStart(ins->object()));
|
||||
defineReturn(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
} else {
|
||||
LIteratorStartO* lir = new(alloc()) LIteratorStartO(useRegister(ins->object()), temp(), temp(), temp());
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
LGetIteratorCache* lir = new(alloc()) LGetIteratorCache(useBoxOrTyped(value), temp(), temp());
|
||||
define(lir, ins);
|
||||
assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -275,7 +275,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
void visitCallInitElementArray(MCallInitElementArray* ins);
|
||||
void visitSetPropertyCache(MSetPropertyCache* ins);
|
||||
void visitCallSetProperty(MCallSetProperty* ins);
|
||||
void visitIteratorStart(MIteratorStart* ins);
|
||||
void visitGetIteratorCache(MGetIteratorCache* ins);
|
||||
void visitIteratorMore(MIteratorMore* ins);
|
||||
void visitIsNoIter(MIsNoIter* ins);
|
||||
void visitIteratorEnd(MIteratorEnd* ins);
|
||||
|
|
|
@ -12472,26 +12472,20 @@ class MNearbyInt
|
|||
ALLOW_CLONE(MNearbyInt)
|
||||
};
|
||||
|
||||
class MIteratorStart
|
||||
class MGetIteratorCache
|
||||
: public MUnaryInstruction,
|
||||
public BoxExceptPolicy<0, MIRType::Object>::Data
|
||||
{
|
||||
uint8_t flags_;
|
||||
|
||||
MIteratorStart(MDefinition* obj, uint8_t flags)
|
||||
: MUnaryInstruction(obj), flags_(flags)
|
||||
explicit MGetIteratorCache(MDefinition* val)
|
||||
: MUnaryInstruction(val)
|
||||
{
|
||||
setResultType(MIRType::Object);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IteratorStart)
|
||||
INSTRUCTION_HEADER(GetIteratorCache)
|
||||
TRIVIAL_NEW_WRAPPERS
|
||||
NAMED_OPERANDS((0, object))
|
||||
|
||||
uint8_t flags() const {
|
||||
return flags_;
|
||||
}
|
||||
NAMED_OPERANDS((0, value))
|
||||
};
|
||||
|
||||
class MIteratorMore
|
||||
|
|
|
@ -252,7 +252,7 @@ namespace jit {
|
|||
_(DeleteProperty) \
|
||||
_(DeleteElement) \
|
||||
_(SetPropertyCache) \
|
||||
_(IteratorStart) \
|
||||
_(GetIteratorCache) \
|
||||
_(IteratorMore) \
|
||||
_(IsNoIter) \
|
||||
_(IteratorEnd) \
|
||||
|
|
|
@ -7340,51 +7340,22 @@ class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
|
|||
}
|
||||
};
|
||||
|
||||
class LCallIteratorStartV : public LCallInstructionHelper<1, BOX_PIECES, 0>
|
||||
class LGetIteratorCache : public LInstructionHelper<1, BOX_PIECES, 2>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CallIteratorStartV)
|
||||
LIR_HEADER(GetIteratorCache)
|
||||
|
||||
static const size_t Value = 0;
|
||||
|
||||
explicit LCallIteratorStartV(const LBoxAllocation& value) {
|
||||
LGetIteratorCache(const LBoxAllocation& value, const LDefinition& temp1,
|
||||
const LDefinition& temp2)
|
||||
{
|
||||
setBoxOperand(Value, value);
|
||||
}
|
||||
MIteratorStart* mir() const {
|
||||
return mir_->toIteratorStart();
|
||||
}
|
||||
};
|
||||
|
||||
class LCallIteratorStartO : public LCallInstructionHelper<1, 1, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(CallIteratorStartO)
|
||||
|
||||
explicit LCallIteratorStartO(const LAllocation& object) {
|
||||
setOperand(0, object);
|
||||
}
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
MIteratorStart* mir() const {
|
||||
return mir_->toIteratorStart();
|
||||
}
|
||||
};
|
||||
|
||||
class LIteratorStartO : public LInstructionHelper<1, 1, 3>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IteratorStartO)
|
||||
|
||||
LIteratorStartO(const LAllocation& object, const LDefinition& temp1,
|
||||
const LDefinition& temp2, const LDefinition& temp3) {
|
||||
setOperand(0, object);
|
||||
setTemp(0, temp1);
|
||||
setTemp(1, temp2);
|
||||
setTemp(2, temp3);
|
||||
}
|
||||
const LAllocation* object() {
|
||||
return getOperand(0);
|
||||
const MGetIteratorCache* mir() const {
|
||||
return mir_->toGetIteratorCache();
|
||||
}
|
||||
const LDefinition* temp1() {
|
||||
return getTemp(0);
|
||||
|
@ -7392,12 +7363,6 @@ class LIteratorStartO : public LInstructionHelper<1, 1, 3>
|
|||
const LDefinition* temp2() {
|
||||
return getTemp(1);
|
||||
}
|
||||
const LDefinition* temp3() {
|
||||
return getTemp(2);
|
||||
}
|
||||
MIteratorStart* mir() const {
|
||||
return mir_->toIteratorStart();
|
||||
}
|
||||
};
|
||||
|
||||
class LIteratorMore : public LInstructionHelper<BOX_PIECES, 1, 1>
|
||||
|
|
|
@ -339,9 +339,7 @@
|
|||
_(SetPropertyCache) \
|
||||
_(SetPropertyPolymorphicV) \
|
||||
_(SetPropertyPolymorphicT) \
|
||||
_(CallIteratorStartV) \
|
||||
_(CallIteratorStartO) \
|
||||
_(IteratorStartO) \
|
||||
_(GetIteratorCache) \
|
||||
_(IteratorMore) \
|
||||
_(IsNoIterAndBranch) \
|
||||
_(IteratorEnd) \
|
||||
|
|
|
@ -89,7 +89,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
|
|||
debugScriptMap(nullptr),
|
||||
debugEnvs(nullptr),
|
||||
enumerators(nullptr),
|
||||
lastCachedNativeIterator(nullptr),
|
||||
compartmentStats_(nullptr),
|
||||
scheduledForDestruction(false),
|
||||
maybeAlive(true),
|
||||
|
@ -1069,7 +1068,6 @@ JSCompartment::purge()
|
|||
{
|
||||
dtoaCache.purge();
|
||||
newProxyCache.purge();
|
||||
lastCachedNativeIterator = nullptr;
|
||||
objectGroups.purge();
|
||||
}
|
||||
|
||||
|
|
|
@ -1129,9 +1129,6 @@ struct JSCompartment
|
|||
*/
|
||||
js::NativeIterator* enumerators;
|
||||
|
||||
/* Native iterator most recently started. */
|
||||
js::PropertyIteratorObject* lastCachedNativeIterator;
|
||||
|
||||
private:
|
||||
/* Used by memory reporters and invalid otherwise. */
|
||||
JS::CompartmentStats* compartmentStats_;
|
||||
|
|
|
@ -865,24 +865,6 @@ CanCompareIterableObjectToCache(JSObject* obj)
|
|||
return false;
|
||||
}
|
||||
|
||||
static MOZ_ALWAYS_INLINE void
|
||||
UpdateLastCachedNativeIterator(JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj)
|
||||
{
|
||||
// lastCachedNativeIterator is only used when there's a single object on
|
||||
// the prototype chain, to simplify JIT code.
|
||||
if (iterobj->getNativeIterator()->guard_length != 2)
|
||||
return;
|
||||
|
||||
// Both GetIterator and JIT code assume the receiver has a non-null proto,
|
||||
// so we have to make sure a Shape change is triggered when the proto
|
||||
// changes. Note that this does not apply to the object on the proto chain
|
||||
// because we always check it has a null proto.
|
||||
if (obj->hasUncacheableProto())
|
||||
return;
|
||||
|
||||
cx->compartment()->lastCachedNativeIterator = iterobj;
|
||||
}
|
||||
|
||||
using ReceiverGuardVector = Vector<ReceiverGuard, 8>;
|
||||
|
||||
static MOZ_ALWAYS_INLINE PropertyIteratorObject*
|
||||
|
@ -932,11 +914,6 @@ LookupInIteratorCache(JSContext* cx, JSObject* obj, ReceiverGuardVector& guards,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
UpdateNativeIterator(ni, obj);
|
||||
RegisterEnumerator(cx, iterobj, ni);
|
||||
|
||||
UpdateLastCachedNativeIterator(cx, obj, iterobj);
|
||||
|
||||
return iterobj;
|
||||
}
|
||||
|
||||
|
@ -974,8 +951,6 @@ StoreInIteratorCache(JSContext* cx, JSObject* obj, uint32_t key, PropertyIterato
|
|||
MOZ_ASSERT(iterobj->getNativeIterator()->guard_length > 0);
|
||||
|
||||
cx->caches().nativeIterCache.set(key, iterobj);
|
||||
|
||||
UpdateLastCachedNativeIterator(cx, obj, iterobj);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
@ -984,30 +959,13 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
|
|||
ReceiverGuardVector guards(cx);
|
||||
uint32_t key = 0;
|
||||
if (flags == JSITER_ENUMERATE) {
|
||||
// Check to see if this is the same as the most recent object which was
|
||||
// iterated over.
|
||||
if (PropertyIteratorObject* last = cx->compartment()->lastCachedNativeIterator) {
|
||||
NativeIterator* lastni = last->getNativeIterator();
|
||||
if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
|
||||
CanCompareIterableObjectToCache(obj) &&
|
||||
ReceiverGuard(obj) == lastni->guard_array[0])
|
||||
{
|
||||
JSObject* proto = obj->staticPrototype();
|
||||
if (CanCompareIterableObjectToCache(proto) &&
|
||||
ReceiverGuard(proto) == lastni->guard_array[1] &&
|
||||
!proto->staticPrototype())
|
||||
{
|
||||
assertSameCompartment(cx, last);
|
||||
UpdateNativeIterator(lastni, obj);
|
||||
RegisterEnumerator(cx, last, lastni);
|
||||
return last;
|
||||
}
|
||||
}
|
||||
if (PropertyIteratorObject* iterobj = LookupInIteratorCache(cx, obj, guards, &key)) {
|
||||
NativeIterator* ni = iterobj->getNativeIterator();
|
||||
UpdateNativeIterator(ni, obj);
|
||||
RegisterEnumerator(cx, iterobj, ni);
|
||||
return iterobj;
|
||||
}
|
||||
|
||||
if (PropertyIteratorObject* iterObj = LookupInIteratorCache(cx, obj, guards, &key))
|
||||
return iterObj;
|
||||
|
||||
if (!guards.empty() && !CanStoreInIteratorCache(cx, obj))
|
||||
guards.clear();
|
||||
}
|
||||
|
@ -1057,6 +1015,14 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
|
|||
return iterobj;
|
||||
}
|
||||
|
||||
PropertyIteratorObject*
|
||||
js::LookupInIteratorCache(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
ReceiverGuardVector guards(cx);
|
||||
uint32_t key;
|
||||
return LookupInIteratorCache(cx, obj, guards, &key);
|
||||
}
|
||||
|
||||
// ES 2017 draft 7.4.7.
|
||||
JSObject*
|
||||
js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
|
||||
|
|
|
@ -160,6 +160,9 @@ NewStringIteratorObject(JSContext* cx, NewObjectKind newKind = GenericObject);
|
|||
JSObject*
|
||||
GetIterator(JSContext* cx, HandleObject obj, unsigned flags);
|
||||
|
||||
PropertyIteratorObject*
|
||||
LookupInIteratorCache(JSContext* cx, HandleObject obj);
|
||||
|
||||
/*
|
||||
* Creates either a key or value iterator, depending on flags. For a value
|
||||
* iterator, performs value-lookup to convert the given list of jsids.
|
||||
|
|
Загрузка…
Ссылка в новой задаче