mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-07 08:18:41 +09:00
Issue #2097 - Implement logical assignment operators
Based-on: 1629106/1, 1684020
This commit is contained in:
parent
ba9647a12a
commit
10951a169c
9 changed files with 302 additions and 6 deletions
|
|
@ -53,6 +53,8 @@ enum AssignmentOperator {
|
|||
AOP_LSH, AOP_RSH, AOP_URSH,
|
||||
/* binary */
|
||||
AOP_BITOR, AOP_BITXOR, AOP_BITAND,
|
||||
/* short-circuit */
|
||||
AOP_COALESCE, AOP_OR, AOP_AND,
|
||||
|
||||
AOP_LIMIT
|
||||
};
|
||||
|
|
@ -122,6 +124,9 @@ static const char* const aopNames[] = {
|
|||
"|=", /* AOP_BITOR */
|
||||
"^=", /* AOP_BITXOR */
|
||||
"&=" /* AOP_BITAND */
|
||||
"\?\?=", /* AOP_COALESCE */
|
||||
"||=", /* AOP_OR */
|
||||
"&&=", /* AOP_AND */
|
||||
};
|
||||
|
||||
static const char* const binopNames[] = {
|
||||
|
|
@ -1974,6 +1979,12 @@ ASTSerializer::aop(JSOp op)
|
|||
return AOP_BITXOR;
|
||||
case JSOP_BITAND:
|
||||
return AOP_BITAND;
|
||||
case JSOP_COALESCE:
|
||||
return AOP_COALESCE;
|
||||
case JSOP_OR:
|
||||
return AOP_OR;
|
||||
case JSOP_AND:
|
||||
return AOP_AND;
|
||||
default:
|
||||
return AOP_ERR;
|
||||
}
|
||||
|
|
@ -3103,6 +3114,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
|
|||
|
|
@ -517,6 +517,17 @@ BytecodeEmitter::emitPopN(unsigned n)
|
|||
return emitUint16Operand(JSOP_POPN, n);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitUnpickN(unsigned n)
|
||||
{
|
||||
MOZ_ASSERT(n != 0);
|
||||
|
||||
if (n == 1)
|
||||
return emit1(JSOP_SWAP);
|
||||
|
||||
return emit2(JSOP_UNPICK, n);
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitCheckIsObj(CheckIsObjectKind kind)
|
||||
{
|
||||
|
|
@ -1221,6 +1232,9 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
@ -3977,6 +3991,225 @@ BytecodeEmitter::emitAssignmentOrInit(ParseNodeKind kind, JSOp compoundOp,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BytecodeEmitter::emitShortCircuitAssignment(ParseNodeKind kind, JSOp op,
|
||||
ParseNode* lhs, ParseNode* rhs)
|
||||
{
|
||||
TDZCheckCache tdzCache(this);
|
||||
|
||||
// |name| is used within NameOpEmitter, so its lifetime must surpass |noe|.
|
||||
RootedAtom name(cx);
|
||||
|
||||
// Select the appropriate emitter based on the left-hand side.
|
||||
Maybe<NameOpEmitter> noe;
|
||||
Maybe<PropOpEmitter> poe;
|
||||
Maybe<ElemOpEmitter> eoe;
|
||||
|
||||
int32_t startDepth = stackDepth;
|
||||
|
||||
// Number of values pushed onto the stack in addition to the lhs value.
|
||||
int32_t numPushed;
|
||||
|
||||
// Evaluate the left-hand side expression and compute any stack values needed
|
||||
// for the assignment.
|
||||
switch (lhs->getKind()) {
|
||||
case PNK_NAME: {
|
||||
name = lhs->as<NameNode>().name();
|
||||
noe.emplace(this, name, NameOpEmitter::Kind::CompoundAssignment);
|
||||
|
||||
if (!noe->prepareForRhs()) {
|
||||
// [stack] ENV? LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
numPushed = noe->emittedBindOp();
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_DOT: {
|
||||
PropertyAccess* prop = &lhs->as<PropertyAccess>();
|
||||
bool isSuper = prop->isSuper();
|
||||
|
||||
poe.emplace(this, PropOpEmitter::Kind::CompoundAssignment,
|
||||
isSuper ? PropOpEmitter::ObjKind::Super
|
||||
: PropOpEmitter::ObjKind::Other);
|
||||
|
||||
if (!poe->prepareForObj())
|
||||
return false;
|
||||
|
||||
if (isSuper) {
|
||||
UnaryNode* base = &prop->expression().as<UnaryNode>();
|
||||
if (!emitGetThisForSuperBase(base)) {
|
||||
// [stack] THIS SUPERBASE
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitTree(&prop->expression())) {
|
||||
// [stack] OBJ
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!poe->emitGet(prop->key().atom())) {
|
||||
// [stack] # if Super
|
||||
// [stack] THIS SUPERBASE LHS
|
||||
// [stack] # otherwise
|
||||
// [stack] OBJ LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!poe->prepareForRhs()) {
|
||||
// [stack] # if Super
|
||||
// [stack] THIS SUPERBASE LHS
|
||||
// [stack] # otherwise
|
||||
// [stack] OBJ LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
numPushed = 1 + isSuper;
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_ELEM: {
|
||||
PropertyByValue* elem = &lhs->as<PropertyByValue>();
|
||||
bool isSuper = elem->isSuper();
|
||||
|
||||
eoe.emplace(this, ElemOpEmitter::Kind::CompoundAssignment,
|
||||
isSuper ? ElemOpEmitter::ObjKind::Super
|
||||
: ElemOpEmitter::ObjKind::Other);
|
||||
|
||||
if (!emitElemObjAndKey(elem, isSuper, *eoe)) {
|
||||
// [stack] # if Super
|
||||
// [stack] THIS KEY
|
||||
// [stack] # otherwise
|
||||
// [stack] OBJ KEY
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eoe->emitGet()) {
|
||||
// [stack] # if Super
|
||||
// [stack] THIS KEY SUPERBASE LHS
|
||||
// [stack] # otherwise
|
||||
// [stack] OBJ KEY LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eoe->prepareForRhs()) {
|
||||
// [stack] # if Super
|
||||
// [stack] THIS KEY SUPERBASE LHS
|
||||
// [stack] # otherwise
|
||||
// [stack] OBJ KEY LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
numPushed = 2 + isSuper;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(stackDepth == startDepth + numPushed + 1);
|
||||
|
||||
// Test for the short-circuit condition.
|
||||
JumpList jump;
|
||||
if (!emitJump(op, &jump)) {
|
||||
// [stack] ... LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
// The short-circuit condition wasn't fulfilled, pop the left-hand side value
|
||||
// which was kept on the stack.
|
||||
if (!emit1(JSOP_POP)) {
|
||||
// [stack] ...
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Open spec issue about setting inferred function names.
|
||||
// <https://github.com/tc39/proposal-logical-assignment/issues/23>
|
||||
if (!emitTree(rhs)) {
|
||||
// [stack] ... RHS
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform the actual assignment.
|
||||
switch (lhs->getKind()) {
|
||||
case PNK_NAME: {
|
||||
if (!noe->emitAssignment()) {
|
||||
// [stack] RHS
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_DOT: {
|
||||
PropertyAccess* prop = &lhs->as<PropertyAccess>();
|
||||
|
||||
if (!poe->emitAssignment(prop->key().atom())) {
|
||||
// [stack] RHS
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_ELEM: {
|
||||
if (!eoe->emitAssignment()) {
|
||||
// [stack] RHS
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(stackDepth == startDepth + 1);
|
||||
|
||||
// Join with the short-circuit jump and pop anything left on the stack.
|
||||
if (numPushed > 0) {
|
||||
JumpList jumpAroundPop;
|
||||
if (!emitJump(JSOP_GOTO, &jumpAroundPop)) {
|
||||
// [stack] RHS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitJumpTargetAndPatch(jump)) {
|
||||
// [stack] ... LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reconstruct the stack depth after the jump.
|
||||
stackDepth = startDepth + 1 + numPushed;
|
||||
|
||||
// Move the left-hand side value to the bottom and pop the rest.
|
||||
if (!emitUnpickN(numPushed)) {
|
||||
// [stack] LHS ...
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitPopN(numPushed)) {
|
||||
// [stack] LHS
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!emitJumpTargetAndPatch(jumpAroundPop)) {
|
||||
// [stack] LHS | RHS
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!emitJumpTargetAndPatch(jump)) {
|
||||
// [stack] LHS | RHS
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(stackDepth == startDepth + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ParseNode::getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects,
|
||||
MutableHandleValue vp, Value* compare, size_t ncompare,
|
||||
|
|
@ -9074,6 +9307,17 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
|
|||
break;
|
||||
}
|
||||
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN: {
|
||||
BinaryNode* assignNode = &pn->as<BinaryNode>();
|
||||
if (!emitShortCircuitAssignment(assignNode->getKind(), assignNode->getOp(),
|
||||
assignNode->left(), assignNode->right())) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PNK_CONDITIONAL:
|
||||
if (!emitConditionalExpression(pn->as<ConditionalExpression>(), valueUsage))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -457,6 +457,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
// Helper to emit JSOP_POP or JSOP_POPN.
|
||||
MOZ_MUST_USE bool emitPopN(unsigned n);
|
||||
|
||||
// Helper to emit JSOP_SWAP or JSOP_UNPICK.
|
||||
MOZ_MUST_USE bool emitUnpickN(unsigned n);
|
||||
|
||||
// Helper to emit JSOP_CHECKISOBJ.
|
||||
MOZ_MUST_USE bool emitCheckIsObj(CheckIsObjectKind kind);
|
||||
|
||||
|
|
@ -701,6 +704,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter
|
|||
MOZ_MUST_USE bool emitTemplateString(ListNode* templateString);
|
||||
MOZ_MUST_USE bool emitAssignmentOrInit(ParseNodeKind kind, JSOp compoundOp,
|
||||
ParseNode* lhs, ParseNode* rhs);
|
||||
MOZ_MUST_USE bool emitShortCircuitAssignment(ParseNodeKind kind, JSOp op,
|
||||
ParseNode* lhs, ParseNode* rhs);
|
||||
|
||||
MOZ_MUST_USE bool emitReturn(UnaryNode* returnNode);
|
||||
MOZ_MUST_USE bool emitStatement(UnaryNode* exprStmt);
|
||||
|
|
|
|||
|
|
@ -355,6 +355,9 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
@ -1884,6 +1887,9 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bo
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
|
|
|
|||
|
|
@ -262,6 +262,9 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
|
|||
case PNK_ASSIGN:
|
||||
case PNK_ADDASSIGN:
|
||||
case PNK_SUBASSIGN:
|
||||
case PNK_COALESCEASSIGN:
|
||||
case PNK_ORASSIGN:
|
||||
case PNK_ANDASSIGN:
|
||||
case PNK_BITORASSIGN:
|
||||
case PNK_BITXORASSIGN:
|
||||
case PNK_BITANDASSIGN:
|
||||
|
|
|
|||
|
|
@ -176,6 +176,9 @@ class ObjectBox;
|
|||
F(ASSIGN) \
|
||||
F(ADDASSIGN) \
|
||||
F(SUBASSIGN) \
|
||||
F(COALESCEASSIGN) \
|
||||
F(ORASSIGN) \
|
||||
F(ANDASSIGN) \
|
||||
F(BITORASSIGN) \
|
||||
F(BITXORASSIGN) \
|
||||
F(BITANDASSIGN) \
|
||||
|
|
@ -395,8 +398,10 @@ IsTypeofKind(ParseNodeKind kind)
|
|||
* PNK_ASSIGN (AssignmentNode)
|
||||
* left: target of assignment
|
||||
* right: value to assign
|
||||
* PNK_ADDASSIGN, PNK_SUBASSIGN, PNK_BITORASSIGN, PNK_BITXORASSIGN,
|
||||
* PNK_BITANDASSIGN, PNK_LSHASSIGN, PNK_RSHASSIGN, PNK_URSHASSIGN,
|
||||
* PNK_ADDASSIGN, PNK_SUBASSIGN,
|
||||
* PNK_COALESCEASSIGN, PNK_ORASSIGN, PNK_ANDASSIGN,
|
||||
* PNK_BITORASSIGN, PNK_BITXORASSIGN, PNK_BITANDASSIGN,
|
||||
* PNK_LSHASSIGN, PNK_RSHASSIGN, PNK_URSHASSIGN,
|
||||
* PNK_MULASSIGN, PNK_DIVASSIGN, PNK_MODASSIGN, PNK_POWASSIGN (AssignmentNode)
|
||||
* left: target of assignment
|
||||
* right: value to assign
|
||||
|
|
|
|||
|
|
@ -9150,6 +9150,9 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
|
|||
case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
|
||||
case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
|
||||
case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
|
||||
case TOK_COALESCEASSIGN: kind = PNK_COALESCEASSIGN; op = JSOP_COALESCE; break;
|
||||
case TOK_ORASSIGN: kind = PNK_ORASSIGN; op = JSOP_OR; break;
|
||||
case TOK_ANDASSIGN: kind = PNK_ANDASSIGN; op = JSOP_AND; break;
|
||||
case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
|
||||
case TOK_BITXORASSIGN: kind = PNK_BITXORASSIGN; op = JSOP_BITXOR; break;
|
||||
case TOK_BITANDASSIGN: kind = PNK_BITANDASSIGN; op = JSOP_BITAND; break;
|
||||
|
|
@ -9279,6 +9282,17 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
|
|||
} else if (handler.isPropertyAccess(lhs)) {
|
||||
// Permitted: no additional testing/fixup needed.
|
||||
} else if (handler.isFunctionCall(lhs)) {
|
||||
// We don't have to worry about backward compatibility issues with the new
|
||||
// compound assignment operators, so we always throw here. Also that way we
|
||||
// don't have to worry if |f() &&= expr| should always throw an error or
|
||||
// only if |f()| returns true.
|
||||
if (kind == PNK_COALESCEASSIGN ||
|
||||
kind == PNK_ORASSIGN ||
|
||||
kind == PNK_ADDASSIGN) {
|
||||
errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS);
|
||||
return null();
|
||||
}
|
||||
|
||||
if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
return null();
|
||||
|
||||
|
|
|
|||
|
|
@ -219,6 +219,9 @@
|
|||
range(ASSIGNMENT_START, ASSIGN) \
|
||||
macro(ADDASSIGN, "'+='") \
|
||||
macro(SUBASSIGN, "'-='") \
|
||||
macro(COALESCEASSIGN, "'\?\?='") /* avoid trigraphs warning */ \
|
||||
macro(ORASSIGN, "'||='") \
|
||||
macro(ANDASSIGN, "'&&='") \
|
||||
macro(BITORASSIGN, "'|='") \
|
||||
macro(BITXORASSIGN, "'^='") \
|
||||
macro(BITANDASSIGN, "'&='") \
|
||||
|
|
|
|||
|
|
@ -1876,7 +1876,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
|
||||
case '|':
|
||||
if (matchChar('|'))
|
||||
tp->type = TOK_OR;
|
||||
tp->type = matchChar('=') ? TOK_ORASSIGN : TOK_OR;
|
||||
else
|
||||
tp->type = matchChar('=') ? TOK_BITORASSIGN : TOK_BITOR;
|
||||
goto out;
|
||||
|
|
@ -1887,7 +1887,7 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
|
||||
case '&':
|
||||
if (matchChar('&'))
|
||||
tp->type = TOK_AND;
|
||||
tp->type = matchChar('=') ? TOK_ANDASSIGN : TOK_AND;
|
||||
else
|
||||
tp->type = matchChar('=') ? TOK_BITANDASSIGN : TOK_BITAND;
|
||||
goto out;
|
||||
|
|
@ -1906,8 +1906,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier)
|
|||
ungetCharIgnoreEOL(c);
|
||||
tp->type = TOK_OPTCHAIN;
|
||||
}
|
||||
} else {
|
||||
tp->type = matchChar('?') ? TOK_COALESCE : TOK_HOOK;
|
||||
} else if (matchChar('?')) {
|
||||
tp->type = matchChar('=') ? TOK_COALESCEASSIGN : TOK_COALESCE;
|
||||
} else {
|
||||
tp->type = TOK_HOOK;
|
||||
}
|
||||
goto out;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue