Bug 568142 - Part 2: Have ParseNode's pn_pos enclose children better. r=jorendorff
authorAlex Crichton <acrichton@mozilla.com>
Wed, 08 Aug 2012 11:32:21 -0700
changeset 105086 a8601aeb1d1d1cb838832fc15cd4d6d0653a6448
parent 105085 753d5e8c80640b35cd5bfd1b2b1cd8d009ac56ef
child 105087 2f60f3a4bdd703c40ac2b49d9547707bcf9614c2
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersjorendorff
bugs568142
milestone17.0a1
Bug 568142 - Part 2: Have ParseNode's pn_pos enclose children better. r=jorendorff
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/TokenStream.h
js/src/jsapi-tests/testScriptInfo.cpp
js/src/jsreflect.cpp
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -845,25 +845,30 @@ struct ParseNode {
         pn_tail = &pn_head;
         pn_count = 0;
         pn_xflags = 0;
         pn_blockid = 0;
     }
 
     void initList(ParseNode *pn) {
         JS_ASSERT(pn_arity == PN_LIST);
+        if (pn->pn_pos.begin < pn_pos.begin)
+            pn_pos.begin = pn->pn_pos.begin;
+        pn_pos.end = pn->pn_pos.end;
         pn_head = pn;
         pn_tail = &pn->pn_next;
         pn_count = 1;
         pn_xflags = 0;
         pn_blockid = 0;
     }
 
     void append(ParseNode *pn) {
         JS_ASSERT(pn_arity == PN_LIST);
+        JS_ASSERT(pn->pn_pos.begin >= pn_pos.begin);
+        pn_pos.end = pn->pn_pos.end;
         *pn_tail = pn;
         pn_tail = &pn->pn_next;
         pn_count++;
     }
 
     void checkListConsistency()
 #ifndef DEBUG
     {}
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -911,16 +911,17 @@ MakeAssignment(ParseNode *pn, ParseNode 
     pn->setKind(PNK_ASSIGN);
     pn->setOp(JSOP_NOP);
     pn->setArity(PN_BINARY);
     pn->setInParens(false);
     pn->setUsed(false);
     pn->setDefn(false);
     pn->pn_left = lhs;
     pn->pn_right = rhs;
+    pn->pn_pos.end = rhs->pn_pos.end;
     return lhs;
 }
 
 /* See comment for use in Parser::functionDef. */
 static bool
 MakeDefIntoUse(Definition *dn, ParseNode *pn, JSAtom *atom, Parser *parser)
 {
     /* Turn pn into a definition. */
@@ -1394,24 +1395,25 @@ Parser::functionArguments(ParseNode **li
                     return false;
 
                 if (!pc->define(context, name, rhs, Definition::ARG))
                     return false;
 
                 ParseNode *item = new_<BinaryNode>(PNK_ASSIGN, JSOP_NOP, lhs->pn_pos, lhs, rhs);
                 if (!item)
                     return false;
-                if (!list) {
+                if (list) {
+                    list->append(item);
+                } else {
                     list = ListNode::create(PNK_VAR, this);
                     if (!list)
                         return false;
-                    list->makeEmpty();
+                    list->initList(item);
                     *listp = list;
                 }
-                list->append(item);
                 break;
               }
 #endif /* JS_HAS_DESTRUCTURING */
 
               case TOK_TRIPLEDOT:
               {
                 hasRest = true;
                 tt = tokenStream.getToken();
@@ -2717,16 +2719,17 @@ Parser::letBlock(LetContext letContext)
          * need to wrap the TOK_LET node in a TOK_SEMI node so that we pop
          * the return value of the expression.
          */
         ParseNode *semi = UnaryNode::create(PNK_SEMI, this);
         if (!semi)
             return NULL;
 
         semi->pn_kid = pnlet;
+        semi->pn_pos = pnlet->pn_pos;
 
         letContext = LetExpresion;
         ret = semi;
     } else {
         ret = pnlet;
     }
 
     if (letContext == LetStatement) {
@@ -2738,16 +2741,19 @@ Parser::letBlock(LetContext letContext)
     } else {
         JS_ASSERT(letContext == LetExpresion);
         block->setOp(JSOP_LEAVEBLOCKEXPR);
         block->pn_expr = assignExpr();
         if (!block->pn_expr)
             return NULL;
     }
 
+    ret->pn_pos.begin = pnlet->pn_pos.begin = pnlet->pn_left->pn_pos.begin;
+    ret->pn_pos.end = pnlet->pn_pos.end = pnlet->pn_right->pn_pos.end;
+
     PopStatementPC(context, pc);
     return ret;
 }
 
 #endif /* JS_HAS_BLOCK_SCOPE */
 
 static bool
 PushBlocklikeStatement(StmtInfoPC *stmt, StmtType type, ParseContext *pc)
@@ -2770,16 +2776,17 @@ NewBindingNode(JSAtom *atom, Parser *par
      */
     if (varContext == HoistVars) {
         if (AtomDefnPtr p = pc->lexdeps->lookup(atom)) {
             ParseNode *lexdep = p.value();
             JS_ASSERT(lexdep->isPlaceholder());
             if (lexdep->pn_blockid >= pc->blockid()) {
                 lexdep->pn_blockid = pc->blockid();
                 pc->lexdeps->remove(p);
+                lexdep->pn_pos = parser->tokenStream.currentToken().pos;
                 return lexdep;
             }
         }
     }
 
     /* Make a new node for this declarator name (or destructuring pattern). */
     JS_ASSERT(parser->tokenStream.currentToken().type == TOK_NAME);
     return NameNode::create(PNK_NAME, atom, parser, parser->pc);
@@ -2935,16 +2942,17 @@ Parser::forStatement()
     pn->pn_iflags = 0;
     if (tokenStream.matchToken(TOK_NAME)) {
         if (tokenStream.currentToken().name() == context->runtime->atomState.eachAtom)
             pn->pn_iflags = JSITER_FOREACH;
         else
             tokenStream.ungetToken();
     }
 
+    TokenPos lp_pos = tokenStream.currentToken().pos;
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
     /*
      * True if we have 'for (var/let/const ...)', except in the oddball case
      * where 'let' begins a let-expression in 'for (let (...) ...)'.
      */
     bool forDecl = false;
 
@@ -3128,16 +3136,17 @@ Parser::forStatement()
 
 #if JS_HAS_DESTRUCTURING
                 if (pn2->isKind(PNK_ASSIGN)) {
                     pn2 = pn2->pn_left;
                     JS_ASSERT(pn2->isKind(PNK_RB) || pn2->isKind(PNK_RC) ||
                               pn2->isKind(PNK_NAME));
                 }
 #endif
+                pnseq->pn_pos.begin = pn->pn_pos.begin;
                 pnseq->append(pn);
                 forParent = pnseq;
             }
         } else {
             /* Not a declaration. */
             JS_ASSERT(!blockObj);
             pn2 = pn1;
             pn1 = NULL;
@@ -3157,16 +3166,17 @@ Parser::forStatement()
              * created by PushLetScope around the for's initializer. This also
              * serves to indicate the let-decl to the emitter.
              */
             ParseNode *block = PushLetScope(context, this, *blockObj, &letStmt);
             if (!block)
                 return NULL;
             letStmt.isForLetBlock = true;
             block->pn_expr = pn1;
+            block->pn_pos = pn1->pn_pos;
             pn1 = block;
         }
 
         if (forDecl) {
             /*
              * pn2 is part of a declaration. Make a copy that can be passed to
              * EmitAssignment. Take care to do this after PushLetScope.
              */
@@ -3257,16 +3267,18 @@ Parser::forStatement()
             return NULL;
     }
 
     forHead->pn_pos = pos;
     forHead->setOp(JSOP_NOP);
     forHead->pn_kid1 = pn1;
     forHead->pn_kid2 = pn2;
     forHead->pn_kid3 = pn3;
+    forHead->pn_pos.begin = lp_pos.begin;
+    forHead->pn_pos.end   = tokenStream.currentToken().pos.end;
     pn->pn_left = forHead;
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
     ParseNode *body = statement();
     if (!body)
         return NULL;
@@ -3421,16 +3433,18 @@ Parser::tryStatement()
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
             pn2->pn_kid3 = statements();
             if (!pn2->pn_kid3)
                 return NULL;
             MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_CATCH);
             PopStatementPC(context, pc);
 
+            pnblock->pn_pos.end = pn2->pn_pos.end = tokenStream.currentToken().pos.end;
+
             catchList->append(pnblock);
             lastCatch = pn2;
             tt = tokenStream.getToken(TSF_OPERAND);
         } while (tt == TOK_CATCH);
     }
     pn->pn_kid2 = catchList;
 
     if (tt == TOK_FINALLY) {
@@ -3444,16 +3458,17 @@ Parser::tryStatement()
         PopStatementPC(context, pc);
     } else {
         tokenStream.ungetToken();
     }
     if (!catchList && !pn->pn_kid3) {
         reportError(NULL, JSMSG_CATCH_OR_FINALLY);
         return NULL;
     }
+    pn->pn_pos.end = (pn->pn_kid3 ? pn->pn_kid3 : catchList)->pn_pos.end;
     return pn;
 }
 
 ParseNode *
 Parser::withStatement()
 {
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_WITH));
 
@@ -5878,16 +5893,17 @@ Parser::attributeIdentifier()
         pn2 = endBracketedExpr();
     } else {
         reportError(NULL, JSMSG_SYNTAX_ERROR);
         return NULL;
     }
     if (!pn2)
         return NULL;
     pn->pn_kid = pn2;
+    pn->pn_pos.end = pn2->pn_pos.end;
     return pn;
 }
 
 /*
  * Make a TOK_LC unary node whose pn_kid is an expression.
  */
 ParseNode *
 Parser::xmlExpr(bool inTag)
@@ -5910,16 +5926,17 @@ Parser::xmlExpr(bool inTag)
     ParseNode *pn2 = expr();
     if (!pn2)
         return NULL;
 
     MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_IN_XML_EXPR);
     tokenStream.setXMLTagMode(oldflag);
     pn->pn_kid = pn2;
     pn->setOp(inTag ? JSOP_XMLTAGEXPR : JSOP_XMLELTEXPR);
+    pn->pn_pos.end = pn2->pn_pos.end;
     return pn;
 }
 
 /*
  * Parse the productions:
  *
  *      XMLNameExpr:
  *              XMLName XMLNameExpr?
@@ -6047,17 +6064,16 @@ Parser::xmlTagContent(ParseNodeKind tagk
             pn2 = xmlExpr(true);
             pn->pn_xflags |= PNX_CANTFOLD;
         } else {
             reportError(NULL, JSMSG_BAD_XML_ATTR_VALUE);
             return NULL;
         }
         if (!pn2)
             return NULL;
-        pn->pn_pos.end = pn2->pn_pos.end;
         pn->append(pn2);
     }
 
     return pn;
 }
 
 #define XML_CHECK_FOR_ERROR_AND_EOF(tt,result)                                              \
     JS_BEGIN_MACRO                                                                          \
@@ -6087,17 +6103,16 @@ Parser::xmlElementContent(ParseNode *pn)
         JSAtom *textAtom = tokenStream.currentToken().atom();
         if (textAtom) {
             /* Non-zero-length XML text scanned. */
             JS_ASSERT(tokenStream.currentToken().t_op == JSOP_STRING);
             ParseNode *pn2 = atomNode(tt == TOK_XMLSPACE ? PNK_XMLSPACE : PNK_XMLTEXT,
                                       JSOP_STRING);
             if (!pn2)
                 return false;
-            pn->pn_pos.end = pn2->pn_pos.end;
             pn->append(pn2);
         }
 
         tt = tokenStream.getToken(TSF_OPERAND);
         XML_CHECK_FOR_ERROR_AND_EOF(tt, false);
         if (tt == TOK_XMLETAGO)
             break;
 
@@ -6120,17 +6135,16 @@ Parser::xmlElementContent(ParseNode *pn)
                 return false;
         } 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;
         pn->append(pn2);
     }
     tokenStream.setXMLTagMode(true);
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_XMLETAGO);
     return true;
 }
 
@@ -6194,16 +6208,17 @@ Parser::xmlElementOrList(bool allowList)
             if (!pn2->isKind(PNK_XMLSTAGO)) {
                 pn->initList(pn2);
                 if (!XML_FOLDABLE(pn2))
                     pn->pn_xflags |= PNX_CANTFOLD;
                 pn2 = pn;
                 pn = ListNode::create(PNK_XMLTAGC, this);
                 if (!pn)
                     return NULL;
+                pn->pn_pos = pn2->pn_pos;
             }
 
             /* Now make pn a nominal-root TOK_XMLELEM list containing pn2. */
             pn->setKind(PNK_XMLELEM);
             pn->pn_pos.begin = pn2->pn_pos.begin;
             pn->initList(pn2);
             if (!XML_FOLDABLE(pn2))
                 pn->pn_xflags |= PNX_CANTFOLD;
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -262,16 +262,20 @@ struct TokenPos {
 
     bool operator >(const TokenPos& bpos) const {
         return !(*this <= bpos);
     }
 
     bool operator >=(const TokenPos& bpos) const {
         return !(*this < bpos);
     }
+
+    bool encloses(const TokenPos& pos) const {
+        return begin <= pos.begin && pos.end <= end;
+    }
 };
 
 struct Token {
     TokenKind           type;           /* char value or above enumerator */
     TokenPos            pos;            /* token position in file */
     const jschar        *ptr;           /* beginning of token in line buffer */
     union {
         struct {                        /* name or string literal */
--- a/js/src/jsapi-tests/testScriptInfo.cpp
+++ b/js/src/jsapi-tests/testScriptInfo.cpp
@@ -40,17 +40,17 @@ BEGIN_TEST(testScriptInfo)
 
     JSScript *script = JS_CompileScript(cx, global, code, strlen(code), __FILE__, startLine);
 
     CHECK(script);
 
     jsbytecode *start = JS_LineNumberToPC(cx, script, startLine);
     CHECK_EQUAL(JS_GetScriptBaseLineNumber(cx, script), startLine);
     CHECK_EQUAL(JS_PCToLineNumber(cx, script, start), startLine);
-    CHECK_EQUAL(JS_GetScriptLineExtent(cx, script), 10);
+    CHECK_EQUAL(JS_GetScriptLineExtent(cx, script), 11);
     CHECK(strcmp(JS_GetScriptFilename(cx, script), __FILE__) == 0);
     const jschar *sourceMap = JS_GetScriptSourceMap(cx, script);
     CHECK(sourceMap);
     CHECK(CharsMatch(sourceMap, "http://example.com/path/to/source-map.json"));
 
     return true;
 }
 END_TEST(testScriptInfo)
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -1815,48 +1815,54 @@ ASTSerializer::statements(ParseNode *pn,
 {
     JS_ASSERT(pn->isKind(PNK_STATEMENTLIST));
     JS_ASSERT(pn->isArity(PN_LIST));
 
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
+        JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
         Value elt;
         if (!sourceElement(next, &elt))
             return false;
         elts.infallibleAppend(elt);
     }
 
     return true;
 }
 
 bool
 ASTSerializer::expressions(ParseNode *pn, NodeVector &elts)
 {
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
+        JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
         Value elt;
         if (!expression(next, &elt))
             return false;
         elts.infallibleAppend(elt);
     }
 
     return true;
 }
 
 bool
 ASTSerializer::xmls(ParseNode *pn, NodeVector &elts)
 {
     if (!elts.reserve(pn->pn_count))
         return false;
 
     for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
+        JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
         Value elt;
         if (!xml(next, &elt))
             return false;
         elts.infallibleAppend(elt);
     }
 
     return true;
 }
@@ -1948,31 +1954,37 @@ ASTSerializer::variableDeclarator(ParseN
     JS_ASSERT(pn->isKind(PNK_NAME) || pn->isKind(PNK_ASSIGN));
 
     ParseNode *pnleft;
     ParseNode *pnright;
 
     if (pn->isKind(PNK_NAME)) {
         pnleft = pn;
         pnright = pn->isUsed() ? NULL : pn->pn_expr;
+        JS_ASSERT_IF(pnright, pn->pn_pos.encloses(pnright->pn_pos));
     } else {
         JS_ASSERT(pn->isKind(PNK_ASSIGN));
         pnleft = pn->pn_left;
         pnright = pn->pn_right;
+        JS_ASSERT(pn->pn_pos.encloses(pnleft->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pnright->pn_pos));
     }
 
     Value left, right;
     return pattern(pnleft, pkind, &left) &&
            optExpression(pnright, &right) &&
            builder.variableDeclarator(left, right, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::let(ParseNode *pn, bool expr, Value *dst)
 {
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
     ParseNode *letHead = pn->pn_left;
     LOCAL_ASSERT(letHead->isArity(PN_LIST));
 
     ParseNode *letBody = pn->pn_right;
     LOCAL_ASSERT(letBody->isKind(PNK_LEXICALSCOPE));
 
     NodeVector dtors(cx);
     if (!dtors.reserve(letHead->pn_count))
@@ -1997,28 +2009,34 @@ ASTSerializer::let(ParseNode *pn, bool e
              builder.letExpression(dtors, v, &pn->pn_pos, dst)
            : statement(letBody->pn_expr, &v) &&
              builder.letStatement(dtors, v, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::switchCase(ParseNode *pn, Value *dst)
 {
+    JS_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
     NodeVector stmts(cx);
 
     Value expr;
 
     return optExpression(pn->pn_left, &expr) &&
            statements(pn->pn_right, stmts) &&
            builder.switchCase(expr, stmts, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::switchStatement(ParseNode *pn, Value *dst)
 {
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
     Value disc;
 
     if (!expression(pn->pn_left, &disc))
         return false;
 
     ParseNode *listNode;
     bool lexical;
 
@@ -2045,27 +2063,35 @@ ASTSerializer::switchStatement(ParseNode
     }
 
     return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::catchClause(ParseNode *pn, Value *dst)
 {
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+    JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+
     Value var, guard, body;
 
     return pattern(pn->pn_kid1, NULL, &var) &&
            optExpression(pn->pn_kid2, &guard) &&
            statement(pn->pn_kid3, &body) &&
            builder.catchClause(var, guard, body, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::tryStatement(ParseNode *pn, Value *dst)
 {
+    JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+    JS_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+    JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+
     Value body;
     if (!statement(pn->pn_kid1, &body))
         return false;
 
     NodeVector clauses(cx);
     if (pn->pn_kid2) {
         if (!clauses.reserve(pn->pn_kid2->pn_count))
             return false;
@@ -2142,16 +2168,20 @@ ASTSerializer::statement(ParseNode *pn, 
             return statement(pn, dst);
         /* FALL THROUGH */
 
       case PNK_STATEMENTLIST:
         return blockStatement(pn, dst);
 
       case PNK_IF:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+        JS_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+
         Value test, cons, alt;
 
         return expression(pn->pn_kid1, &test) &&
                statement(pn->pn_kid2, &cons) &&
                optStatement(pn->pn_kid3, &alt) &&
                builder.ifStatement(test, cons, alt, &pn->pn_pos, dst);
       }
 
@@ -2159,38 +2189,51 @@ ASTSerializer::statement(ParseNode *pn, 
         return switchStatement(pn, dst);
 
       case PNK_TRY:
         return tryStatement(pn, dst);
 
       case PNK_WITH:
       case PNK_WHILE:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         Value expr, stmt;
 
         return expression(pn->pn_left, &expr) &&
                statement(pn->pn_right, &stmt) &&
                (pn->isKind(PNK_WITH)
                 ? builder.withStatement(expr, stmt, &pn->pn_pos, dst)
                 : builder.whileStatement(expr, stmt, &pn->pn_pos, dst));
       }
 
       case PNK_DOWHILE:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         Value stmt, test;
 
         return statement(pn->pn_left, &stmt) &&
                expression(pn->pn_right, &test) &&
                builder.doWhileStatement(stmt, test, &pn->pn_pos, dst);
       }
 
       case PNK_FOR:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         ParseNode *head = pn->pn_left;
 
+        JS_ASSERT_IF(head->pn_kid1, head->pn_pos.encloses(head->pn_kid1->pn_pos));
+        JS_ASSERT_IF(head->pn_kid2, head->pn_pos.encloses(head->pn_kid2->pn_pos));
+        JS_ASSERT_IF(head->pn_kid3, head->pn_pos.encloses(head->pn_kid3->pn_pos));
+
         Value stmt;
         if (!statement(pn->pn_right, &stmt))
             return false;
 
         if (head->isKind(PNK_FORIN)) {
             Value var;
             return (!head->pn_kid1
                     ? pattern(head->pn_kid2, NULL, &var)
@@ -2238,40 +2281,46 @@ ASTSerializer::statement(ParseNode *pn, 
         return optIdentifier(pn->pn_atom, NULL, &label) &&
                (pn->isKind(PNK_BREAK)
                 ? builder.breakStatement(label, &pn->pn_pos, dst)
                 : builder.continueStatement(label, &pn->pn_pos, dst));
       }
 
       case PNK_COLON:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
+
         Value label, stmt;
 
         return identifier(pn->pn_atom, NULL, &label) &&
                statement(pn->pn_expr, &stmt) &&
                builder.labeledStatement(label, stmt, &pn->pn_pos, dst);
       }
 
       case PNK_THROW:
       case PNK_RETURN:
       {
+        JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         Value arg;
 
         return optExpression(pn->pn_kid, &arg) &&
                (pn->isKind(PNK_THROW)
                 ? builder.throwStatement(arg, &pn->pn_pos, dst)
                 : builder.returnStatement(arg, &pn->pn_pos, dst));
       }
 
       case PNK_DEBUGGER:
         return builder.debuggerStatement(&pn->pn_pos, dst);
 
 #if JS_HAS_XML_SUPPORT
       case PNK_DEFXMLNS:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         LOCAL_ASSERT(pn->isArity(PN_UNARY));
 
         Value ns;
 
         return expression(pn->pn_kid, &ns) &&
                builder.xmlDefaultNamespace(ns, &pn->pn_pos, dst);
       }
 #endif
@@ -2418,48 +2467,59 @@ ASTSerializer::expression(ParseNode *pn,
       {
         NodeVector exprs(cx);
         return expressions(pn, exprs) &&
                builder.sequenceExpression(exprs, &pn->pn_pos, dst);
       }
 
       case PNK_CONDITIONAL:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+
         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);
       }
 
       case PNK_OR:
       case PNK_AND:
       {
         if (pn->isArity(PN_BINARY)) {
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
             Value left, right;
             return expression(pn->pn_left, &left) &&
                    expression(pn->pn_right, &right) &&
                    builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst);
         }
         return leftAssociate(pn, dst);
       }
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         bool inc = pn->isKind(PNK_PREINCREMENT);
         Value expr;
         return expression(pn->pn_kid, &expr) &&
                builder.updateExpression(expr, inc, true, &pn->pn_pos, dst);
       }
 
       case PNK_POSTINCREMENT:
       case PNK_POSTDECREMENT:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         bool inc = pn->isKind(PNK_POSTINCREMENT);
         Value expr;
         return expression(pn->pn_kid, &expr) &&
                builder.updateExpression(expr, inc, false, &pn->pn_pos, dst);
       }
 
       case PNK_ASSIGN:
       case PNK_ADDASSIGN:
@@ -2469,16 +2529,19 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_BITANDASSIGN:
       case PNK_LSHASSIGN:
       case PNK_RSHASSIGN:
       case PNK_URSHASSIGN:
       case PNK_MULASSIGN:
       case PNK_DIVASSIGN:
       case PNK_MODASSIGN:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         AssignmentOperator op = aop(pn->getOp());
         LOCAL_ASSERT(op > AOP_ERR && op < AOP_LIMIT);
 
         Value lhs, rhs;
         return pattern(pn->pn_left, NULL, &lhs) &&
                expression(pn->pn_right, &rhs) &&
                builder.assignmentExpression(op, lhs, rhs, &pn->pn_pos, dst);
       }
@@ -2501,16 +2564,19 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
       case PNK_DBLDOT:
         if (pn->isArity(PN_BINARY)) {
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
             BinaryOperator op = binop(pn->getKind(), pn->getOp());
             LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
 
             Value left, right;
             return expression(pn->pn_left, &left) &&
                    expression(pn->pn_right, &right) &&
                    builder.binaryExpression(op, left, right, &pn->pn_pos, dst);
         }
@@ -2518,16 +2584,18 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_DELETE:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG: {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         UnaryOperator op = unop(pn->getKind(), pn->getOp());
         LOCAL_ASSERT(op > UNOP_ERR && op < UNOP_LIMIT);
 
         Value expr;
         return expression(pn->pn_kid, &expr) &&
                builder.unaryExpression(op, expr, &pn->pn_pos, dst);
       }
 
@@ -2535,61 +2603,71 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_LP:
       {
 #if JS_HAS_GENERATOR_EXPRS
         if (pn->isGeneratorExpr())
             return generatorExpression(pn->generatorExpr(), dst);
 #endif
 
         ParseNode *next = pn->pn_head;
+        JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
 
         Value callee;
         if (!expression(next, &callee))
             return false;
 
         NodeVector args(cx);
         if (!args.reserve(pn->pn_count - 1))
             return false;
 
         for (next = next->pn_next; next; next = next->pn_next) {
+            JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
             Value arg;
             if (!expression(next, &arg))
                 return false;
             args.infallibleAppend(arg);
         }
 
         return pn->isKind(PNK_NEW)
                ? builder.newExpression(callee, args, &pn->pn_pos, dst)
 
             : builder.callExpression(callee, args, &pn->pn_pos, dst);
       }
 
       case PNK_DOT:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
+
         Value expr, id;
         return expression(pn->pn_expr, &expr) &&
                identifier(pn->pn_atom, NULL, &id) &&
                builder.memberExpression(false, expr, id, &pn->pn_pos, dst);
       }
 
       case PNK_LB:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         Value left, right;
         return expression(pn->pn_left, &left) &&
                expression(pn->pn_right, &right) &&
                builder.memberExpression(true, left, right, &pn->pn_pos, dst);
       }
 
       case PNK_RB:
       {
         NodeVector elts(cx);
         if (!elts.reserve(pn->pn_count))
             return false;
 
         for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
+            JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
             if (next->isKind(PNK_COMMA)) {
                 elts.infallibleAppend(MagicValue(JS_SERIALIZE_NO_NODE));
             } else {
                 Value expr;
                 if (!expression(next, &expr))
                     return false;
                 elts.infallibleAppend(expr);
             }
@@ -2612,16 +2690,18 @@ ASTSerializer::expression(ParseNode *pn,
             parser->reportError(pn, JSMSG_BAD_OBJECT_INIT);
             return false;
         }
         NodeVector elts(cx);
         if (!elts.reserve(pn->pn_count))
             return false;
 
         for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
+            JS_ASSERT(pn->pn_pos.encloses(next->pn_pos));
+
             Value prop;
             if (!property(next, &prop))
                 return false;
             elts.infallibleAppend(prop);
         }
 
         return builder.objectExpression(elts, &pn->pn_pos, dst);
       }
@@ -2637,22 +2717,26 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_NUMBER:
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_NULL:
         return literal(pn, dst);
 
       case PNK_YIELD:
       {
+        JS_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         Value arg;
         return optExpression(pn->pn_kid, &arg) &&
                builder.yieldExpression(arg, &pn->pn_pos, dst);
       }
 
       case PNK_ARRAYCOMP:
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos));
+
         /* NB: it's no longer the case that pn_count could be 2. */
         LOCAL_ASSERT(pn->pn_count == 1);
         LOCAL_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE));
 
         return comprehension(pn->pn_head->pn_expr, dst);
 
       case PNK_LET:
         return let(pn, true, dst);
@@ -2672,49 +2756,59 @@ ASTSerializer::expression(ParseNode *pn,
         Value right;
 
         LOCAL_ASSERT(pn->isArity(PN_NAME) || pn->isArity(PN_BINARY));
 
         ParseNode *pnleft;
         bool computed;
 
         if (pn->isArity(PN_BINARY)) {
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
             computed = true;
             pnleft = pn->pn_left;
             if (!expression(pn->pn_right, &right))
                 return false;
         } else {
             JS_ASSERT(pn->isArity(PN_NAME));
+            JS_ASSERT(pn->pn_pos.encloses(pn->pn_expr->pn_pos));
+
             computed = false;
             pnleft = pn->pn_expr;
             if (!identifier(pn->pn_atom, NULL, &right))
                 return false;
         }
 
         if (pnleft->isKind(PNK_FUNCTION))
             return builder.xmlFunctionQualifiedIdentifier(right, computed, &pn->pn_pos, dst);
 
         Value left;
         return expression(pnleft, &left) &&
                builder.xmlQualifiedIdentifier(left, right, computed, &pn->pn_pos, dst);
       }
 
       case PNK_AT:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         Value expr;
         ParseNode *kid = pn->pn_kid;
         bool computed = ((!kid->isKind(PNK_NAME) || !kid->isOp(JSOP_QNAMEPART)) &&
                          !kid->isKind(PNK_DBLCOLON) &&
                          !kid->isKind(PNK_ANYNAME));
         return expression(kid, &expr) &&
             builder.xmlAttributeSelector(expr, computed, &pn->pn_pos, dst);
       }
 
       case PNK_FILTER:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
+
         Value left, right;
         return expression(pn->pn_left, &left) &&
                expression(pn->pn_right, &right) &&
                builder.xmlFilterExpression(left, right, &pn->pn_pos, dst);
       }
 
       default:
         return xml(pn, dst);
@@ -2729,16 +2823,18 @@ ASTSerializer::expression(ParseNode *pn,
 bool
 ASTSerializer::xml(ParseNode *pn, Value *dst)
 {
     JS_CHECK_RECURSION(cx, return false);
     switch (pn->getKind()) {
 #if JS_HAS_XML_SUPPORT
       case PNK_XMLCURLYEXPR:
       {
+        JS_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
+
         Value expr;
         return expression(pn->pn_kid, &expr) &&
                builder.xmlEscapeExpression(expr, &pn->pn_pos, dst);
       }
 
       case PNK_XMLELEM:
       {
         NodeVector elts(cx);