summaryrefslogtreecommitdiffstats
path: root/js/src/frontend
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-03-27 13:21:13 +0200
committerwolfbeast <mcwerewolf@gmail.com>2018-03-27 13:21:13 +0200
commit72721d1d032db2099593076bf96f274623af3c26 (patch)
tree33db746920d307db8270ab90ba12fc0c30850b96 /js/src/frontend
parent2b7f231ca7f64c9750f65922d92e3b98a3351f79 (diff)
parent2a57d73c3b5304be3f9be51018a1bbee79f007e2 (diff)
downloadUXP-72721d1d032db2099593076bf96f274623af3c26.tar
UXP-72721d1d032db2099593076bf96f274623af3c26.tar.gz
UXP-72721d1d032db2099593076bf96f274623af3c26.tar.lz
UXP-72721d1d032db2099593076bf96f274623af3c26.tar.xz
UXP-72721d1d032db2099593076bf96f274623af3c26.zip
Merge remote-tracking branch 'janek/js_lhs-before-rhs_1'
Diffstat (limited to 'js/src/frontend')
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp467
-rw-r--r--js/src/frontend/BytecodeEmitter.h23
-rw-r--r--js/src/frontend/ParseNode.h4
3 files changed, 279 insertions, 215 deletions
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index acf734794..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<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.
@@ -4463,13 +4509,6 @@ BytecodeEmitter::emitDestructuringLHS(ParseNode* target, DestructuringFlavor fla
}
bool
-BytecodeEmitter::emitDestructuringLHSInBranch(ParseNode* target, DestructuringFlavor flav)
-{
- TDZCheckCache tdzCache(this);
- return emitDestructuringLHS(target, flav);
-}
-
-bool
BytecodeEmitter::emitIteratorNext(ParseNode* pn, bool allowSelfHosted)
{
MOZ_ASSERT(allowSelfHosted || emitterMode != BytecodeEmitter::SelfHosting,
@@ -4739,123 +4778,138 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
//
// let x, y;
// let a, b, c, d;
- // let tmp, done, iter, result; // 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;
//
- // if (done) {
- // a = undefined;
+ // if (done)
+ // value = undefined;
+ // else
+ // value = result.value;
//
- // result = undefined;
- // done = true;
- // } else {
- // a = result.value;
- //
- // // Do next element's .next() and .done access here
- // result = iter.next();
- // done = result.done;
- // }
+ // SetOrInitialize(lref, value);
//
// // ==== emitted by loop for b ====
- // if (done) {
- // b = undefined;
+ // lref = GetReference(b);
//
- // result = undefined;
- // done = true;
+ // if (done) {
+ // value = undefined;
// } else {
- // b = result.value;
- //
// result = iter.next();
// done = result.done;
+ // if (done)
+ // value = undefined;
+ // else
+ // value = result.value;
// }
//
+ // SetOrInitialize(lref, value);
+ //
// // ==== emitted by loop for elision ====
// if (done) {
- // result = undefined
- // done = true
+ // value = undefined;
// } else {
- // result.value;
- //
// result = iter.next();
// done = result.done;
+ // if (done)
+ // value = undefined;
+ // else
+ // value = result.value;
// }
//
// // ==== emitted by loop for c ====
+ // lref = GetReference(c);
+ //
// if (done) {
- // c = y;
+ // value = undefined;
// } else {
- // tmp = result.value;
- // if (tmp === undefined)
- // tmp = y;
- // c = tmp;
- //
- // // Don't do next element's .next() and .done access if
- // // this is the last non-spread element.
+ // result = iter.next();
+ // done = result.done;
+ // if (done)
+ // value = undefined;
+ // else
+ // value = result.value;
// }
//
+ // if (value === undefined)
+ // value = y;
+ //
+ // SetOrInitialize(lref, value);
+ //
// // ==== emitted by loop for d ====
- // if (done) {
- // // Assing empty array when completed
- // d = [];
- // } else {
- // d = [...iter];
- // }
+ // lref = GetReference(d);
+ //
+ // if (done)
+ // value = [];
+ // else
+ // value = [...iter];
+ //
+ // SetOrInitialize(lref, value);
- /*
- * Use an iterator to destructure the RHS, instead of index lookup. We
- * must leave the *original* value on the stack.
- */
+ // Use an iterator to destructure the RHS, instead of index lookup. We
+ // must leave the *original* value on the stack.
if (!emit1(JSOP_DUP)) // ... OBJ OBJ
return false;
- if (!emitIterator()) // ... OBJ? ITER
+ if (!emitIterator()) // ... OBJ ITER
return false;
- bool needToPopIterator = true;
for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) {
bool isHead = member == pattern->pn_head;
+ 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.
- if (!ifThenElse.emitIfElse()) // ... OBJ? ITER
- return false;
+ // ... OBJ ITER DONE *LREF
+ if (emitted) {
+ if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE
+ return false;
+ }
- if (!emit1(JSOP_POP)) // ... OBJ?
- return false;
- if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ARRAY
- return false;
- if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
+ if (!ifThenElse.emitIfElse()) // ... OBJ ITER *LREF
return false;
- if (!ifThenElse.emitElse()) // ... OBJ? ITER
+ if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ARRAY
+ return false;
+ if (!ifThenElse.emitElse()) // ... OBJ ITER *LREF
return false;
}
// If iterator is not completed, create a new array with the rest
// of the iterator.
- if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ? ITER ARRAY
+ if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER
return false;
- if (!emitNumberOp(0)) // ... OBJ? ITER ARRAY INDEX
+ if (!emitUint32Operand(JSOP_NEWARRAY, 0)) // ... OBJ ITER *LREF ITER ARRAY
return false;
- if (!emitSpread()) // ... OBJ? ARRAY INDEX
+ if (!emitNumberOp(0)) // ... OBJ ITER *LREF ITER ARRAY INDEX
return false;
- if (!emit1(JSOP_POP)) // ... OBJ? ARRAY
+ if (!emitSpread()) // ... OBJ ITER *LREF ARRAY INDEX
return false;
- if (!emitDestructuringLHSInBranch(member, flav)) // ... OBJ?
+ if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF ARRAY
return false;
if (!isHead) {
if (!ifThenElse.emitEnd())
return false;
- MOZ_ASSERT(ifThenElse.popped() == 1);
+ MOZ_ASSERT(ifThenElse.pushed() == 1);
}
- needToPopIterator = false;
- MOZ_ASSERT(!member->pn_next);
+
+ if (!emitSetOrInitializeDestructuring(member, flav)) // ... OBJ ITER
+ return false;
+
+ MOZ_ASSERT(!hasNext);
break;
}
@@ -4867,110 +4921,125 @@ BytecodeEmitter::emitDestructuringOpsArray(ParseNode* pattern, DestructuringFlav
}
bool isElision = subpattern->isKind(PNK_ELISION);
- bool hasNextNonSpread = member->pn_next && !member->pn_next->isKind(PNK_SPREAD);
- bool hasNextSpread = member->pn_next && member->pn_next->isKind(PNK_SPREAD);
MOZ_ASSERT(!subpattern->isKind(PNK_SPREAD));
- auto emitNext = [pattern](ExclusiveContext* cx, BytecodeEmitter* bce) {
- if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER ITER
+ size_t emitted = 0;
+ if (!isElision) {
+ if (!emitDestructuringLHSRef(subpattern, &emitted)) // ... OBJ ITER ?DONE *LREF
return false;
- if (!bce->emitIteratorNext(pattern)) // ... OBJ? ITER RESULT
+ }
+
+ IfThenElseEmitter ifAlreadyDone(this);
+ if (!isHead) {
+ // If this element is not the first element of the pattern,
+ // iterator can already be completed.
+ // ... 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 *LREF
return false;
- if (!bce->emit1(JSOP_DUP)) // ... OBJ? ITER RESULT RESULT
+
+ if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE *LREF UNDEF
return false;
- if (!bce->emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ? ITER RESULT DONE?
+ if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE *LREF UNDEF
return false;
- return true;
- };
- if (isHead) {
- if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
+ if (!ifAlreadyDone.emitElse()) // ... OBJ ITER ?DONE *LREF
return false;
- }
- IfThenElseEmitter ifThenElse(this);
- if (!ifThenElse.emitIfElse()) // ... OBJ? ITER RESULT
- return false;
-
- if (!emit1(JSOP_POP)) // ... OBJ? ITER
- return false;
- if (pndefault) {
- // Emit only pndefault tree here, as undefined check in emitDefault
- // should always be true.
- if (!emitInitializerInBranch(pndefault, subpattern)) // ... OBJ? ITER VALUE
- return false;
- } else {
- if (!isElision) {
- if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER UNDEFINED
- return false;
- if (!emit1(JSOP_NOP_DESTRUCTURING))
+ if (hasNext) {
+ if (emitted) {
+ if (!emit2(JSOP_PICK, emitted)) // ... OBJ ITER *LREF DONE
+ return false;
+ }
+ if (!emit1(JSOP_POP)) // ... OBJ ITER *LREF
return false;
}
}
- if (!isElision) {
- if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
+
+ if (emitted) {
+ if (!emitDupAt(emitted)) // ... OBJ ITER *LREF ITER
return false;
- } else if (pndefault) {
- if (!emit1(JSOP_POP)) // ... OBJ? ITER
+ } 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 *LREF RESULT RESULT
+ return false;
+ if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ... OBJ ITER *LREF RESULT DONE
+ return false;
- // Setup next element's result when the iterator is done.
- if (hasNextNonSpread) {
- if (!emit1(JSOP_UNDEFINED)) // ... OBJ? ITER RESULT
- return false;
- if (!emit1(JSOP_NOP_DESTRUCTURING))
- return false;
- if (!emit1(JSOP_TRUE)) // ... OBJ? ITER RESULT DONE?
+ if (hasNext) {
+ if (!emit1(JSOP_DUP)) // ... OBJ ITER *LREF RESULT DONE DONE
return false;
- } else if (hasNextSpread) {
- if (!emit1(JSOP_TRUE)) // ... OBJ? ITER DONE?
+ if (!emit2(JSOP_UNPICK, emitted + 2)) // ... OBJ ITER DONE *LREF RESULT DONE
return false;
}
- if (!ifThenElse.emitElse()) // ... OBJ? ITER RESULT
+ IfThenElseEmitter ifDone(this);
+ if (!ifDone.emitIfElse()) // ... OBJ ITER ?DONE *LREF RESULT
return false;
- if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ? ITER VALUE
+ if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE *LREF
+ return false;
+ if (!emit1(JSOP_UNDEFINED)) // ... OBJ ITER ?DONE *LREF UNDEF
+ return false;
+ if (!emit1(JSOP_NOP_DESTRUCTURING)) // ... OBJ ITER ?DONE *LREF UNDEF
return false;
- if (pndefault) {
- if (!emitDefault(pndefault, subpattern)) // ... OBJ? ITER VALUE
+ if (!ifDone.emitElse()) // ... OBJ ITER ?DONE *LREF RESULT
+ return false;
+
+ if (!emitAtomOp(cx->names().value, JSOP_GETPROP)) // ... OBJ ITER ?DONE *LREF VALUE
+ return false;
+
+ if (!ifDone.emitEnd())
+ return false;
+ MOZ_ASSERT(ifDone.pushed() == 0);
+
+ if (!isHead) {
+ if (!ifAlreadyDone.emitEnd())
return false;
+ MOZ_ASSERT(ifAlreadyDone.pushed() == 1);
}
- if (!isElision) {
- if (!emitDestructuringLHSInBranch(subpattern, flav)) // ... OBJ? ITER
- return false;
- } else {
- if (!emit1(JSOP_POP)) // ... OBJ? ITER
+ if (pndefault) {
+ if (!emitDefault(pndefault, subpattern)) // ... OBJ ITER ?DONE *LREF VALUE
return false;
}
- // Setup next element's result when the iterator is not done.
- if (hasNextNonSpread) {
- if (!emitNext(cx, this)) // ... OBJ? ITER RESULT DONE?
+ if (!isElision) {
+ if (!emitSetOrInitializeDestructuring(subpattern,
+ flav)) // ... OBJ ITER ?DONE
+ {
return false;
- } else if (hasNextSpread) {
- if (!emit1(JSOP_FALSE)) // ... OBJ? ITER DONE?
+ }
+ } else {
+ if (!emit1(JSOP_POP)) // ... OBJ ITER ?DONE
return false;
}
-
- if (!ifThenElse.emitEnd())
- return false;
- if (hasNextNonSpread)
- MOZ_ASSERT(ifThenElse.pushed() == 1);
- else if (hasNextSpread)
- MOZ_ASSERT(ifThenElse.pushed() == 0);
- else
- MOZ_ASSERT(ifThenElse.popped() == 1);
}
- if (needToPopIterator) {
- if (!emit1(JSOP_POP)) // ... OBJ?
- return false;
- }
+ if (!emit1(JSOP_POP)) // ... OBJ
+ return false;
return true;
}
@@ -4994,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();
@@ -5024,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;
}
@@ -6118,7 +6200,7 @@ BytecodeEmitter::emitSpread(bool allowSelfHosted)
return false;
if (!emit1(JSOP_DUP)) // ITER ARR I RESULT RESULT
return false;
- if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER ARR I RESULT DONE?
+ if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER ARR I RESULT DONE
return false;
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget)) // ITER ARR I RESULT
@@ -6307,7 +6389,7 @@ BytecodeEmitter::emitForOf(ParseNode* forOfLoop, EmitterScope* headLexicalEmitte
return false;
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
- if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE?
+ if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
return false;
if (!emitBackwardJump(JSOP_IFEQ, top, &beq, &breakTarget))
@@ -6803,7 +6885,7 @@ BytecodeEmitter::emitComprehensionForOf(ParseNode* pn)
return false;
if (!emit1(JSOP_DUP)) // ITER RESULT RESULT
return false;
- if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE?
+ if (!emitAtomOp(cx->names().done, JSOP_GETPROP)) // ITER RESULT DONE
return false;
JumpList beq;
@@ -6975,14 +7057,13 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
RootedFunction fun(cx, funbox->function());
RootedAtom name(cx, fun->explicitName());
MOZ_ASSERT_IF(fun->isInterpretedLazy(), fun->lazyScript());
- MOZ_ASSERT_IF(pn->isOp(JSOP_FUNWITHPROTO), needsProto);
/*
* Set the |wasEmitted| flag in the funbox once the function has been
* emitted. Function definitions that need hoisting to the top of the
* function will be seen by emitFunction in two places.
*/
- if (funbox->wasEmitted && pn->functionIsHoisted()) {
+ if (funbox->wasEmitted) {
// Annex B block-scoped functions are hoisted like any other
// block-scoped function to the top of their scope. When their
// definitions are seen for the second time, we need to emit the
@@ -7111,7 +7192,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
}
if (needsProto) {
- MOZ_ASSERT(pn->getOp() == JSOP_FUNWITHPROTO || pn->getOp() == JSOP_LAMBDA);
+ MOZ_ASSERT(pn->getOp() == JSOP_LAMBDA);
pn->setOp(JSOP_FUNWITHPROTO);
}
@@ -10047,15 +10128,6 @@ CGConstList::finish(ConstArray* array)
array->vector[i] = list[i];
}
-bool
-CGObjectList::isAdded(ObjectBox* objbox)
-{
- // An objbox added to CGObjectList as non-first element has non-null
- // emitLink member. The first element has null emitLink.
- // Check for firstbox to cover the first element.
- return objbox->emitLink || objbox == firstbox;
-}
-
/*
* Find the index of the given object for code generator.
*
@@ -10067,15 +10139,9 @@ CGObjectList::isAdded(ObjectBox* objbox)
unsigned
CGObjectList::add(ObjectBox* objbox)
{
- if (isAdded(objbox))
- return indexOf(objbox->object);
-
+ MOZ_ASSERT(!objbox->emitLink);
objbox->emitLink = lastbox;
lastbox = objbox;
-
- // See the comment in CGObjectList::isAdded.
- if (!firstbox)
- firstbox = objbox;
return length++;
}
@@ -10102,12 +10168,7 @@ CGObjectList::finish(ObjectArray* array)
MOZ_ASSERT(!*cursor);
MOZ_ASSERT(objbox->object->isTenured());
*cursor = objbox->object;
-
- ObjectBox* tmp = objbox->emitLink;
- // Clear emitLink for CGObjectList::isAdded.
- objbox->emitLink = nullptr;
- objbox = tmp;
- } while (objbox != nullptr);
+ } while ((objbox = objbox->emitLink) != nullptr);
MOZ_ASSERT(cursor == array->vector);
}
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 9a2ddb568..066c06672 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -43,12 +43,10 @@ class CGConstList {
struct CGObjectList {
uint32_t length; /* number of emitted so far objects */
- ObjectBox* firstbox; /* first emitted object */
ObjectBox* lastbox; /* last emitted object */
- CGObjectList() : length(0), firstbox(nullptr), lastbox(nullptr) {}
+ CGObjectList() : length(0), lastbox(nullptr) {}
- bool isAdded(ObjectBox* objbox);
unsigned add(ObjectBox* objbox);
unsigned indexOf(JSObject* obj);
void finish(ObjectArray* array);
@@ -611,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);
@@ -644,11 +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);
- MOZ_MUST_USE bool emitDestructuringLHSInBranch(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/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index ff26279af..c58dab431 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -649,14 +649,12 @@ class ParseNode
MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
isOp(JSOP_LAMBDA_ARROW) || // arrow function
- isOp(JSOP_FUNWITHPROTO) || // already emitted lambda with needsProto
isOp(JSOP_DEFFUN) || // non-body-level function statement
isOp(JSOP_NOP) || // body-level function stmt in global code
isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
isOp(JSOP_GETARG) || // body-level function redeclaring formal
isOp(JSOP_INITLEXICAL)); // block-level function stmt
- return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) &&
- !isOp(JSOP_FUNWITHPROTO) && !isOp(JSOP_DEFFUN);
+ return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) && !isOp(JSOP_DEFFUN);
}
/*