Bug 1204028: Evaluate LHS reference before RHS in destructuring

Issue #73
[Depends on] Bug 1147371: Implement IteratorClose
This commit is contained in:
janekptacijarabaci 2018-03-20 12:40:00 +01:00 committed by Roy Tam
commit 2f27d8433c
11 changed files with 1743 additions and 97 deletions

View file

@ -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<PropertyAccess>().isSuper()) {
if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
return false;
*emitted = 2;
} else {
if (!emitTree(target->pn_expr))
return false;
*emitted = 1;
}
break;
}
case PNK_ELEM: {
if (target->as<PropertyByValue>().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<PropertyAccess>().isSuper()) {
if (!emitSuperPropLHS(&target->as<PropertyAccess>().expression()))
return false;
if (!emit2(JSOP_PICK, 2))
return false;
if (target->as<PropertyAccess>().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<PropertyByValue>().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
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 (!emitIteratorNext(pattern)) // ... OBJ ITER RESULT
if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT RESULT
return false;
if (!emit1(JSOP_DUP)) // ... OBJ ITER 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;
}

View file

@ -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

View file

@ -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()
{

View file

@ -42,6 +42,7 @@ namespace jit {
_(JSOP_DUP2) \
_(JSOP_SWAP) \
_(JSOP_PICK) \
_(JSOP_UNPICK) \
_(JSOP_GOTO) \
_(JSOP_IFEQ) \
_(JSOP_IFNE) \

View file

@ -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));

View file

@ -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)
{

View file

@ -142,6 +142,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
// 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);

View file

@ -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);

View file

@ -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);

View file

@ -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)
{

View file

@ -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.
*