Bug 710932 - Create <?target data?> expressions using a constructor that doesn't examine the token stream. r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Fri, 09 Dec 2011 23:28:59 -0500
changeset 84784 a2f87c9d7a710672a7d66678d68e693d6e2e4728
parent 84783 9f5264f1827089753ff09798c81fc74855370572
child 84785 a1ca7fe3d8e29a0c2a3cac0206e3dd2bb4425032
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 <?target data?> 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/frontend/TokenStream.h
js/src/jsreflect.cpp
js/src/jsxml.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1858,33 +1858,39 @@ EmitIndexOp(JSContext *cx, JSOp op, uint
  */
 #define EMIT_INDEX_OP(op, index)                                              \
     JS_BEGIN_MACRO                                                            \
         if (!EmitIndexOp(cx, op, index, bce))                                 \
             return JS_FALSE;                                                  \
     JS_END_MACRO
 
 static bool
-EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
+EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
-    if (op == JSOP_GETPROP &&
-        pn->pn_atom == cx->runtime->atomState.lengthAtom) {
+    if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) {
         /* Specialize length accesses for the interpreter. */
         op = JSOP_LENGTH;
     }
 
     jsatomid index;
-    if (!bce->makeAtomIndex(pn->pn_atom, &index))
+    if (!bce->makeAtomIndex(atom, &index))
         return false;
 
     return EmitIndexOp(cx, op, index, bce, psuffix);
 }
 
+static bool
+EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL)
+{
+    JS_ASSERT(pn->pn_atom != NULL);
+    return EmitAtomOp(cx, pn->pn_atom, op, bce, psuffix);
+}
+
 static JSBool
 EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
     return EmitIndexOp(cx, op, bce->objectList.index(objbox), bce);
 }
 
 /*
@@ -5473,26 +5479,26 @@ EmitXMLTag(JSContext *cx, BytecodeEmitte
 
     if ((pn->pn_xflags & PNX_XMLROOT) && Emit1(cx, bce, pn->getOp()) < 0)
         return false;
 
     return true;
 }
 
 static bool
-EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, XMLProcessingInstruction &pi)
 {
     JS_ASSERT(!bce->inStrictMode());
 
     jsatomid index;
-    if (!bce->makeAtomIndex(pn->pn_pidata, &index))
+    if (!bce->makeAtomIndex(pi.data(), &index))
         return false;
     if (!EmitIndexOp(cx, JSOP_QNAMEPART, index, bce))
         return false;
-    if (!EmitAtomOp(cx, pn, JSOP_XMLPI, bce))
+    if (!EmitAtomOp(cx, pi.target(), JSOP_XMLPI, bce))
         return false;
     return true;
 }
 #endif
 
 static bool
 EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
@@ -7589,17 +7595,17 @@ frontend::EmitTree(JSContext *cx, Byteco
             JS_ASSERT(pn->isArity(PN_NULLARY));
             ok = pn->isOp(JSOP_OBJECT)
                  ? EmitObjectOp(cx, pn->pn_objbox, pn->getOp(), bce)
                  : EmitAtomOp(cx, pn, pn->getOp(), bce);
         }
         break;
 
       case PNK_XMLPI:
-        if (!EmitXMLProcessingInstruction(cx, bce, pn))
+        if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction()))
             return false;
         break;
 #endif /* JS_HAS_XML_SUPPORT */
 
       default:
         JS_ASSERT(0);
     }
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -268,21 +268,23 @@ FoldXMLConstants(JSContext *cx, ParseNod
             break;
 
           case PNK_XMLCOMMENT:
             str = js_MakeXMLCommentString(cx, pn2->pn_atom);
             if (!str)
                 return JS_FALSE;
             break;
 
-          case PNK_XMLPI:
-            str = js_MakeXMLPIString(cx, pn2->pn_pitarget, pn2->pn_pidata);
+          case PNK_XMLPI: {
+            XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
+            str = js_MakeXMLPIString(cx, pi.target(), pi.data());
             if (!str)
                 return JS_FALSE;
             break;
+          }
 
           cantfold:
           default:
             JS_ASSERT(*pnp == pn1);
             if ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) &&
                 (i & 1) ^ (j & 1)) {
 #ifdef DEBUG_brendanXXX
                 printf("1: %d, %d => ", i, j);
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -480,16 +480,17 @@ enum ParseNodeArity {
     PN_NAMESET                          /* AtomDefnMapPtr + ParseNode ptr */
 };
 
 struct Definition;
 
 class LoopControlStatement;
 class BreakStatement;
 class ContinueStatement;
+class XMLProcessingInstruction;
 
 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 */
@@ -612,25 +613,26 @@ struct ParseNode {
             uint32_t    dflags:12,      /* definition/use flags, see below */
                         blockid:20;     /* block number, for subset dominance
                                            computation */
         } name;
         struct {                        /* lexical dependencies + sub-tree */
             AtomDefnMapPtr   defnMap;
             ParseNode        *tree;     /* sub-tree containing name uses */
         } nameset;
-        struct {                        /* PN_NULLARY variant for E4X XML PI */
-            PropertyName     *target;   /* target in <?target data?> */
-            JSAtom           *data;     /* data (or null) in <?target data?> */
-        } xmlpi;
         jsdouble        dval;           /* aligned numeric literal value */
         class {
             friend class LoopControlStatement;
             PropertyName     *label;    /* target of break/continue statement */
         } loopControl;
+        class {                         /* E4X <?target data?> XML PI */
+            friend class XMLProcessingInstruction;
+            PropertyName     *target;   /* non-empty */
+            JSAtom           *data;     /* may be empty, never null */
+        } xmlpi;
     } pn_u;
 
 #define pn_funbox       pn_u.name.funbox
 #define pn_body         pn_u.name.expr
 #define pn_cookie       pn_u.name.cookie
 #define pn_dflags       pn_u.name.dflags
 #define pn_blockid      pn_u.name.blockid
 #define pn_index        pn_u.name.blockid /* reuse as object table index */
@@ -651,18 +653,16 @@ struct ParseNode {
 #define pn_prologue     pn_u.unary.hidden
 #define pn_atom         pn_u.name.atom
 #define pn_objbox       pn_u.name.objbox
 #define pn_expr         pn_u.name.expr
 #define pn_lexdef       pn_u.name.lexdef
 #define pn_names        pn_u.nameset.defnMap
 #define pn_tree         pn_u.nameset.tree
 #define pn_dval         pn_u.dval
-#define pn_pitarget     pn_u.xmlpi.target
-#define pn_pidata       pn_u.xmlpi.data
 
   protected:
     void init(TokenKind type, JSOp op, ParseNodeArity arity) {
         pn_type = type;
         pn_op = op;
         pn_arity = arity;
         pn_parens = false;
         JS_ASSERT(!pn_used);
@@ -919,16 +919,19 @@ struct ParseNode {
     }
 
     bool getConstantValue(JSContext *cx, bool strictChecks, Value *vp);
     inline bool isConstant();
 
     /* Casting operations. */
     inline BreakStatement &asBreakStatement();
     inline ContinueStatement &asContinueStatement();
+#if JS_HAS_XML_SUPPORT
+    inline XMLProcessingInstruction &asXMLProcessingInstruction();
+#endif
 };
 
 struct NullaryNode : public ParseNode {
     static inline NullaryNode *create(ParseNodeKind kind, TreeContext *tc) {
         return (NullaryNode *)ParseNode::create(kind, PN_NULLARY, tc);
     }
 };
 
@@ -1061,16 +1064,45 @@ ParseNode::asContinueStatement()
 
 class DebuggerStatement : public ParseNode {
   public:
     DebuggerStatement(const TokenPos &pos)
       : ParseNode(PNK_DEBUGGER, JSOP_NOP, PN_NULLARY, pos)
     { }
 };
 
+#if JS_HAS_XML_SUPPORT
+class XMLProcessingInstruction : public ParseNode {
+  public:
+    XMLProcessingInstruction(PropertyName *target, JSAtom *data, const TokenPos &pos)
+      : ParseNode(PNK_XMLPI, JSOP_NOP, PN_NULLARY, pos)
+    {
+        pn_u.xmlpi.target = target;
+        pn_u.xmlpi.data = data;
+    }
+
+    PropertyName *target() const {
+        return pn_u.xmlpi.target;
+    }
+
+    JSAtom *data() const {
+        return pn_u.xmlpi.data;
+    }
+};
+
+inline XMLProcessingInstruction &
+ParseNode::asXMLProcessingInstruction()
+{
+    JS_ASSERT(isKind(PNK_XMLPI));
+    JS_ASSERT(isOp(JSOP_NOP));
+    JS_ASSERT(pn_arity == PN_NULLARY);
+    return *static_cast<XMLProcessingInstruction *>(this);
+}
+#endif
+
 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
@@ -6358,23 +6358,20 @@ Parser::xmlElementContent(ParseNode *pn)
             pn->pn_xflags |= PNX_CANTFOLD;
         } else if (tt == TOK_XMLSTAGO) {
             pn2 = xmlElementOrList(JS_FALSE);
             if (!pn2)
                 return false;
             pn2->pn_xflags &= ~PNX_XMLROOT;
             pn->pn_xflags |= pn2->pn_xflags;
         } else if (tt == TOK_XMLPI) {
-            pn2 = NullaryNode::create(PNK_XMLPI, tc);
+            const Token &tok = tokenStream.currentToken();
+            pn2 = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
             if (!pn2)
                 return false;
-            const Token &tok = tokenStream.currentToken();
-            pn2->setOp(tok.t_op);
-            pn2->pn_pitarget = tok.xmlPITarget();
-            pn2->pn_pidata = tok.xmlPIData();
         } else {
             JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT);
             pn2 = atomNode(tt == TOK_XMLCDATA ? PNK_XMLCDATA : PNK_XMLCOMMENT,
                            tokenStream.currentToken().t_op);
             if (!pn2)
                 return false;
         }
         pn->pn_pos.end = pn2->pn_pos.end;
@@ -7048,24 +7045,24 @@ Parser::primaryExpr(TokenKind tt, JSBool
 #endif
       case TOK_STRING:
         pn = atomNode(PNK_STRING, JSOP_STRING);
         if (!pn)
             return NULL;
         break;
 
 #if JS_HAS_XML_SUPPORT
-      case TOK_XMLPI:
+      case TOK_XMLPI: {
         JS_ASSERT(!tc->inStrictMode());
-        pn = NullaryNode::create(PNK_XMLPI, tc);
+        const Token &tok = tokenStream.currentToken();
+        pn = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
         if (!pn)
             return NULL;
-        pn->pn_pitarget = tokenStream.currentToken().xmlPITarget();
-        pn->pn_pidata = tokenStream.currentToken().xmlPIData();
         break;
+      }
 #endif
 
       case TOK_NAME:
         pn = NameNode::create(PNK_NAME, tokenStream.currentToken().name(), tc);
         if (!pn)
             return NULL;
         JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
         pn->setOp(JSOP_NAME);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -325,18 +325,18 @@ struct Token {
                 PropertyName *name;     /* non-numeric atom */
                 JSAtom       *atom;     /* potentially-numeric atom */
             } n;
         } s;
 
       private:
         friend struct Token;
         struct {                        /* pair for <?target data?> XML PI */
-            JSAtom       *data;         /* auxiliary atom table entry */
-            PropertyName *target;       /* main atom table entry */
+            PropertyName *target;       /* non-empty */
+            JSAtom       *data;         /* maybe empty, never null */
         } xmlpi;
         uint16_t        sharpNumber;    /* sharp variable number: #1# or #1= */
         jsdouble        number;         /* floating point number */
         RegExpFlag      reflags;        /* regexp flags, use tokenbuf to access
                                            regexp chars */
     } u;
 
     /* Mutators */
@@ -354,16 +354,19 @@ struct Token {
 
     void setAtom(JSOp op, JSAtom *atom) {
         JS_ASSERT(op == JSOP_STRING || op == JSOP_XMLCOMMENT || JSOP_XMLCDATA);
         u.s.op = op;
         u.s.n.atom = atom;
     }
 
     void setProcessingInstruction(PropertyName *target, JSAtom *data) {
+        JS_ASSERT(target);
+        JS_ASSERT(data);
+        JS_ASSERT(!target->empty());
         u.xmlpi.target = target;
         u.xmlpi.data = data;
     }
 
     void setRegExpFlags(js::RegExpFlag flags) {
         JS_ASSERT((flags & AllFlags) == flags);
         u.reflags = flags;
     }
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -614,18 +614,16 @@ class NodeBuilder
     bool xmlName(NodeVector &elts, TokenPos *pos, Value *dst);
 
     bool xmlAttribute(Value text, TokenPos *pos, Value *dst);
 
     bool xmlCdata(Value text, TokenPos *pos, Value *dst);
 
     bool xmlComment(Value text, TokenPos *pos, Value *dst);
 
-    bool xmlPI(Value target, TokenPos *pos, Value *dst);
-
     bool xmlPI(Value target, Value content, TokenPos *pos, Value *dst);
 };
 
 bool
 NodeBuilder::newNode(ASTType type, TokenPos *pos, JSObject **dst)
 {
     JS_ASSERT(type > AST_ERROR && type < AST_LIMIT);
 
@@ -1566,22 +1564,16 @@ NodeBuilder::xmlComment(Value text, Toke
     Value cb = callbacks[AST_XMLCOMMENT];
     if (!cb.isNull())
         return callback(cb, text, pos, dst);
 
     return newNode(AST_XMLCOMMENT, pos, "contents", text, dst);
 }
 
 bool
-NodeBuilder::xmlPI(Value target, TokenPos *pos, Value *dst)
-{
-    return xmlPI(target, NullValue(), pos, dst);
-}
-
-bool
 NodeBuilder::xmlPI(Value target, Value contents, TokenPos *pos, Value *dst)
 {
     Value cb = callbacks[AST_XMLPI];
     if (!cb.isNull())
         return callback(cb, target, contents, pos, dst);
 
     return newNode(AST_XMLPI, pos,
                    "target", target,
@@ -2790,24 +2782,23 @@ ASTSerializer::xml(ParseNode *pn, Value 
         return builder.xmlAttribute(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
       case PNK_XMLCDATA:
         return builder.xmlCdata(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
       case PNK_XMLCOMMENT:
         return builder.xmlComment(atomContents(pn->pn_atom), &pn->pn_pos, dst);
 
-      case PNK_XMLPI:
-        if (!pn->pn_pidata)
-            return builder.xmlPI(atomContents(pn->pn_pitarget), &pn->pn_pos, dst);
-        else
-            return builder.xmlPI(atomContents(pn->pn_pitarget),
-                                 atomContents(pn->pn_pidata),
-                                 &pn->pn_pos,
-                                 dst);
+      case PNK_XMLPI: {
+        XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
+        return builder.xmlPI(atomContents(pi.target()),
+                             atomContents(pi.data()),
+                             &pi.pn_pos,
+                             dst);
+      }
 #endif
 
       default:
         LOCAL_NOT_REACHED("unexpected XML node type");
     }
 }
 
 bool
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1616,34 +1616,35 @@ ParseNodeToXML(Parser *parser, ParseNode
       case PNK_XMLPI:
         str = pn->pn_atom;
         qn = NULL;
         if (pn->isKind(PNK_XMLCOMMENT)) {
             if (flags & XSF_IGNORE_COMMENTS)
                 goto skip_child;
             xml_class = JSXML_CLASS_COMMENT;
         } else if (pn->isKind(PNK_XMLPI)) {
+            XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
             if (IS_XML(str)) {
                 Value v = StringValue(str);
                 JSAutoByteString bytes;
                 if (js_ValueToPrintable(cx, v, &bytes)) {
-                    ReportCompileErrorNumber(cx, &parser->tokenStream, pn,
+                    ReportCompileErrorNumber(cx, &parser->tokenStream, &pi,
                                              JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
                 }
                 goto fail;
             }
 
             if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
                 goto skip_child;
 
-            qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE);
+            qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE);
             if (!qn)
                 goto fail;
 
-            str = pn->pn_pidata ? pn->pn_pidata : cx->runtime->emptyString;
+            str = pi.data();
             xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
         } else {
             /* CDATA section content, or element text. */
             xml_class = JSXML_CLASS_TEXT;
         }
 
         xml = js_NewXML(cx, xml_class);
         if (!xml)