зеркало из https://github.com/mozilla/gecko-dev.git
Bug 831585 - Refactor and simplify for-in bytecode and remove cx->iterValue. r=bhackett
This commit is contained in:
Родитель
3145ec0ae0
Коммит
d9aad557bd
|
@ -604,6 +604,8 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
|
|||
break;
|
||||
|
||||
case STMT_FOR_IN_LOOP:
|
||||
/* The iterator and the current value are on the stack. */
|
||||
npops += 1;
|
||||
FLUSH_POPS();
|
||||
if (!PopIterator(cx, bce))
|
||||
return false;
|
||||
|
@ -718,10 +720,8 @@ PushLoopStatement(BytecodeEmitter *bce, LoopStmtInfo *stmt, StmtType type, ptrdi
|
|||
int loopSlots;
|
||||
if (type == STMT_SPREAD)
|
||||
loopSlots = 3;
|
||||
else if (type == STMT_FOR_OF_LOOP)
|
||||
else if (type == STMT_FOR_IN_LOOP || type == STMT_FOR_OF_LOOP)
|
||||
loopSlots = 2;
|
||||
else if (type == STMT_FOR_IN_LOOP)
|
||||
loopSlots = 1;
|
||||
else
|
||||
loopSlots = 0;
|
||||
|
||||
|
@ -4883,6 +4883,11 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
|
||||
return false;
|
||||
|
||||
// For-in loops have both the iterator and the value on the stack. Push
|
||||
// undefined to balance the stack.
|
||||
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
|
||||
return false;
|
||||
|
||||
// Enter the block before the loop body, after evaluating the obj.
|
||||
// Initialize let bindings with undefined when entering, as the name
|
||||
// assigned to is a plain assignment.
|
||||
|
@ -4917,18 +4922,11 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
int loopDepth = bce->stackDepth;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Emit code to get the next enumeration value and assign it to the
|
||||
* left hand side.
|
||||
*/
|
||||
if (Emit1(cx, bce, JSOP_ITERNEXT) < 0)
|
||||
return false;
|
||||
// Emit code to assign the enumeration value to the left hand side, but
|
||||
// also leave it on the stack.
|
||||
if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
|
||||
/* The stack should be balanced around the assignment opcode sequence. */
|
||||
JS_ASSERT(bce->stackDepth == loopDepth);
|
||||
|
||||
|
@ -4948,9 +4946,13 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
SetJumpOffsetAt(bce, jmp);
|
||||
if (!EmitLoopEntry(cx, bce, nullptr))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_MOREITER) < 0)
|
||||
return false;
|
||||
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
|
||||
if (Emit1(cx, bce, JSOP_ISNOITER) < 0)
|
||||
return false;
|
||||
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset());
|
||||
if (beq < 0)
|
||||
return false;
|
||||
|
||||
|
@ -4962,6 +4964,10 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
|
|||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
||||
// Pop the enumeration value.
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
|
||||
if (!bce->tryNoteList.append(JSTRY_ITER, bce->stackDepth, top, bce->offset()))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_ENDITER) < 0)
|
||||
|
|
|
@ -3092,16 +3092,21 @@ BaselineCompiler::emit_JSOP_MOREITER()
|
|||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_ITERNEXT()
|
||||
BaselineCompiler::emit_JSOP_ISNOITER()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
|
||||
|
||||
ICIteratorNext_Fallback::Compiler compiler(cx);
|
||||
if (!emitOpIC(compiler.getStub(&stubSpace_)))
|
||||
return false;
|
||||
Label isMagic, done;
|
||||
masm.branchTestMagic(Assembler::Equal, frame.addressOfStackValue(frame.peek(-1)),
|
||||
&isMagic);
|
||||
masm.moveValue(BooleanValue(false), R0);
|
||||
masm.jump(&done);
|
||||
|
||||
frame.push(R0);
|
||||
masm.bind(&isMagic);
|
||||
masm.moveValue(BooleanValue(true), R0);
|
||||
|
||||
masm.bind(&done);
|
||||
frame.push(R0, JSVAL_TYPE_BOOLEAN);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ namespace jit {
|
|||
_(JSOP_TABLESWITCH) \
|
||||
_(JSOP_ITER) \
|
||||
_(JSOP_MOREITER) \
|
||||
_(JSOP_ITERNEXT) \
|
||||
_(JSOP_ISNOITER) \
|
||||
_(JSOP_ENDITER) \
|
||||
_(JSOP_CALLEE) \
|
||||
_(JSOP_SETRVAL) \
|
||||
|
|
|
@ -9844,15 +9844,16 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb
|
|||
|
||||
FallbackICSpew(cx, stub, "IteratorMore");
|
||||
|
||||
bool cond;
|
||||
if (!IteratorMore(cx, iterObj, &cond))
|
||||
if (!IteratorMore(cx, iterObj, res))
|
||||
return false;
|
||||
res.setBoolean(cond);
|
||||
|
||||
// Check if debug mode toggling made the stub invalid.
|
||||
if (stub.invalid())
|
||||
return true;
|
||||
|
||||
if (!res.isMagic(JS_NO_ITER_VALUE) && !res.isString())
|
||||
stub->setHasNonStringResult();
|
||||
|
||||
if (iterObj->is<PropertyIteratorObject>() &&
|
||||
!stub->hasStub(ICStub::IteratorMore_Native))
|
||||
{
|
||||
|
@ -9906,107 +9907,27 @@ ICIteratorMore_Native::Compiler::generateStubCode(MacroAssembler &masm)
|
|||
masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_FOREACH), &failure);
|
||||
|
||||
// Set output to true if props_cursor < props_end.
|
||||
masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_end)), scratch);
|
||||
Address cursorAddr = Address(nativeIterator, offsetof(NativeIterator, props_cursor));
|
||||
masm.cmpPtrSet(Assembler::LessThan, cursorAddr, scratch, scratch);
|
||||
// If props_cursor < props_end, load the next string and advance the cursor.
|
||||
// Else, return MagicValue(JS_NO_ITER_VALUE).
|
||||
Label iterDone;
|
||||
Address cursorAddr(nativeIterator, offsetof(NativeIterator, props_cursor));
|
||||
Address cursorEndAddr(nativeIterator, offsetof(NativeIterator, props_end));
|
||||
masm.loadPtr(cursorAddr, scratch);
|
||||
masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, scratch, &iterDone);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// IteratorNext_Fallback
|
||||
//
|
||||
|
||||
static bool
|
||||
DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub_,
|
||||
HandleValue iterValue, MutableHandleValue res)
|
||||
{
|
||||
// This fallback stub may trigger debug mode toggling.
|
||||
DebugModeOSRVolatileStub<ICIteratorNext_Fallback *> stub(frame, stub_);
|
||||
|
||||
FallbackICSpew(cx, stub, "IteratorNext");
|
||||
|
||||
RootedObject iteratorObject(cx, &iterValue.toObject());
|
||||
if (!IteratorNext(cx, iteratorObject, res))
|
||||
return false;
|
||||
|
||||
// Check if debug mode toggling made the stub invalid.
|
||||
if (stub.invalid())
|
||||
return true;
|
||||
|
||||
if (!res.isString() && !stub->hasNonStringResult())
|
||||
stub->setHasNonStringResult();
|
||||
|
||||
if (iteratorObject->is<PropertyIteratorObject>() &&
|
||||
!stub->hasStub(ICStub::IteratorNext_Native))
|
||||
{
|
||||
ICIteratorNext_Native::Compiler compiler(cx);
|
||||
ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
|
||||
if (!newStub)
|
||||
return false;
|
||||
stub->addNewStub(newStub);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*DoIteratorNextFallbackFn)(JSContext *, BaselineFrame *, ICIteratorNext_Fallback *,
|
||||
HandleValue, MutableHandleValue);
|
||||
static const VMFunction DoIteratorNextFallbackInfo =
|
||||
FunctionInfo<DoIteratorNextFallbackFn>(DoIteratorNextFallback);
|
||||
|
||||
bool
|
||||
ICIteratorNext_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
EmitRestoreTailCallReg(masm);
|
||||
|
||||
masm.pushValue(R0);
|
||||
masm.push(BaselineStubReg);
|
||||
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
|
||||
return tailCallVM(DoIteratorNextFallbackInfo, masm);
|
||||
}
|
||||
|
||||
//
|
||||
// IteratorNext_Native
|
||||
//
|
||||
|
||||
bool
|
||||
ICIteratorNext_Native::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
Label failure;
|
||||
|
||||
Register obj = masm.extractObject(R0, ExtractTemp0);
|
||||
|
||||
GeneralRegisterSet regs(availableGeneralRegs(1));
|
||||
Register nativeIterator = regs.takeAny();
|
||||
Register scratch = regs.takeAny();
|
||||
|
||||
masm.branchTestObjClass(Assembler::NotEqual, obj, scratch,
|
||||
&PropertyIteratorObject::class_, &failure);
|
||||
masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, nativeIterator);
|
||||
|
||||
masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_FOREACH), &failure);
|
||||
|
||||
// Get cursor, next string.
|
||||
masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_cursor)), scratch);
|
||||
// Get next string.
|
||||
masm.loadPtr(Address(scratch, 0), scratch);
|
||||
|
||||
// Increase the cursor.
|
||||
masm.addPtr(Imm32(sizeof(JSString *)),
|
||||
Address(nativeIterator, offsetof(NativeIterator, props_cursor)));
|
||||
masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_STRING, scratch, R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
masm.bind(&iterDone);
|
||||
masm.moveValue(MagicValue(JS_NO_ITER_VALUE), R0);
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Failure case - jump to next stub
|
||||
masm.bind(&failure);
|
||||
EmitStubGuardFailure(masm);
|
||||
|
|
|
@ -438,8 +438,6 @@ class ICEntry
|
|||
_(IteratorNew_Fallback) \
|
||||
_(IteratorMore_Fallback) \
|
||||
_(IteratorMore_Native) \
|
||||
_(IteratorNext_Fallback) \
|
||||
_(IteratorNext_Native) \
|
||||
_(IteratorClose_Fallback) \
|
||||
\
|
||||
_(InstanceOf_Fallback) \
|
||||
|
@ -6138,6 +6136,14 @@ class ICIteratorMore_Fallback : public ICFallbackStub
|
|||
return space->allocate<ICIteratorMore_Fallback>(code);
|
||||
}
|
||||
|
||||
void setHasNonStringResult() {
|
||||
extra_ = 1;
|
||||
}
|
||||
bool hasNonStringResult() const {
|
||||
MOZ_ASSERT(extra_ <= 1);
|
||||
return extra_;
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
@ -6184,76 +6190,6 @@ class ICIteratorMore_Native : public ICStub
|
|||
};
|
||||
};
|
||||
|
||||
// IC for getting the next value in an iterator.
|
||||
class ICIteratorNext_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICIteratorNext_Fallback(JitCode *stubCode)
|
||||
: ICFallbackStub(ICStub::IteratorNext_Fallback, stubCode)
|
||||
{ }
|
||||
|
||||
public:
|
||||
static inline ICIteratorNext_Fallback *New(ICStubSpace *space, JitCode *code) {
|
||||
if (!code)
|
||||
return nullptr;
|
||||
return space->allocate<ICIteratorNext_Fallback>(code);
|
||||
}
|
||||
|
||||
void setHasNonStringResult() {
|
||||
JS_ASSERT(extra_ == 0);
|
||||
extra_ = 1;
|
||||
}
|
||||
bool hasNonStringResult() const {
|
||||
return extra_;
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
explicit Compiler(JSContext *cx)
|
||||
: ICStubCompiler(cx, ICStub::IteratorNext_Fallback)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
return ICIteratorNext_Fallback::New(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// IC for getting the next value in a native iterator.
|
||||
class ICIteratorNext_Native : public ICStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
explicit ICIteratorNext_Native(JitCode *stubCode)
|
||||
: ICStub(ICStub::IteratorNext_Native, stubCode)
|
||||
{ }
|
||||
|
||||
public:
|
||||
static inline ICIteratorNext_Native *New(ICStubSpace *space, JitCode *code) {
|
||||
if (!code)
|
||||
return nullptr;
|
||||
return space->allocate<ICIteratorNext_Native>(code);
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
explicit Compiler(JSContext *cx)
|
||||
: ICStubCompiler(cx, ICStub::IteratorNext_Native)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
return ICIteratorNext_Native::New(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// IC for closing an iterator.
|
||||
class ICIteratorClose_Fallback : public ICFallbackStub
|
||||
{
|
||||
|
|
|
@ -381,9 +381,9 @@ BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
|
|||
}
|
||||
|
||||
bool
|
||||
BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
|
||||
BaselineInspector::hasSeenNonStringIterMore(jsbytecode *pc)
|
||||
{
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT);
|
||||
JS_ASSERT(JSOp(*pc) == JSOP_MOREITER);
|
||||
|
||||
if (!hasBaselineScript())
|
||||
return false;
|
||||
|
@ -391,7 +391,7 @@ BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
|
|||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
ICStub *stub = entry.fallbackStub();
|
||||
|
||||
return stub->toIteratorNext_Fallback()->hasNonStringResult();
|
||||
return stub->toIteratorMore_Fallback()->hasNonStringResult();
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -107,7 +107,7 @@ class BaselineInspector
|
|||
bool hasSeenNegativeIndexGetElement(jsbytecode *pc);
|
||||
bool hasSeenAccessedGetter(jsbytecode *pc);
|
||||
bool hasSeenDoubleResult(jsbytecode *pc);
|
||||
bool hasSeenNonStringIterNext(jsbytecode *pc);
|
||||
bool hasSeenNonStringIterMore(jsbytecode *pc);
|
||||
|
||||
JSObject *getTemplateObject(jsbytecode *pc);
|
||||
JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);
|
||||
|
|
|
@ -53,8 +53,10 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
|
|||
|
||||
Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges(alloc);
|
||||
|
||||
for (jsbytecode *pc = script_->code(); pc < end; pc += GetBytecodeLength(pc)) {
|
||||
jsbytecode *nextpc;
|
||||
for (jsbytecode *pc = script_->code(); pc < end; pc = nextpc) {
|
||||
JSOp op = JSOp(*pc);
|
||||
nextpc = pc + GetBytecodeLength(pc);
|
||||
unsigned offset = script_->pcToOffset(pc);
|
||||
|
||||
JitSpew(JitSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
|
||||
|
@ -64,7 +66,6 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
|
|||
if (!infos_[offset].initialized)
|
||||
continue;
|
||||
|
||||
|
||||
unsigned stackDepth = infos_[offset].stackDepth;
|
||||
#ifdef DEBUG
|
||||
for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
|
||||
|
@ -190,25 +191,20 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
|
|||
infos_[targetOffset].jumpTarget = true;
|
||||
|
||||
if (jumpBack)
|
||||
pc = script_->offsetToPC(targetOffset);
|
||||
nextpc = script_->offsetToPC(targetOffset);
|
||||
}
|
||||
|
||||
// Handle any fallthrough from this opcode.
|
||||
if (BytecodeFallsThrough(op)) {
|
||||
jsbytecode *nextpc = pc + GetBytecodeLength(pc);
|
||||
JS_ASSERT(nextpc < end);
|
||||
unsigned nextOffset = script_->pcToOffset(nextpc);
|
||||
jsbytecode *fallthrough = pc + GetBytecodeLength(pc);
|
||||
JS_ASSERT(fallthrough < end);
|
||||
unsigned fallthroughOffset = script_->pcToOffset(fallthrough);
|
||||
|
||||
infos_[nextOffset].init(stackDepth);
|
||||
|
||||
if (jump)
|
||||
infos_[nextOffset].jumpFallthrough = true;
|
||||
infos_[fallthroughOffset].init(stackDepth);
|
||||
|
||||
// Treat the fallthrough of a branch instruction as a jump target.
|
||||
if (jump)
|
||||
infos_[nextOffset].jumpTarget = true;
|
||||
else
|
||||
infos_[nextOffset].fallthrough = true;
|
||||
infos_[fallthroughOffset].jumpTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,6 @@ struct BytecodeInfo
|
|||
uint16_t stackDepth;
|
||||
bool initialized : 1;
|
||||
bool jumpTarget : 1;
|
||||
bool jumpFallthrough : 1;
|
||||
bool fallthrough : 1;
|
||||
|
||||
// If true, this is a JSOP_LOOPENTRY op inside a catch or finally block.
|
||||
bool loopEntryInCatchOrFinally : 1;
|
||||
|
|
|
@ -6369,66 +6369,64 @@ LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *fai
|
|||
masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
|
||||
}
|
||||
|
||||
typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
|
||||
static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(IteratorNext);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitIteratorNext(LIteratorNext *lir)
|
||||
{
|
||||
const Register obj = ToRegister(lir->object());
|
||||
const Register temp = ToRegister(lir->temp());
|
||||
const ValueOperand output = ToOutValue(lir);
|
||||
|
||||
OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
|
||||
if (!ool)
|
||||
return false;
|
||||
|
||||
LoadNativeIterator(masm, obj, temp, ool->entry());
|
||||
|
||||
masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_FOREACH), ool->entry());
|
||||
|
||||
// Get cursor, next string.
|
||||
masm.loadPtr(Address(temp, offsetof(NativeIterator, props_cursor)), output.scratchReg());
|
||||
masm.loadPtr(Address(output.scratchReg(), 0), output.scratchReg());
|
||||
masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
|
||||
|
||||
// Increase the cursor.
|
||||
masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *);
|
||||
typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, MutableHandleValue);
|
||||
static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(IteratorMore);
|
||||
|
||||
bool
|
||||
CodeGenerator::visitIteratorMore(LIteratorMore *lir)
|
||||
{
|
||||
const Register obj = ToRegister(lir->object());
|
||||
const Register output = ToRegister(lir->output());
|
||||
const ValueOperand output = ToOutValue(lir);
|
||||
const Register temp = ToRegister(lir->temp());
|
||||
|
||||
OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
|
||||
(ArgList(), obj), StoreRegisterTo(output));
|
||||
OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir, (ArgList(), obj), StoreValueTo(output));
|
||||
if (!ool)
|
||||
return false;
|
||||
|
||||
LoadNativeIterator(masm, obj, output, ool->entry());
|
||||
Register outputScratch = output.scratchReg();
|
||||
LoadNativeIterator(masm, obj, outputScratch, ool->entry());
|
||||
|
||||
masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
|
||||
masm.branchTest32(Assembler::NonZero, Address(outputScratch, offsetof(NativeIterator, flags)),
|
||||
Imm32(JSITER_FOREACH), ool->entry());
|
||||
|
||||
// Set output to true if props_cursor < props_end.
|
||||
masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
|
||||
masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)),
|
||||
temp, output);
|
||||
// If props_cursor < props_end, load the next string and advance the cursor.
|
||||
// Else, return MagicValue(JS_NO_ITER_VALUE).
|
||||
Label iterDone;
|
||||
Address cursorAddr(outputScratch, offsetof(NativeIterator, props_cursor));
|
||||
Address cursorEndAddr(outputScratch, offsetof(NativeIterator, props_end));
|
||||
masm.loadPtr(cursorAddr, temp);
|
||||
masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, temp, &iterDone);
|
||||
|
||||
// Get next string.
|
||||
masm.loadPtr(Address(temp, 0), temp);
|
||||
|
||||
// Increase the cursor.
|
||||
masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
|
||||
|
||||
masm.tagValue(JSVAL_TYPE_STRING, temp, output);
|
||||
masm.jump(ool->rejoin());
|
||||
|
||||
masm.bind(&iterDone);
|
||||
masm.moveValue(MagicValue(JS_NO_ITER_VALUE), output);
|
||||
|
||||
masm.bind(ool->rejoin());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch *lir)
|
||||
{
|
||||
ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input);
|
||||
Label *ifTrue = getJumpLabelForBranch(lir->ifTrue());
|
||||
Label *ifFalse = getJumpLabelForBranch(lir->ifFalse());
|
||||
|
||||
masm.branchTestMagic(Assembler::Equal, input, ifTrue);
|
||||
|
||||
if (!isNextBlock(lir->ifFalse()->lir()))
|
||||
masm.jump(ifFalse);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
|
||||
static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);
|
||||
|
||||
|
|
|
@ -265,8 +265,8 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitClampVToUint8(LClampVToUint8 *lir);
|
||||
bool visitCallIteratorStart(LCallIteratorStart *lir);
|
||||
bool visitIteratorStart(LIteratorStart *lir);
|
||||
bool visitIteratorNext(LIteratorNext *lir);
|
||||
bool visitIteratorMore(LIteratorMore *lir);
|
||||
bool visitIsNoIterAndBranch(LIsNoIterAndBranch *lir);
|
||||
bool visitIteratorEnd(LIteratorEnd *lir);
|
||||
bool visitArgumentsLength(LArgumentsLength *lir);
|
||||
bool visitGetFrameArgument(LGetFrameArgument *lir);
|
||||
|
|
|
@ -491,6 +491,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecod
|
|||
continue;
|
||||
if (!analysis().maybeInfo(pc))
|
||||
continue;
|
||||
if (!last)
|
||||
continue;
|
||||
|
||||
MPhi *phi = entry->getSlot(slot)->toPhi();
|
||||
|
||||
|
@ -561,7 +563,6 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecod
|
|||
case JSOP_STRING:
|
||||
case JSOP_TYPEOF:
|
||||
case JSOP_TYPEOFEXPR:
|
||||
case JSOP_ITERNEXT:
|
||||
type = MIRType_String;
|
||||
break;
|
||||
case JSOP_ADD:
|
||||
|
@ -1783,12 +1784,12 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_ITER:
|
||||
return jsop_iter(GET_INT8(pc));
|
||||
|
||||
case JSOP_ITERNEXT:
|
||||
return jsop_iternext();
|
||||
|
||||
case JSOP_MOREITER:
|
||||
return jsop_itermore();
|
||||
|
||||
case JSOP_ISNOITER:
|
||||
return jsop_isnoiter();
|
||||
|
||||
case JSOP_ENDITER:
|
||||
return jsop_iterend();
|
||||
|
||||
|
@ -2258,6 +2259,23 @@ IonBuilder::processWhileCondEnd(CFGState &state)
|
|||
pc = state.loop.bodyStart;
|
||||
if (!setCurrentAndSpecializePhis(body))
|
||||
return ControlStatus_Error;
|
||||
|
||||
// If this is a for-in loop, unbox the current value as string if possible.
|
||||
if (ins->isIsNoIter()) {
|
||||
MIteratorMore *iterMore = ins->toIsNoIter()->input()->toIteratorMore();
|
||||
jsbytecode *iterMorePc = iterMore->resumePoint()->pc();
|
||||
MOZ_ASSERT(*iterMorePc == JSOP_MOREITER);
|
||||
|
||||
if (!nonStringIteration_ && !inspector->hasSeenNonStringIterMore(iterMorePc)) {
|
||||
MDefinition *val = current->peek(-1);
|
||||
MOZ_ASSERT(val == iterMore);
|
||||
MInstruction *ins = MUnbox::New(alloc(), val, MIRType_String, MUnbox::Fallible,
|
||||
Bailout_NonStringInputInvalidate);
|
||||
current->add(ins);
|
||||
current->rewriteAtDepth(-1, ins);
|
||||
}
|
||||
}
|
||||
|
||||
return ControlStatus_Jumped;
|
||||
}
|
||||
|
||||
|
@ -10453,28 +10471,6 @@ IonBuilder::jsop_iter(uint8_t flags)
|
|||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_iternext()
|
||||
{
|
||||
MDefinition *iter = current->peek(-1);
|
||||
MInstruction *ins = MIteratorNext::New(alloc(), iter);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
|
||||
if (!nonStringIteration_ && !inspector->hasSeenNonStringIterNext(pc)) {
|
||||
ins = MUnbox::New(alloc(), ins, MIRType_String, MUnbox::Fallible,
|
||||
Bailout_NonStringInputInvalidate);
|
||||
current->add(ins);
|
||||
current->rewriteAtDepth(-1, ins);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_itermore()
|
||||
{
|
||||
|
@ -10487,6 +10483,19 @@ IonBuilder::jsop_itermore()
|
|||
return resumeAfter(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_isnoiter()
|
||||
{
|
||||
MDefinition *def = current->peek(-1);
|
||||
MOZ_ASSERT(def->isIteratorMore());
|
||||
|
||||
MInstruction *ins = MIsNoIter::New(alloc(), def);
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonBuilder::jsop_iterend()
|
||||
{
|
||||
|
|
|
@ -651,8 +651,8 @@ class IonBuilder
|
|||
bool jsop_typeof();
|
||||
bool jsop_toid();
|
||||
bool jsop_iter(uint8_t flags);
|
||||
bool jsop_iternext();
|
||||
bool jsop_itermore();
|
||||
bool jsop_isnoiter();
|
||||
bool jsop_iterend();
|
||||
bool jsop_in();
|
||||
bool jsop_in_dense();
|
||||
|
|
|
@ -5571,27 +5571,7 @@ class LIteratorStart : public LInstructionHelper<1, 1, 3>
|
|||
}
|
||||
};
|
||||
|
||||
class LIteratorNext : public LInstructionHelper<BOX_PIECES, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IteratorNext)
|
||||
|
||||
LIteratorNext(const LAllocation &iterator, const LDefinition &temp) {
|
||||
setOperand(0, iterator);
|
||||
setTemp(0, temp);
|
||||
}
|
||||
const LAllocation *object() {
|
||||
return getOperand(0);
|
||||
}
|
||||
const LDefinition *temp() {
|
||||
return getTemp(0);
|
||||
}
|
||||
MIteratorNext *mir() const {
|
||||
return mir_->toIteratorNext();
|
||||
}
|
||||
};
|
||||
|
||||
class LIteratorMore : public LInstructionHelper<1, 1, 1>
|
||||
class LIteratorMore : public LInstructionHelper<BOX_PIECES, 1, 1>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IteratorMore)
|
||||
|
@ -5611,6 +5591,26 @@ class LIteratorMore : public LInstructionHelper<1, 1, 1>
|
|||
}
|
||||
};
|
||||
|
||||
class LIsNoIterAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
|
||||
{
|
||||
public:
|
||||
LIR_HEADER(IsNoIterAndBranch)
|
||||
|
||||
LIsNoIterAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse) {
|
||||
setSuccessor(0, ifTrue);
|
||||
setSuccessor(1, ifFalse);
|
||||
}
|
||||
|
||||
static const size_t Input = 0;
|
||||
|
||||
MBasicBlock *ifTrue() const {
|
||||
return getSuccessor(0);
|
||||
}
|
||||
MBasicBlock *ifFalse() const {
|
||||
return getSuccessor(1);
|
||||
}
|
||||
};
|
||||
|
||||
class LIteratorEnd : public LInstructionHelper<0, 1, 3>
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -259,8 +259,8 @@
|
|||
_(SetPropertyPolymorphicT) \
|
||||
_(CallIteratorStart) \
|
||||
_(IteratorStart) \
|
||||
_(IteratorNext) \
|
||||
_(IteratorMore) \
|
||||
_(IsNoIterAndBranch) \
|
||||
_(IteratorEnd) \
|
||||
_(ArrayLength) \
|
||||
_(SetArrayLength) \
|
||||
|
|
|
@ -874,6 +874,18 @@ LIRGenerator::visitTest(MTest *test)
|
|||
}
|
||||
}
|
||||
|
||||
if (opd->isIsNoIter()) {
|
||||
MOZ_ASSERT(opd->isEmittedAtUses());
|
||||
|
||||
MDefinition *input = opd->toIsNoIter()->input();
|
||||
MOZ_ASSERT(input->type() == MIRType_Value);
|
||||
|
||||
LIsNoIterAndBranch *lir = new(alloc()) LIsNoIterAndBranch(ifTrue, ifFalse);
|
||||
if (!useBox(lir, LIsNoIterAndBranch::Input, input))
|
||||
return false;
|
||||
return add(lir, test);
|
||||
}
|
||||
|
||||
if (opd->type() == MIRType_Double)
|
||||
return add(new(alloc()) LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
|
||||
|
||||
|
@ -3332,17 +3344,17 @@ LIRGenerator::visitIteratorStart(MIteratorStart *ins)
|
|||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitIteratorNext(MIteratorNext *ins)
|
||||
LIRGenerator::visitIteratorMore(MIteratorMore *ins)
|
||||
{
|
||||
LIteratorNext *lir = new(alloc()) LIteratorNext(useRegister(ins->iterator()), temp());
|
||||
LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
|
||||
return defineBox(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitIteratorMore(MIteratorMore *ins)
|
||||
LIRGenerator::visitIsNoIter(MIsNoIter *ins)
|
||||
{
|
||||
LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
|
||||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
MOZ_ASSERT(ins->hasOneUse());
|
||||
return emitAtUses(ins);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -236,8 +236,8 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitSetElementCache(MSetElementCache *ins);
|
||||
bool visitCallSetProperty(MCallSetProperty *ins);
|
||||
bool visitIteratorStart(MIteratorStart *ins);
|
||||
bool visitIteratorNext(MIteratorNext *ins);
|
||||
bool visitIteratorMore(MIteratorMore *ins);
|
||||
bool visitIsNoIter(MIsNoIter *ins);
|
||||
bool visitIteratorEnd(MIteratorEnd *ins);
|
||||
bool visitStringLength(MStringLength *ins);
|
||||
bool visitArgumentsLength(MArgumentsLength *ins);
|
||||
|
|
|
@ -10213,31 +10213,6 @@ class MIteratorStart
|
|||
}
|
||||
};
|
||||
|
||||
class MIteratorNext
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
{
|
||||
explicit MIteratorNext(MDefinition *iter)
|
||||
: MUnaryInstruction(iter)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IteratorNext)
|
||||
|
||||
static MIteratorNext *New(TempAllocator &alloc, MDefinition *iter) {
|
||||
return new(alloc) MIteratorNext(iter);
|
||||
}
|
||||
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
MDefinition *iterator() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
class MIteratorMore
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
|
@ -10245,7 +10220,7 @@ class MIteratorMore
|
|||
explicit MIteratorMore(MDefinition *iter)
|
||||
: MUnaryInstruction(iter)
|
||||
{
|
||||
setResultType(MIRType_Boolean);
|
||||
setResultType(MIRType_Value);
|
||||
}
|
||||
|
||||
public:
|
||||
|
@ -10263,6 +10238,28 @@ class MIteratorMore
|
|||
}
|
||||
};
|
||||
|
||||
class MIsNoIter
|
||||
: public MUnaryInstruction
|
||||
{
|
||||
MIsNoIter(MDefinition *def)
|
||||
: MUnaryInstruction(def)
|
||||
{
|
||||
setResultType(MIRType_Boolean);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(IsNoIter)
|
||||
|
||||
static MIsNoIter *New(TempAllocator &alloc, MDefinition *def) {
|
||||
return new(alloc) MIsNoIter(def);
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MIteratorEnd
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
|
|
|
@ -192,8 +192,8 @@ namespace jit {
|
|||
_(DeleteElement) \
|
||||
_(SetPropertyCache) \
|
||||
_(IteratorStart) \
|
||||
_(IteratorNext) \
|
||||
_(IteratorMore) \
|
||||
_(IsNoIter) \
|
||||
_(IteratorEnd) \
|
||||
_(StringLength) \
|
||||
_(ArgumentsLength) \
|
||||
|
|
|
@ -274,8 +274,8 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
|
|||
UNSAFE_OP(DeleteElement)
|
||||
WRITE_GUARDED_OP(SetPropertyCache, object)
|
||||
UNSAFE_OP(IteratorStart)
|
||||
UNSAFE_OP(IteratorNext)
|
||||
UNSAFE_OP(IteratorMore)
|
||||
UNSAFE_OP(IsNoIter)
|
||||
UNSAFE_OP(IteratorEnd)
|
||||
SAFE_OP(StringLength)
|
||||
SAFE_OP(ArgumentsLength)
|
||||
|
|
|
@ -2062,6 +2062,12 @@ RangeAnalysis::addRangeAssertions()
|
|||
continue;
|
||||
}
|
||||
|
||||
// MIsNoIter is fused with the MTest that follows it and emitted as
|
||||
// LIsNoIterAndBranch. Skip it to avoid complicating MIsNoIter
|
||||
// lowering.
|
||||
if (ins->isIsNoIter())
|
||||
continue;
|
||||
|
||||
Range r(ins);
|
||||
|
||||
// Don't insert assertions if there's nothing interesting to assert.
|
||||
|
|
|
@ -1109,7 +1109,6 @@ JSContext::JSContext(JSRuntime *rt)
|
|||
data(nullptr),
|
||||
data2(nullptr),
|
||||
outstandingRequests(0),
|
||||
iterValue(MagicValue(JS_NO_ITER_VALUE)),
|
||||
jitIsBroken(false),
|
||||
#ifdef MOZ_TRACE_JSCALLS
|
||||
functionCallback(nullptr),
|
||||
|
@ -1304,8 +1303,6 @@ JSContext::mark(JSTracer *trc)
|
|||
MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
|
||||
|
||||
TraceCycleDetectionSet(trc, cycleDetectorSet);
|
||||
|
||||
MarkValueRoot(trc, &iterValue, "iterValue");
|
||||
}
|
||||
|
||||
void *
|
||||
|
|
|
@ -490,9 +490,6 @@ struct JSContext : public js::ExclusiveContext,
|
|||
without the corresponding
|
||||
JS_EndRequest. */
|
||||
|
||||
/* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */
|
||||
js::Value iterValue;
|
||||
|
||||
bool jitIsBroken;
|
||||
|
||||
void updateJITEnabled();
|
||||
|
|
|
@ -865,16 +865,15 @@ iterator_next_impl(JSContext *cx, CallArgs args)
|
|||
|
||||
RootedObject thisObj(cx, &args.thisv().toObject());
|
||||
|
||||
bool more;
|
||||
if (!IteratorMore(cx, thisObj, &more))
|
||||
if (!IteratorMore(cx, thisObj, args.rval()))
|
||||
return false;
|
||||
|
||||
if (!more) {
|
||||
if (args.rval().isMagic(JS_NO_ITER_VALUE)) {
|
||||
ThrowStopIteration(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return IteratorNext(cx, thisObj, args.rval());
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1017,13 +1016,6 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
|
|||
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
|
||||
JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
|
||||
|
||||
/*
|
||||
* Make sure the more/next state machine doesn't get stuck. A value might
|
||||
* be left in iterValue when a trace is left due to an interrupt after
|
||||
* JSOP_MOREITER but before the value is picked up by FOR*.
|
||||
*/
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
|
||||
RootedObject obj(cx);
|
||||
if (vp.isObject()) {
|
||||
/* Common case. */
|
||||
|
@ -1047,8 +1039,6 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
|
|||
bool
|
||||
js::CloseIterator(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
|
||||
if (obj->is<PropertyIteratorObject>()) {
|
||||
/* Remove enumerators from the active list, which is a stack. */
|
||||
NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
|
||||
|
@ -1255,31 +1245,28 @@ js::SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uin
|
|||
}
|
||||
|
||||
bool
|
||||
js::IteratorMore(JSContext *cx, HandleObject iterobj, bool *res)
|
||||
js::IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
|
||||
{
|
||||
/* Fast path for native iterators */
|
||||
NativeIterator *ni = nullptr;
|
||||
if (iterobj->is<PropertyIteratorObject>()) {
|
||||
/* Key iterators are handled by fast-paths. */
|
||||
ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
|
||||
bool more = ni->props_cursor < ni->props_end;
|
||||
if (ni->isKeyIter() || !more) {
|
||||
*res = more;
|
||||
if (ni->props_cursor >= ni->props_end) {
|
||||
rval.setMagic(JS_NO_ITER_VALUE);
|
||||
return true;
|
||||
}
|
||||
if (ni->isKeyIter()) {
|
||||
rval.setString(*ni->current());
|
||||
ni->incCursor();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* We might still have a pending value. */
|
||||
if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
|
||||
*res = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We're reentering below and can call anything. */
|
||||
JS_CHECK_RECURSION(cx, return false);
|
||||
|
||||
/* Fetch and cache the next value from the iterator. */
|
||||
RootedValue val(cx);
|
||||
if (ni) {
|
||||
JS_ASSERT(!ni->isKeyIter());
|
||||
RootedId id(cx);
|
||||
|
@ -1288,60 +1275,31 @@ js::IteratorMore(JSContext *cx, HandleObject iterobj, bool *res)
|
|||
return false;
|
||||
ni->incCursor();
|
||||
RootedObject obj(cx, ni->obj);
|
||||
if (!JSObject::getGeneric(cx, obj, obj, id, &val))
|
||||
if (!JSObject::getGeneric(cx, obj, obj, id, rval))
|
||||
return false;
|
||||
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, val, &val))
|
||||
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
|
||||
return false;
|
||||
} else {
|
||||
/* Call the iterator object's .next method. */
|
||||
if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, &val))
|
||||
return false;
|
||||
if (!Invoke(cx, ObjectValue(*iterobj), val, 0, nullptr, &val)) {
|
||||
/* Check for StopIteration. */
|
||||
if (!cx->isExceptionPending())
|
||||
return false;
|
||||
RootedValue exception(cx);
|
||||
if (!cx->getPendingException(&exception))
|
||||
return false;
|
||||
if (!JS_IsStopIteration(exception))
|
||||
return false;
|
||||
|
||||
cx->clearPendingException();
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
*res = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
|
||||
JS_ASSERT(!val.isMagic(JS_NO_ITER_VALUE));
|
||||
cx->iterValue = val;
|
||||
*res = true;
|
||||
return true;
|
||||
}
|
||||
/* Call the iterator object's .next method. */
|
||||
if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
|
||||
return false;
|
||||
if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
|
||||
/* Check for StopIteration. */
|
||||
if (!cx->isExceptionPending())
|
||||
return false;
|
||||
RootedValue exception(cx);
|
||||
if (!cx->getPendingException(&exception))
|
||||
return false;
|
||||
if (!JS_IsStopIteration(exception))
|
||||
return false;
|
||||
|
||||
bool
|
||||
js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
|
||||
{
|
||||
/* Fast path for native iterators */
|
||||
if (iterobj->is<PropertyIteratorObject>()) {
|
||||
/*
|
||||
* Implement next directly as all the methods of the native iterator are
|
||||
* read-only and permanent.
|
||||
*/
|
||||
NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
|
||||
if (ni->isKeyIter()) {
|
||||
JS_ASSERT(ni->props_cursor < ni->props_end);
|
||||
rval.setString(*ni->current());
|
||||
ni->incCursor();
|
||||
return true;
|
||||
}
|
||||
cx->clearPendingException();
|
||||
rval.setMagic(JS_NO_ITER_VALUE);
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
|
||||
rval.set(cx->iterValue);
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -201,15 +201,11 @@ extern bool
|
|||
SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end);
|
||||
|
||||
/*
|
||||
* IteratorMore() indicates whether another value is available. It might
|
||||
* internally call iterobj.next() and then cache the value until its
|
||||
* picked up by IteratorNext(). The value is cached in the current context.
|
||||
* IteratorMore() returns the next iteration value. If no value is available,
|
||||
* MagicValue(JS_NO_ITER_VALUE) is returned.
|
||||
*/
|
||||
extern bool
|
||||
IteratorMore(JSContext *cx, HandleObject iterobj, bool *res);
|
||||
|
||||
extern bool
|
||||
IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
|
||||
IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
|
||||
|
||||
extern bool
|
||||
ThrowStopIteration(JSContext *cx);
|
||||
|
|
|
@ -1340,21 +1340,6 @@ Debugger::onSingleStep(JSContext *cx, MutableHandleValue vp)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Preserve the debuggee's iterValue while handlers run. */
|
||||
class PreserveIterValue {
|
||||
JSContext *cx;
|
||||
RootedValue savedIterValue;
|
||||
|
||||
public:
|
||||
explicit PreserveIterValue(JSContext *cx) : cx(cx), savedIterValue(cx, cx->iterValue) {
|
||||
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
|
||||
}
|
||||
~PreserveIterValue() {
|
||||
cx->iterValue = savedIterValue;
|
||||
}
|
||||
};
|
||||
PreserveIterValue piv(cx);
|
||||
|
||||
/* Call all the onStep handlers we found. */
|
||||
for (JSObject **p = frames.begin(); p != frames.end(); p++) {
|
||||
RootedObject frame(cx, *p);
|
||||
|
|
|
@ -455,9 +455,6 @@ js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
|
|||
JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
|
||||
JS_ASSERT(!cx->compartment()->activeAnalysis);
|
||||
|
||||
/* We should never enter a new script while cx->iterValue is live. */
|
||||
JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
|
||||
|
||||
/* Perform GC if necessary on exit from the function. */
|
||||
AutoGCIfNeeded gcIfNeeded(cx);
|
||||
|
||||
|
@ -1931,24 +1928,17 @@ CASE(JSOP_MOREITER)
|
|||
PUSH_NULL();
|
||||
RootedObject &obj = rootObject0;
|
||||
obj = ®S.sp[-2].toObject();
|
||||
bool cond;
|
||||
if (!IteratorMore(cx, obj, &cond))
|
||||
if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1)))
|
||||
goto error;
|
||||
REGS.sp[-1].setBoolean(cond);
|
||||
}
|
||||
END_CASE(JSOP_MOREITER)
|
||||
|
||||
CASE(JSOP_ITERNEXT)
|
||||
CASE(JSOP_ISNOITER)
|
||||
{
|
||||
JS_ASSERT(REGS.sp[-1].isObject());
|
||||
PUSH_NULL();
|
||||
MutableHandleValue res = REGS.stackHandleAt(-1);
|
||||
RootedObject &obj = rootObject0;
|
||||
obj = ®S.sp[-2].toObject();
|
||||
if (!IteratorNext(cx, obj, res))
|
||||
goto error;
|
||||
bool b = REGS.sp[-1].isMagic(JS_NO_ITER_VALUE);
|
||||
PUSH_BOOLEAN(b);
|
||||
}
|
||||
END_CASE(JSOP_ITERNEXT)
|
||||
END_CASE(JSOP_ISNOITER)
|
||||
|
||||
CASE(JSOP_ENDITER)
|
||||
{
|
||||
|
|
|
@ -615,30 +615,25 @@
|
|||
*/ \
|
||||
macro(JSOP_ITER, 75, "iter", NULL, 2, 1, 1, JOF_UINT8) \
|
||||
/*
|
||||
* Stores the next iterated value into 'cx->iterValue' and pushes 'true'
|
||||
* onto the stack if another value is available, and 'false' otherwise.
|
||||
* It is followed immediately by JSOP_IFNE.
|
||||
* Pushes the next iterated value onto the stack. If no value is available,
|
||||
* MagicValue(JS_NO_ITER_VALUE) is pushed.
|
||||
*
|
||||
* This opcode increments iterator cursor if current iteration has
|
||||
* JSITER_FOREACH flag.
|
||||
* Category: Statements
|
||||
* Type: For-In Statement
|
||||
* Operands:
|
||||
* Stack: iter => iter, cond
|
||||
*/ \
|
||||
macro(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes the value produced by the preceding JSOP_MOREITER operation
|
||||
* ('cx->iterValue') onto the stack
|
||||
*
|
||||
* This opcode increments iterator cursor if current iteration does not have
|
||||
* JSITER_FOREACH flag.
|
||||
* Category: Statements
|
||||
* Type: For-In Statement
|
||||
* Operands:
|
||||
* Stack: iter => iter, val
|
||||
*/ \
|
||||
macro(JSOP_ITERNEXT, 77, "iternext", "<next>", 1, 0, 1, JOF_BYTE) \
|
||||
macro(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, JOF_BYTE) \
|
||||
/*
|
||||
* Pushes a boolean indicating whether the value on top of the stack is
|
||||
* MagicValue(JS_NO_ITER_VALUE).
|
||||
*
|
||||
* Category: Statements
|
||||
* Type: For-In Statement
|
||||
* Operands:
|
||||
* Stack: val => val, res
|
||||
*/ \
|
||||
macro(JSOP_ISNOITER, 77, "isnoiter", NULL, 1, 1, 2, JOF_BYTE) \
|
||||
/*
|
||||
* Exits a for-in loop by popping the iterator object from the stack and
|
||||
* closing it.
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 183);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 184);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
|
Загрузка…
Ссылка в новой задаче