Bug 710932 - Create ?: conditional expressions using a constructor that doesn't examine the token stream. r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Tue, 13 Dec 2011 15:53:30 -0500
changeset 84785 a1ca7fe3d8e29a0c2a3cac0206e3dd2bb4425032
parent 84784 a2f87c9d7a710672a7d66678d68e693d6e2e4728
child 84786 b1560ef4222cfae85cf3474669144de4979512d7
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs710932
milestone12.0a1
Bug 710932 - Create ?: conditional expressions using a constructor that doesn't examine the token stream. r=jorendorff
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/jsreflect.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -6767,26 +6767,26 @@ EmitSyntheticStatements(JSContext *cx, B
     for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
         if (!EmitTree(cx, bce, pn2))
             return false;
     }
     return PopStatementBCE(cx, bce);
 }
 
 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. */
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
     CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, beq);
 
@@ -6797,17 +6797,17 @@ EmitConditionalExpression(JSContext *cx,
      * only one path, so we must decrement bce->stackDepth.
      *
      * Failing to do this will foil code, such as let expression and block
      * code generation, which must use the stack depth to compute local
      * stack indexes correctly.
      */
     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);
 }
 
 static bool
 EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, jsint sharpnum)
 {
@@ -7222,18 +7222,18 @@ frontend::EmitTree(JSContext *cx, Byteco
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
         if (!EmitAssignment(cx, bce, pn->pn_left, pn->getOp(), pn->pn_right))
             return false;
         break;
 
-      case PNK_HOOK:
-        ok = EmitConditionalExpression(cx, bce, pn);
+      case PNK_CONDITIONAL:
+        ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression());
         break;
 
       case PNK_OR:
       case PNK_AND:
         ok = EmitLogical(cx, bce, pn);
         break;
 
       case PNK_ADD:
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -553,17 +553,17 @@ js::FoldConstants(JSContext *cx, ParseNo
     }
 
     switch (pn->getKind()) {
       case PNK_IF:
         if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
             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:
             if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
                 pn2 = pn3;
             break;
           case PNK_STRING:
             if (pn1->pn_atom->length() == 0)
@@ -588,18 +588,18 @@ js::FoldConstants(JSContext *cx, ParseNo
 
         if (pn2 && !pn2->isDefn())
             pn->become(pn2);
         if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
             /*
              * 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);
             pn->makeEmpty();
         }
         tc->freeTree(pn2);
         if (pn3 && pn3 != pn2)
             tc->freeTree(pn3);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -36,16 +36,18 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef ParseNode_h__
 #define ParseNode_h__
 
+#include "mozilla/Attributes.h"
+
 #include "jsscript.h"
 
 #include "frontend/ParseMaps.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 
 /*
@@ -56,17 +58,17 @@ namespace js {
  * around <B && C> when A is false).  Nodes are labeled by kind, with a
  * secondary JSOp label when needed.
  *
  * The long comment after this enum block describes the kinds in detail.
  */
 enum ParseNodeKind {
     PNK_SEMI,
     PNK_COMMA,
-    PNK_HOOK,
+    PNK_CONDITIONAL,
     PNK_COLON,
     PNK_OR,
     PNK_AND,
     PNK_BITOR,
     PNK_BITXOR,
     PNK_BITAND,
     PNK_POS,
     PNK_NEG,
@@ -308,17 +310,18 @@ enum ParseNodeKind {
  * PNK_BITXORASSIGN,
  * PNK_BITANDASSIGN,
  * PNK_LSHASSIGN,
  * PNK_RSHASSIGN,
  * PNK_URSHASSIGN,
  * 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
  * PNK_BITXOR   binary      pn_left: left-assoc ^ expr, pn_right: & expr
  * PNK_BITAND   binary      pn_left: left-assoc & expr, pn_right: EQ expr
  *
  * PNK_EQ,      binary      pn_left: left-assoc EQ expr, pn_right: REL expr
  * PNK_NE,
@@ -481,26 +484,29 @@ enum ParseNodeArity {
 };
 
 struct Definition;
 
 class LoopControlStatement;
 class BreakStatement;
 class ContinueStatement;
 class XMLProcessingInstruction;
+class ConditionalExpression;
 
 struct ParseNode {
   private:
     uint32_t            pn_type   : 16, /* PNK_* type */
                         pn_op     : 8,  /* see JSOp enum and jsopcode.tbl */
                         pn_arity  : 5,  /* see ParseNodeArity enum */
                         pn_parens : 1,  /* this expr was enclosed in parens */
                         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),
         pn_offset(0), pn_next(NULL), pn_link(NULL)
     {
         JS_ASSERT(kind < PNK_LIMIT);
         pn_pos.begin.index = 0;
         pn_pos.begin.lineno = 0;
@@ -922,16 +928,17 @@ struct ParseNode {
     inline bool isConstant();
 
     /* Casting operations. */
     inline BreakStatement &asBreakStatement();
     inline ContinueStatement &asContinueStatement();
 #if JS_HAS_XML_SUPPORT
     inline XMLProcessingInstruction &asXMLProcessingInstruction();
 #endif
+    inline ConditionalExpression &asConditionalExpression();
 };
 
 struct NullaryNode : public ParseNode {
     static inline NullaryNode *create(ParseNodeKind kind, TreeContext *tc) {
         return (NullaryNode *)ParseNode::create(kind, PN_NULLARY, tc);
     }
 };
 
@@ -1093,16 +1100,52 @@ ParseNode::asXMLProcessingInstruction()
 {
     JS_ASSERT(isKind(PNK_XMLPI));
     JS_ASSERT(isOp(JSOP_NOP));
     JS_ASSERT(pn_arity == PN_NULLARY);
     return *static_cast<XMLProcessingInstruction *>(this);
 }
 #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);
 
 /*
  * js::Definition is a degenerate subtype of the PN_FUNC and PN_NAME variants
  * of js::ParseNode, allocated only for function, var, const, and let
  * declarations that define truly lexical bindings. This means that a child of
  * a PNK_VAR list may be a Definition as well as a ParseNode. The pn_defn bit
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4638,47 +4638,40 @@ Parser::orExpr1()
     while (pn && tokenStream.isCurrentTokenType(TOK_OR))
         pn = ParseNode::newBinaryOrAppend(PNK_OR, JSOP_OR, pn, andExpr1n(), tc);
     return pn;
 }
 
 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;
-
-        /*
-         * 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);
-
-        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;
+    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 *thenExpr = assignExpr();
+    tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS);
+    if (!thenExpr)
+        return NULL;
+
+    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
 Parser::setAssignmentLhsOps(ParseNode *pn, JSOp op)
 {
     switch (pn->getKind()) {
       case PNK_NAME:
         if (!CheckStrictAssignment(context, tc, pn))
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2397,17 +2397,17 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_COMMA:
       {
         NodeVector exprs(cx);
         return expressions(pn, exprs) &&
                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
       }
 
-      case PNK_HOOK:
+      case PNK_CONDITIONAL:
       {
         Value test, cons, alt;
 
         return expression(pn->pn_kid1, &test) &&
                expression(pn->pn_kid2, &cons) &&
                expression(pn->pn_kid3, &alt) &&
                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
       }