Bug 742612 - Reflect.parse: separate guarded/unguarded catch clauses. r=jorendorff
authorDave Herman <dherman@mozilla.com>
Fri, 24 Aug 2012 08:30:44 -0700
changeset 105343 c3cff93ee869647bbfedc3cf33bc3da714b939ac
parent 105342 a1756976e61dd0e6800db361b450fc74389dad10
child 105344 8aa9e479b24f2be5f2e507142d84ebfa0533f5af
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersjorendorff
bugs742612
milestone17.0a1
Bug 742612 - Reflect.parse: separate guarded/unguarded catch clauses. r=jorendorff
js/src/jsreflect.cpp
js/src/tests/js1_8_5/extensions/reflect-parse.js
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -504,17 +504,17 @@ class NodeBuilder
     bool withStatement(Value expr, Value stmt, TokenPos *pos, Value *dst);
 
     bool whileStatement(Value test, Value stmt, TokenPos *pos, Value *dst);
 
     bool doWhileStatement(Value stmt, Value test, TokenPos *pos, Value *dst);
 
     bool switchStatement(Value disc, NodeVector &elts, bool lexical, TokenPos *pos, Value *dst);
 
-    bool tryStatement(Value body, NodeVector &catches, Value finally, TokenPos *pos, Value *dst);
+    bool tryStatement(Value body, NodeVector &guarded, Value unguarded, Value finally, TokenPos *pos, Value *dst);
 
     bool debuggerStatement(TokenPos *pos, Value *dst);
 
     bool letStatement(NodeVector &head, Value stmt, TokenPos *pos, Value *dst);
 
     /*
      * expressions
      */
@@ -911,33 +911,34 @@ NodeBuilder::switchStatement(Value disc,
     return newNode(AST_SWITCH_STMT, pos,
                    "discriminant", disc,
                    "cases", array,
                    "lexical", BooleanValue(lexical),
                    dst);
 }
 
 bool
-NodeBuilder::tryStatement(Value body, NodeVector &catches, Value finally,
+NodeBuilder::tryStatement(Value body, NodeVector &guarded, Value unguarded, Value finally,
                           TokenPos *pos, Value *dst)
 {
-    Value handlers;
+    Value guardedHandlers;
 
     Value cb = callbacks[AST_TRY_STMT];
     if (!cb.isNull()) {
-        return newArray(catches, &handlers) &&
-               callback(cb, body, handlers, opt(finally), pos, dst);
+        return newArray(guarded, &guardedHandlers) &&
+               callback(cb, body, guardedHandlers, unguarded, opt(finally), pos, dst);
     }
 
-    if (!newArray(catches, &handlers))
+    if (!newArray(guarded, &guardedHandlers))
         return false;
 
     return newNode(AST_TRY_STMT, pos,
                    "block", body,
-                   "handlers", handlers,
+                   "guardedHandlers", guardedHandlers,
+                   "handler", unguarded,
                    "finalizer", finally,
                    dst);
 }
 
 bool
 NodeBuilder::debuggerStatement(TokenPos *pos, Value *dst)
 {
     Value cb = callbacks[AST_DEBUGGER_STMT];
@@ -1632,17 +1633,17 @@ class ASTSerializer
 
     bool forInit(ParseNode *pn, Value *dst);
     bool forOfOrIn(ParseNode *loop, ParseNode *head, Value var, Value stmt, Value *dst);
     bool statement(ParseNode *pn, Value *dst);
     bool blockStatement(ParseNode *pn, Value *dst);
     bool switchStatement(ParseNode *pn, Value *dst);
     bool switchCase(ParseNode *pn, Value *dst);
     bool tryStatement(ParseNode *pn, Value *dst);
-    bool catchClause(ParseNode *pn, Value *dst);
+    bool catchClause(ParseNode *pn, bool *isGuarded, Value *dst);
 
     bool optExpression(ParseNode *pn, Value *dst) {
         if (!pn) {
             dst->setMagic(JS_SERIALIZE_NO_NODE);
             return true;
         }
         return expression(pn, dst);
     }
@@ -2061,57 +2062,68 @@ ASTSerializer::switchStatement(ParseNode
             return false;
         cases.infallibleAppend(child);
     }
 
     return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
 }
 
 bool
-ASTSerializer::catchClause(ParseNode *pn, Value *dst)
+ASTSerializer::catchClause(ParseNode *pn, bool *isGuarded, 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) &&
+    if (!pattern(pn->pn_kid1, NULL, &var) ||
+        !optExpression(pn->pn_kid2, &guard)) {
+        return false;
+    }
+
+    *isGuarded = !guard.isMagic(JS_SERIALIZE_NO_NODE);
+
+    return 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);
+    NodeVector guarded(cx);
+    Value unguarded = NullValue();
+
     if (pn->pn_kid2) {
-        if (!clauses.reserve(pn->pn_kid2->pn_count))
+        if (!guarded.reserve(pn->pn_kid2->pn_count))
             return false;
 
         for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) {
             Value clause;
-            if (!catchClause(next->pn_expr, &clause))
+            bool isGuarded;
+            if (!catchClause(next->pn_expr, &isGuarded, &clause))
                 return false;
-            clauses.infallibleAppend(clause);
+            if (isGuarded)
+                guarded.infallibleAppend(clause);
+            else
+                unguarded = clause;
         }
     }
 
     Value finally;
     return optStatement(pn->pn_kid3, &finally) &&
-           builder.tryStatement(body, clauses, finally, &pn->pn_pos, dst);
+           builder.tryStatement(body, guarded, unguarded, finally, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::forInit(ParseNode *pn, Value *dst)
 {
     if (!pn) {
         dst->setMagic(JS_SERIALIZE_NO_NODE);
         return true;
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -58,17 +58,17 @@ function ifStmt(test, cons, alt) Pattern
 function labStmt(lab, stmt) Pattern({ type: "LabeledStatement", label: lab, body: stmt })
 function withStmt(obj, stmt) Pattern({ type: "WithStatement", object: obj, body: stmt })
 function whileStmt(test, stmt) Pattern({ type: "WhileStatement", test: test, body: stmt })
 function doStmt(stmt, test) Pattern({ type: "DoWhileStatement", test: test, body: stmt })
 function switchStmt(disc, cases) Pattern({ type: "SwitchStatement", discriminant: disc, cases: cases })
 function caseClause(test, stmts) Pattern({ type: "SwitchCase", test: test, consequent: stmts })
 function defaultClause(stmts) Pattern({ type: "SwitchCase", test: null, consequent: stmts })
 function catchClause(id, guard, body) Pattern({ type: "CatchClause", param: id, guard: guard, body: body })
-function tryStmt(body, catches, fin) Pattern({ type: "TryStatement", block: body, handlers: catches, finalizer: fin })
+function tryStmt(body, guarded, unguarded, fin) Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handler: unguarded, finalizer: fin })
 function letStmt(head, body) Pattern({ type: "LetStatement", head: head, body: body })
 function funExpr(id, args, body, gen) Pattern({ type: "FunctionExpression",
                                                 id: id,
                                                 params: args,
                                                 body: body,
                                                 generator: false })
 function genFunExpr(id, args, body) Pattern({ type: "FunctionExpression",
                                               id: id,
@@ -409,36 +409,40 @@ assertStmt("switch (foo) { case 1: 1; br
 assertStmt("switch (foo) { case 1: 1; break; case 2: 2; break; default: 3; case 42: 42; }",
            switchStmt(ident("foo"),
                       [ caseClause(lit(1), [ exprStmt(lit(1)), breakStmt(null) ]),
                         caseClause(lit(2), [ exprStmt(lit(2)), breakStmt(null) ]),
                         defaultClause([ exprStmt(lit(3)) ]),
                         caseClause(lit(42), [ exprStmt(lit(42)) ]) ]));
 assertStmt("try { } catch (e) { }",
            tryStmt(blockStmt([]),
-                   [ catchClause(ident("e"), null, blockStmt([])) ],
+                   [],
+		   catchClause(ident("e"), null, blockStmt([])),
                    null));
 assertStmt("try { } catch (e) { } finally { }",
            tryStmt(blockStmt([]),
-                   [ catchClause(ident("e"), null, blockStmt([])) ],
+                   [],
+		   catchClause(ident("e"), null, blockStmt([])),
                    blockStmt([])));
 assertStmt("try { } finally { }",
            tryStmt(blockStmt([]),
                    [],
+		   null,
                    blockStmt([])));
 assertStmt("try { } catch (e if foo) { } catch (e if bar) { } finally { }",
            tryStmt(blockStmt([]),
                    [ catchClause(ident("e"), ident("foo"), blockStmt([])),
                      catchClause(ident("e"), ident("bar"), blockStmt([])) ],
+		   null,
                    blockStmt([])));
 assertStmt("try { } catch (e if foo) { } catch (e if bar) { } catch (e) { } finally { }",
            tryStmt(blockStmt([]),
                    [ catchClause(ident("e"), ident("foo"), blockStmt([])),
-                     catchClause(ident("e"), ident("bar"), blockStmt([])),
-                     catchClause(ident("e"), null, blockStmt([])) ],
+                     catchClause(ident("e"), ident("bar"), blockStmt([])) ],
+                   catchClause(ident("e"), null, blockStmt([])),
                    blockStmt([])));
 
 // Bug 632028: yield outside of a function should throw
 (function() {
     var threw = false;
     try {
         Reflect.parse("yield 0");
     } catch (expected) {
@@ -1032,19 +1036,21 @@ assertGlobalExpr("[1,2,3]", 12, { arrayE
 assertGlobalExpr("({ x: y })", 13, { objectExpression: function() 13 });
 assertGlobalExpr("this", 14, { thisExpression: function() 14 });
 assertGlobalExpr("[x for (x in y)]", 17, { comprehensionExpression: function() 17 });
 assertGlobalExpr("(x for (x in y))", 18, { generatorExpression: function() 18 });
 assertGlobalExpr("(function() { yield 42 })", genFunExpr(null, [], blockStmt([exprStmt(19)])), { yieldExpression: function() 19 });
 assertGlobalExpr("(let (x) x)", 20, { letExpression: function() 20 });
 
 assertGlobalStmt("switch (x) { case y: }", switchStmt(ident("x"), [1]), { switchCase: function() 1 });
-assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [2], null), { catchClause: function() 2 });
+assertGlobalStmt("try { } catch (e) { }", 2, { tryStatement: (function(b, g, u, f) u), catchClause: function() 2 });
+assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", [2, 2], { tryStatement: (function(b, g, u, f) g), catchClause: function() 2 });
+assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [], 2, null), { catchClause: function() 2 });
 assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }",
-                 tryStmt(blockStmt([]), [2, 2], null),
+                 tryStmt(blockStmt([]), [2, 2], null, null),
                  { catchClause: function() 2 });
 assertGlobalExpr("[x for (y in z) for (x in y)]", compExpr(ident("x"), [3, 3], null), { comprehensionBlock: function() 3 });
 
 assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: function() 1 });
 assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: function() 2 });
 assertGlobalExpr("[ x ] = y", aExpr("=", 3, ident("y")), { arrayPattern: function() 3 });
 
 assertGlobalExpr("({a:x::y}) = foo", aExpr("=", singletonObjPatt("a", 1), ident("foo")), { xmlQualifiedIdentifier: function() 1 });