Bug 710932 - Create ?: conditional expressions using a constructor that doesn't examine the token stream. r=jorendorff

--HG--
extra : rebase_source : cf8d22e47436d543a46115171460a4bfeae10971
This commit is contained in:
Jeff Walden 2011-12-13 15:53:30 -05:00
parent 4ac1f980e9
commit d87cdbf3d5
5 changed files with 77 additions and 41 deletions

View File

@ -6772,16 +6772,16 @@ EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrd
}
static bool
EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional)
{
/* Emit the condition, then branch if false to the else part. */
if (!EmitTree(cx, bce, pn->pn_kid1))
if (!EmitTree(cx, bce, &conditional.condition()))
return false;
ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND);
if (noteIndex < 0)
return false;
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0);
if (beq < 0 || !EmitTree(cx, bce, pn->pn_kid2))
if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression()))
return false;
/* Jump around else, fixup the branch, emit else, fixup jump. */
@ -6802,7 +6802,7 @@ EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
*/
JS_ASSERT(bce->stackDepth > 0);
bce->stackDepth--;
if (!EmitTree(cx, bce, pn->pn_kid3))
if (!EmitTree(cx, bce, &conditional.elseExpression()))
return false;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp);
return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
@ -7227,8 +7227,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return false;
break;
case PNK_HOOK:
ok = EmitConditionalExpression(cx, bce, pn);
case PNK_CONDITIONAL:
ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression());
break;
case PNK_OR:

View File

@ -558,7 +558,7 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
break;
/* FALL THROUGH */
case PNK_HOOK:
case PNK_CONDITIONAL:
/* Reduce 'if (C) T; else E' into T for true C, E for false. */
switch (pn1->getKind()) {
case PNK_NUMBER:
@ -593,8 +593,8 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
* False condition and no else, or an empty then-statement was
* moved up over pn. Either way, make pn an empty block (not an
* empty statement, which does not decompile, even when labeled).
* NB: pn must be a PNK_IF as PNK_HOOK can never have a null kid
* or an empty statement for a child.
* NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
* kid or an empty statement for a child.
*/
pn->setKind(PNK_STATEMENTLIST);
pn->setArity(PN_LIST);

View File

@ -41,6 +41,8 @@
#ifndef ParseNode_h__
#define ParseNode_h__
#include "mozilla/Attributes.h"
#include "jsscript.h"
#include "frontend/ParseMaps.h"
@ -61,7 +63,7 @@ namespace js {
enum ParseNodeKind {
PNK_SEMI,
PNK_COMMA,
PNK_HOOK,
PNK_CONDITIONAL,
PNK_COLON,
PNK_OR,
PNK_AND,
@ -313,7 +315,8 @@ enum ParseNodeKind {
* PNK_MULASSIGN,
* PNK_DIVASSIGN,
* PNK_MODASSIGN
* PNK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else
* PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr)
* pn_kid1: cond, pn_kid2: then, pn_kid3: else
* PNK_OR binary pn_left: first in || chain, pn_right: rest of chain
* PNK_AND binary pn_left: first in && chain, pn_right: rest of chain
* PNK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr
@ -486,6 +489,7 @@ class LoopControlStatement;
class BreakStatement;
class ContinueStatement;
class XMLProcessingInstruction;
class ConditionalExpression;
struct ParseNode {
private:
@ -496,6 +500,8 @@ struct ParseNode {
pn_used : 1, /* name node is on a use-chain */
pn_defn : 1; /* this node is a Definition */
void operator=(const ParseNode &other) MOZ_DELETE;
public:
ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity)
: pn_type(kind), pn_op(op), pn_arity(arity), pn_parens(0), pn_used(0), pn_defn(0),
@ -927,6 +933,7 @@ struct ParseNode {
#if JS_HAS_XML_SUPPORT
inline XMLProcessingInstruction &asXMLProcessingInstruction();
#endif
inline ConditionalExpression &asConditionalExpression();
};
struct NullaryNode : public ParseNode {
@ -1098,6 +1105,42 @@ ParseNode::asXMLProcessingInstruction()
}
#endif
class ConditionalExpression : public ParseNode {
public:
ConditionalExpression(ParseNode *condition, ParseNode *thenExpr, ParseNode *elseExpr)
: ParseNode(PNK_CONDITIONAL, JSOP_NOP, PN_TERNARY,
TokenPos::make(condition->pn_pos.begin, elseExpr->pn_pos.end))
{
JS_ASSERT(condition);
JS_ASSERT(thenExpr);
JS_ASSERT(elseExpr);
pn_u.ternary.kid1 = condition;
pn_u.ternary.kid2 = thenExpr;
pn_u.ternary.kid3 = elseExpr;
}
ParseNode &condition() const {
return *pn_u.ternary.kid1;
}
ParseNode &thenExpression() const {
return *pn_u.ternary.kid2;
}
ParseNode &elseExpression() const {
return *pn_u.ternary.kid3;
}
};
inline ConditionalExpression &
ParseNode::asConditionalExpression()
{
JS_ASSERT(isKind(PNK_CONDITIONAL));
JS_ASSERT(isOp(JSOP_NOP));
JS_ASSERT(pn_arity == PN_TERNARY);
return *static_cast<ConditionalExpression *>(this);
}
ParseNode *
CloneLeftHandSide(ParseNode *opn, TreeContext *tc);

View File

@ -4643,37 +4643,30 @@ Parser::orExpr1()
JS_ALWAYS_INLINE ParseNode *
Parser::condExpr1()
{
ParseNode *pn = orExpr1();
if (pn && tokenStream.isCurrentTokenType(TOK_HOOK)) {
ParseNode *pn1 = pn;
pn = TernaryNode::create(PNK_HOOK, tc);
if (!pn)
return NULL;
ParseNode *condition = orExpr1();
if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
return condition;
/*
* Always accept the 'in' operator in the middle clause of a ternary,
* where it's unambiguous, even if we might be parsing the init of a
* for statement.
*/
uintN oldflags = tc->flags;
tc->flags &= ~TCF_IN_FOR_INIT;
ParseNode *pn2 = assignExpr();
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
/*
* Always accept the 'in' operator in the middle clause of a ternary,
* where it's unambiguous, even if we might be parsing the init of a
* for statement.
*/
uintN oldflags = tc->flags;
tc->flags &= ~TCF_IN_FOR_INIT;
ParseNode *thenExpr = assignExpr();
tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
if (!thenExpr)
return NULL;
if (!pn2)
return NULL;
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
ParseNode *pn3 = assignExpr();
if (!pn3)
return NULL;
pn->pn_pos.begin = pn1->pn_pos.begin;
pn->pn_pos.end = pn3->pn_pos.end;
pn->pn_kid1 = pn1;
pn->pn_kid2 = pn2;
pn->pn_kid3 = pn3;
tokenStream.getToken(); /* need to read one token past the end */
}
return pn;
MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
ParseNode *elseExpr = assignExpr();
if (!elseExpr)
return NULL;
tokenStream.getToken(); /* read one token past the end */
return new_<ConditionalExpression>(condition, thenExpr, elseExpr);
}
bool

View File

@ -2402,7 +2402,7 @@ ASTSerializer::expression(ParseNode *pn, Value *dst)
builder.sequenceExpression(exprs, &pn->pn_pos, dst);
}
case PNK_HOOK:
case PNK_CONDITIONAL:
{
Value test, cons, alt;