Bug 1169171 - Split PNK_DELETE into several different kinds for each of the syntactically distinct modes of |delete| operation, depending upon the syntax of the operand. r=efaust

This commit is contained in:
Jeff Walden 2015-05-28 13:47:44 -07:00
parent e8686eca4b
commit e778a50c9d
9 changed files with 212 additions and 108 deletions

View File

@ -1998,31 +1998,26 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
case PN_UNARY:
switch (pn->getKind()) {
case PNK_DELETE:
{
ParseNode* pn2 = pn->pn_kid;
switch (pn2->getKind()) {
case PNK_NAME:
if (!bindNameToSlot(pn2))
return false;
if (pn2->isConst()) {
MOZ_ASSERT(*answer == false);
return true;
}
/* FALL THROUGH */
case PNK_DOT:
case PNK_CALL:
case PNK_ELEM:
case PNK_SUPERELEM:
/* All these delete addressing modes have effects too. */
*answer = true;
return true;
default:
return checkSideEffects(pn2, answer);
}
MOZ_CRASH("We have a returning default case");
case PNK_DELETENAME: {
ParseNode* nameExpr = pn->pn_kid;
MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
if (!bindNameToSlot(nameExpr))
return false;
*answer = !nameExpr->isConst();
return true;
}
case PNK_DELETEPROP:
case PNK_DELETESUPERPROP:
case PNK_DELETEELEM:
case PNK_DELETESUPERELEM:
// All these delete addressing modes have effects, too.
*answer = true;
return true;
case PNK_DELETEEXPR:
return checkSideEffects(pn->pn_kid, answer);
case PNK_TYPEOF:
case PNK_VOID:
case PNK_NOT:
@ -6157,82 +6152,110 @@ BytecodeEmitter::emitStatement(ParseNode* pn)
}
bool
BytecodeEmitter::emitDelete(ParseNode* pn)
BytecodeEmitter::emitDeleteName(ParseNode* node)
{
/*
* Under ECMA 3, deleting a non-reference returns true -- but alas we
* must evaluate the operand if it appears it might have side effects.
*/
ParseNode* pn2 = pn->pn_kid;
switch (pn2->getKind()) {
case PNK_NAME:
if (!bindNameToSlot(pn2))
return false;
if (!emitAtomOp(pn2, pn2->getOp()))
return false;
break;
case PNK_DOT:
{
JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
if (!emitPropOp(pn2, delOp))
return false;
break;
}
case PNK_SUPERPROP:
// Still have to calculate the base, even though we are are going
// to throw unconditionally, as calculating the base could also
// throw.
if (!emit1(JSOP_SUPERBASE))
return false;
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
return false;
break;
case PNK_ELEM:
{
JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
if (!emitElemOp(pn2, delOp))
return false;
break;
}
case PNK_SUPERELEM:
// Still have to calculate everything, even though we're gonna throw
// since it may have side effects
if (!emitTree(pn2->pn_kid))
return false;
if (!emit1(JSOP_SUPERBASE))
return false;
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
return false;
MOZ_ASSERT(node->isKind(PNK_DELETENAME));
MOZ_ASSERT(node->isArity(PN_UNARY));
// Another wrinkle: Balance the stack from the emitter's point of view.
// Execution will not reach here, as the last bytecode threw.
ParseNode* nameExpr = node->pn_kid;
MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
if (!bindNameToSlot(nameExpr))
return false;
MOZ_ASSERT(nameExpr->isOp(JSOP_DELNAME));
return emitAtomOp(nameExpr, JSOP_DELNAME);
}
bool
BytecodeEmitter::emitDeleteProperty(ParseNode* node)
{
MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode* propExpr = node->pn_kid;
MOZ_ASSERT(propExpr->isKind(PNK_DOT));
JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
return emitPropOp(propExpr, delOp);
}
bool
BytecodeEmitter::emitDeleteSuperProperty(ParseNode* node)
{
MOZ_ASSERT(node->isKind(PNK_DELETESUPERPROP));
MOZ_ASSERT(node->isArity(PN_UNARY));
MOZ_ASSERT(node->pn_kid->isKind(PNK_SUPERPROP));
// Still have to calculate the base, even though we are are going
// to throw unconditionally, as calculating the base could also
// throw.
if (!emit1(JSOP_SUPERBASE))
return false;
return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER);
}
bool
BytecodeEmitter::emitDeleteElement(ParseNode* node)
{
MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode* elemExpr = node->pn_kid;
MOZ_ASSERT(elemExpr->isKind(PNK_ELEM));
JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
return emitElemOp(elemExpr, delOp);
}
bool
BytecodeEmitter::emitDeleteSuperElement(ParseNode* node)
{
MOZ_ASSERT(node->isKind(PNK_DELETESUPERELEM));
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode* superElemExpr = node->pn_kid;
MOZ_ASSERT(superElemExpr->isKind(PNK_SUPERELEM));
// Still have to calculate everything, even though we're gonna throw
// since it may have side effects
MOZ_ASSERT(superElemExpr->isArity(PN_UNARY));
if (!emitTree(superElemExpr->pn_kid))
return false;
if (!emit1(JSOP_SUPERBASE))
return false;
if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
return false;
// Another wrinkle: Balance the stack from the emitter's point of view.
// Execution will not reach here, as the last bytecode threw.
return emit1(JSOP_POP);
}
bool
BytecodeEmitter::emitDeleteExpression(ParseNode* node)
{
MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode* expression = node->pn_kid;
// If useless, just emit JSOP_TRUE; otherwise convert |delete <expr>| to
// effectively |<expr>, true|.
bool useful = false;
if (!checkSideEffects(expression, &useful))
return false;
if (useful) {
MOZ_ASSERT_IF(expression->isKind(PNK_CALL), !(expression->pn_xflags & PNX_SETCALL));
if (!emitTree(expression))
return false;
if (!emit1(JSOP_POP))
return false;
break;
default:
{
/*
* If useless, just emit JSOP_TRUE; otherwise convert delete foo()
* to foo(), true (a comma expression).
*/
bool useful = false;
if (!checkSideEffects(pn2, &useful))
return false;
if (useful) {
MOZ_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL));
if (!emitTree(pn2))
return false;
if (!emit1(JSOP_POP))
return false;
}
if (!emit1(JSOP_TRUE))
return false;
}
}
return true;
return emit1(JSOP_TRUE);
}
bool
@ -7461,8 +7484,28 @@ BytecodeEmitter::emitTree(ParseNode* pn)
ok = emitIncOrDec(pn);
break;
case PNK_DELETE:
ok = emitDelete(pn);
case PNK_DELETENAME:
ok = emitDeleteName(pn);
break;
case PNK_DELETEPROP:
ok = emitDeleteProperty(pn);
break;
case PNK_DELETESUPERPROP:
ok = emitDeleteSuperProperty(pn);
break;
case PNK_DELETEELEM:
ok = emitDeleteElement(pn);
break;
case PNK_DELETESUPERELEM:
ok = emitDeleteSuperElement(pn);
break;
case PNK_DELETEEXPR:
ok = emitDeleteExpression(pn);
break;
case PNK_DOT:

View File

@ -545,7 +545,13 @@ struct BytecodeEmitter
bool emitStatement(ParseNode* pn);
bool emitStatementList(ParseNode* pn, ptrdiff_t top);
bool emitDelete(ParseNode* pn);
bool emitDeleteName(ParseNode* pn);
bool emitDeleteProperty(ParseNode* pn);
bool emitDeleteSuperProperty(ParseNode* pn);
bool emitDeleteElement(ParseNode* pn);
bool emitDeleteSuperElement(ParseNode* pn);
bool emitDeleteExpression(ParseNode* pn);
bool emitLogical(ParseNode* pn);
bool emitUnary(ParseNode* pn);

View File

@ -325,7 +325,12 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
case PNK_VOID:
case PNK_NOT:
case PNK_BITNOT:
case PNK_DELETE:
case PNK_DELETENAME:
case PNK_DELETEPROP:
case PNK_DELETESUPERPROP:
case PNK_DELETEELEM:
case PNK_DELETESUPERELEM:
case PNK_DELETEEXPR:
case PNK_POS:
case PNK_NEG:
case PNK_PREINCREMENT:
@ -716,7 +721,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
SyntacticContext kidsc =
pn->isKind(PNK_NOT)
? SyntacticContext::Condition
: pn->isKind(PNK_DELETE)
: IsDeleteKind(pn->getKind())
? SyntacticContext::Delete
: SyntacticContext::Other;
if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
@ -746,7 +751,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp,
break;
}
// The immediate child of a PNK_DELETE node should not be replaced
// The immediate child of a PNK_DELETE* node should not be replaced
// with node indicating a different syntactic form; |delete x| is not
// the same as |delete (true && x)|. See bug 888002.
//

View File

@ -187,11 +187,23 @@ class FullParseHandler
}
ParseNode* newDelete(uint32_t begin, ParseNode* expr) {
if (expr->getKind() == PNK_NAME) {
if (expr->isKind(PNK_NAME)) {
expr->pn_dflags |= PND_DEOPTIMIZED;
expr->setOp(JSOP_DELNAME);
return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr);
}
return newUnary(PNK_DELETE, JSOP_NOP, begin, expr);
if (expr->isKind(PNK_DOT))
return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr);
if (expr->isKind(PNK_SUPERPROP))
return newUnary(PNK_DELETESUPERPROP, JSOP_NOP, begin, expr);
if (expr->isKind(PNK_ELEM))
return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr);
if (expr->isKind(PNK_SUPERELEM))
return newUnary(PNK_DELETESUPERELEM, JSOP_NOP, begin, expr);
return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr);
}
ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) {

View File

@ -383,7 +383,12 @@ class NameResolver
case PNK_NOT:
case PNK_BITNOT:
case PNK_THROW:
case PNK_DELETE:
case PNK_DELETENAME:
case PNK_DELETEPROP:
case PNK_DELETESUPERPROP:
case PNK_DELETEELEM:
case PNK_DELETESUPERELEM:
case PNK_DELETEEXPR:
case PNK_NEG:
case PNK_POS:
case PNK_PREINCREMENT:

View File

@ -226,7 +226,12 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_NOT:
case PNK_BITNOT:
case PNK_THROW:
case PNK_DELETE:
case PNK_DELETENAME:
case PNK_DELETEPROP:
case PNK_DELETESUPERPROP:
case PNK_DELETEELEM:
case PNK_DELETESUPERELEM:
case PNK_DELETEEXPR:
case PNK_POS:
case PNK_NEG:
case PNK_PREINCREMENT:

View File

@ -118,7 +118,13 @@ class UpvarCookie
F(WITH) \
F(RETURN) \
F(NEW) \
F(DELETE) \
/* Delete operations. These must be sequential. */ \
F(DELETENAME) \
F(DELETEPROP) \
F(DELETESUPERPROP) \
F(DELETEELEM) \
F(DELETESUPERELEM) \
F(DELETEEXPR) \
F(TRY) \
F(CATCH) \
F(CATCHLIST) \
@ -227,6 +233,12 @@ enum ParseNodeKind
PNK_ASSIGNMENT_LAST = PNK_MODASSIGN
};
inline bool
IsDeleteKind(ParseNodeKind kind)
{
return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
}
/*
* Label Variant Members
* ----- ------- -------
@ -393,7 +405,16 @@ enum ParseNodeKind
* PNK_NEW list pn_head: list of ctor, arg1, arg2, ... argN
* pn_count: 1 + N (where N is number of args)
* ctor is a MEMBER expr
* PNK_DELETE unary pn_kid: MEMBER expr
* PNK_DELETENAME unary pn_kid: PNK_NAME expr
* PNK_DELETEPROP unary pn_kid: PNK_DOT expr
* PNK_DELETESUPERPROP unary pn_kid: PNK_SUPERPROP expr
* PNK_DELETEELEM unary pn_kid: PNK_ELEM expr
* PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr
* PNK_DELETEEXPR unary pn_kid: MEMBER expr that's evaluated, then the
* overall delete evaluates to true; can't be a kind
* for a more-specific PNK_DELETE* unless constant
* folding (or a similar parse tree manipulation) has
* occurred
* PNK_DOT name pn_expr: MEMBER expr to left of .
* pn_atom: name to right of .
* PNK_ELEM binary pn_left: MEMBER expr to left of [

View File

@ -159,7 +159,9 @@ class SyntaxParseHandler
Node newElision() { return NodeGeneric; }
Node newDelete(uint32_t begin, Node expr) { return NodeGeneric; }
Node newDelete(uint32_t begin, Node expr) {
return NodeGeneric;
}
Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
return NodeGeneric;

View File

@ -1910,7 +1910,7 @@ ASTSerializer::aop(JSOp op)
UnaryOperator
ASTSerializer::unop(ParseNodeKind kind, JSOp op)
{
if (kind == PNK_DELETE)
if (IsDeleteKind(kind))
return UNOP_DELETE;
switch (op) {
@ -2926,7 +2926,12 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
case PNK_INSTANCEOF:
return leftAssociate(pn, dst);
case PNK_DELETE:
case PNK_DELETENAME:
case PNK_DELETEPROP:
case PNK_DELETESUPERPROP:
case PNK_DELETEELEM:
case PNK_DELETESUPERELEM:
case PNK_DELETEEXPR:
case PNK_TYPEOF:
case PNK_VOID:
case PNK_NOT: