From 2a57d73c3b5304be3f9be51018a1bbee79f007e2 Mon Sep 17 00:00:00 2001 From: janekptacijarabaci Date: Tue, 20 Mar 2018 12:40:00 +0100 Subject: Bug 1204028: Evaluate LHS reference before RHS in destructuring Issue #73 [Depends on] Bug 1147371: Implement IteratorClose --- js/src/frontend/BytecodeEmitter.cpp | 277 ++++++--- js/src/frontend/BytecodeEmitter.h | 18 +- js/src/jit/BaselineCompiler.cpp | 30 +- js/src/jit/BaselineCompiler.h | 1 + js/src/jit/IonBuilder.cpp | 5 + js/src/jit/MIRGraph.cpp | 13 + js/src/jit/MIRGraph.h | 3 + js/src/tests/ecma_6/Destructuring/order-super.js | 727 ++++++++++++++++++++++ js/src/tests/ecma_6/Destructuring/order.js | 745 +++++++++++++++++++++++ js/src/vm/Interpreter.cpp | 11 +- js/src/vm/Opcodes.h | 10 +- 11 files changed, 1743 insertions(+), 97 deletions(-) create mode 100644 js/src/tests/ecma_6/Destructuring/order-super.js create mode 100644 js/src/tests/ecma_6/Destructuring/order.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index a68fe8538..a4cfeb753 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4325,7 +4325,69 @@ BytecodeEmitter::emitDestructuringDeclsWithEmitter(ParseNode* pattern, NameEmitt } bool -BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav) +BytecodeEmitter::emitDestructuringLHSRef(ParseNode* target, size_t* emitted) +{ + *emitted = 0; + + if (target->isKind(PNK_SPREAD)) + target = target->pn_kid; + else if (target->isKind(PNK_ASSIGN)) + target = target->pn_left; + + // No need to recur into PNK_ARRAY and PNK_OBJECT subpatterns here, since + // emitSetOrInitializeDestructuring does the recursion when setting or + // initializing value. Getting reference doesn't recur. + if (target->isKind(PNK_NAME) || target->isKind(PNK_ARRAY) || target->isKind(PNK_OBJECT)) + return true; + +#ifdef DEBUG + int depth = stackDepth; +#endif + + switch (target->getKind()) { + case PNK_DOT: { + if (target->as().isSuper()) { + if (!emitSuperPropLHS(&target->as().expression())) + return false; + *emitted = 2; + } else { + if (!emitTree(target->pn_expr)) + return false; + *emitted = 1; + } + break; + } + + case PNK_ELEM: { + if (target->as().isSuper()) { + if (!emitSuperElemOperands(target, EmitElemOption::Ref)) + return false; + *emitted = 3; + } else { + if (!emitElemOperands(target, EmitElemOption::Ref)) + return false; + *emitted = 2; + } + break; + } + + case PNK_CALL: + MOZ_ASSERT_UNREACHABLE("Parser::reportIfNotValidSimpleAssignmentTarget " + "rejects function calls as assignment " + "targets in destructuring assignments"); + break; + + default: + MOZ_CRASH("emitDestructuringLHSRef: bad lhs kind"); + } + + MOZ_ASSERT(stackDepth == depth + int(*emitted)); + + return true; +} + +bool +BytecodeEmitter::emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav) { // Now emit the lvalue opcode sequence. If the lvalue is a nested // destructuring initialiser-form, call ourselves to handle it, then pop @@ -4401,44 +4463,28 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla } case PNK_DOT: { - // See the (PNK_NAME, JSOP_SETNAME) case above. - // - // In `a.x = b`, `a` is evaluated first, then `b`, then a - // JSOP_SETPROP instruction. - // - // In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we - // need a property set -- but the operands are on the stack in the - // wrong order for JSOP_SETPROP, so we have to add a JSOP_SWAP. + // The reference is already pushed by emitDestructuringLHSRef. JSOp setOp; - if (target->as().isSuper()) { - if (!emitSuperPropLHS(&target->as().expression())) - return false; - if (!emit2(JSOP_PICK, 2)) - return false; + if (target->as().isSuper()) setOp = sc->strict() ? JSOP_STRICTSETPROP_SUPER : JSOP_SETPROP_SUPER; - } else { - if (!emitTree(target->pn_expr)) - return false; - if (!emit1(JSOP_SWAP)) - return false; + else setOp = sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP; - } if (!emitAtomOp(target, setOp)) return false; break; } case PNK_ELEM: { - // See the comment at `case PNK_DOT:` above. This case, - // `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP - // is emitted by emitElemOperands. + // The reference is already pushed by emitDestructuringLHSRef. if (target->as().isSuper()) { JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM_SUPER : JSOP_SETELEM_SUPER; - if (!emitSuperElemOp(target, setOp)) + // emitDestructuringLHSRef already did emitSuperElemOperands + // part of emitSuperElemOp. Perform remaining part here. + if (!emitElemOpBase(setOp)) return false; } else { JSOp setOp = sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM; - if (!emitElemOp(target, setOp)) + if (!emitElemOpBase(setOp)) return false; } break; @@ -4451,7 +4497,7 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla break; default: - MOZ_CRASH("emitDestructuringLHS: bad lhs kind"); + MOZ_CRASH("emitSetOrInitializeDestructuring: bad lhs kind"); } // Pop the assigned value. @@ -4732,11 +4778,13 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // // let x, y; // let a, b, c, d; - // let iter, result, done, value; // stack values + // let iter, lref, result, done, value; // stack values // // iter = x[Symbol.iterator](); // // // ==== emitted by loop for a ==== + // lref = GetReference(a); + // // result = iter.next(); // done = result.done; // @@ -4745,9 +4793,11 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // else // value = result.value; // - // a = value; + // SetOrInitialize(lref, value); // // // ==== emitted by loop for b ==== + // lref = GetReference(b); + // // if (done) { // value = undefined; // } else { @@ -4759,7 +4809,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // value = result.value; // } // - // b = value; + // SetOrInitialize(lref, value); // // // ==== emitted by loop for elision ==== // if (done) { @@ -4774,6 +4824,8 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // } // // // ==== emitted by loop for c ==== + // lref = GetReference(c); + // // if (done) { // value = undefined; // } else { @@ -4788,15 +4840,17 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav // if (value === undefined) // value = y; // - // c = value; + // SetOrInitialize(lref, value); // // // ==== emitted by loop for d ==== + // lref = GetReference(d); + // // if (done) // value = []; // else // value = [...iter]; // - // d = value; + // SetOrInitialize(lref, value); // Use an iterator to destructure the RHS, instead of index lookup. We // must leave the *original* value on the stack. @@ -4810,31 +4864,40 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav bool hasNext = !!member->pn_next; if (member->isKind(PNK_SPREAD)) { + size_t emitted = 0; + if (!emitDestructuringLHSRef(member, &emitted)) // ... OBJ ITER ?DONE *LREF + return false; + IfThenElseEmitter ifThenElse(this); if (!isHead) { // If spread is not the first element of the pattern, // iterator can already be completed. - // ... OBJ ITER DONE - if (!ifThenElse.emitIfElse()) // ... OBJ ITER + // ... OBJ ITER DONE *LREF + if (emitted) { + if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE + return false; + } + + if (!ifThenElse.emitIfElse()) // ... OBJ ITER *LREF return false; - if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER ARRAY + if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ARRAY return false; - if (!ifThenElse.emitElse()) // ... OBJ ITER + if (!ifThenElse.emitElse()) // ... OBJ ITER *LREF return false; } // If iterator is not completed, create a new array with the rest // of the iterator. - if (!emit1(JSOP_DUP)) // ... OBJ ITER + if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER return false; - if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER ITER ARRAY + if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ITER ARRAY return false; - if (!emitNumberOp(0)) // ... OBJ ITER ITER ARRAY INDEX + if (!emitNumberOp(0)) // ... OBJ ITER *LREF ITER ARRAY INDEX return false; - if (!emitSpread()) // ... OBJ ITER ARRAY INDEX + if (!emitSpread()) // ... OBJ ITER *LREF ARRAY INDEX return false; - if (!emit1(JSOP_POP)) // ... OBJ ITER ARRAY + if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF ARRAY return false; if (!isHead) { @@ -4843,7 +4906,7 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav MOZ_ASSERT(ifThenElse.pushed() == 1); } - if (!emitDestructuringLHS(member, flav)) // ... OBJ ITER + if (!emitSetOrInitializeDestructuring(member, flav)) // ... OBJ ITER return false; MOZ_ASSERT(!hasNext); @@ -4861,69 +4924,91 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD)); + size_t emitted = 0; + if (!isElision) { + if (!emitDestructuringLHSRef(subpattern, &emitted)) // ... OBJ ITER ?DONE *LREF + return false; + } + IfThenElseEmitter ifAlreadyDone(this); if (!isHead) { // If this element is not the first element of the pattern, // iterator can already be completed. - // ... OBJ ITER DONE - if (hasNext) { - if (!emit1(JSOP_DUP)) // ... OBJ ITER DONE DONE - return false; + // ... OBJ ITER DONE *LREF + if (emitted) { + if (hasNext) { + if (!emitDupAt(emitted)) // ... OBJ ITER DONE *LREF DONE + return false; + } else { + if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE + return false; + } + } else { + if (hasNext) { + // The position of LREF in the following stack comment + // isn't accurate for the operation, but it's equivalent + // since LREF is nothing + if (!emit1(JSOP_DUP)) // ... OBJ ITER DONE *LREF DONE + return false; + } } - if (!ifAlreadyDone.emitIfElse()) // ... OBJ ITER ?DONE + if (!ifAlreadyDone.emitIfElse()) // ... OBJ ITER ?DONE *LREF return false; - if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE UNDEF + if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE *LREF UNDEF return false; - if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE UNDEF + if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE *LREF UNDEF return false; - if (!ifAlreadyDone.emitElse()) // ... OBJ ITER ?DONE + if (!ifAlreadyDone.emitElse()) // ... OBJ ITER ?DONE *LREF return false; if (hasNext) { - if (!emit1(JSOP_POP)) // ... OBJ ITER + if (emitted) { + if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE + return false; + } + if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF return false; } } - if (!emit1(JSOP_DUP)) // ... OBJ ITER ITER - return false; - if (!emitIteratorNext(pattern)) // ... OBJ ITER RESULT + if (emitted) { + if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER + return false; + } else { + if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF ITER + return false; + } + if (!emitIteratorNext(pattern)) // ... OBJ ITER *LREF RESULT return false; - if (!emit1(JSOP_DUP)) // ... OBJ ITER RESULT RESULT + if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT RESULT return false; - if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ ITER RESULT DONE + if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ ITER *LREF RESULT DONE return false; if (hasNext) { - if (!emit1(JSOP_DUP)) // ... OBJ ITER RESULT DONE DONE + if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT DONE DONE + return false; + if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ ITER DONE *LREF RESULT DONE return false; } IfThenElseEmitter ifDone(this); - if (!ifDone.emitIfElse()) // ... OBJ ITER RESULT ?DONE + if (!ifDone.emitIfElse()) // ... OBJ ITER ?DONE *LREF RESULT return false; - if (hasNext) { - if (!emit1(JSOP_SWAP)) // ... OBJ ITER ?DONE RESULT - return false; - } - if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE + if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE *LREF return false; - if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE UNDEF + if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE *LREF UNDEF return false; - if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE UNDEF + if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE *LREF UNDEF return false; - if (!ifDone.emitElse()) // ... OBJ ITER RESULT ?DONE + if (!ifDone.emitElse()) // ... OBJ ITER ?DONE *LREF RESULT return false; - if (hasNext) { - if (!emit1(JSOP_SWAP)) // ... OBJ ITER ?DONE RESULT - return false; - } - if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ ITER ?DONE VALUE + if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ ITER ?DONE *LREF VALUE return false; if (!ifDone.emitEnd()) @@ -4937,13 +5022,16 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav } if (pndefault) { - if (!emitDefault(pndefault, subpattern)) // ... OBJ ITER ?DONE VALUE + if (!emitDefault(pndefault, subpattern)) // ... OBJ ITER ?DONE *LREF VALUE return false; } if (!isElision) { - if (!emitDestructuringLHS(subpattern, flav)) // ... OBJ ITER ?DONE + if (!emitSetOrInitializeDestructuring(subpattern, + flav)) // ... OBJ ITER ?DONE + { return false; + } } else { if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE return false; @@ -4975,27 +5063,43 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla return false; for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { - // Duplicate the value being destructured to use as a reference base. - if (!emit1(JSOP_DUP)) // ... RHS RHS + ParseNode* subpattern; + if (member->isKind(PNK_MUTATEPROTO)) + subpattern = member->pn_kid; + else + subpattern = member->pn_right; + ParseNode* lhs = subpattern; + if (lhs->isKind(PNK_ASSIGN)) + lhs = lhs->pn_left; + + size_t emitted; + if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF return false; + // Duplicate the value being destructured to use as a reference base. + if (emitted) { + if (!emitDupAt(emitted)) // ... RHS *LREF RHS + return false; + } else { + if (!emit1(JSOP_DUP)) // ... RHS RHS + return false; + } + // Now push the property name currently being matched, which is the // current property name "label" on the left of a colon in the object // initialiser. bool needsGetElem = true; - ParseNode* subpattern; if (member->isKind(PNK_MUTATEPROTO)) { - if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS PROP + if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP return false; needsGetElem = false; - subpattern = member->pn_kid; } else { MOZ_ASSERT(member->isKind(PNK_COLON) || member->isKind(PNK_SHORTHAND)); ParseNode* key = member->pn_left; if (key->isKind(PNK_NUMBER)) { - if (!emitNumberOp(key->pn_dval)) // ... RHS RHS KEY + if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { PropertyName* name = key->pn_atom->asPropertyName(); @@ -5005,33 +5109,30 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla // as indexes for simplification of downstream analysis. jsid id = NameToId(name); if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... RHS RHS KEY + if (!emitTree(key)) // ... RHS *LREF RHS KEY return false; } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ...RHS PROP + if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP return false; needsGetElem = false; } } else { - if (!emitComputedPropertyName(key)) // ... RHS RHS KEY + if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY return false; } - - subpattern = member->pn_right; } // Get the property value if not done already. - if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS PROP + if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP return false; if (subpattern->isKind(PNK_ASSIGN)) { - if (!emitDefault(subpattern->pn_right, subpattern->pn_left)) + if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE return false; - subpattern = subpattern->pn_left; } - // Destructure PROP per this member's subpattern. - if (!emitDestructuringLHS(subpattern, flav)) + // Destructure PROP per this member's lhs. + if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS return false; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index f09b529ed..066c06672 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -609,7 +609,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter // Emit bytecode to put operands for a JSOP_GETELEM/CALLELEM/SETELEM/DELELEM // opcode onto the stack in the right order. In the case of SETELEM, the // value to be assigned must already be pushed. - enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign }; + enum class EmitElemOption { Get, Set, Call, IncDec, CompoundAssign, Ref }; MOZ_MUST_USE bool emitElemOperands(ParseNode* pn, EmitElemOption opts); MOZ_MUST_USE bool emitElemOpBase(JSOp op); @@ -642,10 +642,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter DestructuringAssignment }; - // emitDestructuringLHS assumes the to-be-destructured value has been pushed on - // the stack and emits code to destructure a single lhs expression (either a - // name or a compound []/{} expression). - MOZ_MUST_USE bool emitDestructuringLHS(ParseNode* target, DestructuringFlavor flav); + // emitDestructuringLHSRef emits the lhs expression's reference. + // 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. + MOZ_MUST_USE bool emitDestructuringLHSRef(ParseNode* target, size_t* emitted); + + // 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). + MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav); // emitDestructuringOps assumes the to-be-destructured value has been // pushed on the stack and emits code to destructure each part of a [] or diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 4dcc10b61..2f0d41707 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1147,7 +1147,7 @@ BaselineCompiler::emit_JSOP_PICK() // after : A B D E C // First, move value at -(amount + 1) into R0. - int depth = -(GET_INT8(pc) + 1); + int32_t depth = -(GET_INT8(pc) + 1); masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0); // Move the other values down. @@ -1165,6 +1165,34 @@ BaselineCompiler::emit_JSOP_PICK() return true; } +bool +BaselineCompiler::emit_JSOP_UNPICK() +{ + frame.syncStack(0); + + // Pick takes the top of the stack value and moves it under the nth value. + // For instance, unpick 2: + // before: A B C D E + // after : A B E C D + + // First, move value at -1 into R0. + masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0); + + // Move the other values up. + int32_t depth = -(GET_INT8(pc) + 1); + for (int32_t i = -1; i > depth; i--) { + Address source = frame.addressOfStackValue(frame.peek(i - 1)); + Address dest = frame.addressOfStackValue(frame.peek(i)); + masm.loadValue(source, R1); + masm.storeValue(R1, dest); + } + + // Store R0 under the nth value. + Address dest = frame.addressOfStackValue(frame.peek(depth)); + masm.storeValue(R0, dest); + return true; +} + bool BaselineCompiler::emit_JSOP_GOTO() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index 77f4dd005..60dac0966 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -42,6 +42,7 @@ namespace jit { _(JSOP_DUP2) \ _(JSOP_SWAP) \ _(JSOP_PICK) \ + _(JSOP_UNPICK) \ _(JSOP_GOTO) \ _(JSOP_IFEQ) \ _(JSOP_IFNE) \ diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 2e7784ff4..c4df415a4 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1563,6 +1563,7 @@ IonBuilder::traverseBytecode() case JSOP_DUP: case JSOP_DUP2: case JSOP_PICK: + case JSOP_UNPICK: case JSOP_SWAP: case JSOP_SETARG: case JSOP_SETLOCAL: @@ -2017,6 +2018,10 @@ IonBuilder::inspectOpcode(JSOp op) current->pick(-GET_INT8(pc)); return true; + case JSOP_UNPICK: + current->unpick(-GET_INT8(pc)); + return true; + case JSOP_GETALIASEDVAR: return jsop_getaliasedvar(EnvironmentCoordinate(pc)); diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 3a363a5bf..d6e0fa8ff 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -789,6 +789,19 @@ MBasicBlock::pick(int32_t depth) swapAt(depth); } +void +MBasicBlock::unpick(int32_t depth) +{ + // unpick take the top of the stack element and move it under the depth-th + // element; + // unpick(-2): + // A B C D E + // A B C E D [ swapAt(-1) ] + // A B E C D [ swapAt(-2) ] + for (int32_t n = -1; n >= depth; n--) + swapAt(n); +} + void MBasicBlock::swapAt(int32_t depth) { diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h index b986218f4..705d70fa1 100644 --- a/js/src/jit/MIRGraph.h +++ b/js/src/jit/MIRGraph.h @@ -142,6 +142,9 @@ class MBasicBlock : public TempObject, public InlineListNode // Move the definition to the top of the stack. void pick(int32_t depth); + // Move the top of the stack definition under the depth-th stack value. + void unpick(int32_t depth); + // Exchange 2 stack slots at the defined depth void swapAt(int32_t depth); diff --git a/js/src/tests/ecma_6/Destructuring/order-super.js b/js/src/tests/ecma_6/Destructuring/order-super.js new file mode 100644 index 000000000..afe11e2d9 --- /dev/null +++ b/js/src/tests/ecma_6/Destructuring/order-super.js @@ -0,0 +1,727 @@ +var BUGNUMBER = 1204028; +var summary = "Destructuring should evaluate lhs reference before rhs in super property"; + +if (typeof assertEq === "undefined") { + assertEq = function(a, b) { + if (a !== b) + throw new Error(`expected ${b}, got ${a}\n${new Error().stack}`); + }; +} + +print(BUGNUMBER + ": " + summary); + +let logs = []; +function log(x) { + logs.push(x); +} + +let unwrapMap = new Map(); +function unwrap(maybeWrapped) { + if (unwrapMap.has(maybeWrapped)) + return unwrapMap.get(maybeWrapped); + return maybeWrapped; +} +function ToString(name) { + if (name == Symbol.iterator) + return "@@iterator"; + return String(name); +} +function logger(obj, prefix=[]) { + let wrapped = new Proxy(obj, { + get(that, name) { + if (name == "return") { + // FIXME: Bug 1147371. + // We ignore IteratorClose for now. + return obj[name]; + } + + let names = prefix.concat(ToString(name)); + log("rhs get " + names.join("::")); + let v = obj[name]; + if (typeof v === "object" || typeof v === "function") + return logger(v, names); + return v; + }, + apply(that, thisArg, args) { + let names = prefix.slice(); + log("rhs call " + names.join("::")); + let v = obj.apply(unwrap(thisArg), args); + if (typeof v === "object" || typeof v === "function") { + names[names.length - 1] += "()"; + return logger(v, names); + } + return v; + } + }); + unwrapMap.set(wrapped, obj); + return wrapped; +} + +class C1 { + constructor() { + this.clear(); + } + clear() { + this.values = {}; + } +} +for (let name of [ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", + "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "0", "1", "length" +]) { + Object.defineProperty(C1.prototype, name, { + set: function(value) { + log("lhs set " + name); + this.values[name] = value; + } + }); +} +class C2 extends C1 { + constructor() { + super(); + + let clear = () => { + logs = []; + this.clear(); + }; + + // Array. + + clear(); + [ + super.a + ] = logger(["A"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + ].join(",")); + assertEq(this.values.a, "A"); + + clear(); + [ + super[ (log("lhs before name a"), "a") ] + ] = logger(["A"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + ].join(",")); + assertEq(this.values.a, "A"); + + // Array rest. + + clear(); + [ + ...super.a + ] = logger(["A", "B", "C"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set a", + ].join(",")); + assertEq(this.values.a.join(","), "A,B,C"); + + clear(); + [ + ...super[ (log("lhs before name a"), "a") ] + ] = logger(["A", "B", "C"]);; + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set a", + ].join(",")); + assertEq(this.values.a.join(","), "A,B,C"); + + // Array combined. + + clear(); + [ + super.a, + super[ (log("lhs before name b"), "b") ], + ...super[ (log("lhs before name c"), "c") ] + ] = logger(["A", "B", "C"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + + "lhs before name b", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set b", + + "lhs before name c", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set c", + ].join(",")); + assertEq(this.values.a, "A"); + assertEq(this.values.b, "B"); + assertEq(this.values.c.join(","), "C"); + + // Object. + + clear(); + ({ + a: super.a + } = logger({a: "A"})); + assertEq(logs.join(","), + [ + "rhs get a", + "lhs set a", + ].join(",")); + assertEq(this.values.a, "A"); + + clear(); + ({ + a: super[ (log("lhs before name a"), "a") ] + } = logger({a: "A"})); + assertEq(logs.join(","), + [ + "lhs before name a", + "rhs get a", + "lhs set a", + ].join(",")); + assertEq(this.values.a, "A"); + + // Object combined. + + clear(); + ({ + a: super.a, + b: super[ (log("lhs before name b"), "b") ] + } = logger({a: "A", b: "B"})); + assertEq(logs.join(","), + [ + "rhs get a", + "lhs set a", + + "lhs before name b", + "rhs get b", + "lhs set b", + ].join(",")); + assertEq(this.values.a, "A"); + assertEq(this.values.b, "B"); + + // == Nested == + + // Array -> Array + + clear(); + [ + [ + super[ (log("lhs before name a"), "a") ], + ...super[ (log("lhs before name b"), "b") ] + ] + ] = logger([["A", "B"]]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before name a", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set a", + + "lhs before name b", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "lhs set b", + ].join(",")); + assertEq(this.values.a, "A"); + assertEq(this.values.b.length, 1); + assertEq(this.values.b[0], "B"); + + // Array rest -> Array + + clear(); + [ + ...[ + super[ (log("lhs before name a"), "a") ], + ...super[ (log("lhs before name b"), "b") ] + ] + ] = logger(["A", "B"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before name a", + "lhs set a", + + "lhs before name b", + "lhs set b", + ].join(",")); + assertEq(this.values.a, "A"); + assertEq(this.values.b.join(","), "B"); + + // Array -> Object + clear(); + [ + { + a: super[ (log("lhs before name a"), "a") ] + } + ] = logger([{a: "A"}]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + + "lhs before name a", + "rhs get @@iterator()::next()::value::a", + "lhs set a", + ].join(",")); + assertEq(this.values.a, "A"); + + // Array rest -> Object + clear(); + [ + ...{ + 0: super[ (log("lhs before name 0"), "0") ], + 1: super[ (log("lhs before name 1"), "1") ], + length: super[ (log("lhs before name length"), "length") ], + } + ] = logger(["A", "B"]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before name 0", + "lhs set 0", + + "lhs before name 1", + "lhs set 1", + + "lhs before name length", + "lhs set length", + ].join(",")); + assertEq(this.values["0"], "A"); + assertEq(this.values["1"], "B"); + assertEq(this.values.length, 2); + + // Object -> Array + clear(); + ({ + a: [ + super[ (log("lhs before name b"), "b") ] + ] + } = logger({a: ["B"]})); + assertEq(logs.join(","), + [ + "rhs get a", + "rhs get a::@@iterator", + "rhs call a::@@iterator", + + "lhs before name b", + "rhs get a::@@iterator()::next", + "rhs call a::@@iterator()::next", + "rhs get a::@@iterator()::next()::done", + "rhs get a::@@iterator()::next()::value", + "lhs set b", + ].join(",")); + assertEq(this.values.b, "B"); + + // Object -> Object + clear(); + ({ + a: { + b: super[ (log("lhs before name b"), "b") ] + } + } = logger({a: {b: "B"}})); + assertEq(logs.join(","), + [ + "rhs get a", + "lhs before name b", + "rhs get a::b", + "lhs set b", + ].join(",")); + assertEq(this.values.b, "B"); + + // All combined + + clear(); + [ + super[ (log("lhs before name a"), "a") ], + [ + super[ (log("lhs before name b"), "b") ], + { + c: super[ (log("lhs before name c"), "c") ], + d: { + e: super[ (log("lhs before name e"), "e") ], + f: [ + super[ (log("lhs before name g"), "g") ] + ] + } + } + ], + { + h: super[ (log("lhs before name h"), "h") ], + i: [ + super[ (log("lhs before name j"), "j") ], + { + k: [ + super[ (log("lhs before name l"), "l") ] + ] + } + ] + }, + ...[ + super[ (log("lhs before name m"), "m") ], + [ + super[ (log("lhs before name n"), "n") ], + { + o: super[ (log("lhs before name o"), "o") ], + p: { + q: super[ (log("lhs before name q"), "q") ], + r: [ + super[ (log("lhs before name s"), "s") ] + ] + } + } + ], + ...{ + 0: super[ (log("lhs before name t"), "t") ], + 1: [ + super[ (log("lhs before name u"), "u") ], + { + v: super[ (log("lhs before name v"), "v") ], + w: { + x: super[ (log("lhs before name x"), "x") ], + y: [ + super[ (log("lhs before name z"), "z") ] + ] + } + } + ], + length: super[ (log("lhs before name length"), "length") ], + } + ] + ] = logger(["A", + ["B", {c: "C", d: {e: "E", f: ["G"]}}], + {h: "H", i: ["J", {k: ["L"]}]}, + "M", + ["N", {o: "O", p: {q: "Q", r: ["S"]}}], + "T", ["U", {v: "V", w: {x: "X", y: ["Z"]}}]]); + assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before name b", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set b", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before name c", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::c", + "lhs set c", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d", + + "lhs before name e", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::e", + "lhs set e", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator", + + "lhs before name g", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::value", + "lhs set g", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + + "lhs before name h", + "rhs get @@iterator()::next()::value::h", + "lhs set h", + + "rhs get @@iterator()::next()::value::i", + "rhs get @@iterator()::next()::value::i::@@iterator", + "rhs call @@iterator()::next()::value::i::@@iterator", + + "lhs before name j", + "rhs get @@iterator()::next()::value::i::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value", + "lhs set j", + + "rhs get @@iterator()::next()::value::i::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value", + + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator", + "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator", + + "lhs before name l", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::value", + "lhs set l", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before name m", + "lhs set m", + + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before name n", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set n", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before name o", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::o", + "lhs set o", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p", + + "lhs before name q", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::q", + "lhs set q", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator", + + "lhs before name s", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::value", + "lhs set s", + + "lhs before name t", + "lhs set t", + + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before name u", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set u", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before name v", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::v", + "lhs set v", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w", + + "lhs before name x", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::x", + "lhs set x", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator", + + "lhs before name z", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::value", + "lhs set z", + + "lhs before name length", + "lhs set length", + ].join(",")); + assertEq(this.values.a, "A"); + assertEq(this.values.b, "B"); + assertEq(this.values.c, "C"); + assertEq(this.values.e, "E"); + assertEq(this.values.g, "G"); + assertEq(this.values.h, "H"); + assertEq(this.values.j, "J"); + assertEq(this.values.l, "L"); + assertEq(this.values.m, "M"); + assertEq(this.values.n, "N"); + assertEq(this.values.o, "O"); + assertEq(this.values.q, "Q"); + assertEq(this.values.s, "S"); + assertEq(this.values.t, "T"); + assertEq(this.values.u, "U"); + assertEq(this.values.v, "V"); + assertEq(this.values.x, "X"); + assertEq(this.values.z, "Z"); + assertEq(this.values.length, 2); + } +} + +new C2(); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/Destructuring/order.js b/js/src/tests/ecma_6/Destructuring/order.js new file mode 100644 index 000000000..4eb48cd2b --- /dev/null +++ b/js/src/tests/ecma_6/Destructuring/order.js @@ -0,0 +1,745 @@ +var BUGNUMBER = 1204028; +var summary = "Destructuring should evaluate lhs reference before rhs"; + +print(BUGNUMBER + ": " + summary); + +let storage = { + clear() { + this.values = {}; + } +}; +storage.clear(); +let obj = new Proxy(storage, { + set(that, name, value) { + log("lhs set " + name); + storage.values[name] = value; + } +}); + +let logs = []; +function log(x) { + logs.push(x); +} + +function clear() { + logs = []; + storage.clear(); +} + +let unwrapMap = new Map(); +function unwrap(maybeWrapped) { + if (unwrapMap.has(maybeWrapped)) + return unwrapMap.get(maybeWrapped); + return maybeWrapped; +} +function ToString(name) { + if (name == Symbol.iterator) + return "@@iterator"; + return String(name); +} +function logger(obj, prefix=[]) { + let wrapped = new Proxy(obj, { + get(that, name) { + if (name == "return") { + // FIXME: Bug 1147371. + // We ignore IteratorClose for now. + return obj[name]; + } + + let names = prefix.concat(ToString(name)); + log("rhs get " + names.join("::")); + let v = obj[name]; + if (typeof v === "object" || typeof v === "function") + return logger(v, names); + return v; + }, + apply(that, thisArg, args) { + let names = prefix.slice(); + log("rhs call " + names.join("::")); + let v = obj.apply(unwrap(thisArg), args); + if (typeof v === "object" || typeof v === "function") { + names[names.length - 1] += "()"; + return logger(v, names); + } + return v; + } + }); + unwrapMap.set(wrapped, obj); + return wrapped; +} + +// Array. + +clear(); +[ + ( log("lhs before obj a"), obj ).a +] = logger(["A"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + ].join(",")); +assertEq(storage.values.a, "A"); + +clear(); +[ + ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ] +] = logger(["A"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + ].join(",")); +assertEq(storage.values.a, "A"); + +// Array rest. + +clear(); +[ + ...( log("lhs before obj a"), obj ).a +] = logger(["A", "B", "C"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set a", + ].join(",")); +assertEq(storage.values.a.join(","), "A,B,C"); + +clear(); +[ + ...( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ] +] = logger(["A", "B", "C"]);; +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set a", + ].join(",")); +assertEq(storage.values.a.join(","), "A,B,C"); + +// Array combined. + +clear(); +[ + ( log("lhs before obj a"), obj ).a, + ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ], + ...( log("lhs before obj c"), obj )[ (log("lhs before name c"), "c") ] +] = logger(["A", "B", "C"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + + "lhs before obj b", + "lhs before name b", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set b", + + "lhs before obj c", + "lhs before name c", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "lhs set c", + ].join(",")); +assertEq(storage.values.a, "A"); +assertEq(storage.values.b, "B"); +assertEq(storage.values.c.join(","), "C"); + +// Object. + +clear(); +({ + a: ( log("lhs before obj a"), obj ).a +} = logger({a: "A"})); +assertEq(logs.join(","), + [ + "lhs before obj a", + "rhs get a", + "lhs set a", + ].join(",")); +assertEq(storage.values.a, "A"); + +clear(); +({ + a: ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ] +} = logger({a: "A"})); +assertEq(logs.join(","), + [ + "lhs before obj a", + "lhs before name a", + "rhs get a", + "lhs set a", + ].join(",")); +assertEq(storage.values.a, "A"); + +// Object combined. + +clear(); +({ + a: ( log("lhs before obj a"), obj ).a, + b: ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ] +} = logger({a: "A", b: "B"})); +assertEq(logs.join(","), + [ + "lhs before obj a", + "rhs get a", + "lhs set a", + + "lhs before obj b", + "lhs before name b", + "rhs get b", + "lhs set b", + ].join(",")); +assertEq(storage.values.a, "A"); +assertEq(storage.values.b, "B"); + +// == Nested == + +// Array -> Array + +clear(); +[ + [ + ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ], + ...( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ] + ] +] = logger([["A", "B"]]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before obj a", + "lhs before name a", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set a", + + "lhs before obj b", + "lhs before name b", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "lhs set b", + ].join(",")); +assertEq(storage.values.a, "A"); +assertEq(storage.values.b.length, 1); +assertEq(storage.values.b[0], "B"); + +// Array rest -> Array + +clear(); +[ + ...[ + ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ], + ...( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ] + ] +] = logger(["A", "B"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before obj a", + "lhs before name a", + "lhs set a", + + "lhs before obj b", + "lhs before name b", + "lhs set b", + ].join(",")); +assertEq(storage.values.a, "A"); +assertEq(storage.values.b.join(","), "B"); + +// Array -> Object +clear(); +[ + { + a: ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ] + } +] = logger([{a: "A"}]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + + "lhs before obj a", + "lhs before name a", + "rhs get @@iterator()::next()::value::a", + "lhs set a", + ].join(",")); +assertEq(storage.values.a, "A"); + +// Array rest -> Object +clear(); +[ + ...{ + 0: ( log("lhs before obj 0"), obj )[ (log("lhs before name 0"), "0") ], + 1: ( log("lhs before obj 1"), obj )[ (log("lhs before name 1"), "1") ], + length: ( log("lhs before obj length"), obj )[ (log("lhs before name length"), "length") ], + } +] = logger(["A", "B"]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before obj 0", + "lhs before name 0", + "lhs set 0", + + "lhs before obj 1", + "lhs before name 1", + "lhs set 1", + + "lhs before obj length", + "lhs before name length", + "lhs set length", + ].join(",")); +assertEq(storage.values["0"], "A"); +assertEq(storage.values["1"], "B"); +assertEq(storage.values.length, 2); + +// Object -> Array +clear(); +({ + a: [ + ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ] + ] +} = logger({a: ["B"]})); +assertEq(logs.join(","), + [ + "rhs get a", + "rhs get a::@@iterator", + "rhs call a::@@iterator", + + "lhs before obj b", + "lhs before name b", + "rhs get a::@@iterator()::next", + "rhs call a::@@iterator()::next", + "rhs get a::@@iterator()::next()::done", + "rhs get a::@@iterator()::next()::value", + "lhs set b", + ].join(",")); +assertEq(storage.values.b, "B"); + +// Object -> Object +clear(); +({ + a: { + b: ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ] + } +} = logger({a: {b: "B"}})); +assertEq(logs.join(","), + [ + "rhs get a", + "lhs before obj b", + "lhs before name b", + "rhs get a::b", + "lhs set b", + ].join(",")); +assertEq(storage.values.b, "B"); + +// All combined + +clear(); +[ + ( log("lhs before obj a"), obj )[ (log("lhs before name a"), "a") ], + [ + ( log("lhs before obj b"), obj )[ (log("lhs before name b"), "b") ], + { + c: ( log("lhs before obj c"), obj )[ (log("lhs before name c"), "c") ], + d: { + e: ( log("lhs before obj e"), obj )[ (log("lhs before name e"), "e") ], + f: [ + ( log("lhs before obj g"), obj )[ (log("lhs before name g"), "g") ] + ] + } + } + ], + { + h: ( log("lhs before obj h"), obj )[ (log("lhs before name h"), "h") ], + i: [ + ( log("lhs before obj j"), obj )[ (log("lhs before name j"), "j") ], + { + k: [ + ( log("lhs before obj l"), obj )[ (log("lhs before name l"), "l") ] + ] + } + ] + }, + ...[ + ( log("lhs before obj m"), obj )[ (log("lhs before name m"), "m") ], + [ + ( log("lhs before obj n"), obj )[ (log("lhs before name n"), "n") ], + { + o: ( log("lhs before obj o"), obj )[ (log("lhs before name o"), "o") ], + p: { + q: ( log("lhs before obj q"), obj )[ (log("lhs before name q"), "q") ], + r: [ + ( log("lhs before obj s"), obj )[ (log("lhs before name s"), "s") ] + ] + } + } + ], + ...{ + 0: ( log("lhs before obj t"), obj )[ (log("lhs before name t"), "t") ], + 1: [ + ( log("lhs before obj u"), obj )[ (log("lhs before name u"), "u") ], + { + v: ( log("lhs before obj v"), obj )[ (log("lhs before name v"), "v") ], + w: { + x: ( log("lhs before obj x"), obj )[ (log("lhs before name x"), "x") ], + y: [ + ( log("lhs before obj z"), obj )[ (log("lhs before name z"), "z") ] + ] + } + } + ], + length: ( log("lhs before obj length"), obj )[ (log("lhs before name length"), "length") ], + } + ] +] = logger(["A", + ["B", {c: "C", d: {e: "E", f: ["G"]}}], + {h: "H", i: ["J", {k: ["L"]}]}, + "M", + ["N", {o: "O", p: {q: "Q", r: ["S"]}}], + "T", ["U", {v: "V", w: {x: "X", y: ["Z"]}}]]); +assertEq(logs.join(","), + [ + "rhs get @@iterator", + "rhs call @@iterator", + + "lhs before obj a", + "lhs before name a", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "lhs set a", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before obj b", + "lhs before name b", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set b", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before obj c", + "lhs before name c", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::c", + "lhs set c", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d", + + "lhs before obj e", + "lhs before name e", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::e", + "lhs set e", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator", + + "lhs before obj g", + "lhs before name g", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::d::f::@@iterator()::next()::value", + "lhs set g", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + + "lhs before obj h", + "lhs before name h", + "rhs get @@iterator()::next()::value::h", + "lhs set h", + + "rhs get @@iterator()::next()::value::i", + "rhs get @@iterator()::next()::value::i::@@iterator", + "rhs call @@iterator()::next()::value::i::@@iterator", + + "lhs before obj j", + "lhs before name j", + "rhs get @@iterator()::next()::value::i::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value", + "lhs set j", + + "rhs get @@iterator()::next()::value::i::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value", + + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator", + "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator", + + "lhs before obj l", + "lhs before name l", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next", + "rhs call @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::i::@@iterator()::next()::value::k::@@iterator()::next()::value", + "lhs set l", + + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + "rhs get @@iterator()::next()::value", + "rhs get @@iterator()::next", + "rhs call @@iterator()::next", + "rhs get @@iterator()::next()::done", + + "lhs before obj m", + "lhs before name m", + "lhs set m", + + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before obj n", + "lhs before name n", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set n", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before obj o", + "lhs before name o", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::o", + "lhs set o", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p", + + "lhs before obj q", + "lhs before name q", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::q", + "lhs set q", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator", + + "lhs before obj s", + "lhs before name s", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::p::r::@@iterator()::next()::value", + "lhs set s", + + "lhs before obj t", + "lhs before name t", + "lhs set t", + + "rhs get @@iterator()::next()::value::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator", + + "lhs before obj u", + "lhs before name u", + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + "lhs set u", + + "rhs get @@iterator()::next()::value::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value", + + "lhs before obj v", + "lhs before name v", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::v", + "lhs set v", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w", + + "lhs before obj x", + "lhs before name x", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::x", + "lhs set x", + + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator", + + "lhs before obj z", + "lhs before name z", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next", + "rhs call @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::done", + "rhs get @@iterator()::next()::value::@@iterator()::next()::value::w::y::@@iterator()::next()::value", + "lhs set z", + + "lhs before obj length", + "lhs before name length", + "lhs set length", + ].join(",")); +assertEq(storage.values.a, "A"); +assertEq(storage.values.b, "B"); +assertEq(storage.values.c, "C"); +assertEq(storage.values.e, "E"); +assertEq(storage.values.g, "G"); +assertEq(storage.values.h, "H"); +assertEq(storage.values.j, "J"); +assertEq(storage.values.l, "L"); +assertEq(storage.values.m, "M"); +assertEq(storage.values.n, "N"); +assertEq(storage.values.o, "O"); +assertEq(storage.values.q, "Q"); +assertEq(storage.values.s, "S"); +assertEq(storage.values.t, "T"); +assertEq(storage.values.u, "U"); +assertEq(storage.values.v, "V"); +assertEq(storage.values.x, "X"); +assertEq(storage.values.z, "Z"); +assertEq(storage.values.length, 2); + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 9cba1f4dc..8ae9c43b0 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -1860,7 +1860,6 @@ CASE(EnableInterruptsPseudoOpcode) /* Various 1-byte no-ops. */ CASE(JSOP_NOP) CASE(JSOP_NOP_DESTRUCTURING) -CASE(JSOP_UNUSED183) CASE(JSOP_UNUSED187) CASE(JSOP_UNUSED192) CASE(JSOP_UNUSED209) @@ -2193,6 +2192,16 @@ CASE(JSOP_PICK) } END_CASE(JSOP_PICK) +CASE(JSOP_UNPICK) +{ + int i = GET_UINT8(REGS.pc); + MOZ_ASSERT(REGS.stackDepth() >= unsigned(i) + 1); + Value lval = REGS.sp[-1]; + memmove(REGS.sp - i, REGS.sp - (i + 1), sizeof(Value) * i); + REGS.sp[-(i + 1)] = lval; +} +END_CASE(JSOP_UNPICK) + CASE(JSOP_BINDGNAME) CASE(JSOP_BINDNAME) { diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index f6636004d..b59b9388c 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1880,8 +1880,14 @@ * Stack: fun, name => fun */ \ macro(JSOP_SETFUNNAME, 182,"setfunname", NULL, 2, 2, 1, JOF_UINT8) \ - macro(JSOP_UNUSED183, 183,"unused183", NULL, 1, 0, 0, JOF_BYTE) \ - \ + /* + * Moves the top of the stack value under the nth element of the stack. + * Category: Operators + * Type: Stack Operations + * Operands: uint8_t n + * Stack: v[n], v[n-1], ..., v[1], v[0] => v[0], v[n], v[n-1], ..., v[1] + */ \ + macro(JSOP_UNPICK, 183,"unpick", NULL, 2, 0, 0, JOF_UINT8) \ /* * Pops the top of stack value, pushes property of it onto the stack. * -- cgit v1.2.3