Backed out 6 changesets (bug 1397422, bug 1228841) for devtools mochitest failure devtools/client/debugger/test/mochitest/browser_dbg_search-symbols.js r=backout a=backout on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Tue, 05 Dec 2017 06:26:49 +0200
changeset 394973 a26e71300a880ad99bd5bb0fd2d4be514113c7d5
parent 394972 46463dab3129e5c56a96d34f1c275d14149d32bd
child 394974 b4cef8d1dff06a1ec2b9bb17211c0c3c7f5b76fa
child 395086 ae6557856c9236786286ab08cb700712f9d5a80a
push id33026
push usershindli@mozilla.com
push dateTue, 05 Dec 2017 09:59:24 +0000
treeherdermozilla-central@b4cef8d1dff0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbackout, backout
bugs1397422, 1228841
milestone59.0a1
backs out28f34951d1b3fc6c0399435f355dd9ae5a12b3fb
37581537c8121e3edc8b3fdf1678974ce232bfc4
41b4f7b178638a76f3606f5871ae9b7036aec500
9651b5f82d299c7d7b043ea9dbdbd3b783af0e8a
901ed6dd87f2fb46741efb56082c48bee214a570
b36c6610678d3bfae4ac29feebece8971fed6a7b
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 6 changesets (bug 1397422, bug 1228841) for devtools mochitest failure devtools/client/debugger/test/mochitest/browser_dbg_search-symbols.js r=backout a=backout on a CLOSED TREE Backed out changeset 28f34951d1b3 (bug 1228841) Backed out changeset 37581537c812 (bug 1228841) Backed out changeset 41b4f7b17863 (bug 1228841) Backed out changeset 9651b5f82d29 (bug 1228841) Backed out changeset 901ed6dd87f2 (bug 1397422) Backed out changeset b36c6610678d (bug 1397422)
dom/media/tests/mochitest/test_peerConnection_transceivers.html
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/NameFunctions.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/SyntaxParseHandler.h
js/src/jit-test/lib/nightly-only.js
js/src/jit-test/lib/syntax.js
js/src/jit-test/tests/baseline/bug843811-1.js
js/src/jit-test/tests/baseline/bug843811-2.js
js/src/jit-test/tests/baseline/bug843811-3.js
js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js
js/src/jit-test/tests/basic/bug593663-regexp.js
js/src/jit-test/tests/basic/bug640078.js
js/src/jit-test/tests/basic/bug787309.js
js/src/jit-test/tests/basic/bug787848.js
js/src/jit-test/tests/debug/Frame-onStep-12.js
js/src/jit-test/tests/gc/bug-1259490.js
js/src/jit-test/tests/ion/bug1293542.js
js/src/jit-test/tests/ion/bug799185-1.js
js/src/jit-test/tests/ion/bug799185-4.js
js/src/jit-test/tests/ion/bug799185-5.js
js/src/jit-test/tests/ion/bug799185-7.js
js/src/jit-test/tests/ion/throw.js
js/src/jit-test/tests/jaeger/bug553781-2.js
js/src/jit-test/tests/jaeger/bug553781.js
js/src/jit-test/tests/parser/arrow-rest.js
js/src/jsversion.h
js/src/tests/ecma_3_1/Object/regress-444787.js
js/src/tests/ecma_5/extensions/iterator-in-catch.js
js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
js/src/tests/ecma_6/Class/newTargetEval.js
js/src/tests/ecma_6/Class/outerBinding.js
js/src/tests/ecma_6/Class/superCallThisInit.js
js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js
js/src/tests/js1_5/extensions/catchguard-001-n.js
js/src/tests/js1_5/extensions/catchguard-001.js
js/src/tests/js1_5/extensions/catchguard-002.js
js/src/tests/js1_5/extensions/catchguard-003.js
js/src/tests/js1_5/extensions/regress-104077.js
js/src/tests/js1_5/extensions/regress-346494-01.js
js/src/tests/js1_5/extensions/regress-346494.js
js/src/tests/js1_5/extensions/regress-350312-02.js
js/src/tests/js1_5/extensions/regress-350312-03.js
js/src/tests/js1_5/extensions/regress-351102-01.js
js/src/tests/js1_5/extensions/regress-351102-02.js
js/src/tests/js1_5/extensions/regress-351102-06.js
js/src/tests/js1_5/extensions/regress-374589.js
js/src/tests/js1_7/extensions/regress-350312.js
js/src/tests/js1_7/extensions/regress-351102-03.js
js/src/tests/js1_7/extensions/regress-351102-04.js
js/src/tests/js1_7/extensions/regress-351102-05.js
js/src/tests/js1_7/extensions/regress-351102-07.js
js/src/tests/js1_7/regress/regress-375695.js
js/src/tests/js1_8_5/extensions/regress-677589.js
js/src/tests/js1_8_5/reflect-parse/Match.js
js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js
js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js
js/src/tests/js1_8_5/reflect-parse/basicBuilder.js
js/src/tests/js1_8_5/reflect-parse/builderExceptions.js
js/src/tests/js1_8_5/reflect-parse/statements.js
toolkit/content/browser-content.js
--- a/dom/media/tests/mochitest/test_peerConnection_transceivers.html
+++ b/dom/media/tests/mochitest/test_peerConnection_transceivers.html
@@ -418,22 +418,21 @@
   };
 
   let checkAddTransceiverBadKind = async () => {
     let pc = new RTCPeerConnection();
     try {
       pc.addTransceiver("foo");
       ok(false, 'addTransceiver("foo") throws');
     }
+    catch (e if e instanceof TypeError) {
+      ok(true, 'addTransceiver("foo") throws a TypeError');
+    }
     catch (e) {
-      if (e instanceof TypeError) {
-        ok(true, 'addTransceiver("foo") throws a TypeError');
-      } else {
-        ok(false, 'addTransceiver("foo") throws a TypeError');
-      }
+      ok(false, 'addTransceiver("foo") throws a TypeError');
     }
 
     hasProps(pc.getTransceivers(), []);
 
     pc.close();
   };
 
   let checkAddTransceiverNoTrackDoesntPair = async () => {
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -470,17 +470,17 @@ class NodeBuilder
                                HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
                                bool isAsync, bool isExpression, MutableHandleValue dst);
 
     MOZ_MUST_USE bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
                                          MutableHandleValue dst);
 
     MOZ_MUST_USE bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
 
-    MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue body, TokenPos* pos,
+    MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
                                   MutableHandleValue dst);
 
     MOZ_MUST_USE bool prototypeMutation(HandleValue val, TokenPos* pos, MutableHandleValue dst);
     MOZ_MUST_USE bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind,
                                           bool isShorthand, bool isMethod, TokenPos* pos,
                                           MutableHandleValue dst);
 
 
@@ -522,17 +522,17 @@ class NodeBuilder
     MOZ_MUST_USE bool whileStatement(HandleValue test, HandleValue stmt, TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool doWhileStatement(HandleValue stmt, HandleValue test, TokenPos* pos,
                                        MutableHandleValue dst);
 
     MOZ_MUST_USE bool switchStatement(HandleValue disc, NodeVector& elts, bool lexical, TokenPos* pos,
                                       MutableHandleValue dst);
 
-    MOZ_MUST_USE bool tryStatement(HandleValue body, HandleValue handler,
+    MOZ_MUST_USE bool tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded,
                                    HandleValue finally, TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool importDeclaration(NodeVector& elts, HandleValue moduleSpec, TokenPos* pos, MutableHandleValue dst);
 
     MOZ_MUST_USE bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos* pos, MutableHandleValue dst);
 
@@ -949,26 +949,31 @@ NodeBuilder::switchStatement(HandleValue
     return newNode(AST_SWITCH_STMT, pos,
                    "discriminant", disc,
                    "cases", array,
                    "lexical", lexicalVal,
                    dst);
 }
 
 bool
-NodeBuilder::tryStatement(HandleValue body, HandleValue handler,
+NodeBuilder::tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded,
                           HandleValue finally, TokenPos* pos, MutableHandleValue dst)
 {
+    RootedValue guardedHandlers(cx);
+    if (!newArray(guarded, &guardedHandlers))
+        return false;
+
     RootedValue cb(cx, callbacks[AST_TRY_STMT]);
     if (!cb.isNull())
-        return callback(cb, body, handler, opt(finally), pos, dst);
+        return callback(cb, body, guardedHandlers, unguarded, opt(finally), pos, dst);
 
     return newNode(AST_TRY_STMT, pos,
                    "block", body,
-                   "handler", handler,
+                   "guardedHandlers", guardedHandlers,
+                   "handler", unguarded,
                    "finalizer", finally,
                    dst);
 }
 
 bool
 NodeBuilder::debuggerStatement(TokenPos* pos, MutableHandleValue dst)
 {
     RootedValue cb(cx, callbacks[AST_DEBUGGER_STMT]);
@@ -1441,25 +1446,26 @@ NodeBuilder::switchCase(HandleValue expr
 
     return newNode(AST_CASE, pos,
                    "test", expr,
                    "consequent", array,
                    dst);
 }
 
 bool
-NodeBuilder::catchClause(HandleValue var, HandleValue body, TokenPos* pos,
+NodeBuilder::catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
                          MutableHandleValue dst)
 {
     RootedValue cb(cx, callbacks[AST_CATCH]);
     if (!cb.isNull())
-        return callback(cb, opt(var), body, pos, dst);
+        return callback(cb, opt(var), opt(guard), body, pos, dst);
 
     return newNode(AST_CATCH, pos,
                    "param", var,
+                   "guard", guard,
                    "body", body,
                    dst);
 }
 
 bool
 NodeBuilder::literal(HandleValue val, TokenPos* pos, MutableHandleValue dst)
 {
     RootedValue cb(cx, callbacks[AST_LITERAL]);
@@ -1669,17 +1675,17 @@ class ASTSerializer
                MutableHandleValue dst);
     bool forOf(ParseNode* loop, ParseNode* head, HandleValue var, HandleValue stmt,
                MutableHandleValue dst);
     bool statement(ParseNode* pn, MutableHandleValue dst);
     bool blockStatement(ParseNode* pn, MutableHandleValue dst);
     bool switchStatement(ParseNode* pn, MutableHandleValue dst);
     bool switchCase(ParseNode* pn, MutableHandleValue dst);
     bool tryStatement(ParseNode* pn, MutableHandleValue dst);
-    bool catchClause(ParseNode* pn, MutableHandleValue dst);
+    bool catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst);
 
     bool optExpression(ParseNode* pn, MutableHandleValue dst) {
         if (!pn) {
             dst.setMagic(JS_SERIALIZE_NO_NODE);
             return true;
         }
         return expression(pn, dst);
     }
@@ -2162,52 +2168,68 @@ ASTSerializer::switchStatement(ParseNode
             return false;
         cases.infallibleAppend(child);
     }
 
     return builder.switchStatement(disc, cases, lexical, &pn->pn_pos, dst);
 }
 
 bool
-ASTSerializer::catchClause(ParseNode* pn, MutableHandleValue dst)
+ASTSerializer::catchClause(ParseNode* pn, bool* isGuarded, MutableHandleValue dst)
 {
-    MOZ_ASSERT(pn->isKind(PNK_CATCH));
-    MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
-    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-    RootedValue var(cx), body(cx);
-
-    if (!optPattern(pn->pn_left, &var))
+    MOZ_ASSERT_IF(pn->pn_kid1, pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
+    MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
+    MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
+
+    RootedValue var(cx), guard(cx), body(cx);
+
+    if (!optPattern(pn->pn_kid1, &var) ||
+        !optExpression(pn->pn_kid2, &guard)) {
         return false;
-
-    return statement(pn->pn_right, &body) &&
-           builder.catchClause(var, body, &pn->pn_pos, dst);
+    }
+
+    *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, MutableHandleValue dst)
 {
     MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid1->pn_pos));
     MOZ_ASSERT_IF(pn->pn_kid2, pn->pn_pos.encloses(pn->pn_kid2->pn_pos));
     MOZ_ASSERT_IF(pn->pn_kid3, pn->pn_pos.encloses(pn->pn_kid3->pn_pos));
 
     RootedValue body(cx);
     if (!statement(pn->pn_kid1, &body))
         return false;
 
-    RootedValue handler(cx, NullValue());
-    if (ParseNode* catchScope = pn->pn_kid2) {
-        MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE));
-        if (!catchClause(catchScope->scopeBody(), &handler))
+    NodeVector guarded(cx);
+    RootedValue unguarded(cx, NullValue());
+
+    if (ParseNode* catchList = pn->pn_kid2) {
+        if (!guarded.reserve(catchList->pn_count))
             return false;
+
+        for (ParseNode* next = catchList->pn_head; next; next = next->pn_next) {
+            RootedValue clause(cx);
+            bool isGuarded;
+            if (!catchClause(next->pn_expr, &isGuarded, &clause))
+                return false;
+            if (isGuarded)
+                guarded.infallibleAppend(clause);
+            else
+                unguarded = clause;
+        }
     }
 
     RootedValue finally(cx);
     return optStatement(pn->pn_kid3, &finally) &&
-           builder.tryStatement(body, handler, finally, &pn->pn_pos, dst);
+           builder.tryStatement(body, guarded, unguarded, finally, &pn->pn_pos, dst);
 }
 
 bool
 ASTSerializer::forInit(ParseNode* pn, MutableHandleValue dst)
 {
     if (!pn) {
         dst.setMagic(JS_SERIALIZE_NO_NODE);
         return true;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -296,16 +296,19 @@ class LoopControl : public BreakableCont
 class TryFinallyControl : public BytecodeEmitter::NestableControl
 {
     bool emittingSubroutine_;
 
   public:
     // The subroutine when emitting a finally block.
     JumpList gosubs;
 
+    // Offset of the last catch guard, if any.
+    JumpList guardJump;
+
     TryFinallyControl(BytecodeEmitter* bce, StatementKind kind)
       : NestableControl(bce, kind),
         emittingSubroutine_(false)
     {
         MOZ_ASSERT(is<TryFinallyControl>());
     }
 
     void setEmittingSubroutine() {
@@ -1516,16 +1519,17 @@ class MOZ_STACK_CLASS TryEmitter
     // When a finally block is active, non-local jumps (including
     // jumps-over-catches) result in a GOSUB being written into the bytecode
     // stream and fixed-up later.
     //
     // If ShouldUseControl is DontUseControl, all that handling is skipped.
     // DontUseControl is used by yield* and the internal try-catch around
     // IteratorClose. These internal uses must:
     //   * have only one catch block
+    //   * have no catch guard
     //   * have JSOP_GOTO at the end of catch-block
     //   * have no non-local-jump
     //   * don't use finally block for normal completion of try-block and
     //     catch-block
     //
     // Additionally, a finally block may be emitted when ShouldUseControl is
     // DontUseControl, even if the kind is not TryCatchFinally or TryFinally,
     // because GOSUBs are not emitted. This internal use shares the
@@ -1625,19 +1629,24 @@ class MOZ_STACK_CLASS TryEmitter
         if (!bce_->emitJumpTarget(&tryEnd_))
             return false;
 
         return true;
     }
 
   public:
     bool emitCatch() {
-        MOZ_ASSERT(state_ == Try);
-        if (!emitTryEnd())
-            return false;
+        if (state_ == Try) {
+            if (!emitTryEnd())
+                return false;
+        } else {
+            MOZ_ASSERT(state_ == Catch);
+            if (!emitCatchEnd(true))
+                return false;
+        }
 
         MOZ_ASSERT(bce_->stackDepth == depth_);
 
         if (retValKind_ == UseRetVal) {
             // Clear the frame's return value that might have been set by the
             // try block:
             //
             //   eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
@@ -1658,20 +1667,38 @@ class MOZ_STACK_CLASS TryEmitter
         if (!controlInfo_)
             return true;
 
         // gosub <finally>, if required.
         if (hasFinally()) {
             if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
                 return false;
             MOZ_ASSERT(bce_->stackDepth == depth_);
-
-            // Jump over the finally block.
-            if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
-                return false;
+        }
+
+        // Jump over the remaining catch blocks.  This will get fixed
+        // up to jump to after catch/finally.
+        if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
+            return false;
+
+        // If this catch block had a guard clause, patch the guard jump to
+        // come here.
+        if (controlInfo_->guardJump.offset != -1) {
+            if (!bce_->emitJumpTargetAndPatch(controlInfo_->guardJump))
+                return false;
+            controlInfo_->guardJump.offset = -1;
+
+            // If this catch block is the last one, rethrow, delegating
+            // execution of any finally block to the exception handler.
+            if (!hasNext) {
+                if (!bce_->emit1(JSOP_EXCEPTION))
+                    return false;
+                if (!bce_->emit1(JSOP_THROW))
+                    return false;
+            }
         }
 
         return true;
     }
 
   public:
     bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) {
         // If we are using controlInfo_ (i.e., emitting a syntactic try
@@ -3190,16 +3217,17 @@ BytecodeEmitter::checkSideEffects(ParseN
       case PNK_MODASSIGN:
       case PNK_POWASSIGN:
       case PNK_SETTHIS:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         *answer = true;
         return true;
 
       case PNK_STATEMENTLIST:
+      case PNK_CATCHLIST:
       // Strict equality operations and logical operators are well-behaved and
       // perform no conversions.
       case PNK_OR:
       case PNK_AND:
       case PNK_STRICTEQ:
       case PNK_STRICTNE:
       // Any subexpression of a comma expression could be effectful.
       case PNK_COMMA:
@@ -3378,38 +3406,44 @@ BytecodeEmitter::checkSideEffects(ParseN
         return true;
 
       case PNK_TRY:
         MOZ_ASSERT(pn->isArity(PN_TERNARY));
         if (!checkSideEffects(pn->pn_kid1, answer))
             return false;
         if (*answer)
             return true;
-        if (ParseNode* catchScope = pn->pn_kid2) {
-            MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE));
-            if (!checkSideEffects(catchScope, answer))
+        if (ParseNode* catchList = pn->pn_kid2) {
+            MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
+            if (!checkSideEffects(catchList, answer))
                 return false;
             if (*answer)
                 return true;
         }
         if (ParseNode* finallyBlock = pn->pn_kid3) {
             if (!checkSideEffects(finallyBlock, answer))
                 return false;
         }
         return true;
 
       case PNK_CATCH:
-        MOZ_ASSERT(pn->isArity(PN_BINARY));
-        if (ParseNode* name = pn->pn_left) {
+        MOZ_ASSERT(pn->isArity(PN_TERNARY));
+        if (ParseNode* name = pn->pn_kid1) {
             if (!checkSideEffects(name, answer))
                 return false;
             if (*answer)
                 return true;
         }
-        return checkSideEffects(pn->pn_right, answer);
+        if (ParseNode* cond = pn->pn_kid2) {
+            if (!checkSideEffects(cond, answer))
+                return false;
+            if (*answer)
+                return true;
+        }
+        return checkSideEffects(pn->pn_kid3, answer);
 
       case PNK_SWITCH:
         MOZ_ASSERT(pn->isArity(PN_BINARY));
         if (!checkSideEffects(pn->pn_left, answer))
             return false;
         return *answer || checkSideEffects(pn->pn_right, answer);
 
       case PNK_LABEL:
@@ -6576,23 +6610,30 @@ class EmitLevelManager
 };
 
 } /* anonymous namespace */
 
 bool
 BytecodeEmitter::emitCatch(ParseNode* pn)
 {
     // We must be nested under a try-finally statement.
-    MOZ_ASSERT(innermostNestableControl->is<TryFinallyControl>());
+    TryFinallyControl& controlInfo = innermostNestableControl->as<TryFinallyControl>();
 
     /* Pick up the pending exception and bind it to the catch variable. */
     if (!emit1(JSOP_EXCEPTION))
         return false;
 
-    ParseNode* pn2 = pn->pn_left;
+    /*
+     * Dup the exception object if there is a guard for rethrowing to use
+     * it later when rethrowing or in other catches.
+     */
+    if (pn->pn_kid2 && !emit1(JSOP_DUP))
+        return false;
+
+    ParseNode* pn2 = pn->pn_kid1;
     if (!pn2) {
         // Catch parameter was omitted; just discard the exception.
         if (!emit1(JSOP_POP))
             return false;
     } else {
         switch (pn2->getKind()) {
           case PNK_ARRAY:
           case PNK_OBJECT:
@@ -6609,30 +6650,69 @@ BytecodeEmitter::emitCatch(ParseNode* pn
                 return false;
             break;
 
           default:
             MOZ_ASSERT(0);
         }
     }
 
+    // If there is a guard expression, emit it and arrange to jump to the next
+    // catch block if the guard expression is false.
+    if (pn->pn_kid2) {
+        if (!emitTree(pn->pn_kid2))
+            return false;
+
+        // If the guard expression is false, fall through, pop the block scope,
+        // and jump to the next catch block.  Otherwise jump over that code and
+        // pop the dupped exception.
+        JumpList guardCheck;
+        if (!emitJump(JSOP_IFNE, &guardCheck))
+            return false;
+
+        {
+            NonLocalExitControl nle(this, NonLocalExitControl::Throw);
+
+            // Move exception back to cx->exception to prepare for
+            // the next catch.
+            if (!emit1(JSOP_THROWING))
+                return false;
+
+            // Leave the scope for this catch block.
+            if (!nle.prepareForNonLocalJump(&controlInfo))
+                return false;
+
+            // Jump to the next handler added by emitTry.
+            if (!emitJump(JSOP_GOTO, &controlInfo.guardJump))
+                return false;
+        }
+
+        // Back to normal control flow.
+        if (!emitJumpTargetAndPatch(guardCheck))
+            return false;
+
+        // Pop duplicated exception object as we no longer need it.
+        if (!emit1(JSOP_POP))
+            return false;
+    }
+
     /* Emit the catch body. */
-    return emitTree(pn->pn_right);
+    return emitTree(pn->pn_kid3);
 }
 
 // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See the
 // comment on EmitSwitch.
 MOZ_NEVER_INLINE bool
 BytecodeEmitter::emitTry(ParseNode* pn)
 {
-    ParseNode* catchScope = pn->pn_kid2;
+    ParseNode* catchList = pn->pn_kid2;
     ParseNode* finallyNode = pn->pn_kid3;
 
     TryEmitter::Kind kind;
-    if (catchScope) {
+    if (catchList) {
         if (finallyNode)
             kind = TryEmitter::TryCatchFinally;
         else
             kind = TryEmitter::TryCatch;
     } else {
         MOZ_ASSERT(finallyNode);
         kind = TryEmitter::TryFinally;
     }
@@ -6640,35 +6720,53 @@ BytecodeEmitter::emitTry(ParseNode* pn)
 
     if (!tryCatch.emitTry())
         return false;
 
     if (!emitTree(pn->pn_kid1))
         return false;
 
     // If this try has a catch block, emit it.
-    if (catchScope) {
+    if (catchList) {
+        MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
+
         // The emitted code for a catch block looks like:
         //
         // [pushlexicalenv]             only if any local aliased
         // exception
+        // if there is a catchguard:
+        //   dup
         // setlocal 0; pop              assign or possibly destructure exception
+        // if there is a catchguard:
+        //   < catchguard code >
+        //   ifne POST
+        //   debugleaveblock
+        //   [poplexicalenv]            only if any local aliased
+        //   throwing                   pop exception to cx->exception
+        //   goto <next catch block>
+        //   POST: pop
         // < catch block contents >
         // debugleaveblock
         // [poplexicalenv]              only if any local aliased
-        // if there is a finally block:
-        //   gosub <finally>
-        //   goto <after finally>
-        if (!tryCatch.emitCatch())
-            return false;
-
-        // Emit the lexical scope and catch body.
-        MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE));
-        if (!emitTree(catchScope))
-            return false;
+        // goto <end of catch blocks>   non-local; finally applies
+        //
+        // If there's no catch block without a catchguard, the last <next catch
+        // block> points to rethrow code.  This code will [gosub] to the finally
+        // code if appropriate, and is also used for the catch-all trynote for
+        // capturing exceptions thrown from catch{} blocks.
+        //
+        for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
+            if (!tryCatch.emitCatch())
+                return false;
+
+            // Emit the lexical scope and catch body.
+            MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
+            if (!emitTree(pn3))
+                return false;
+        }
     }
 
     // Emit the finally handler, if there is one.
     if (finallyNode) {
         if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin)))
             return false;
 
         if (!emitTree(finallyNode))
@@ -6793,17 +6891,17 @@ BytecodeEmitter::emitLexicalScope(ParseN
             pnForPos = body->pn_head;
         if (!updateLineNumberNotes(pnForPos->pn_pos.begin))
             return false;
     }
 
     EmitterScope emitterScope(this);
     ScopeKind kind;
     if (body->isKind(PNK_CATCH))
-        kind = (!body->pn_left || body->pn_left->isKind(PNK_NAME))
+        kind = (!body->pn_kid1 || body->pn_kid1->isKind(PNK_NAME))
                ? ScopeKind::SimpleCatch
                : ScopeKind::Catch;
     else
         kind = ScopeKind::Lexical;
 
     if (!emitterScope.enterLexical(this, kind, pn->scopeBindings()))
         return false;
 
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -183,27 +183,32 @@ ContainsHoistedDeclaration(JSContext* cx
                    "must have either catch(es) or finally");
 
         ParseNode* tryBlock = node->pn_kid1;
         if (!ContainsHoistedDeclaration(cx, tryBlock, result))
             return false;
         if (*result)
             return true;
 
-        if (ParseNode* catchScope = node->pn_kid2) {
-            MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE));
-
-            ParseNode* catchNode = catchScope->pn_expr;
-            MOZ_ASSERT(catchNode->isKind(PNK_CATCH));
+        if (ParseNode* catchList = node->pn_kid2) {
+            for (ParseNode* lexicalScope = catchList->pn_head;
+                 lexicalScope;
+                 lexicalScope = lexicalScope->pn_next)
+            {
+                MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE));
 
-            ParseNode* catchStatements = catchNode->pn_right;
-            if (!ContainsHoistedDeclaration(cx, catchStatements, result))
-                return false;
-            if (*result)
-                return true;
+                ParseNode* catchNode = lexicalScope->pn_expr;
+                MOZ_ASSERT(catchNode->isKind(PNK_CATCH));
+
+                ParseNode* catchStatements = catchNode->pn_kid3;
+                if (!ContainsHoistedDeclaration(cx, catchStatements, result))
+                    return false;
+                if (*result)
+                    return true;
+            }
         }
 
         if (ParseNode* finallyBlock = node->pn_kid3)
             return ContainsHoistedDeclaration(cx, finallyBlock, result);
 
         *result = false;
         return true;
       }
@@ -360,16 +365,17 @@ ContainsHoistedDeclaration(JSContext* cx
       case PNK_NULL:
       case PNK_RAW_UNDEFINED:
       case PNK_THIS:
       case PNK_ELISION:
       case PNK_NUMBER:
       case PNK_NEW:
       case PNK_GENERATOR:
       case PNK_PARAMSBODY:
+      case PNK_CATCHLIST:
       case PNK_CATCH:
       case PNK_FORIN:
       case PNK_FOROF:
       case PNK_FORHEAD:
       case PNK_CLASSMETHOD:
       case PNK_CLASSMETHODLIST:
       case PNK_CLASSNAMES:
       case PNK_NEWTARGET:
@@ -1195,41 +1201,46 @@ FoldTry(JSContext* cx, ParseNode* node, 
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
     MOZ_ASSERT(node->isArity(PN_TERNARY));
 
     ParseNode*& statements = node->pn_kid1;
     if (!Fold(cx, &statements, parser))
         return false;
 
-    if (ParseNode*& catchScope = node->pn_kid2) {
-        if (!Fold(cx, &catchScope, parser))
+    if (ParseNode*& catchList = node->pn_kid2) {
+        if (!Fold(cx, &catchList, parser))
             return false;
     }
 
     if (ParseNode*& finally = node->pn_kid3) {
         if (!Fold(cx, &finally, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
 FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
 {
     MOZ_ASSERT(node->isKind(PNK_CATCH));
-    MOZ_ASSERT(node->isArity(PN_BINARY));
+    MOZ_ASSERT(node->isArity(PN_TERNARY));
 
-    if (ParseNode*& declPattern = node->pn_left) {
+    if (ParseNode*& declPattern = node->pn_kid1) {
         if (!Fold(cx, &declPattern, parser))
             return false;
     }
 
-    if (ParseNode*& statements = node->pn_right) {
+    if (ParseNode*& cond = node->pn_kid2) {
+        if (!FoldCondition(cx, &cond, parser))
+            return false;
+    }
+
+    if (ParseNode*& statements = node->pn_kid3) {
         if (!Fold(cx, &statements, parser))
             return false;
     }
 
     return true;
 }
 
 static bool
@@ -1708,16 +1719,17 @@ Fold(JSContext* cx, ParseNode** pnp, Par
       case PNK_INSTANCEOF:
       case PNK_IN:
       case PNK_COMMA:
       case PNK_NEW:
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_STATEMENTLIST:
       case PNK_CLASSMETHODLIST:
+      case PNK_CATCHLIST:
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_VAR:
       case PNK_CONST:
       case PNK_LET:
       case PNK_PARAMSBODY:
       case PNK_CALLSITEOBJ:
       case PNK_EXPORT_SPEC_LIST:
       case PNK_IMPORT_SPEC_LIST:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -587,47 +587,37 @@ class FullParseHandler
         return new_<LabeledStatement>(label, stmt, begin);
     }
 
     ParseNode* newThrowStatement(ParseNode* expr, const TokenPos& pos) {
         MOZ_ASSERT(pos.encloses(expr->pn_pos));
         return new_<UnaryNode>(PNK_THROW, pos, expr);
     }
 
-    ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchScope,
+    ParseNode* newTryStatement(uint32_t begin, ParseNode* body, ParseNode* catchList,
                                ParseNode* finallyBlock) {
-        TokenPos pos(begin, (finallyBlock ? finallyBlock : catchScope)->pn_pos.end);
-        return new_<TernaryNode>(PNK_TRY, body, catchScope, finallyBlock, pos);
+        TokenPos pos(begin, (finallyBlock ? finallyBlock : catchList)->pn_pos.end);
+        return new_<TernaryNode>(PNK_TRY, body, catchList, finallyBlock, pos);
     }
 
     ParseNode* newDebuggerStatement(const TokenPos& pos) {
         return new_<DebuggerStatement>(pos);
     }
 
     ParseNode* newPropertyAccess(ParseNode* pn, PropertyName* name, uint32_t end) {
         return new_<PropertyAccess>(pn, name, pn->pn_pos.begin, end);
     }
 
     ParseNode* newPropertyByValue(ParseNode* lhs, ParseNode* index, uint32_t end) {
         return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
     }
 
-    bool setupCatchScope(ParseNode* lexicalScope, ParseNode* catchName, ParseNode* catchBody) {
-        ParseNode* catchpn;
-        if (catchName) {
-            catchpn = new_<BinaryNode>(PNK_CATCH, JSOP_NOP, catchName, catchBody);
-        } else {
-            catchpn = new_<BinaryNode>(PNK_CATCH, JSOP_NOP, catchBody->pn_pos, catchName,
-                                       catchBody);
-        }
-        if (!catchpn)
-            return false;
-        lexicalScope->setScopeBody(catchpn);
-        return true;
-    }
+    inline MOZ_MUST_USE bool addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
+                                           ParseNode* catchName, ParseNode* catchGuard,
+                                           ParseNode* catchBody);
 
     inline MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(ParseNode* funcpn,
                                                                    ParseNode* pn);
 
     void checkAndSetIsDirectRHSAnonFunction(ParseNode* pn) {
         if (IsAnonymousFunctionDefinition(pn))
             pn->setDirectRHSAnonFunction(true);
     }
@@ -774,16 +764,20 @@ class FullParseHandler
     }
 
     ParseNode* singleBindingFromDeclaration(ParseNode* decl) {
         MOZ_ASSERT(isDeclarationList(decl));
         MOZ_ASSERT(decl->pn_count == 1);
         return decl->pn_head;
     }
 
+    ParseNode* newCatchList(const TokenPos& pos) {
+        return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos);
+    }
+
     ParseNode* newCommaExpressionList(ParseNode* kid) {
         return new_<ListNode>(PNK_COMMA, JSOP_NOP, kid);
     }
 
     void addList(ParseNode* list, ParseNode* kid) {
         list->append(kid);
     }
 
@@ -854,16 +848,29 @@ class FullParseHandler
     }
     JSAtom* nextLazyClosedOverBinding() {
         MOZ_ASSERT(lazyClosedOverBindingIndex < lazyOuterFunction_->numClosedOverBindings());
         return lazyOuterFunction_->closedOverBindings()[lazyClosedOverBindingIndex++];
     }
 };
 
 inline bool
+FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
+                                ParseNode* catchName, ParseNode* catchGuard,
+                                ParseNode* catchBody)
+{
+    ParseNode* catchpn = new_<TernaryNode>(PNK_CATCH, catchName, catchGuard, catchBody);
+    if (!catchpn)
+        return false;
+    catchList->append(lexicalScope);
+    lexicalScope->setScopeBody(catchpn);
+    return true;
+}
+
+inline bool
 FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
                                                         ParseNode* defaultValue)
 {
     ParseNode* arg = funcpn->pn_body->last();
     ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue);
     if (!pn)
         return false;
 
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -633,40 +633,42 @@ class NameResolver
           // The statements in the try-block are mandatory.  The catch-blocks
           // and finally block are optional (but at least one or the other must
           // be present).
           case PNK_TRY:
             MOZ_ASSERT(cur->isArity(PN_TERNARY));
             if (!resolve(cur->pn_kid1, prefix))
                 return false;
             MOZ_ASSERT(cur->pn_kid2 || cur->pn_kid3);
-            if (ParseNode* catchScope = cur->pn_kid2) {
-                MOZ_ASSERT(catchScope->isKind(PNK_LEXICALSCOPE));
-                MOZ_ASSERT(catchScope->scopeBody()->isKind(PNK_CATCH));
-                MOZ_ASSERT(catchScope->scopeBody()->isArity(PN_BINARY));
-                if (!resolve(catchScope->scopeBody(), prefix))
+            if (ParseNode* catchList = cur->pn_kid2) {
+                MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
+                if (!resolve(catchList, prefix))
                     return false;
             }
             if (ParseNode* finallyBlock = cur->pn_kid3) {
                 if (!resolve(finallyBlock, prefix))
                     return false;
             }
             break;
 
           // The first child, the catch-pattern, may contain functions via
           // computed property names.  The optional catch-conditions may
           // contain any expression.  The catch statements, of course, may
           // contain arbitrary expressions.
           case PNK_CATCH:
-            MOZ_ASSERT(cur->isArity(PN_BINARY));
-            if (cur->pn_left) {
-              if (!resolve(cur->pn_left, prefix))
+            MOZ_ASSERT(cur->isArity(PN_TERNARY));
+            if (cur->pn_kid1) {
+              if (!resolve(cur->pn_kid1, prefix))
                   return false;
             }
-            if (!resolve(cur->pn_right, prefix))
+            if (cur->pn_kid2) {
+                if (!resolve(cur->pn_kid2, prefix))
+                    return false;
+            }
+            if (!resolve(cur->pn_kid3, prefix))
                 return false;
             break;
 
           // Nodes with arbitrary-expression children.
           case PNK_OR:
           case PNK_AND:
           case PNK_BITOR:
           case PNK_BITXOR:
@@ -753,16 +755,28 @@ class NameResolver
                 MOZ_ASSERT(!item->pn_left->expr());
                 MOZ_ASSERT(item->pn_right->isKind(PNK_NAME));
                 MOZ_ASSERT(!item->pn_right->expr());
             }
 #endif
             break;
           }
 
+          case PNK_CATCHLIST: {
+            MOZ_ASSERT(cur->isArity(PN_LIST));
+            for (ParseNode* catchNode = cur->pn_head; catchNode; catchNode = catchNode->pn_next) {
+                MOZ_ASSERT(catchNode->isKind(PNK_LEXICALSCOPE));
+                MOZ_ASSERT(catchNode->scopeBody()->isKind(PNK_CATCH));
+                MOZ_ASSERT(catchNode->scopeBody()->isArity(PN_TERNARY));
+                if (!resolve(catchNode->scopeBody(), prefix))
+                    return false;
+            }
+            break;
+          }
+
           case PNK_DOT:
             MOZ_ASSERT(cur->isArity(PN_NAME));
 
             // Super prop nodes do not have a meaningful LHS
             if (cur->as<PropertyAccess>().isSuper())
                 break;
             if (!resolve(cur->expr(), prefix))
                 return false;
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -402,23 +402,27 @@ PushNodeChildren(ParseNode* pn, NodeStac
         stack->push(pn->pn_kid1);
         if (pn->pn_kid2)
             stack->push(pn->pn_kid2);
         if (pn->pn_kid3)
             stack->push(pn->pn_kid3);
         return PushResult::Recyclable;
       }
 
-      // A catch node has left node as catch-variable pattern (or null if
-      // omitted) and right node as the statements in the catch block.
+      // A catch node has first kid as catch-variable pattern, the second kid
+      // as catch condition (which, if non-null, records the |<cond>| in
+      // SpiderMonkey's |catch (e if <cond>)| extension), and third kid as the
+      // statements in the catch block.
       case PNK_CATCH: {
-        MOZ_ASSERT(pn->isArity(PN_BINARY));
-        if (pn->pn_left)
-            stack->push(pn->pn_left);
-        stack->push(pn->pn_right);
+        MOZ_ASSERT(pn->isArity(PN_TERNARY));
+        if (pn->pn_kid1)
+            stack->push(pn->pn_kid1);
+        if (pn->pn_kid2)
+            stack->push(pn->pn_kid2);
+        stack->push(pn->pn_kid3);
         return PushResult::Recyclable;
       }
 
       // List nodes with all non-null children.
       case PNK_OR:
       case PNK_AND:
       case PNK_BITOR:
       case PNK_BITXOR:
@@ -450,16 +454,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_ARRAY:
       case PNK_OBJECT:
       case PNK_TEMPLATE_STRING_LIST:
       case PNK_TAGGED_TEMPLATE:
       case PNK_CALLSITEOBJ:
       case PNK_VAR:
       case PNK_CONST:
       case PNK_LET:
+      case PNK_CATCHLIST:
       case PNK_STATEMENTLIST:
       case PNK_IMPORT_SPEC_LIST:
       case PNK_EXPORT_SPEC_LIST:
       case PNK_PARAMSBODY:
       case PNK_CLASSMETHODLIST:
         return PushListNodeChildren(pn, stack);
 
       case PNK_LABEL:
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -74,16 +74,17 @@ class ObjectBox;
     F(NEW) \
     /* Delete operations.  These must be sequential. */ \
     F(DELETENAME) \
     F(DELETEPROP) \
     F(DELETEELEM) \
     F(DELETEEXPR) \
     F(TRY) \
     F(CATCH) \
+    F(CATCHLIST) \
     F(THROW) \
     F(DEBUGGER) \
     F(GENERATOR) \
     F(INITIALYIELD) \
     F(YIELD) \
     F(YIELD_STAR) \
     F(LEXICALSCOPE) \
     F(LET) \
@@ -254,24 +255,25 @@ IsTypeofKind(ParseNodeKind kind)
  * PNK_FOROF    ternary     pn_kid1: declaration or expression to left of 'of'
  *                          pn_kid2: null
  *                          pn_kid3: expr to right of 'of'
  * PNK_FORHEAD  ternary     pn_kid1:  init expr before first ';' or nullptr
  *                          pn_kid2:  cond expr before second ';' or nullptr
  *                          pn_kid3:  update expr after second ';' or nullptr
  * PNK_THROW    unary       pn_kid: exception
  * PNK_TRY      ternary     pn_kid1: try block
- *                          pn_kid2: null or PNK_LEXICALSCOPE for catch-block
- *                                   with pn_expr pointing to a PNK_CATCH node
+ *                          pn_kid2: null or PNK_CATCHLIST list
  *                          pn_kid3: null or finally block
- * PNK_CATCH    binary      pn_left: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch
- *                                   var node
- *                                   (PNK_ARRAY or PNK_OBJECT if destructuring),
- *                                   or null if optional catch binding
- *                          pn_right: catch block statements
+ * PNK_CATCHLIST list       pn_head: list of PNK_LEXICALSCOPE nodes, one per
+ *                                   catch-block, each with pn_expr pointing
+ *                                   to a PNK_CATCH node
+ * PNK_CATCH    ternary     pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node
+ *                                   (PNK_ARRAY or PNK_OBJECT if destructuring)
+ *                          pn_kid2: null or the catch guard expression
+ *                          pn_kid3: catch block statements
  * PNK_BREAK    name        pn_atom: label or null
  * PNK_CONTINUE name        pn_atom: label or null
  * PNK_WITH     binary      pn_left: head expr; pn_right: body;
  * PNK_VAR,     list        pn_head: list of PNK_NAME or PNK_ASSIGN nodes
  * PNK_LET,                          each name node has either
  * PNK_CONST                           pn_used: false
  *                                     pn_atom: variable name
  *                                     pn_expr: initializer or null
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6770,19 +6770,20 @@ Parser<ParseHandler, CharT>::tryStatemen
     uint32_t begin = pos().begin;
 
     /*
      * try nodes are ternary.
      * kid1 is the try statement
      * kid2 is the catch node list or null
      * kid3 is the finally statement
      *
-     * catch nodes are binary.
-     * left is the catch-name/pattern or null
-     * right is the catch block
+     * catch nodes are ternary.
+     * kid1 is the lvalue (possible identifier, TOK_LB, or TOK_LC)
+     * kid2 is the catch guard or null if no guard
+     * kid3 is the catch block
      *
      * catch lvalue nodes are either:
      *   a single identifier
      *   TOK_RB or TOK_RC for a destructuring left-hand side
      *
      * finally nodes are TOK_LC statement lists.
      */
 
@@ -6805,90 +6806,128 @@ Parser<ParseHandler, CharT>::tryStatemen
         if (!innerBlock)
             return null();
 
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     }
 
-    Node catchScope = null();
+    bool hasUnconditionalCatch = false;
+    Node catchList = null();
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     if (tt == TOK_CATCH) {
-        /*
-         * Create a lexical scope node around the whole catch clause,
-         * including the head.
-         */
-        ParseContext::Statement stmt(pc, StatementKind::Catch);
-        ParseContext::Scope scope(this);
-        if (!scope.init(pc))
-            return null();
-
-        /*
-         * Legal catch forms are:
-         *   catch (lhs) {
-         *   catch {
-         * where lhs is a name or a destructuring left-hand side.
-         */
-        bool omittedBinding;
-        if (!tokenStream.matchToken(&omittedBinding, TOK_LC))
-            return null();
-
-        Node catchName;
-        if (omittedBinding) {
-            catchName = null();
-        } else {
-            MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
-
-            if (!tokenStream.getToken(&tt))
+        catchList = handler.newCatchList(pos());
+        if (!catchList)
+            return null();
+
+        do {
+            Node pnblock;
+
+            /* Check for another catch after unconditional catch. */
+            if (hasUnconditionalCatch) {
+                error(JSMSG_CATCH_AFTER_GENERAL);
+                return null();
+            }
+
+            /*
+             * Create a lexical scope node around the whole catch clause,
+             * including the head.
+             */
+            ParseContext::Statement stmt(pc, StatementKind::Catch);
+            ParseContext::Scope scope(this);
+            if (!scope.init(pc))
                 return null();
-            switch (tt) {
-              case TOK_LB:
-              case TOK_LC:
-                catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
-                                                     yieldHandling, tt);
-                if (!catchName)
+
+            /*
+             * Legal catch forms are:
+             *   catch (lhs) {
+             *   catch (lhs if <boolean_expression>) {
+             *   catch {
+             * where lhs is a name or a destructuring left-hand side.
+             * The second is legal only #ifdef JS_HAS_CATCH_GUARD.
+             */
+            bool omittedBinding;
+            if (!tokenStream.matchToken(&omittedBinding, TOK_LC))
+                return null();
+
+            Node catchName;
+            Node catchGuard = null();
+
+            if (omittedBinding) {
+                catchName = null();
+            } else {
+                MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_CATCH);
+
+                if (!tokenStream.getToken(&tt))
                     return null();
-                break;
-
-              default: {
-                if (!TokenKindIsPossibleIdentifierName(tt)) {
-                    error(JSMSG_CATCH_IDENTIFIER);
-                    return null();
+                switch (tt) {
+                  case TOK_LB:
+                  case TOK_LC:
+                    catchName = destructuringDeclaration(DeclarationKind::CatchParameter,
+                                                         yieldHandling, tt);
+                    if (!catchName)
+                        return null();
+                    break;
+
+                  default: {
+                    if (!TokenKindIsPossibleIdentifierName(tt)) {
+                        error(JSMSG_CATCH_IDENTIFIER);
+                        return null();
+                    }
+
+                    catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
+                                                  yieldHandling);
+                    if (!catchName)
+                        return null();
+                    break;
+                  }
                 }
 
-                catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter,
-                                              yieldHandling);
-                if (!catchName)
+#if JS_HAS_CATCH_GUARD
+                /*
+                 * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
+                 * to avoid conflicting with the JS2/ECMAv4 type annotation
+                 * catchguard syntax.
+                 */
+                bool matched;
+                if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
                     return null();
-                break;
-              }
+                if (matched) {
+                    catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
+                    if (!catchGuard)
+                        return null();
+                }
+#endif
+                MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
+
+                MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
             }
 
-            MUST_MATCH_TOKEN_MOD(TOK_RP, TokenStream::Operand, JSMSG_PAREN_AFTER_CATCH);
-
-            MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
-        }
-
-        Node catchBody = catchBlockStatement(yieldHandling, scope);
-        if (!catchBody)
-            return null();
-
-        catchScope = finishLexicalScope(scope, catchBody);
-        if (!catchScope)
-            return null();
-
-        if (!handler.setupCatchScope(catchScope, catchName, catchBody))
-            return null();
-        handler.setEndPosition(catchScope, pos().end);
-
-        if (!tokenStream.getToken(&tt, TokenStream::Operand))
-            return null();
+            Node catchBody = catchBlockStatement(yieldHandling, scope);
+            if (!catchBody)
+                return null();
+
+            if (!catchGuard)
+                hasUnconditionalCatch = true;
+
+            pnblock = finishLexicalScope(scope, catchBody);
+            if (!pnblock)
+                return null();
+
+            if (!handler.addCatchBlock(catchList, pnblock, catchName, catchGuard, catchBody))
+                return null();
+            handler.setEndPosition(catchList, pos().end);
+            handler.setEndPosition(pnblock, pos().end);
+
+            if (!tokenStream.getToken(&tt, TokenStream::Operand))
+                return null();
+        } while (tt == TOK_CATCH);
     }
 
     Node finallyBlock = null();
 
     if (tt == TOK_FINALLY) {
         MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
 
         uint32_t openedPos = pos().begin;
@@ -6907,22 +6946,22 @@ Parser<ParseHandler, CharT>::tryStatemen
             return null();
 
         MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
                                          reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
                                                               JSMSG_CURLY_OPENED, openedPos));
     } else {
         tokenStream.ungetToken();
     }
-    if (!catchScope && !finallyBlock) {
+    if (!catchList && !finallyBlock) {
         error(JSMSG_CATCH_OR_FINALLY);
         return null();
     }
 
-    return handler.newTryStatement(begin, innerBlock, catchScope, finallyBlock);
+    return handler.newTryStatement(begin, innerBlock, catchList, finallyBlock);
 }
 
 template <class ParseHandler, typename CharT>
 typename ParseHandler::Node
 Parser<ParseHandler, CharT>::catchBlockStatement(YieldHandling yieldHandling,
                                                  ParseContext::Scope& catchParamScope)
 {
     uint32_t openedPos = pos().begin;
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -308,31 +308,30 @@ class SyntaxParseHandler
     Node newExpressionBody(Node expr) { return NodeReturn; }
     Node newWithStatement(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
 
     Node newLabeledStatement(PropertyName* label, Node stmt, uint32_t begin) {
         return NodeGeneric;
     }
 
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
-    Node newTryStatement(uint32_t begin, Node body, Node catchScope, Node finallyBlock) {
+    Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
     Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
         lastAtom = name;
         return NodeDottedProperty;
     }
 
     Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
-    MOZ_MUST_USE bool setupCatchScope(Node letBlock, Node catchName, Node catchBody) {
-        return true;
-    }
+    MOZ_MUST_USE bool addCatchBlock(Node catchList, Node letBlock, Node catchName,
+                                    Node catchGuard, Node catchBody) { return true; }
 
     MOZ_MUST_USE bool setLastFunctionFormalParameterDefault(Node funcpn, Node pn) { return true; }
 
     void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
 
     Node newFunctionStatement(const TokenPos& pos) { return NodeFunctionStatement; }
 
     Node newFunctionExpression(const TokenPos& pos) {
@@ -402,16 +401,20 @@ class SyntaxParseHandler
 
     bool isDeclarationList(Node node) {
         return node == NodeVarDeclaration || node == NodeLexicalDeclaration;
     }
 
     // This method should only be called from parsers using FullParseHandler.
     Node singleBindingFromDeclaration(Node decl) = delete;
 
+    Node newCatchList(const TokenPos& pos) {
+        return NodeGeneric;
+    }
+
     Node newCommaExpressionList(Node kid) {
         return NodeGeneric;
     }
 
     void addList(Node list, Node kid) {
         MOZ_ASSERT(list == NodeGeneric ||
                    list == NodeUnparenthesizedArray ||
                    list == NodeUnparenthesizedObject ||
--- a/js/src/jit-test/lib/nightly-only.js
+++ b/js/src/jit-test/lib/nightly-only.js
@@ -6,18 +6,15 @@
 
 // Call the function f. On beta and release, expect it to throw an error that is
 // an instance of error.
 function nightlyOnly(error, f) {
   if (getBuildConfiguration().release_or_beta) {
     try {
       f();
       throw new Error("use of feature expected to fail on release and beta, but succeeded; please update test");
-    } catch (e) {
-      if (!(e instanceof error)) {
-        throw e;
-      }
+    } catch (e if e instanceof error) {
       // All is well.
     }
   } else {
     f();
   }
 }
--- a/js/src/jit-test/lib/syntax.js
+++ b/js/src/jit-test/lib/syntax.js
@@ -179,16 +179,24 @@ function test_syntax(postfixes, check_er
   test("try {} catch (e ");
   test("try {} catch (e) ");
   test("try {} catch (e) { ");
   test("try {} catch (e) {} ");
   test("try {} catch (e) {} finally ");
   test("try {} catch (e) {} finally { ");
   test("try {} catch (e) {} finally {} ");
 
+  test("try {} catch (e if ");
+  test("try {} catch (e if e  ");
+  test("try {} catch (e if e instanceof ");
+  test("try {} catch (e if e instanceof x ");
+  test("try {} catch (e if e instanceof x) ");
+  test("try {} catch (e if e instanceof x) { ");
+  test("try {} catch (e if e instanceof x) {} ");
+
   // ---- Declarations ----
 
   // var
 
   test("var ");
   test("var x ");
   test("var x = ");
   test("var x = 1 ");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug843811-1.js
@@ -0,0 +1,11 @@
+// |jit-test| error: uncaught exception:
+evalcx("\
+    try {\
+        throw\"\"\
+    } catch (\
+        x if (function(){\
+            x\
+        })()\
+    ) {}\
+", newGlobal(""))
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug843811-2.js
@@ -0,0 +1,10 @@
+// |jit-test| error: uncaught exception:
+eval("\
+    try {\
+        throw\"\"\
+    } catch (\
+        x if (function(){\
+            x\
+        })()\
+    ) {}\
+")
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug843811-3.js
@@ -0,0 +1,11 @@
+// |jit-test| error: uncaught exception:
+Function("\
+    try {\
+        throw\"\"\
+    } catch (\
+        x if (function(){\
+            x\
+        })()\
+    ) {}\
+")()
+
--- a/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js
+++ b/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js
@@ -13,12 +13,9 @@ class foo {
 
 // Compile foo()
 for (let i = 0; i < 11; i++)
     new foo();
 
 try {
     test(foo);
     throw new Error("Invoking a class constructor without new must throw");
-} catch (e) {
-    if (!(e instanceof TypeError))
-        throw e;
-}
+} catch (e if e instanceof TypeError) { }
--- a/js/src/jit-test/tests/basic/bug593663-regexp.js
+++ b/js/src/jit-test/tests/basic/bug593663-regexp.js
@@ -2,19 +2,17 @@
  * Ensure that flat matches with metachars in them don't have their metachars
  * interpreted as special.
  */
 
 function isPatternSyntaxError(pattern) {
     try {
         new RegExp(pattern);
         return false;
-    } catch (e) {
-        if (!(e instanceof SyntaxError))
-            throw e;
+    } catch (e if e instanceof SyntaxError) {
         return true;
     }
 }
 
 // Bug example.
 assertEq("1+2".replace("1+2", "$&+3"), "1+2+3");
 assertEq("1112".replace("1+2", ""), "1112");
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug640078.js
@@ -0,0 +1,4 @@
+eval("\
+  try{}\
+  catch(w if(function(){})){4067286856}\
+")
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug787309.js
@@ -0,0 +1,7 @@
+// |jit-test| error: TypeError
+try {
+    h
+} catch (x
+if gc()) {} finally {
+    this.z.z
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug787848.js
@@ -0,0 +1,8 @@
+// |jit-test| error: TypeError
+try {
+    i
+}
+catch (x if (function() {})()) {}
+catch (d) {
+    this.z.z
+}
--- a/js/src/jit-test/tests/debug/Frame-onStep-12.js
+++ b/js/src/jit-test/tests/debug/Frame-onStep-12.js
@@ -82,16 +82,29 @@ testOne("testCatchFinally",
            throw new TypeError();
          } catch (e) {
            ${bitOfCode}
          } finally {            // +6
          }                      // +7
          nothing();             // +8
         `, "1689");
 
+// The same but without a finally clause.  This relies on a
+// SpiderMonkey extension, because otherwise there's no way to see
+// extra instructions at the end of a catch.
+testOne("testCatch",
+        `try {
+           throw new TypeError();
+         } catch (e if e instanceof TypeError) {
+           ${bitOfCode}
+         } catch (e) {          // +6
+         }                      // +7
+         nothing();             // +8
+        `, "189");
+
 // Test the instruction at the end of a "finally" clause.
 testOne("testFinally",
         `try {
          } finally {
            ${bitOfCode}
          }                      // +6
          nothing();             // +7
         `, "178");
--- a/js/src/jit-test/tests/gc/bug-1259490.js
+++ b/js/src/jit-test/tests/gc/bug-1259490.js
@@ -1,13 +1,9 @@
-try { eval("3 ** 4") } catch (e) {
-    if (!(e instanceof SyntaxError))
-        throw e;
-    quit();
-}
+try { eval("3 ** 4") } catch (e if e instanceof SyntaxError) { quit(); };
 eval(`
 
 gczeal(8);
 for (var k = 0; k < 99; ++k) {
     uneval(-(0 ** (Object | 0 * Object)))
 }
 
 `)
--- a/js/src/jit-test/tests/ion/bug1293542.js
+++ b/js/src/jit-test/tests/ion/bug1293542.js
@@ -1,11 +1,7 @@
 
-try { eval("3 ** 4") } catch (e) {
-    if (!(e instanceof SyntaxError))
-        throw e;
-    quit();
-}
+try { eval("3 ** 4") } catch (e if e instanceof SyntaxError) { quit(); };
 
 var f = new Function("x", "return (x ** (1 / ~4294967297)) && x");
 for (var i = 0; i < 2; ++i) {
     assertEq(f(-Infinity), 0);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug799185-1.js
@@ -0,0 +1,14 @@
+options('strict')
+f = (function() {
+  for (var z = 0; z < 9; ++z) {
+    x = z
+  }
+  try {
+    i
+  } catch (x if null) {
+    let e
+  } catch (l) {
+    x.m
+  }
+})
+for (a in f()) {}
--- a/js/src/jit-test/tests/ion/bug799185-4.js
+++ b/js/src/jit-test/tests/ion/bug799185-4.js
@@ -1,12 +1,9 @@
 function foo(aObject)
 {
     try { }
-    catch (ex) {
-        if (ex.name != "TypeError")
-            throw ex;
-    }
+    catch (ex if (ex.name == "TypeError")) { }
     try { Object.getPrototypeOf(aObject); }
     catch (ex) { }
 }
 
 foo(true);
--- a/js/src/jit-test/tests/ion/bug799185-5.js
+++ b/js/src/jit-test/tests/ion/bug799185-5.js
@@ -1,26 +1,19 @@
 function foo(aObject)
 {
     try {
         try {
             if (!aObject)
                 return;
         }
-        catch (ex) {
-            if (ex.name != "TypeError")
-                throw ex;
-        }
+        catch (ex if (ex.name == "TypeError")) { }
         finally {
         }
         undefined.x;
     }
-    catch (ex) {
-        if (ex.name != "TypeError")
-            throw ex;
-        if (ex.name != "TypeError")
-            throw ex;
-    }
+    catch (ex if (ex.name == "TypeError")) { }
+    catch (ex if (ex.name == "TypeError")) { }
     finally {
     }
 }
 
 foo(true);
--- a/js/src/jit-test/tests/ion/bug799185-7.js
+++ b/js/src/jit-test/tests/ion/bug799185-7.js
@@ -2,19 +2,26 @@ var y = undefined;
 
 try {} catch (x) {
     try {} catch (x) {
         try {} catch (x) {
         }
     }
 }
 
+try {} catch (x if y) {
+    try {} catch (x if y) {
+        try {} catch (x if y) {
+        }
+    }
+}
+
 while (false) {
-    try {} catch ({x,y}) {
-        try {} catch ({a,b,c,d}) {
+    try {} catch ({x,y} if x) {
+        try {} catch ({a,b,c,d} if a) {
             if (b) break;
             if (c) continue;
         }
     } finally {}
 }
 
 Label1:
 for (let foo = 0; foo < 0; foo++) {
--- a/js/src/jit-test/tests/ion/throw.js
+++ b/js/src/jit-test/tests/ion/throw.js
@@ -95,18 +95,17 @@ for (var i = 0; i < 100; i++) {
 }
 assertEq(count, 4500);
 
 function test5() {
     var res = 0;
     for (var i=0; i<40; i++) {
 	try {
 	    throw i;
+	} catch (e if e % 2) {
+	    res += e;
 	} catch (e) {
-          if (e % 2)
-	       res += e;
-          else
-	       res += e * 3;
+	    res += e * 3;
 	}
     }
     return res;
 }
 assertEq(test5(), 1540);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug553781-2.js
@@ -0,0 +1,13 @@
+(function() {
+  do {
+    try {
+      return
+    }
+    catch(x if (c)) {
+      return
+    } (x)
+  } while (x)
+})()
+
+/* Don't assert. */
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug553781.js
@@ -0,0 +1,11 @@
+(function () {
+    try {
+        return
+    } catch (x if i) {
+        return
+    }
+    for (z in []);
+})()
+
+/* Don't assert */
+
--- a/js/src/jit-test/tests/parser/arrow-rest.js
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -130,16 +130,20 @@ function* f(x) { yield ...a)=>
 `, 23);
 
 // throw statement
 
 testThrow(`
 throw ...a) =>
 `, 6);
 
+testThrow(`
+try {} catch (x if ...a) =>
+`, 19);
+
 // class
 
 testThrow(`
 class A extends ...a) =>
 `, 16);
 
 // conditional expression
 
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -7,16 +7,17 @@
 #ifndef jsversion_h
 #define jsversion_h
 
 /*
  * JS Capability Macros.
  */
 #define JS_HAS_OBJ_PROTO_PROP   1       /* has o.__proto__ etc. */
 #define JS_HAS_TOSOURCE         1       /* has Object/Array toSource method */
+#define JS_HAS_CATCH_GUARD      1       /* has exception handling catch guard */
 #define JS_HAS_UNEVAL           1       /* has uneval() top-level function */
 
 #ifndef NIGHTLY_BUILD
 #define JS_HAS_EXPR_CLOSURES    1       /* has function (formals) listexpr */
 #endif
 
 /*
  * Feature for Object.prototype.__{define,lookup}{G,S}etter__ legacy support;
--- a/js/src/tests/ecma_3_1/Object/regress-444787.js
+++ b/js/src/tests/ecma_3_1/Object/regress-444787.js
@@ -48,23 +48,24 @@ function test()
 
     try
     {
       eval('instance = new ' + type.name);
       expect = type.prototype;
       actual = Object.getPrototypeOf(instance);
       reportCompare(expect, actual, summary + ': new ' + type.name);
     }
-    catch(ex) {
-      if (ex instanceof TypeError) {
-        print('Ignore ' + ex);
-      } else {
-        actual = ex + '';
-        reportCompare(expect, actual, summary + ': new ' + type.name);
-      }
+    catch(ex if ex instanceof TypeError)
+    {
+      print('Ignore ' + ex);
+    }
+    catch(ex)
+    {
+      actual = ex + '';
+      reportCompare(expect, actual, summary + ': new ' + type.name);
     }
 
   }
 
   types = [null, undefined];
 
   for (i = 0; i < types.length; i++)
   {
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/iterator-in-catch.js
@@ -0,0 +1,20 @@
+//Bug 350712
+
+function iterator () {
+    for (var i in []);
+}
+
+try {
+    try {
+        throw 5;
+    }
+    catch(error if iterator()) {
+        assertEq(false, true);
+    }
+}
+catch(error) {
+  assertEq(error, 5);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
+++ b/js/src/tests/ecma_6/ArrowFunctions/arrow-not-as-end-of-statement.js
@@ -67,16 +67,32 @@ assertEq(gen.next().value, 99);
 assertEq(gen.next().done, true);
 
 switch (1)
 {
   case a => {}:
    break;
 }
 
+try
+{
+  // Catch guards are non-standard, so ignore a syntax error.
+  eval(`try
+  {
+  }
+  catch (x if a => {})
+  {
+  }`);
+}
+catch (e)
+{
+  assertEq(e instanceof SyntaxError, true,
+           "should only have thrown SyntaxError, instead got " + e);
+}
+
 assertEq(0[a => {}], undefined);
 
 class Y {};
 class X extends Y { constructor() { super[a => {}](); } };
 
 if (a => {})
   assertEq(true, true);
 else
--- a/js/src/tests/ecma_6/Class/newTargetEval.js
+++ b/js/src/tests/ecma_6/Class/newTargetEval.js
@@ -1,29 +1,23 @@
 // Eval of new.target is invalid outside functions.
 try {
     eval('new.target');
     assertEq(false, true);
-} catch (e) {
-    if (!(e instanceof SyntaxError))
-        throw e;
-}
+} catch (e if e instanceof SyntaxError) { }
 
 // new.target is invalid inside eval inside top-level arrow functions
 assertThrowsInstanceOf(() => eval('new.target'), SyntaxError);
 
 // new.target is invalid inside indirect eval.
 let ieval = eval;
 try {
     (function () { return ieval('new.target'); })();
     assertEq(false, true);
-} catch (e) {
-    if (!(e instanceof SyntaxError))
-        throw e;
-}
+} catch (e if e instanceof SyntaxError) { }
 
 function assertNewTarget(expected) {
     assertEq(eval('new.target'), expected);
     assertEq((()=>eval('new.target'))(), expected);
 
     // Also test nestings "by induction"
     assertEq(eval('eval("new.target")'), expected);
     assertEq(eval("eval('eval(`new.target`)')"), expected);
--- a/js/src/tests/ecma_6/Class/outerBinding.js
+++ b/js/src/tests/ecma_6/Class/outerBinding.js
@@ -23,20 +23,17 @@ assertEq(Foo, 5);
     // That...didn't actually work, right?
     assertEq(typeof PermanentBinding, "function");
 }
 
 evaluate("const globalConstant = 0; var earlyError = true;");
 
 try {
     evaluate("earlyError = false; class globalConstant { constructor() { } }");
-} catch (e) {
-    if (!(e instanceof SyntaxError))
-        throw e;
-}
+} catch (e if e instanceof SyntaxError) { }
 assertEq(earlyError, true);
 
 function strictEvalShadows() {
     "use strict";
     let x = 4;
     eval(`class x { constructor() { } }
            assertEq(typeof x, "function");
          `);
--- a/js/src/tests/ecma_6/Class/superCallThisInit.js
+++ b/js/src/tests/ecma_6/Class/superCallThisInit.js
@@ -2,20 +2,17 @@ function base() { this.prop = 42; }
 
 class testInitialize extends base {
     constructor() {
         // A poor man's assertThrowsInstanceOf, as arrow functions are currently
         // disabled in this context
         try {
             this;
             throw new Error();
-        } catch (e) {
-            if (!(e instanceof ReferenceError))
-                throw e;
-        }
+        } catch (e if e instanceof ReferenceError) { }
         super();
         assertEq(this.prop, 42);
     }
 }
 assertEq(new testInitialize().prop, 42);
 
 // super() twice is a no-go.
 class willThrow extends base {
--- a/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js
+++ b/js/src/tests/ecma_6/Expressions/destructuring-pattern-parenthesized.js
@@ -91,19 +91,18 @@ Function("var a, b; [(demolition['man' +
 
 function classesEnabled()
 {
   try
   {
     new Function("class B { constructor() { } }; class D extends B { constructor() { super(); } }");
     return true;
   }
-  catch (e) {
-    if (!(e instanceof SyntaxError))
-      throw e;
+  catch (e if e instanceof SyntaxError)
+  {
     return false;
   }
 }
 
 if (classesEnabled())
 {
   Function("var a, b; var obj = { x() { [(super.man), b] = [1, 2]; } };")();
   Function("var a, b; var obj = { x() { [(super[8]) = 'motel', b] = [1, 2]; } };")();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/catchguard-001-n.js
@@ -0,0 +1,38 @@
+/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+DESCRIPTION = " the non-guarded catch should HAVE to appear last";
+
+test();
+
+function test()
+{
+  var EXCEPTION_DATA = "String exception";
+  var e;
+
+  printStatus ("Catchguard syntax negative test.");
+   
+  try
+  {   
+    throw EXCEPTION_DATA;  
+  }
+  catch (e) /* the non-guarded catch should HAVE to appear last */
+  {  
+
+  }
+  catch (e if true)
+  {
+
+  }
+  catch (e if false)
+  {  
+
+  }
+
+  reportCompare('PASS', 'FAIL',
+		"Illegally constructed catchguard should have thrown " +
+		"an exception.");
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/catchguard-001.js
@@ -0,0 +1,44 @@
+/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+test();
+
+function test()
+{
+  var EXCEPTION_DATA = "String exception";
+  var e = "foo";
+  var caught = false;
+
+  printStatus ("Basic catchguard test.");
+   
+  try
+  {   
+    throw EXCEPTION_DATA;  
+  }
+  catch (e if true)
+  {
+    caught = true;
+    e = "this change should not propagate outside of this scope";
+  }
+  catch (e if false)
+  {  
+    reportCompare('PASS', 'FAIL', "Catch block (e if false) should not have executed.");
+  }
+  catch (e)
+  {  
+    reportCompare('PASS', 'FAIL', "Catch block (e) should not have executed.");
+  }
+
+  if (!caught)
+    reportCompare('PASS', 'FAIL', "Exception was never caught.");
+   
+  if (e != "foo")
+    reportCompare('PASS', 'FAIL', "Exception data modified inside catch() scope should " +
+		  "not be visible in the function scope (e = '" +
+		  e + "'.)");
+
+  reportCompare('PASS', 'PASS', '');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/catchguard-002.js
@@ -0,0 +1,39 @@
+/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+test();
+
+function test()
+{
+  var EXCEPTION_DATA = "String exception";
+  var e;
+  var caught = false;
+
+  printStatus ("Basic catchguard test.");
+   
+  try
+  {   
+    throw EXCEPTION_DATA;  
+  }
+  catch (e if true)
+  {
+    caught = true;
+  }
+  catch (e if true)
+  {  
+    reportCompare('PASS', 'FAIL',
+		  "Second (e if true) catch block should not have executed.");
+  }
+  catch (e)
+  {  
+    reportCompare('PASS', 'FAIL', "Catch block (e) should not have executed.");
+  }
+
+  if (!caught)
+    reportCompare('PASS', 'FAIL', "Exception was never caught.");
+   
+  reportCompare('PASS', 'PASS', 'Basic catchguard test');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/catchguard-003.js
@@ -0,0 +1,54 @@
+/* -*- tab-width: 8; indent-tabs-mode: nil; js-indent-level: 4 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+test();
+
+function test()
+{
+  var EXCEPTION_DATA = "String exception";
+  var e = "foo", x = "foo";
+  var caught = false;
+
+  printStatus ("Catchguard 'Common Scope' test.");
+   
+  try
+  {   
+    throw EXCEPTION_DATA;  
+  }
+  catch (e if ((x = 1) && false))
+  {
+    reportCompare('PASS', 'FAIL',
+		  "Catch block (e if ((x = 1) && false) should not " +
+		  "have executed.");
+  }
+  catch (e if (x == 1))
+  {  
+    caught = true;
+  }
+  catch (e)
+  {  
+    reportCompare('PASS', 'FAIL',
+		  "Same scope should be used across all catchguards.");
+  }
+
+  if (!caught)
+    reportCompare('PASS', 'FAIL',
+		  "Exception was never caught.");
+   
+  if (e != "foo")
+    reportCompare('PASS', 'FAIL',
+		  "Exception data modified inside catch() scope should " +
+		  "not be visible in the function scope (e ='" +
+		  e + "'.)");
+
+  if (x != 1)
+    reportCompare('PASS', 'FAIL',
+		  "Data modified in 'catchguard expression' should " +
+		  "be visible in the function scope (x = '" +
+		  x + "'.)");
+
+  reportCompare('PASS', 'PASS', 'Catchguard Common Scope test');
+}
--- a/js/src/tests/js1_5/extensions/regress-104077.js
+++ b/js/src/tests/js1_5/extensions/regress-104077.js
@@ -65,19 +65,18 @@ function addValues_3(obj)
     }
     finally
     {
       try
       {
         sum +=1;
         print("In finally block of addValues_3() function: sum = " + sum);
       }
-      catch (e) {
-        if (e != 42)
-          throw e;
+      catch (e if e == 42)
+      {
         sum +=1;
         print('In finally catch block of addValues_3() function: sum = ' + sum + ', e = ' + e);
       }
       finally
       {
         sum +=1;
         print("In finally finally block of addValues_3() function: sum = " + sum);
         return sum;
@@ -128,26 +127,25 @@ function addValues_4(obj)
     }
     finally
     {
       try
       {
         sum += 1;
         print("In finally block of addValues_4() function: sum = " + sum);
       }
-      catch (e) {
-        if (e == 42) {
-          sum += 1;
-          print("In 1st finally catch block of addValues_4() function: sum = " + sum + ", e = " + e);
-        } else if (e == 43) {
-          sum += 1;
-          print("In 2nd finally catch block of addValues_4() function: sum = " + sum + ", e = " + e);
-        } else {
-          throw e;
-        }
+      catch (e if e == 42)
+      {
+        sum += 1;
+        print("In 1st finally catch block of addValues_4() function: sum = " + sum + ", e = " + e);
+      }
+      catch (e if e == 43)
+      {
+        sum += 1;
+        print("In 2nd finally catch block of addValues_4() function: sum = " + sum + ", e = " + e);
       }
       finally
       {
         sum += 1;
         print("In finally finally block of addValues_4() function: sum = " + sum);
         return sum;
       }
     }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-346494-01.js
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 346494;
+var summary = 'various try...catch tests';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var pfx = "(function (x) {try {throw x}",
+    cg1 = " catch (e if e === 42) {var v = 'catch guard 1 ' + e; actual += v + ','; print(v);}"
+    cg2 = " catch (e if e === 43) {var v = 'catch guard 2 ' + e; actual += v + ','; print(v);}"
+    cat = " catch (e) {var v = 'catch all ' + e; actual += v + ','; print(v);}"
+    fin = " finally{var v = 'fin'; actual += v + ','; print(v)}",
+    end = "})";
+
+  var exphash = {
+    pfx: "(function (y) { var result = ''; y = y + ',';",
+    cg1: "result += (y === '42,') ? ('catch guard 1 ' + y):'';",
+    cg2: "result += (y === '43,') ? ('catch guard 2 ' + y):'';",
+    cat: "result += /catch guard/.test(result) ? '': ('catch all ' + y);",
+    fin: "result += 'fin,';",
+    end: "return result;})"
+  };
+
+  var src = [
+    pfx + fin + end,
+    pfx + cat + end,
+    pfx + cat + fin + end,
+    pfx + cg1 + end,
+    pfx + cg1 + fin + end,
+    pfx + cg1 + cat + end,
+    pfx + cg1 + cat + fin + end,
+    pfx + cg1 + cg2 + end,
+    pfx + cg1 + cg2 + fin + end,
+    pfx + cg1 + cg2 + cat + end,
+    pfx + cg1 + cg2 + cat + fin + end,
+    ];
+
+  var expsrc = [
+    exphash.pfx + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1 + exphash.cg2 + exphash.cat + exphash.fin + exphash.end,
+    ];
+
+  for (var i in src) {
+    print("\n=== " + src[i]);
+    var f = eval(src[i]);
+    print(src[i]);
+    var exp = eval(expsrc[i]);
+    // dis(f);
+    print('decompiling: ' + f);
+
+    actual = '';
+    try { expect = exp(42); f(42) } catch (e) { print('tried f(42), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(43); f(43) } catch (e) { print('tried f(43), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(44); f(44) } catch (e) { print('tried f(44), caught ' + e) }
+    reportCompare(expect, actual, summary);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-346494.js
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 346494;
+var summary = 'try-catch-finally scope';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  function g()
+  {
+    try
+    {
+      throw "foo";
+    }
+    catch(e if e == "bar")
+    {
+    }
+    catch(e if e == "baz")
+    {
+    }
+    finally
+    {
+    }
+  }
+
+  expect = "foo";
+  try
+  {
+    g();
+    actual = 'No Exception';
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
+
+  function h()
+  {
+    try
+    {
+      throw "foo";
+    }
+    catch(e if e == "bar")
+    {
+    }
+    catch(e)
+    {
+    }
+    finally
+    {
+    }
+  }
+
+  expect = "No Exception";
+  try
+  {
+    h();
+    actual = 'No Exception';
+  }
+  catch(ex)
+  {
+    actual = ex + '';
+  }
+  reportCompare(expect, actual, summary);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-350312-02.js
@@ -0,0 +1,109 @@
+/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 350312;
+var summary = 'Accessing wrong stack slot with nested catch/finally';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  function createPrint(obj)
+  {
+    return new Function("actual += " + obj + " + ','; " +
+			"print(" + obj + ");");
+  }
+
+  function createThrow(obj)
+  {
+    return new Function("throw " + obj + "; ");
+  }
+
+
+  function f(a, b, c)
+  {
+    try {
+      a();
+    } catch (e if e == null) {
+      b();
+    } finally {
+      c();
+    }
+  }
+
+  print('test 1');
+  expect = 'a,c,';
+  actual = '';
+  try
+  {
+    f(createPrint("'a'"), createPrint("'b'"), createPrint("'c'"));
+  }
+  catch(ex)
+  {
+    actual += 'caught ' + ex;
+  }
+  reportCompare(expect, actual, summary + ': 1');
+
+  print('test 2');
+  expect = 'c,caught a';
+  actual = '';
+  try
+  {
+    f(createThrow("'a'"), createPrint("'b'"), createPrint("'c'"));
+  }
+  catch(ex)
+  {
+    actual += 'caught ' + ex;
+  }
+  reportCompare(expect, actual, summary + ': 2');
+
+  print('test 3');
+  expect = 'b,c,';
+  actual = '';
+  try
+  {
+    f(createThrow("null"), createPrint("'b'"), createPrint("'c'"));
+  }
+  catch(ex)
+  {
+    actual += 'caught ' + ex;
+  }
+  reportCompare(expect, actual, summary + ': 3');
+
+  print('test 4');
+  expect = 'a,c,';
+  actual = '';
+  try
+  {
+    f(createPrint("'a'"), createThrow("'b'"), createPrint("'c'"));
+  }
+  catch(ex)
+  {
+    actual += 'caught ' + ex;
+  }
+  reportCompare(expect, actual, summary + ': 4');
+
+  print('test 5');
+  expect = 'c,caught b';
+  actual = '';
+  try
+  {
+    f(createThrow("null"), createThrow("'b'"), createPrint("'c'"));
+  }
+  catch(ex)
+  {
+    actual += 'caught ' + ex;
+  }
+  reportCompare(expect, actual, summary + ': 5');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-350312-03.js
@@ -0,0 +1,113 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 350312;
+var summary = 'Accessing wrong stack slot with nested catch/finally';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  var pfx  = "(function (x) {try {if (x > 41) throw x}",
+    cg1a = " catch (e if e === 42) {var v = 'catch guard 1 ' + e; actual += v + ',';print(v);}"
+    cg1b = " catch (e if e === 42) {var v = 'catch guard 1 + throw ' + e; actual += v + ',';print(v); throw e;}"
+    cg2  = " catch (e if e === 43) {var v = 'catch guard 2 ' + e; actual += v + ',';print(v)}"
+    cat  = " catch (e) {var v = 'catch all ' + e; print(v); if (e == 44) throw e}"
+    fin  = " finally{var v = 'fin'; actual += v + ',';print(v)}",
+    end  = "})";
+
+  var exphash  = {
+    pfx: "(function (y) { var result = ''; y = y + ',';",
+    cg1a: " result += (y === '42,') ? ('catch guard 1 ' + y):'';",
+    cg1b: " result += (y === '42,') ? ('catch guard 1 + throw ' + y):'';",
+    cg2:  " result += (y === '43,') ? ('catch guard 2 ' + y):'';",
+    cat:  " result += (y > 41) ? ('catch all ' + y):'';",
+    fin:  " result += 'fin,';",
+    end:  "return result;})"
+  };
+
+  var src = [
+    pfx + fin + end,
+    pfx + cat + end,
+    pfx + cat + fin + end,
+    pfx + cg1a + end,
+    pfx + cg1a + fin + end,
+    pfx + cg1a + cat + end,
+    pfx + cg1a + cat + fin + end,
+    pfx + cg1a + cg2 + end,
+    pfx + cg1a + cg2 + fin + end,
+    pfx + cg1a + cg2 + cat + end,
+    pfx + cg1a + cg2 + cat + fin + end,
+    pfx + cg1b + end,
+    pfx + cg1b + fin + end,
+    pfx + cg1b + cat + end,
+    pfx + cg1b + cat + fin + end,
+    pfx + cg1b + cg2 + end,
+    pfx + cg1b + cg2 + fin + end,
+    pfx + cg1b + cg2 + cat + end,
+    pfx + cg1b + cg2 + cat + fin + end,
+    ];
+
+  var expsrc = [
+    exphash.pfx + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1a + exphash.cg2 + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cat + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.fin + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.cat + exphash.end,
+    exphash.pfx + exphash.cg1b + exphash.cg2 + exphash.cat + exphash.fin + exphash.end,
+    ];
+
+  for (var i in src) {
+    print("\n=== " + i + ": " + src[i]);
+    var f = eval(src[i]);
+    var exp = eval(expsrc[i]);
+    // dis(f);
+    print('decompiling: ' + f);
+    //print('decompiling exp: ' + exp);
+
+    actual = '';
+    try { expect = exp(41); f(41) } catch (e) { print('tried f(41), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(42); f(42) } catch (e) { print('tried f(42), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(43); f(43) } catch (e) { print('tried f(43), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(44); f(44) } catch (e) { print('tried f(44), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+    actual = '';
+    try { expect = exp(45); f(45) } catch (e) { print('tried f(44), caught ' + e) }
+    reportCompare(expect, actual, summary);
+
+  }
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-351102-01.js
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+
+  f = function () {
+    try {
+      throw new Error('bad');
+    } catch (e if (e = null, gc(), false)) {
+    } catch (e) {
+      // e is dangling now
+    }
+  };
+
+  f();
+
+  reportCompare(expect, actual, summary + ': 1');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-351102-02.js
@@ -0,0 +1,43 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  f = function ()
+    {
+      var a = null;
+      try {  
+        a();
+      } catch (e) {
+      }
+      return false;
+    };
+
+  try {  
+    throw 1;
+  } catch (e if f()) {
+  } catch (e if e == 1) {
+    print("GOOD");
+  } catch (e) {
+    print("BAD: "+e);
+  }
+
+  reportCompare(expect, actual, summary + ': 2');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-351102-06.js
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  try
+  {
+    try { null.a } catch(e if (e = null, gc())) { }
+  }
+  catch(ex)
+  {
+  }
+  reportCompare(expect, actual, summary + ': 6');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_5/extensions/regress-374589.js
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 374589;
+var summary = 'Do not assert decompiling try { } catch(x if true) { } ' +
+  'catch(y) { } finally { this.a.b; }';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f = function () {
+    try { } catch(x if true) { } catch(y) { } finally { this.a.b; } };
+
+  expect = 'function () {\n\
+    try { } catch(x if true) { } catch(y) { } finally { this.a.b; } }';
+
+  actual = f + '';
+  compareSource(expect, actual, summary);
+}
--- a/js/src/tests/js1_7/extensions/regress-350312.js
+++ b/js/src/tests/js1_7/extensions/regress-350312.js
@@ -19,19 +19,17 @@ function test()
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
   var iter;
   function* gen()
   {
     try {
       yield iter;
-    } catch (e) {
-      if (e != null)
-        throw e;
+    } catch (e if e == null) {
       actual += 'CATCH,';
       print("CATCH");
     } finally {
       actual += 'FINALLY';
       print("FINALLY");
     }
   }
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_7/extensions/regress-351102-03.js
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  f = function()
+    {
+      try
+      {
+        d.d.d;
+      }
+      catch({} if gc())
+      {
+      }
+      catch(y)
+      {
+      }
+    };
+
+  f();
+  f();
+
+  reportCompare(expect, actual, summary + ': 3');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_7/extensions/regress-351102-04.js
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  try
+  {
+    try { foo() } catch([] if gc()) { }
+  }
+  catch(ex)
+  {
+  }
+  reportCompare(expect, actual, summary + ': 4');
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_7/extensions/regress-351102-05.js
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  try
+  {
+    try { d.d.d } catch([] if gc()) { }
+  }
+  catch(ex)
+  {
+  }
+  reportCompare(expect, actual, summary + ': 5');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_7/extensions/regress-351102-07.js
@@ -0,0 +1,40 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 351102;
+var summary = 'try/catch-guard/finally GC issues';
+var actual = '';
+var expect = '';
+
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+ 
+  var f;
+  var obj = { get a() {
+      try {
+        throw 1;
+      } catch (e) {
+      }
+      return false;
+    }};
+
+  try {
+    throw obj;
+  } catch ({a: a} if a) {
+    throw "Unreachable";
+  } catch (e) {
+    if (e !== obj)
+      throw "Unexpected exception: "+uneval(e);
+  }
+  reportCompare(expect, actual, summary + ': 7');
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_7/regress/regress-375695.js
@@ -0,0 +1,24 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 375695;
+var summary = 'Do not assert: !fp->blockChain || OBJ_GET_PARENT(cx, obj) == fp->blockChain';
+var actual = '';
+var expect = '';
+
+//-----------------------------------------------------------------------------
+test();
+//-----------------------------------------------------------------------------
+
+function test()
+{
+  printBugNumber(BUGNUMBER);
+  printStatus (summary);
+
+  try { try { throw 1 } catch([] if false) { } } catch(ex) {}
+ 
+  reportCompare(expect, actual, summary);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/regress-677589.js
@@ -0,0 +1,10 @@
+// |reftest| skip-if(!xulRuntime.shell)
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+try {
+    clone(null);  // don't crash
+} catch (exc if exc instanceof TypeError) {
+}
+
+reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/reflect-parse/Match.js
+++ b/js/src/tests/js1_8_5/reflect-parse/Match.js
@@ -17,30 +17,26 @@ var Match =
         match: function(act) {
             return match(act, this.template);
         },
 
         matches: function(act) {
             try {
                 return this.match(act);
             }
-            catch (e) {
-                if (!(e instanceof MatchError))
-                    throw e;
+            catch (e if e instanceof MatchError) {
                 return false;
             }
         },
 
         assert: function(act, message) {
             try {
                 return this.match(act);
             }
-            catch (e) {
-                if (!(e instanceof MatchError))
-                    throw e;
+            catch (e if e instanceof MatchError) {
                 throw new Error((message || "failed match") + ": " + e.message);
             }
         },
 
         toString: () => "[object Pattern]"
     };
 
     Pattern.ANY = new Pattern;
--- a/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js
+++ b/js/src/tests/js1_8_5/reflect-parse/PatternAsserts.js
@@ -81,15 +81,13 @@ function assertDecl(src, patt) {
     assertLocalDecl(src, patt);
     assertGlobalDecl(src, patt);
     assertBlockDecl(src, patt);
 }
 
 function assertError(src, errorType) {
     try {
         Reflect.parse(src);
-    } catch (expected) {
-        if (!(expected instanceof errorType))
-            throw expected;
+    } catch (expected if expected instanceof errorType) {
         return;
     }
     throw new Error("expected " + errorType.name + " for " + uneval(src));
 }
--- a/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
+++ b/js/src/tests/js1_8_5/reflect-parse/PatternBuilders.js
@@ -115,21 +115,21 @@ function switchStmt(disc, cases) {
     return Pattern({ type: "SwitchStatement", discriminant: disc, cases: cases });
 }
 function caseClause(test, stmts) {
     return Pattern({ type: "SwitchCase", test: test, consequent: stmts });
 }
 function defaultClause(stmts) {
     return Pattern({ type: "SwitchCase", test: null, consequent: stmts });
 }
-function catchClause(id, body) {
-    return Pattern({ type: "CatchClause", param: id, body: body });
+function catchClause(id, guard, body) {
+    return Pattern({ type: "CatchClause", param: id, guard: guard, body: body });
 }
-function tryStmt(body, handler, fin) {
-    return Pattern({ type: "TryStatement", block: body, handler: handler, finalizer: fin });
+function tryStmt(body, guarded, unguarded, fin) {
+    return Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handler: unguarded, finalizer: fin });
 }
 
 function superProp(id) {
     return dotExpr(Pattern({ type: "Super" }), id);
 }
 function superElem(id) {
     return memExpr(Pattern({ type: "Super" }), id);
 }
--- a/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js
+++ b/js/src/tests/js1_8_5/reflect-parse/alternateBuilder.js
@@ -169,35 +169,39 @@ return {
         return lab ? ["ContinueStmt", {}, lab] : ["ContinueStmt", {}];
     },
     withStatement: function(expr, stmt) {
         return ["WithStmt", {}, expr, stmt];
     },
     returnStatement: function(expr) {
         return expr ? ["ReturnStmt", {}, expr] : ["ReturnStmt", {}];
     },
-    tryStatement: function(body, handler, fin) {
-        var node = ["TryStmt", body, handler];
+    tryStatement: function(body, catches, fin) {
+        if (catches.length > 1)
+            throw new SyntaxError("multiple catch clauses not supported");
+        var node = ["TryStmt", body, catches[0] || ["Empty"]];
         if (fin)
             node.push(fin);
         return node;
     },
     throwStatement: function(expr) {
         return ["ThrowStmt", {}, expr];
     },
     debuggerStatement: () => ["DebuggerStmt", {}],
     letStatement: reject,
     switchCase: function(expr, stmts) {
         if (expr)
             stmts.unshift("SwitchCase", {}, expr);
         else
             stmts.unshift("DefaultCase", {});
         return stmts;
     },
-    catchClause: function(param, body) {
+    catchClause: function(param, guard, body) {
+        if (guard)
+            throw new SyntaxError("catch guards not supported");
         param[0] = "IdPatt";
         return ["CatchClause", {}, param, body];
     },
 
     arrayPattern: reject,
     objectPattern: reject,
     propertyPattern: reject,
 };
--- a/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js
+++ b/js/src/tests/js1_8_5/reflect-parse/basicBuilder.js
@@ -37,18 +37,22 @@ assertGlobalExpr("x()", 9, { callExpress
 assertGlobalExpr("x.y", 10, { memberExpression: () => 10 });
 assertGlobalExpr("(function() { })", 11, { functionExpression: () => 11 });
 assertGlobalExpr("[1,2,3]", 12, { arrayExpression: () => 12 });
 assertGlobalExpr("({ x: y })", 13, { objectExpression: () => 13 });
 assertGlobalExpr("this", 14, { thisExpression: () => 14 });
 assertGlobalExpr("(function*() { yield 42 })", genFunExpr("es6", null, [], blockStmt([exprStmt(19)])), { yieldExpression: () => 19 });
 
 assertGlobalStmt("switch (x) { case y: }", switchStmt(ident("x"), [1]), { switchCase: () => 1 });
-assertGlobalStmt("try { } catch (e) { }", 2, { tryStatement: (b, h, f) => h, catchClause: () => 2 });
-assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), 2, null), { catchClause: () => 2 });
+assertGlobalStmt("try { } catch (e) { }", 2, { tryStatement: (b, g, u, f) => u, catchClause: () => 2 });
+assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }", [2, 2], { tryStatement: (b, g, u, f) => g, catchClause: () => 2 });
+assertGlobalStmt("try { } catch (e) { }", tryStmt(blockStmt([]), [], 2, null), { catchClause: () => 2 });
+assertGlobalStmt("try { } catch (e if e instanceof A) { } catch (e if e instanceof B) { }",
+                 tryStmt(blockStmt([]), [2, 2], null, null),
+                 { catchClause: () => 2 });
 
 assertGlobalExpr("({ x: y } = z)", aExpr("=", 1, ident("z")), { objectPattern: () => 1 });
 assertGlobalExpr("({ x: y } = z)", aExpr("=", objPatt([2]), ident("z")), { propertyPattern: () => 2 });
 assertGlobalExpr("[ x ] = y", aExpr("=", 3, ident("y")), { arrayPattern: () => 3 });
 
 }
 
 runtest(test);
--- a/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js
+++ b/js/src/tests/js1_8_5/reflect-parse/builderExceptions.js
@@ -1,15 +1,13 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // Ensure that exceptions thrown by builder methods propagate.
 var thrown = false;
 try {
     Reflect.parse("42", { builder: { program: function() { throw "expected" } } });
-} catch (e) {
-    if (e !== "expected")
-        throw e;
+} catch (e if e === "expected") {
     thrown = true;
 }
 if (!thrown)
     throw new Error("builder exception not propagated");
 
 if (typeof reportCompare === 'function')
     reportCompare(true, true);
--- a/js/src/tests/js1_8_5/reflect-parse/statements.js
+++ b/js/src/tests/js1_8_5/reflect-parse/statements.js
@@ -47,33 +47,50 @@ 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"), blockStmt([])),
+                   [],
+		   catchClause(ident("e"), null, blockStmt([])),
                    null));
 assertStmt("try { } catch (e) { } finally { }",
            tryStmt(blockStmt([]),
-		   catchClause(ident("e"), 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([])),
+                   blockStmt([])));
 assertStmt("try { } catch { }",
            tryStmt(blockStmt([]),
-       catchClause(null, blockStmt([])),
+                   [],
+       catchClause(null, null, blockStmt([])),
                    null));
 assertStmt("try { } catch { } finally { }",
            tryStmt(blockStmt([]),
-       catchClause(null, blockStmt([])),
+                   [],
+       catchClause(null, null, blockStmt([])),
                    blockStmt([])));
 
 
 // Bug 632028: yield outside of a function should throw
 (function() {
     var threw = false;
     try {
         Reflect.parse("yield 0");
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -70,100 +70,54 @@ var ClickEventHandler = {
         return true;
       }
 
       node = node.parentNode;
     }
     return false;
   },
 
-  isScrollableElement(aNode) {
-    if (aNode instanceof content.HTMLElement) {
-      return !(aNode instanceof content.HTMLSelectElement) || aNode.multiple;
-    }
-
-    return aNode instanceof content.XULElement;
-  },
-
-  getXBLNodes(parent, array) {
-    let anonNodes = content.document.getAnonymousNodes(parent);
-    let nodes = Array.from(anonNodes || parent.childNodes || []);
-    for (let node of nodes) {
-      if (node.nodeName == "children") {
-        return true;
-      }
-      if (this.getXBLNodes(node, array)) {
-        array.push(node);
-        return true;
-      }
-    }
-    return false;
-  },
-
-  * parentNodeIterator(aNode) {
-    while (aNode) {
-      yield aNode;
-
-      let parent = aNode.parentNode;
-      if (parent && parent instanceof content.XULElement) {
-        let anonNodes = content.document.getAnonymousNodes(parent);
-        if (anonNodes && !Array.from(anonNodes).includes(aNode)) {
-          // XBL elements are skipped by parentNode property.
-          // Yield elements between parent and <children> here.
-          let nodes = [];
-          this.getXBLNodes(parent, nodes);
-          for (let node of nodes) {
-            yield node;
-          }
-        }
-      }
-
-      aNode = parent;
-    }
-  },
-
   findNearestScrollableElement(aNode) {
     // this is a list of overflow property values that allow scrolling
     const scrollingAllowed = ["scroll", "auto"];
 
     // go upward in the DOM and find any parent element that has a overflow
     // area and can therefore be scrolled
-    this._scrollable = null;
-    for (let node of this.parentNodeIterator(aNode)) {
+    for (this._scrollable = aNode; this._scrollable;
+         this._scrollable = this._scrollable.parentNode) {
       // do not use overflow based autoscroll for <html> and <body>
-      // Elements or non-html/non-xul elements such as svg or Document nodes
+      // Elements or non-html elements such as svg or Document nodes
       // also make sure to skip select elements that are not multiline
-      if (!this.isScrollableElement(node)) {
+      if (!(this._scrollable instanceof content.HTMLElement) ||
+          ((this._scrollable instanceof content.HTMLSelectElement) && !this._scrollable.multiple)) {
         continue;
       }
 
-      var overflowx = node.ownerGlobal
-                          .getComputedStyle(node)
+      var overflowx = this._scrollable.ownerGlobal
+                          .getComputedStyle(this._scrollable)
                           .getPropertyValue("overflow-x");
-      var overflowy = node.ownerGlobal
-                          .getComputedStyle(node)
+      var overflowy = this._scrollable.ownerGlobal
+                          .getComputedStyle(this._scrollable)
                           .getPropertyValue("overflow-y");
       // we already discarded non-multiline selects so allow vertical
       // scroll for multiline ones directly without checking for a
       // overflow property
-      var scrollVert = node.scrollTopMax &&
-        (node instanceof content.HTMLSelectElement ||
+      var scrollVert = this._scrollable.scrollTopMax &&
+        (this._scrollable instanceof content.HTMLSelectElement ||
          scrollingAllowed.indexOf(overflowy) >= 0);
 
       // do not allow horizontal scrolling for select elements, it leads
       // to visual artifacts and is not the expected behavior anyway
-      if (!(node instanceof content.HTMLSelectElement) &&
-          node.scrollLeftMin != node.scrollLeftMax &&
+      if (!(this._scrollable instanceof content.HTMLSelectElement) &&
+          this._scrollable.scrollLeftMin != this._scrollable.scrollLeftMax &&
           scrollingAllowed.indexOf(overflowx) >= 0) {
         this._scrolldir = scrollVert ? "NSEW" : "EW";
-        this._scrollable = node;
         break;
       } else if (scrollVert) {
         this._scrolldir = "NS";
-        this._scrollable = node;
         break;
       }
     }
 
     if (!this._scrollable) {
       this._scrollable = aNode.ownerGlobal;
       if (this._scrollable.scrollMaxX != this._scrollable.scrollMinX) {
         this._scrolldir = this._scrollable.scrollMaxY !=