author | Jan de Mooij <jdemooij@mozilla.com> |
Wed, 17 Sep 2014 21:07:37 +0200 | |
changeset 205902 | 26d86ab7c4f35da9e7db86c1e0e7cf2203f4bbd6 |
parent 205901 | c0021e5609b2fd656b02f2711652ddcece27bc91 |
child 205903 | c9115053bdc6c7cdfd25daf174320f674637c357 |
push id | 27507 |
push user | ryanvm@gmail.com |
push date | Thu, 18 Sep 2014 02:16:54 +0000 |
treeherder | mozilla-central@488d490da742 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bhackett |
bugs | 831585 |
milestone | 35.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -599,16 +599,18 @@ NonLocalExitScope::prepareForNonLocalJum return false; break; case STMT_FOR_OF_LOOP: npops += 2; 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; break; case STMT_SPREAD: MOZ_ASSERT_UNREACHABLE("can't break/continue/return from inside a spread"); break; @@ -713,20 +715,18 @@ PushLoopStatement(BytecodeEmitter *bce, } stmt->stackDepth = bce->stackDepth; stmt->loopDepth = downLoop ? downLoop->loopDepth + 1 : 1; 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; MOZ_ASSERT(loopSlots <= stmt->stackDepth); if (downLoop) stmt->canIonOsr = (downLoop->canIonOsr && stmt->stackDepth == downLoop->stackDepth + loopSlots); @@ -4878,16 +4878,21 @@ EmitForIn(ExclusiveContext *cx, Bytecode * Emit a bytecode to convert top of stack value to the iterator * object depending on the loop variant (for-in, for-each-in, or * destructuring for-in). */ JS_ASSERT(pn->isOp(JSOP_ITER)); 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. StmtInfoBCE letStmt(cx); if (letDecl) { if (!EnterBlockScope(cx, bce, &letStmt, pn1->pn_objbox, JSOP_UNDEFINED, 0)) return false; } @@ -4912,28 +4917,21 @@ EmitForIn(ExclusiveContext *cx, Bytecode SET_STATEMENT_TOP(&stmtInfo, top); if (EmitLoopHead(cx, bce, nullptr) < 0) return false; #ifdef DEBUG 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); /* Emit code for the loop body. */ if (!EmitTree(cx, bce, forBody)) return false; /* Set loop and enclosing "update" offsets, for continue. */ @@ -4943,30 +4941,38 @@ EmitForIn(ExclusiveContext *cx, Bytecode } while ((stmt = stmt->down) != nullptr && stmt->type == STMT_LABEL); /* * Fixup the goto that starts the loop to jump down to JSOP_MOREITER. */ 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; /* Set the srcnote offset so we can find the closing jump. */ if (!SetSrcNoteOffset(cx, bce, (unsigned)noteIndex, 0, beq - jmp)) return false; // Fix up breaks and continues. 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) return false; if (letDecl) { if (!LeaveNestedScope(cx, bce, &letStmt)) return false;
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -3087,26 +3087,31 @@ BaselineCompiler::emit_JSOP_MOREITER() if (!emitOpIC(compiler.getStub(&stubSpace_))) return false; frame.push(R0); return true; } 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; - - frame.push(R0); + + Label isMagic, done; + masm.branchTestMagic(Assembler::Equal, frame.addressOfStackValue(frame.peek(-1)), + &isMagic); + masm.moveValue(BooleanValue(false), R0); + masm.jump(&done); + + masm.bind(&isMagic); + masm.moveValue(BooleanValue(true), R0); + + masm.bind(&done); + frame.push(R0, JSVAL_TYPE_BOOLEAN); return true; } bool BaselineCompiler::emit_JSOP_ENDITER() { frame.popRegsAndSync(1);
--- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -165,17 +165,17 @@ namespace jit { _(JSOP_DEBUGGER) \ _(JSOP_ARGUMENTS) \ _(JSOP_RUNONCE) \ _(JSOP_REST) \ _(JSOP_TOID) \ _(JSOP_TABLESWITCH) \ _(JSOP_ITER) \ _(JSOP_MOREITER) \ - _(JSOP_ITERNEXT) \ + _(JSOP_ISNOITER) \ _(JSOP_ENDITER) \ _(JSOP_CALLEE) \ _(JSOP_SETRVAL) \ _(JSOP_RETRVAL) \ _(JSOP_RETURN) class BaselineCompiler : public BaselineCompilerSpecific {
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -9839,25 +9839,26 @@ static bool DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallback *stub_, HandleObject iterObj, MutableHandleValue res) { // This fallback stub may trigger debug mode toggling. DebugModeOSRVolatileStub<ICIteratorMore_Fallback *> stub(frame, stub_); FallbackICSpew(cx, stub, "IteratorMore"); - bool cond; - if (!IteratorMore(cx, iterObj, &cond)) - return false; - res.setBoolean(cond); + if (!IteratorMore(cx, iterObj, res)) + return false; // 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)) { ICIteratorMore_Native::Compiler compiler(cx); ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script())); if (!newStub) return false; stub->addNewStub(newStub); @@ -9901,117 +9902,37 @@ ICIteratorMore_Native::Compiler::generat 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); - // 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); - - 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); + // 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); + + // 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); return true; } // // IteratorClose_Fallback
--- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -433,18 +433,16 @@ class ICEntry _(SetProp_CallScripted) \ _(SetProp_CallNative) \ \ _(TableSwitch) \ \ _(IteratorNew_Fallback) \ _(IteratorMore_Fallback) \ _(IteratorMore_Native) \ - _(IteratorNext_Fallback) \ - _(IteratorNext_Native) \ _(IteratorClose_Fallback) \ \ _(InstanceOf_Fallback) \ \ _(TypeOf_Fallback) \ _(TypeOf_Typed) \ \ _(Rest_Fallback) \ @@ -6133,16 +6131,24 @@ class ICIteratorMore_Fallback : public I public: static inline ICIteratorMore_Fallback *New(ICStubSpace *space, JitCode *code) { if (!code) return nullptr; 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); public: explicit Compiler(JSContext *cx) : ICStubCompiler(cx, ICStub::IteratorMore_Fallback) { } @@ -6179,86 +6185,16 @@ class ICIteratorMore_Native : public ICS { } ICStub *getStub(ICStubSpace *space) { return ICIteratorMore_Native::New(space, getStubCode()); } }; }; -// 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 { friend class ICStubSpace; explicit ICIteratorClose_Fallback(JitCode *stubCode) : ICFallbackStub(ICStub::IteratorClose_Fallback, stubCode) { }
--- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -376,27 +376,27 @@ BaselineInspector::hasSeenAccessedGetter ICStub *stub = entry.fallbackStub(); if (stub->isGetProp_Fallback()) return stub->toGetProp_Fallback()->hasAccessedGetter(); return false; } 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; const ICEntry &entry = icEntryFromPC(pc); ICStub *stub = entry.fallbackStub(); - return stub->toIteratorNext_Fallback()->hasNonStringResult(); + return stub->toIteratorMore_Fallback()->hasNonStringResult(); } bool BaselineInspector::hasSeenDoubleResult(jsbytecode *pc) { if (!hasBaselineScript()) return false;
--- a/js/src/jit/BaselineInspector.h +++ b/js/src/jit/BaselineInspector.h @@ -102,17 +102,17 @@ class BaselineInspector MIRType expectedResultType(jsbytecode *pc); MCompare::CompareType expectedCompareType(jsbytecode *pc); MIRType expectedBinaryArithSpecialization(jsbytecode *pc); bool hasSeenNonNativeGetElement(jsbytecode *pc); 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); DeclEnvObject *templateDeclEnvObject(); CallObject *templateCallObject(); JSObject *commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter);
--- a/js/src/jit/BytecodeAnalysis.cpp +++ b/js/src/jit/BytecodeAnalysis.cpp @@ -48,28 +48,29 @@ BytecodeAnalysis::init(TempAllocator &al jsbytecode *end = script_->codeEnd(); // Clear all BytecodeInfo. mozilla::PodZero(infos_.begin(), infos_.length()); infos_[0].init(/*stackDepth=*/0); 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", int(script_->pcToOffset(pc)), int(script_->length()), js_CodeName[op]); // If this bytecode info has not yet been initialized, it's not reachable. if (!infos_[offset].initialized) continue; - unsigned stackDepth = infos_[offset].stackDepth; #ifdef DEBUG for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++) JS_ASSERT(!infos_[script_->pcToOffset(chkpc)].initialized); #endif unsigned nuses = GetUseCount(script_, offset); unsigned ndefs = GetDefCount(script_, offset); @@ -185,32 +186,27 @@ BytecodeAnalysis::init(TempAllocator &al // If this is a a backedge to an un-analyzed segment, analyze from there. bool jumpBack = (targetOffset < offset) && !infos_[targetOffset].initialized; infos_[targetOffset].init(newStackDepth); 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; } } return true; }
--- a/js/src/jit/BytecodeAnalysis.h +++ b/js/src/jit/BytecodeAnalysis.h @@ -16,18 +16,16 @@ namespace jit { // Basic information about bytecodes in the script. Used to help baseline compilation. struct BytecodeInfo { static const uint16_t MAX_STACK_DEPTH = 0xffffU; 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; void init(unsigned depth) { JS_ASSERT(depth <= MAX_STACK_DEPTH); JS_ASSERT_IF(initialized, stackDepth == depth); initialized = true;
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -6364,76 +6364,74 @@ LoadNativeIterator(MacroAssembler &masm, // Test class. masm.branchTestObjClass(Assembler::NotEqual, obj, dest, &PropertyIteratorObject::class_, failures); // Load NativeIterator object. 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()); - - masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)), + Register outputScratch = output.scratchReg(); + LoadNativeIterator(masm, obj, outputScratch, ool->entry()); + + 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); bool CodeGenerator::visitIteratorEnd(LIteratorEnd *lir) { const Register obj = ToRegister(lir->object()); const Register temp1 = ToRegister(lir->temp1());
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -260,18 +260,18 @@ class CodeGenerator : public CodeGenerat bool visitLoadTypedArrayElementHole(LLoadTypedArrayElementHole *lir); bool visitStoreTypedArrayElement(LStoreTypedArrayElement *lir); bool visitStoreTypedArrayElementHole(LStoreTypedArrayElementHole *lir); bool visitClampIToUint8(LClampIToUint8 *lir); bool visitClampDToUint8(LClampDToUint8 *lir); 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); bool visitSetFrameArgumentT(LSetFrameArgumentT *lir); bool visitSetFrameArgumentC(LSetFrameArgumentC *lir); bool visitSetFrameArgumentV(LSetFrameArgumentV *lir); bool visitRunOncePrologue(LRunOncePrologue *lir); bool emitRest(LInstruction *lir, Register array, Register numActuals,
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -486,16 +486,18 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl else if (*pc == JSOP_SETARG) slot = info().argSlotUnchecked(GET_ARGNO(pc)); else continue; if (slot >= info().firstStackSlot()) continue; if (!analysis().maybeInfo(pc)) continue; + if (!last) + continue; MPhi *phi = entry->getSlot(slot)->toPhi(); if (*last == JSOP_POS) last = earlier; if (js_CodeSpec[*last].format & JOF_TYPESET) { types::TemporaryTypeSet *typeSet = bytecodeTypes(last); @@ -556,17 +558,16 @@ IonBuilder::analyzeNewLoopTypes(MBasicBl type = MIRType_Boolean; break; case JSOP_DOUBLE: type = MIRType_Double; break; case JSOP_STRING: case JSOP_TYPEOF: case JSOP_TYPEOFEXPR: - case JSOP_ITERNEXT: type = MIRType_String; break; case JSOP_ADD: case JSOP_SUB: case JSOP_MUL: case JSOP_DIV: case JSOP_MOD: case JSOP_NEG: @@ -1778,22 +1779,22 @@ IonBuilder::inspectOpcode(JSOp op) return jsop_lambda(info().getFunction(pc)); case JSOP_LAMBDA_ARROW: return jsop_lambda_arrow(info().getFunction(pc)); 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(); case JSOP_IN: return jsop_in(); case JSOP_SETRVAL: JS_ASSERT(!script()->noScriptRval()); @@ -2253,16 +2254,33 @@ IonBuilder::processWhileCondEnd(CFGState test = newTest(ins, state.loop.successor, body); current->end(test); state.state = CFGState::WHILE_LOOP_BODY; state.stopAt = state.loop.bodyEnd; 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; } IonBuilder::ControlStatus IonBuilder::processWhileBodyEnd(CFGState &state) { if (!processDeferredContinues(state)) return ControlStatus_Error; @@ -10449,50 +10467,41 @@ IonBuilder::jsop_iter(uint8_t flags) current->add(ins); current->push(ins); 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() { MDefinition *iter = current->peek(-1); MInstruction *ins = MIteratorMore::New(alloc(), iter); current->add(ins); current->push(ins); 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() { MDefinition *iter = current->pop(); MInstruction *ins = MIteratorEnd::New(alloc(), iter); current->add(ins); return resumeAfter(ins);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -646,18 +646,18 @@ class IonBuilder bool jsop_regexp(RegExpObject *reobj); bool jsop_object(JSObject *obj); bool jsop_lambda(JSFunction *fun); bool jsop_lambda_arrow(JSFunction *fun); bool jsop_this(); 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(); bool jsop_instanceof(); bool jsop_getaliasedvar(ScopeCoordinate sc); bool jsop_setaliasedvar(ScopeCoordinate sc); /* Inlining. */
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -5566,37 +5566,17 @@ class LIteratorStart : public LInstructi const LDefinition *temp3() { return getTemp(2); } MIteratorStart *mir() const { return mir_->toIteratorStart(); } }; -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) LIteratorMore(const LAllocation &iterator, const LDefinition &temp) { setOperand(0, iterator); setTemp(0, temp); } @@ -5606,16 +5586,36 @@ class LIteratorMore : public LInstructio const LDefinition *temp() { return getTemp(0); } MIteratorMore *mir() const { return mir_->toIteratorMore(); } }; +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: LIR_HEADER(IteratorEnd) LIteratorEnd(const LAllocation &iterator, const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3) { setOperand(0, iterator);
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -254,18 +254,18 @@ _(SetPropertyCacheV) \ _(SetPropertyCacheT) \ _(SetElementCacheV) \ _(SetElementCacheT) \ _(SetPropertyPolymorphicV) \ _(SetPropertyPolymorphicT) \ _(CallIteratorStart) \ _(IteratorStart) \ - _(IteratorNext) \ _(IteratorMore) \ + _(IsNoIterAndBranch) \ _(IteratorEnd) \ _(ArrayLength) \ _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ _(TypedObjectProto) \ _(TypedObjectElements) \ _(SetTypedObjectOffset) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -869,16 +869,28 @@ LIRGenerator::visitTest(MTest *test) MDefinition *lhs = opd->getOperand(0); MDefinition *rhs = opd->getOperand(1); if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) { ReorderCommutative(&lhs, &rhs, test); return lowerForBitAndAndBranch(new(alloc()) LBitAndAndBranch(ifTrue, ifFalse), test, lhs, rhs); } } + 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)); if (opd->type() == MIRType_Float32) return add(new(alloc()) LTestFAndBranch(useRegister(opd), ifTrue, ifFalse)); JS_ASSERT(opd->type() == MIRType_Int32 || opd->type() == MIRType_Boolean); return add(new(alloc()) LTestIAndBranch(useRegister(opd), ifTrue, ifFalse)); @@ -3327,27 +3339,27 @@ LIRGenerator::visitIteratorStart(MIterat return defineReturn(lir, ins) && assignSafepoint(lir, ins); } LIteratorStart *lir = new(alloc()) LIteratorStart(useRegister(ins->object()), temp(), temp(), temp()); return define(lir, ins) && assignSafepoint(lir, 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 LIRGenerator::visitIteratorEnd(MIteratorEnd *ins) { LIteratorEnd *lir = new(alloc()) LIteratorEnd(useRegister(ins->iterator()), temp(), temp(), temp()); return add(lir, ins) && assignSafepoint(lir, ins); }
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -231,18 +231,18 @@ class LIRGenerator : public LIRGenerator bool visitCallsiteCloneCache(MCallsiteCloneCache *ins); bool visitCallGetElement(MCallGetElement *ins); bool visitCallSetElement(MCallSetElement *ins); bool visitCallInitElementArray(MCallInitElementArray *ins); bool visitSetPropertyCache(MSetPropertyCache *ins); 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); bool visitGetFrameArgument(MGetFrameArgument *ins); bool visitSetFrameArgument(MSetFrameArgument *ins); bool visitRunOncePrologue(MRunOncePrologue *ins); bool visitRest(MRest *ins); bool visitRestPar(MRestPar *ins);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -10208,49 +10208,24 @@ class MIteratorStart MDefinition *object() const { return getOperand(0); } uint8_t flags() const { return flags_; } }; -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 { explicit MIteratorMore(MDefinition *iter) : MUnaryInstruction(iter) { - setResultType(MIRType_Boolean); + setResultType(MIRType_Value); } public: INSTRUCTION_HEADER(IteratorMore) static MIteratorMore *New(TempAllocator &alloc, MDefinition *iter) { return new(alloc) MIteratorMore(iter); } @@ -10258,16 +10233,38 @@ class MIteratorMore TypePolicy *typePolicy() { return this; } MDefinition *iterator() const { return getOperand(0); } }; +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 { explicit MIteratorEnd(MDefinition *iter) : MUnaryInstruction(iter) { }
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -187,18 +187,18 @@ namespace jit { _(CallGetElement) \ _(CallSetElement) \ _(CallSetProperty) \ _(CallInitElementArray) \ _(DeleteProperty) \ _(DeleteElement) \ _(SetPropertyCache) \ _(IteratorStart) \ - _(IteratorNext) \ _(IteratorMore) \ + _(IsNoIter) \ _(IteratorEnd) \ _(StringLength) \ _(ArgumentsLength) \ _(GetFrameArgument) \ _(SetFrameArgument) \ _(RunOncePrologue) \ _(Rest) \ _(Floor) \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -269,18 +269,18 @@ class ParallelSafetyVisitor : public MDe UNSAFE_OP(CallGetElement) WRITE_GUARDED_OP(CallSetElement, object) UNSAFE_OP(CallInitElementArray) WRITE_GUARDED_OP(CallSetProperty, object) UNSAFE_OP(DeleteProperty) 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) SAFE_OP(GetFrameArgument) UNSAFE_OP(SetFrameArgument) UNSAFE_OP(RunOncePrologue) CUSTOM_OP(Rest) SAFE_OP(RestPar)
--- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -2057,16 +2057,22 @@ RangeAnalysis::addRangeAssertions() // Perform range checking for all numeric and numeric-like types. if (!IsNumberType(ins->type()) && ins->type() != MIRType_Boolean && ins->type() != MIRType_Value) { 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. if (r.isUnknown() || (ins->type() == MIRType_Int32 && r.isUnknownInt32())) continue; MAssertRange *guard = MAssertRange::New(alloc(), ins, new(alloc()) Range(r));
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1104,17 +1104,16 @@ JSContext::JSContext(JSRuntime *rt) reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY), resolvingList(nullptr), generatingError(false), savedFrameChains_(), cycleDetectorSet(MOZ_THIS_IN_INITIALIZER_LIST()), data(nullptr), data2(nullptr), outstandingRequests(0), - iterValue(MagicValue(JS_NO_ITER_VALUE)), jitIsBroken(false), #ifdef MOZ_TRACE_JSCALLS functionCallback(nullptr), #endif innermostGenerator_(nullptr) { JS_ASSERT(static_cast<ContextFriendFields*>(this) == ContextFriendFields::get(this)); @@ -1299,18 +1298,16 @@ JSContext::mark(JSTracer *trc) { /* Stack frames and slots are traced by StackSpace::mark. */ /* Mark other roots-by-definition in the JSContext. */ if (isExceptionPending()) MarkValueRoot(trc, &unwrappedException_, "unwrapped exception"); TraceCycleDetectionSet(trc, cycleDetectorSet); - - MarkValueRoot(trc, &iterValue, "iterValue"); } void * ThreadSafeContext::stackLimitAddressForJitCode(StackKind kind) { #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR) return runtime_->mainThread.addressOfSimulatorStackLimit(); #endif
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -485,19 +485,16 @@ struct JSContext : public js::ExclusiveC } js::LifoAlloc &tempLifoAlloc() { return runtime()->tempLifoAlloc; } unsigned outstandingRequests;/* number of JS_BeginRequest calls without the corresponding JS_EndRequest. */ - /* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */ - js::Value iterValue; - bool jitIsBroken; void updateJITEnabled(); /* Whether this context has JS frames on the stack. */ bool currentlyRunning() const; bool currentlyRunningInInterpreter() const {
--- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -860,26 +860,25 @@ IsIterator(HandleValue v) MOZ_ALWAYS_INLINE bool iterator_next_impl(JSContext *cx, CallArgs args) { JS_ASSERT(IsIterator(args.thisv())); 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 iterator_next(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args); } @@ -1012,23 +1011,16 @@ static bool CloseLegacyGenerator(JSContext *cx, HandleObject genobj); bool 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. */ obj = &vp.toObject(); } else { /* * Enumerating over null and undefined gives an empty enumerator, so * that |for (var p in <null or undefined>) <loop>;| never executes @@ -1042,18 +1034,16 @@ js::ValueToIterator(JSContext *cx, unsig } return GetIterator(cx, obj, flags, 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(); if (ni->flags & JSITER_ENUMERATE) { ni->unlink(); JS_ASSERT(ni->flags & JSITER_ACTIVE); @@ -1250,103 +1240,71 @@ class IndexRangePredicate { bool js::SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end) { return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end)); } 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; } - } - - /* We might still have a pending value. */ - if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) { - *res = true; - return true; + if (ni->isKeyIter()) { + rval.setString(*ni->current()); + ni->incCursor(); + 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); RootedValue current(cx, StringValue(*ni->current())); if (!ValueToId<CanGC>(cx, current, &id)) return false; ni->incCursor(); RootedObject obj(cx, ni->obj); - if (!JSObject::getGeneric(cx, obj, obj, id, &val)) - return false; - if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, val, &val)) - return false; - } else { - /* Call the iterator object's .next method. */ - if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, &val)) + if (!JSObject::getGeneric(cx, obj, obj, id, rval)) 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; - } + if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval)) + return false; + 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; } static bool stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp) { *bp = JS_IsStopIteration(v); return true;
--- a/js/src/jsiter.h +++ b/js/src/jsiter.h @@ -196,25 +196,21 @@ SuppressDeletedProperty(JSContext *cx, H extern bool SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index); 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); /* * Create an object of the form { value: VALUE, done: DONE }. * ES6 draft from 2013-09-05, section 25.4.3.4. */
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1335,31 +1335,16 @@ Debugger::onSingleStep(JSContext *cx, Mu } if (trappingScript->compileAndGo()) JS_ASSERT(stepperCount == trappingScript->stepModeCount()); else JS_ASSERT(stepperCount <= trappingScript->stepModeCount()); } #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); Debugger *dbg = Debugger::fromChildJSObject(frame); Maybe<AutoCompartment> ac; ac.emplace(cx, dbg->object);
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -450,19 +450,16 @@ struct AutoGCIfNeeded * when done. Then push the return value. */ bool 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); /* MaybeConstruct is a subset of InitialFrameFlags */ InitialFrameFlags initial = (InitialFrameFlags) construct; if (args.calleev().isPrimitive()) return ReportIsNotFunction(cx, args.calleev(), args.length() + 1, construct); @@ -1926,34 +1923,27 @@ END_CASE(JSOP_ITER) CASE(JSOP_MOREITER) { JS_ASSERT(REGS.stackDepth() >= 1); JS_ASSERT(REGS.sp[-1].isObject()); 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) { JS_ASSERT(REGS.stackDepth() >= 1); RootedObject &obj = rootObject0; obj = ®S.sp[-1].toObject(); bool ok = CloseIterator(cx, obj); REGS.sp--;
--- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -610,40 +610,35 @@ 1234567890123456789012345678901234567890 * 'val' and pushes 'iter' which is an iterator for 'val'. * Category: Statements * Type: For-In Statement * Operands: uint8_t flags * Stack: val => iter */ \ 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. * Category: Statements * Type: For-In Statement * Operands: * Stack: iter => */ \
--- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -23,17 +23,17 @@ namespace js { * versions. If deserialization fails, the data should be invalidated if * possible. * * When you change this, run make_opcode_doc.py and copy the new output into * this wiki page: * * 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: explicit XDRBuffer(JSContext *cx) : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { } JSContext *cx() const { return context;