diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index b32e833d641e..43ae68d1368c 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2595,23 +2595,63 @@ bool BytecodeEmitter::emitFunctionScript(FunctionNode* funNode) { return fse.intoStencil(); } +class js::frontend::DestructuringLHSRef { + struct None { + size_t numReferenceSlots() const { return 0; } + }; + + mozilla::Variant + emitter_ = AsVariant(None{}); + + public: + template + void from(T&& emitter) { + emitter_.emplace(std::forward(emitter)); + } + + template + T& emitter() { + return emitter_.as(); + } + + /** + * Return the number of values pushed onto the stack. + */ + size_t numReferenceSlots() const { + return emitter_.match([](auto& e) { return e.numReferenceSlots(); }); + } +}; + bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, - size_t* emitted) { + DestructuringFlavor flav, + DestructuringLHSRef& lref) { #ifdef DEBUG int depth = bytecodeSection().stackDepth(); #endif switch (target->getKind()) { - case ParseNodeKind::Name: case ParseNodeKind::ArrayExpr: case ParseNodeKind::ObjectExpr: // No need to recurse into ParseNodeKind::Array and ParseNodeKind::Object // subpatterns here, since emitSetOrInitializeDestructuring does the - // recursion when setting or initializing the value. Getting reference - // doesn't recurse. - *emitted = 0; + // recursion when setting or initializing the value. break; + case ParseNodeKind::Name: { + auto* name = &target->as(); + NameOpEmitter noe(this, name->atom(), + flav == DestructuringFlavor::Assignment + ? NameOpEmitter::Kind::SimpleAssignment + : NameOpEmitter::Kind::Initialize); + if (!noe.prepareForRhs()) { + return false; + } + + lref.from(std::move(noe)); + return true; + } + case ParseNodeKind::ArgumentsLength: case ParseNodeKind::DotExpr: { PropertyAccess* prop = &target->as(); @@ -2642,8 +2682,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, return false; } - // SUPERBASE was pushed onto THIS in poe.prepareForRhs above. - *emitted = 1 + isSuper; + lref.from(std::move(poe)); break; } @@ -2669,8 +2708,7 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, return false; } - // SUPERBASE was pushed onto KEY in eoe.prepareForRhs above. - *emitted = 2 + isSuper; + lref.from(std::move(eoe)); break; } @@ -2686,7 +2724,8 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, // [stack] OBJ NAME return false; } - *emitted = xoe.numReferenceSlots(); + + lref.from(std::move(xoe)); break; } @@ -2701,13 +2740,14 @@ bool BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, MOZ_CRASH("emitDestructuringLHSRef: bad lhs kind"); } - MOZ_ASSERT(bytecodeSection().stackDepth() == depth + int(*emitted)); + MOZ_ASSERT(bytecodeSection().stackDepth() == + depth + int(lref.numReferenceSlots())); return true; } bool BytecodeEmitter::emitSetOrInitializeDestructuring( - ParseNode* target, DestructuringFlavor flav) { + ParseNode* target, DestructuringFlavor flav, DestructuringLHSRef& lref) { // Now emit the lvalue opcode sequence. If the lvalue is a nested // destructuring initialiser-form, call ourselves to handle it, then pop // the matched value. Otherwise emit an lvalue bytecode sequence followed @@ -2724,51 +2764,14 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( break; case ParseNodeKind::Name: { - auto name = target->as().name(); - NameLocation loc = lookupName(name); - NameOpEmitter::Kind kind; - switch (flav) { - case DestructuringFlavor::Declaration: - kind = NameOpEmitter::Kind::Initialize; - break; + // The environment is already pushed by emitDestructuringLHSRef. + // [stack] ENV? VAL + auto& noe = lref.emitter(); - case DestructuringFlavor::Assignment: - kind = NameOpEmitter::Kind::SimpleAssignment; - break; - } - - NameOpEmitter noe(this, name, loc, kind); - if (!noe.prepareForRhs()) { - // [stack] V ENV? - return false; - } - if (noe.emittedBindOp()) { - // This is like ordinary assignment, but with one difference. - // - // In `a = b`, we first determine a binding for `a` (using - // JSOp::BindName or JSOp::BindGName), then we evaluate `b`, then - // a JSOp::SetName instruction. - // - // In `[a] = [b]`, per spec, `b` is evaluated first, then we - // determine a binding for `a`. Then we need to do assignment-- - // but the operands are on the stack in the wrong order for - // JSOp::SetProp, so we have to add a JSOp::Swap. - // - // In the cases where we are emitting a name op, emit a swap - // because of this. - if (!emit1(JSOp::Swap)) { - // [stack] ENV V - return false; - } - } else { - // In cases of emitting a frame slot or environment slot, - // nothing needs be done. - } if (!noe.emitAssignment()) { - // [stack] V + // [stack] VAL return false; } - break; } @@ -2779,16 +2782,11 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( // [stack] THIS SUPERBASE VAL // [stack] # otherwise // [stack] OBJ VAL - PropertyAccess* prop = &target->as(); - bool isSuper = prop->isSuper(); - PropOpEmitter poe(this, PropOpEmitter::Kind::SimpleAssignment, - isSuper ? PropOpEmitter::ObjKind::Super - : PropOpEmitter::ObjKind::Other); - if (!poe.skipObjAndRhs()) { - return false; - } - // [stack] # VAL + auto& poe = lref.emitter(); + auto* prop = &target->as(); + if (!poe.emitAssignment(prop->key().atom())) { + // [stack] # VAL return false; } break; @@ -2800,15 +2798,8 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( // [stack] THIS KEY SUPERBASE VAL // [stack] # otherwise // [stack] OBJ KEY VAL - PropertyByValue* elem = &target->as(); - bool isSuper = elem->isSuper(); - MOZ_ASSERT(!elem->key().isKind(ParseNodeKind::PrivateName)); - ElemOpEmitter eoe(this, ElemOpEmitter::Kind::SimpleAssignment, - isSuper ? ElemOpEmitter::ObjKind::Super - : ElemOpEmitter::ObjKind::Other); - if (!eoe.skipObjAndKeyAndRhs()) { - return false; - } + auto& eoe = lref.emitter(); + if (!eoe.emitAssignment()) { // [stack] VAL return false; @@ -2819,12 +2810,8 @@ bool BytecodeEmitter::emitSetOrInitializeDestructuring( case ParseNodeKind::PrivateMemberExpr: { // The reference is already pushed by emitDestructuringLHSRef. // [stack] OBJ NAME VAL - PrivateMemberAccess* privateExpr = &target->as(); - PrivateOpEmitter xoe(this, PrivateOpEmitter::Kind::SimpleAssignment, - privateExpr->privateName().name()); - if (!xoe.skipReference()) { - return false; - } + auto& xoe = lref.emitter(); + if (!xoe.emitAssignment()) { // [stack] VAL return false; @@ -3375,6 +3362,7 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, idx += 1; continue; } + MOZ_ASSERT(member->isKind(ParseNodeKind::Name)); if (!emit1(JSOp::Dup)) { // [stack] LENGTH OBJ OBJ @@ -3432,7 +3420,13 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, return false; } - if (!emitSetOrInitializeDestructuring(member, flav)) { + DestructuringLHSRef lref; + if (!emitDestructuringLHSRef(member, flav, lref)) { + // [stack] LENGTH OBJ + return false; + } + + if (!emitSetOrInitializeDestructuring(member, flav, lref)) { // [stack] LENGTH OBJ return false; } @@ -3519,14 +3513,12 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, pndefault = subpattern->as().right(); } - // Number of stack slots emitted for the LHS reference. - size_t emitted = 0; - // Spec requires LHS reference to be evaluated first. + DestructuringLHSRef lref; bool isElision = lhsPattern->isKind(ParseNodeKind::Elision); if (!isElision) { - auto emitLHSRef = [lhsPattern, &emitted](BytecodeEmitter* bce) { - return bce->emitDestructuringLHSRef(lhsPattern, &emitted); + auto emitLHSRef = [lhsPattern, flav, &lref](BytecodeEmitter* bce) { + return bce->emitDestructuringLHSRef(lhsPattern, flav, lref); // [stack] ... OBJ NEXT ITER DONE LREF* }; if (!wrapWithDestructuringTryNote(tryNoteDepth, emitLHSRef)) { @@ -3534,6 +3526,9 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, } } + // Number of stack slots emitted for the LHS reference. + size_t emitted = lref.numReferenceSlots(); + // Pick the DONE value to the top of the stack. if (emitted) { if (!emitPickN(emitted)) { @@ -3616,8 +3611,8 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, return false; } - auto emitAssignment = [lhsPattern, flav](BytecodeEmitter* bce) { - return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); + auto emitAssignment = [lhsPattern, flav, &lref](BytecodeEmitter* bce) { + return bce->emitSetOrInitializeDestructuring(lhsPattern, flav, lref); // [stack] ... OBJ NEXT ITER TRUE }; if (!wrapWithDestructuringTryNote(tryNoteDepth, emitAssignment)) { @@ -3741,8 +3736,8 @@ bool BytecodeEmitter::emitDestructuringOpsArray(ListNode* pattern, } if (!isElision) { - auto emitAssignment = [lhsPattern, flav](BytecodeEmitter* bce) { - return bce->emitSetOrInitializeDestructuring(lhsPattern, flav); + auto emitAssignment = [lhsPattern, flav, &lref](BytecodeEmitter* bce) { + return bce->emitSetOrInitializeDestructuring(lhsPattern, flav, lref); // [stack] ... OBJ NEXT ITER DONE }; @@ -3863,15 +3858,16 @@ bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, pndefault = subpattern->as().right(); } - // Number of stack slots emitted for the LHS reference. - size_t emitted = 0; - // Spec requires LHS reference to be evaluated first. - if (!emitDestructuringLHSRef(lhs, &emitted)) { + DestructuringLHSRef lref; + if (!emitDestructuringLHSRef(lhs, flav, lref)) { // [stack] ... SET? RHS KEY? LREF* return false; } + // Number of stack slots emitted for the LHS reference. + size_t emitted = lref.numReferenceSlots(); + // Duplicate the value being destructured to use as a reference base. if (!emitDupAt(emitted + hasKeyOnStack)) { // [stack] ... SET? RHS KEY? LREF* RHS @@ -3911,7 +3907,7 @@ bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, } // Destructure TARGET per this member's lhs. - if (!emitSetOrInitializeDestructuring(lhs, flav)) { + if (!emitSetOrInitializeDestructuring(lhs, flav, lref)) { // [stack] ... RHS return false; } @@ -3998,7 +3994,7 @@ bool BytecodeEmitter::emitDestructuringOpsObject(ListNode* pattern, } // Destructure PROP per this member's lhs. - if (!emitSetOrInitializeDestructuring(lhs, flav)) { + if (!emitSetOrInitializeDestructuring(lhs, flav, lref)) { // [stack] ... SET? RHS return false; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index ca02a03491ff..cfa59817c90a 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -55,6 +55,7 @@ namespace frontend { class BytecodeOffset; class CallOrNewEmitter; class ClassEmitter; +class DestructuringLHSRef; class ElemOpEmitter; class EmitterScope; class ErrorReporter; @@ -787,16 +788,16 @@ struct MOZ_STACK_CLASS BytecodeEmitter { // If the lhs expression is object property |OBJ.prop|, it emits |OBJ|. // If it's object element |OBJ[ELEM]|, it emits |OBJ| and |ELEM|. // If there's nothing to evaluate for the reference, it emits nothing. - // |emitted| parameter receives the number of values pushed onto the stack. [[nodiscard]] bool emitDestructuringLHSRef(ParseNode* target, - size_t* emitted); + DestructuringFlavor flav, + DestructuringLHSRef& lref); // emitSetOrInitializeDestructuring assumes the lhs expression's reference // and the to-be-destructured value has been pushed on the stack. It emits // code to destructure a single lhs expression (either a name or a compound // []/{} expression). - [[nodiscard]] bool emitSetOrInitializeDestructuring(ParseNode* target, - DestructuringFlavor flav); + [[nodiscard]] bool emitSetOrInitializeDestructuring( + ParseNode* target, DestructuringFlavor flav, DestructuringLHSRef& lref); // emitDestructuringObjRestExclusionSet emits the property exclusion set // for the rest-property in an object pattern. diff --git a/js/src/frontend/ElemOpEmitter.cpp b/js/src/frontend/ElemOpEmitter.cpp index 553022ad6757..307aaccd7833 100644 --- a/js/src/frontend/ElemOpEmitter.cpp +++ b/js/src/frontend/ElemOpEmitter.cpp @@ -138,16 +138,6 @@ bool ElemOpEmitter::prepareForRhs() { return true; } -bool ElemOpEmitter::skipObjAndKeyAndRhs() { - MOZ_ASSERT(state_ == State::Start); - MOZ_ASSERT(isSimpleAssignment() || isPropInit()); - -#ifdef DEBUG - state_ = State::Rhs; -#endif - return true; -} - bool ElemOpEmitter::emitDelete() { MOZ_ASSERT(state_ == State::Key); MOZ_ASSERT(isDelete()); diff --git a/js/src/frontend/ElemOpEmitter.h b/js/src/frontend/ElemOpEmitter.h index cf528bf3125f..d40c2673735b 100644 --- a/js/src/frontend/ElemOpEmitter.h +++ b/js/src/frontend/ElemOpEmitter.h @@ -142,38 +142,36 @@ class MOZ_STACK_CLASS ElemOpEmitter { #ifdef DEBUG // The state of this emitter. // - // skipObjAndKeyAndRhs - // +------------------------------------------------+ - // | | - // +-------+ | prepareForObj +-----+ prepareForKey +-----+ | - // | Start |-+-------------->| Obj |-------------->| Key |-+ | - // +-------+ +-----+ +-----+ | | - // | | - // +-------------------------------------------------------+ | - // | | - // | [Get] | - // | [Call] | - // | emitGet +-----+ | - // +---------->| Get | | - // | +-----+ | - // | | - // | [Delete] | - // | emitDelete +--------+ | - // +------------->| Delete | | - // | +--------+ | - // | | - // | [PostIncrement] | - // | [PreIncrement] | - // | [PostDecrement] | - // | [PreDecrement] | - // | emitIncDec +--------+ | - // +------------->| IncDec | | - // | +--------+ | - // | +-------------------+ - // | [SimpleAssignment] | - // | [PropInit] | - // | prepareForRhs v +-----+ - // +--------------------->+-------------->+->| Rhs |-+ + // + // +-------+ prepareForObj +-----+ prepareForKey +-----+ + // | Start |---------------->| Obj |-------------->| Key |-+ + // +-------+ +-----+ +-----+ | + // | + // +-------------------------------------------------------+ + // | + // | [Get] + // | [Call] + // | emitGet +-----+ + // +---------->| Get | + // | +-----+ + // | + // | [Delete] + // | emitDelete +--------+ + // +------------->| Delete | + // | +--------+ + // | + // | [PostIncrement] + // | [PreIncrement] + // | [PostDecrement] + // | [PreDecrement] + // | emitIncDec +--------+ + // +------------->| IncDec | + // | +--------+ + // | + // | [SimpleAssignment] + // | [PropInit] + // | prepareForRhs +-----+ + // +--------------------->+----------------->| Rhs |-+ // | ^ +-----+ | // | | | // | | +-------------+ @@ -200,7 +198,7 @@ class MOZ_STACK_CLASS ElemOpEmitter { // After calling emitIncDec. IncDec, - // After calling prepareForRhs or skipObjAndKeyAndRhs. + // After calling prepareForRhs. Rhs, // After calling emitAssignment. @@ -252,13 +250,14 @@ class MOZ_STACK_CLASS ElemOpEmitter { [[nodiscard]] bool emitGet(); [[nodiscard]] bool prepareForRhs(); - [[nodiscard]] bool skipObjAndKeyAndRhs(); [[nodiscard]] bool emitDelete(); [[nodiscard]] bool emitAssignment(); [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); + + size_t numReferenceSlots() const { return 2 + isSuper(); } }; } /* namespace frontend */ diff --git a/js/src/frontend/NameOpEmitter.h b/js/src/frontend/NameOpEmitter.h index 62d632a321b0..a844bf5be35a 100644 --- a/js/src/frontend/NameOpEmitter.h +++ b/js/src/frontend/NameOpEmitter.h @@ -174,6 +174,8 @@ class MOZ_STACK_CLASS NameOpEmitter { [[nodiscard]] bool prepareForRhs(); [[nodiscard]] bool emitAssignment(); [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); + + size_t numReferenceSlots() const { return emittedBindOp(); } }; } /* namespace frontend */ diff --git a/js/src/frontend/PrivateOpEmitter.cpp b/js/src/frontend/PrivateOpEmitter.cpp index c10463eb56a7..3520235cf22e 100644 --- a/js/src/frontend/PrivateOpEmitter.cpp +++ b/js/src/frontend/PrivateOpEmitter.cpp @@ -100,19 +100,6 @@ bool PrivateOpEmitter::emitReference() { return true; } -bool PrivateOpEmitter::skipReference() { - MOZ_ASSERT(state_ == State::Start); - - if (!init()) { - return false; - } - -#ifdef DEBUG - state_ = State::Reference; -#endif - return true; -} - bool PrivateOpEmitter::emitGet() { MOZ_ASSERT(state_ == State::Reference); diff --git a/js/src/frontend/PrivateOpEmitter.h b/js/src/frontend/PrivateOpEmitter.h index 558541b05c36..09c80218ea12 100644 --- a/js/src/frontend/PrivateOpEmitter.h +++ b/js/src/frontend/PrivateOpEmitter.h @@ -114,8 +114,7 @@ class MOZ_STACK_CLASS PrivateOpEmitter { #ifdef DEBUG // The state of this emitter. // - // emitReference - // +-------+ skipReference +-----------+ + // +-------+ emitReference +-----------+ // | Start |---------------->| Reference | // +-------+ +-----+-----+ // | @@ -144,7 +143,7 @@ class MOZ_STACK_CLASS PrivateOpEmitter { // The initial state. Start, - // After calling emitReference or skipReference. + // After calling emitReference. Reference, // After calling emitGet. @@ -216,13 +215,12 @@ class MOZ_STACK_CLASS PrivateOpEmitter { [[nodiscard]] bool emitBrandCheck(); [[nodiscard]] bool emitReference(); - [[nodiscard]] bool skipReference(); [[nodiscard]] bool emitGet(); [[nodiscard]] bool emitGetForCallOrNew(); [[nodiscard]] bool emitAssignment(); [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); - [[nodiscard]] size_t numReferenceSlots() { return 2; } + size_t numReferenceSlots() const { return 2; } }; } /* namespace frontend */ diff --git a/js/src/frontend/PropOpEmitter.cpp b/js/src/frontend/PropOpEmitter.cpp index d9bd5b6d482e..9e420bd6ab1b 100644 --- a/js/src/frontend/PropOpEmitter.cpp +++ b/js/src/frontend/PropOpEmitter.cpp @@ -112,16 +112,6 @@ bool PropOpEmitter::prepareForRhs() { return true; } -bool PropOpEmitter::skipObjAndRhs() { - MOZ_ASSERT(state_ == State::Start); - MOZ_ASSERT(isSimpleAssignment() || isPropInit()); - -#ifdef DEBUG - state_ = State::Rhs; -#endif - return true; -} - bool PropOpEmitter::emitDelete(TaggedParserAtomIndex prop) { MOZ_ASSERT(state_ == State::Obj); MOZ_ASSERT(isDelete()); diff --git a/js/src/frontend/PropOpEmitter.h b/js/src/frontend/PropOpEmitter.h index 76402025ea0e..fefe327662ca 100644 --- a/js/src/frontend/PropOpEmitter.h +++ b/js/src/frontend/PropOpEmitter.h @@ -128,39 +128,37 @@ class MOZ_STACK_CLASS PropOpEmitter { #ifdef DEBUG // The state of this emitter. // - // skipObjAndRhs - // +----------------------------+ - // | | - // +-------+ | prepareForObj +-----+ | - // | Start |-+-------------->| Obj |-+ | - // +-------+ +-----+ | | - // | | - // +---------------------------------+ | - // | | - // | | - // | [Get] | - // | [Call] | - // | emitGet +-----+ | - // +---------->| Get | | - // | +-----+ | - // | | - // | [Delete] | - // | emitDelete +--------+ | - // +------------->| Delete | | - // | +--------+ | - // | | - // | [PostIncrement] | - // | [PreIncrement] | - // | [PostDecrement] | - // | [PreDecrement] | - // | emitIncDec +--------+ | - // +------------->| IncDec | | - // | +--------+ | - // | | - // | [SimpleAssignment] | - // | [PropInit] | - // | prepareForRhs | +-----+ - // +--------------------->+-------------->+->| Rhs |-+ + // + // +-------+ prepareForObj +-----+ + // | Start |---------------->| Obj |-+ + // +-------+ +-----+ | + // | + // +---------------------------------+ + // | + // | + // | [Get] + // | [Call] + // | emitGet +-----+ + // +---------->| Get | + // | +-----+ + // | + // | [Delete] + // | emitDelete +--------+ + // +------------->| Delete | + // | +--------+ + // | + // | [PostIncrement] + // | [PreIncrement] + // | [PostDecrement] + // | [PreDecrement] + // | emitIncDec +--------+ + // +------------->| IncDec | + // | +--------+ + // | + // | [SimpleAssignment] + // | [PropInit] + // | prepareForRhs +-----+ + // +--------------------->+----------------->| Rhs |-+ // | ^ +-----+ | // | | | // | | +---------+ @@ -184,7 +182,7 @@ class MOZ_STACK_CLASS PropOpEmitter { // After calling emitIncDec. IncDec, - // After calling prepareForRhs or skipObjAndRhs. + // After calling prepareForRhs. Rhs, // After calling emitAssignment. @@ -237,7 +235,6 @@ class MOZ_STACK_CLASS PropOpEmitter { [[nodiscard]] bool emitGet(TaggedParserAtomIndex prop); [[nodiscard]] bool prepareForRhs(); - [[nodiscard]] bool skipObjAndRhs(); [[nodiscard]] bool emitDelete(TaggedParserAtomIndex prop); @@ -246,6 +243,8 @@ class MOZ_STACK_CLASS PropOpEmitter { [[nodiscard]] bool emitIncDec(TaggedParserAtomIndex prop, ValueUsage valueUsage); + + size_t numReferenceSlots() const { return 1 + isSuper(); } }; } /* namespace frontend */