Bug 1202134 - Save return value onto the stack before executing finally block. r=jandem
authorTooru Fujisawa <arai_a@mac.com>
Sat, 17 Oct 2015 23:30:20 +0900
changeset 304547 00dac1d05d6097e885f1115ec74a64a9f92d044d
parent 304546 d90b11b242df8dc9295341c56000b05c1dee6013
child 304548 6e1c8c268f842b440ff04295ba2bb994f04b4be7
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1202134
milestone44.0a1
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
Bug 1202134 - Save return value onto the stack before executing finally block. r=jandem
js/src/asmjs/AsmJSValidate.cpp
js/src/builtin/ReflectParse.cpp
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
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/SharedContext.h
js/src/frontend/SyntaxParseHandler.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineCompiler.h
js/src/tests/ecma_6/Function/return-finally.js
js/src/tests/ecma_6/Generators/return-finally.js
js/src/vm/CommonPropertyNames.h
js/src/vm/GeneratorObject.cpp
js/src/vm/Interpreter.cpp
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -100,17 +100,17 @@ BinaryLeft(ParseNode* pn)
     MOZ_ASSERT(pn->isArity(PN_BINARY));
     return pn->pn_left;
 }
 
 static inline ParseNode*
 ReturnExpr(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_RETURN));
-    return BinaryLeft(pn);
+    return UnaryKid(pn);
 }
 
 static inline ParseNode*
 TernaryKid1(ParseNode* pn)
 {
     MOZ_ASSERT(pn->isArity(PN_TERNARY));
     return pn->pn_kid1;
 }
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -2701,21 +2701,21 @@ ASTSerializer::statement(ParseNode* pn, 
         RootedValue arg(cx);
 
         return optExpression(pn->pn_kid, &arg) &&
                builder.throwStatement(arg, &pn->pn_pos, dst);
       }
 
       case PNK_RETURN:
       {
-        MOZ_ASSERT_IF(pn->pn_left, pn->pn_pos.encloses(pn->pn_left->pn_pos));
+        MOZ_ASSERT_IF(pn->pn_kid, pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         RootedValue arg(cx);
 
-        return optExpression(pn->pn_left, &arg) &&
+        return optExpression(pn->pn_kid, &arg) &&
                builder.returnStatement(arg, &pn->pn_pos, dst);
       }
 
       case PNK_DEBUGGER:
         return builder.debuggerStatement(&pn->pn_pos, dst);
 
       case PNK_CLASS:
         return classDefinition(pn, false, dst);
@@ -3610,17 +3610,17 @@ ASTSerializer::functionArgsAndBody(Parse
         pnargs = nullptr;
         pnbody = pn;
     }
 
     /* Serialize the arguments and body. */
     switch (pnbody->getKind()) {
       case PNK_RETURN: /* expression closure, no destructured args */
         return functionArgs(pn, pnargs, pnbody, args, defaults, rest) &&
-               expression(pnbody->pn_left, body);
+               expression(pnbody->pn_kid, body);
 
       case PNK_STATEMENTLIST:     /* statement closure */
       {
         ParseNode* pnstart = pnbody->pn_head;
 
         // Skip over initial yield in generator.
         if (pnstart && pnstart->isKind(PNK_YIELD)) {
             MOZ_ASSERT(pnstart->getOp() == JSOP_INITIALYIELD);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -636,20 +636,20 @@ NonLocalExitScope::prepareForNonLocalJum
             break;
 
           case StmtType::SPREAD:
             MOZ_ASSERT_UNREACHABLE("can't break/continue/return from inside a spread");
             break;
 
           case StmtType::SUBROUTINE:
             /*
-             * There's a [exception or hole, retsub pc-index] pair on the
-             * stack that we need to pop.
+             * There's a [exception or hole, retsub pc-index] pair and the
+             * possible return value on the stack that we need to pop.
              */
-            npops += 2;
+            npops += 3;
             break;
 
           default:;
         }
 
         if (stmt->isBlockScope) {
             StaticBlockObject& blockObj = stmt->staticBlock();
             if (blockObj.needsClone()) {
@@ -1032,18 +1032,18 @@ BytecodeEmitter::emitIndexOp(JSOp op, ui
 }
 
 bool
 BytecodeEmitter::emitAtomOp(JSAtom* atom, JSOp op)
 {
     MOZ_ASSERT(atom);
     MOZ_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
-    // .generator and .genrval lookups should be emitted as JSOP_GETALIASEDVAR
-    // instead of JSOP_GETNAME etc, to bypass |with| objects on the scope chain.
+    // .generator lookups should be emitted as JSOP_GETALIASEDVAR instead of
+    // JSOP_GETNAME etc, to bypass |with| objects on the scope chain.
     MOZ_ASSERT_IF(op == JSOP_GETNAME || op == JSOP_GETGNAME, !sc->isDotVariable(atom));
 
     if (op == JSOP_GETPROP && atom == cx->names().length) {
         /* Specialize length accesses for the interpreter. */
         op = JSOP_LENGTH;
     }
 
     jsatomid index;
@@ -5030,17 +5030,19 @@ BytecodeEmitter::emitTry(ParseNode* pn)
 
         finallyStart = offset();
 
         // Indicate that we're emitting a subroutine body.
         stmtInfo.type = StmtType::SUBROUTINE;
         if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
             return false;
         if (!emit1(JSOP_FINALLY) ||
+            !emit1(JSOP_GETRVAL) ||
             !emitTree(pn->pn_kid3) ||
+            !emit1(JSOP_SETRVAL) ||
             !emit1(JSOP_RETSUB))
         {
             return false;
         }
         hasTryFinally = true;
         MOZ_ASSERT(this->stackDepth == depth);
     }
     popStatement();
@@ -6063,38 +6065,28 @@ BytecodeEmitter::emitContinue(PropertyNa
         while (!stmt->isLoop())
             stmt = stmt->enclosing;
     }
 
     return emitGoto(stmt, &stmt->continues, SRC_CONTINUE);
 }
 
 bool
-BytecodeEmitter::inTryBlockWithFinally()
-{
-    for (StmtInfoBCE* stmt = innermostStmt(); stmt; stmt = stmt->enclosing) {
-        if (stmt->type == StmtType::FINALLY)
-            return true;
-    }
-    return false;
-}
-
-bool
 BytecodeEmitter::emitReturn(ParseNode* pn)
 {
     if (!updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
     if (sc->isFunctionBox() && sc->asFunctionBox()->isStarGenerator()) {
         if (!emitPrepareIteratorResult())
             return false;
     }
 
     /* Push a return value */
-    if (ParseNode* pn2 = pn->pn_left) {
+    if (ParseNode* pn2 = pn->pn_kid) {
         if (!emitTree(pn2))
             return false;
     } else {
         /* No explicit return value provided */
         if (!emit1(JSOP_UNDEFINED))
             return false;
     }
 
@@ -6112,54 +6104,29 @@ BytecodeEmitter::emitReturn(ParseNode* p
      * for/in, etc., slots nested inside the finally's try).
      *
      * In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
      * extra JSOP_RETRVAL after the fixups.
      */
     ptrdiff_t top = offset();
 
     bool isGenerator = sc->isFunctionBox() && sc->asFunctionBox()->isGenerator();
-    bool useGenRVal = false;
-    if (isGenerator) {
-        if (sc->asFunctionBox()->isStarGenerator() && inTryBlockWithFinally()) {
-            // Emit JSOP_SETALIASEDVAR .genrval to store the return value on the
-            // scope chain, so it's not lost when we yield in a finally block.
-            useGenRVal = true;
-            MOZ_ASSERT(pn->pn_right);
-            if (!emitTree(pn->pn_right))
-                return false;
-            if (!emit1(JSOP_POP))
-                return false;
-        } else {
-            if (!emit1(JSOP_SETRVAL))
-                return false;
-        }
-    } else {
-        if (!emit1(JSOP_RETURN))
-            return false;
-    }
+    if (!emit1(isGenerator ? JSOP_SETRVAL : JSOP_RETURN))
+        return false;
 
     NonLocalExitScope nle(this);
 
     if (!nle.prepareForNonLocalJump(nullptr))
         return false;
 
     if (isGenerator) {
         ScopeCoordinate sc;
-        // We know that .generator and .genrval are on the top scope chain node,
-        // as we just exited nested scopes.
+        // We know that .generator is on the top scope chain node, as we just
+        // exited nested scopes.
         sc.setHops(0);
-        if (useGenRVal) {
-            MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenRVal, &sc));
-            if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
-                return false;
-            if (!emit1(JSOP_SETRVAL))
-                return false;
-        }
-
         MOZ_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().dotGenerator, &sc));
         if (!emitAliasedVarOp(JSOP_GETALIASEDVAR, sc, DontCheckLexical))
             return false;
         if (!emitYieldOp(JSOP_FINALYIELDRVAL))
             return false;
     } else if (top + static_cast<ptrdiff_t>(JSOP_RETURN_LENGTH) != offset()) {
         code()[top] = JSOP_SETRVAL;
         if (!emit1(JSOP_RETRVAL))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -299,18 +299,16 @@ struct BytecodeEmitter
     // The caller should initialize *answer to false and invoke this function on
     // an expression statement or similar subtree to decide whether the tree
     // could produce code that has any side effects.  For an expression
     // statement, we define useless code as code with no side effects, because
     // the main effect, the value left on the stack after the code executes,
     // will be discarded by a pop bytecode.
     bool checkSideEffects(ParseNode* pn, bool* answer);
 
-    bool inTryBlockWithFinally();
-
 #ifdef DEBUG
     bool checkStrictOrSloppy(JSOp op);
 #endif
 
     // Append a new source note of the given type (and therefore size) to the
     // notes dynamic array, updating noteCount. Return the new note's index
     // within the array pointed at by current->notes as outparam.
     bool newSrcNote(SrcNoteType type, unsigned* indexp = nullptr);
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -109,21 +109,21 @@ ContainsHoistedDeclaration(ExclusiveCont
       case PNK_DEBUGGER:
         MOZ_ASSERT(node->isArity(PN_NULLARY));
         *result = false;
         return true;
 
       // Statements containing only an expression have no declarations.
       case PNK_SEMI:
       case PNK_THROW:
+      case PNK_RETURN:
         MOZ_ASSERT(node->isArity(PN_UNARY));
         *result = false;
         return true;
 
-      case PNK_RETURN:
       // These two aren't statements in the spec, but we sometimes insert them
       // in statement lists anyway.
       case PNK_YIELD_STAR:
       case PNK_YIELD:
         MOZ_ASSERT(node->isArity(PN_BINARY));
         *result = false;
         return true;
 
@@ -1257,31 +1257,23 @@ FoldList(ExclusiveContext* cx, ParseNode
     return true;
 }
 
 static bool
 FoldReturn(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
            bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_RETURN));
-    MOZ_ASSERT(node->isArity(PN_BINARY));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
 
-    if (ParseNode*& expr = node->pn_left) {
+    if (ParseNode*& expr = node->pn_kid) {
         if (!Fold(cx, &expr, parser, inGenexpLambda))
             return false;
     }
 
-#ifdef DEBUG
-    if (ParseNode* generatorSpecific = node->pn_right) {
-        MOZ_ASSERT(generatorSpecific->isKind(PNK_NAME));
-        MOZ_ASSERT(generatorSpecific->pn_atom->equals(".genrval"));
-        MOZ_ASSERT(generatorSpecific->isAssigned());
-    }
-#endif
-
     return true;
 }
 
 static bool
 FoldTry(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
         bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_TRY));
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -588,19 +588,19 @@ class FullParseHandler
     ParseNode* newContinueStatement(PropertyName* label, const TokenPos& pos) {
         return new_<ContinueStatement>(label, pos);
     }
 
     ParseNode* newBreakStatement(PropertyName* label, const TokenPos& pos) {
         return new_<BreakStatement>(label, pos);
     }
 
-    ParseNode* newReturnStatement(ParseNode* expr, ParseNode* genrval, const TokenPos& pos) {
+    ParseNode* newReturnStatement(ParseNode* expr, const TokenPos& pos) {
         MOZ_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
-        return new_<BinaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr, genrval);
+        return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr);
     }
 
     ParseNode* newWithStatement(uint32_t begin, ParseNode* expr, ParseNode* body,
                                 ObjectBox* staticWith) {
         return new_<BinaryObjNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end),
                                    expr, body, staticWith);
     }
 
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -496,28 +496,21 @@ class NameResolver
             }
             MOZ_ASSERT((cur->pn_right->isKind(PNK_NAME) && !cur->pn_right->isAssigned()) ||
                        (cur->pn_right->isKind(PNK_ASSIGN) &&
                         cur->pn_right->pn_left->isKind(PNK_NAME) &&
                         cur->pn_right->pn_right->isKind(PNK_GENERATOR)));
             break;
 
           case PNK_RETURN:
-            MOZ_ASSERT(cur->isArity(PN_BINARY));
-            if (ParseNode* returnValue = cur->pn_left) {
+            MOZ_ASSERT(cur->isArity(PN_UNARY));
+            if (ParseNode* returnValue = cur->pn_kid) {
                 if (!resolve(returnValue, prefix))
                     return false;
             }
-#ifdef DEBUG
-            if (ParseNode* internalAssignForGenerators = cur->pn_right) {
-                MOZ_ASSERT(internalAssignForGenerators->isKind(PNK_NAME));
-                MOZ_ASSERT(internalAssignForGenerators->pn_atom == cx->names().dotGenRVal);
-                MOZ_ASSERT(internalAssignForGenerators->isAssigned());
-            }
-#endif
             break;
 
           case PNK_IMPORT:
           case PNK_EXPORT_FROM:
           case PNK_EXPORT_DEFAULT:
             MOZ_ASSERT(cur->isArity(PN_BINARY));
             // The left halves of these nodes don't contain any unconstrained
             // expressions, but it's very hard to assert this to safely rely on
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -332,29 +332,22 @@ PushNodeChildren(ParseNode* pn, NodeStac
                     pn->pn_right->pn_left->isKind(PNK_NAME) &&
                     pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
         if (pn->pn_left)
             stack->push(pn->pn_left);
         stack->push(pn->pn_right);
         return PushResult::Recyclable;
       }
 
-      // A return node's left half is what you'd expect: the return expression,
-      // if any.  The right half is non-null only for returns inside generator
-      // functions, with the structure described in the assertions.
+      // A return node's child is what you'd expect: the return expression,
+      // if any.
       case PNK_RETURN: {
-        MOZ_ASSERT(pn->isArity(PN_BINARY));
-        if (pn->pn_left)
-            stack->push(pn->pn_left);
-        if (pn->pn_right) {
-            MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
-            MOZ_ASSERT(pn->pn_right->pn_atom->equals(".genrval"));
-            MOZ_ASSERT(pn->pn_right->isAssigned());
-            stack->push(pn->pn_right);
-        }
+        MOZ_ASSERT(pn->isArity(PN_UNARY));
+        if (pn->pn_kid)
+            stack->push(pn->pn_kid);
         return PushResult::Recyclable;
       }
 
       // Import and export-from nodes have a list of specifiers on the left
       // and a module string on the right.
       case PNK_IMPORT:
       case PNK_EXPORT_FROM: {
         MOZ_ASSERT(pn->isArity(PN_BINARY));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -351,18 +351,17 @@ IsDeleteKind(ParseNodeKind kind)
  *                                   or
  *                                     pn_used: true
  *                                     pn_atom: variable name
  *                                     pn_lexdef: def node
  *                                   each assignment node has
  *                                     pn_left: PNK_NAME with pn_used true and
  *                                              pn_lexdef (NOT pn_expr) set
  *                                     pn_right: initializer
- * PNK_RETURN   binary      pn_left: return expr or null
- *                          pn_right: .genrval name or null
+ * PNK_RETURN   unary       pn_kid: return expr or null
  * PNK_SEMI     unary       pn_kid: expr or null statement
  *                          pn_prologue: true if Directive Prologue member
  *                              in original source, not introduced via
  *                              constant folding or other tree rewriting
  * PNK_LABEL    name        pn_atom: label, pn_expr: labeled statement
  * PNK_IMPORT   binary      pn_left: PNK_IMPORT_SPEC_LIST import specifiers
  *                          pn_right: PNK_STRING module specifier
  * PNK_EXPORT   unary       pn_kid: declaration expression
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1229,17 +1229,17 @@ Parser<ParseHandler>::functionBody(InHan
             return null();
     } else {
         MOZ_ASSERT(type == ExpressionBody);
 
         Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
 
-        pn = handler.newReturnStatement(kid, null(), handler.getPosition(kid));
+        pn = handler.newReturnStatement(kid, handler.getPosition(kid));
         if (!pn)
             return null();
     }
 
     switch (pc->generatorKind()) {
       case NotGenerator:
         MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
         break;
@@ -1269,24 +1269,16 @@ Parser<ParseHandler>::functionBody(InHan
     if (pc->isGenerator()) {
         MOZ_ASSERT(type == StatementListBody);
         Node generator = newName(context->names().dotGenerator);
         if (!generator)
             return null();
         if (!pc->define(tokenStream, context->names().dotGenerator, generator, Definition::VAR))
             return null();
 
-        if (pc->isStarGenerator()) {
-            Node genrval = newName(context->names().dotGenRVal);
-            if (!genrval)
-                return null();
-            if (!pc->define(tokenStream, context->names().dotGenRVal, genrval, Definition::VAR))
-                return null();
-        }
-
         generator = newName(context->names().dotGenerator);
         if (!generator)
             return null();
         if (!noteNameUse(context->names().dotGenerator, generator))
             return null();
         if (!handler.prependInitialYield(pn, generator))
             return null();
     }
@@ -5999,28 +5991,17 @@ Parser<ParseHandler>::returnStatement(Yi
     if (exprNode) {
         if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
             return null();
     } else {
         if (!MatchOrInsertSemicolonAfterNonExpression(tokenStream))
             return null();
     }
 
-    Node genrval = null();
-    if (pc->isStarGenerator()) {
-        genrval = newName(context->names().dotGenRVal);
-        if (!genrval)
-            return null();
-        if (!noteNameUse(context->names().dotGenRVal, genrval))
-            return null();
-        if (!checkAndMarkAsAssignmentLhs(genrval, PlainAssignment))
-            return null();
-    }
-
-    Node pn = handler.newReturnStatement(exprNode, genrval, TokenPos(begin, pos().end));
+    Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
     if (!pn)
         return null();
 
     if (pc->isLegacyGenerator() && exprNode) {
         /* Disallow "return v;" in legacy generators. */
         reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
                         JSMSG_BAD_ANON_GENERATOR_RETURN);
         return null();
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -250,17 +250,17 @@ class SharedContext
     }
 
     // JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
     bool needStrictChecks() const {
         return strict() || extraWarnings;
     }
 
     bool isDotVariable(JSAtom* atom) const {
-        return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
+        return atom == context->names().dotGenerator;
     }
 };
 
 class MOZ_STACK_CLASS GlobalSharedContext : public SharedContext
 {
     Rooted<ScopeObject*> staticScope_;
 
   public:
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -299,17 +299,17 @@ class SyntaxParseHandler
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos& pos) { return NodeGeneric; }
     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
     Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; }
     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
     Node newContinueStatement(PropertyName* label, const TokenPos& pos) { return NodeGeneric; }
     Node newBreakStatement(PropertyName* label, const TokenPos& pos) { return NodeBreak; }
-    Node newReturnStatement(Node expr, Node genrval, const TokenPos& pos) { return NodeReturn; }
+    Node newReturnStatement(Node expr, const TokenPos& pos) { return NodeReturn; }
 
     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 catchList, Node finallyBlock) {
         return NodeGeneric;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3575,16 +3575,39 @@ BaselineCompiler::emit_JSOP_ENDITER()
 {
     frame.popRegsAndSync(1);
 
     ICIteratorClose_Fallback::Compiler compiler(cx);
     return emitOpIC(compiler.getStub(&stubSpace_));
 }
 
 bool
+BaselineCompiler::emit_JSOP_GETRVAL()
+{
+    frame.syncStack(0);
+
+    Label norval, done;
+    Address flags = frame.addressOfFlags();
+    masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), &norval);
+    // Get the value from the return value slot, if any.
+    masm.loadValue(frame.addressOfReturnValue(), R0);
+    masm.jump(&done);
+
+    // Push undefined otherwise.  When we throw an exception in the try
+    // block, rval is not yet initialized when entering finally block.
+    masm.bind(&norval);
+    masm.moveValue(UndefinedValue(), R0);
+
+    masm.bind(&done);
+    frame.push(R0);
+
+    return true;
+}
+
+bool
 BaselineCompiler::emit_JSOP_SETRVAL()
 {
     // Store to the frame's return value slot.
     storeValue(frame.peek(-1), frame.addressOfReturnValue(), R2);
     masm.or32(Imm32(BaselineFrame::HAS_RVAL), frame.addressOfFlags());
     frame.pop();
     return true;
 }
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -193,16 +193,17 @@ namespace jit {
     _(JSOP_ENDITER)            \
     _(JSOP_GENERATOR)          \
     _(JSOP_INITIALYIELD)       \
     _(JSOP_YIELD)              \
     _(JSOP_DEBUGAFTERYIELD)    \
     _(JSOP_FINALYIELDRVAL)     \
     _(JSOP_RESUME)             \
     _(JSOP_CALLEE)             \
+    _(JSOP_GETRVAL)            \
     _(JSOP_SETRVAL)            \
     _(JSOP_RETRVAL)            \
     _(JSOP_RETURN)             \
     _(JSOP_NEWTARGET)          \
     _(JSOP_SUPERCALL)          \
     _(JSOP_SPREADSUPERCALL)    \
     _(JSOP_THROWSETCONST)      \
     _(JSOP_THROWSETALIASEDCONST)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/return-finally.js
@@ -0,0 +1,172 @@
+var BUGNUMBER = 1202134;
+var summary = "Return value should not be overwritten by finally block with normal execution.";
+
+print(BUGNUMBER + ": " + summary);
+
+// ==== single ====
+
+var f;
+f = function() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+  }
+};
+assertEq(f(), 42);
+
+f = function() {
+  // F.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    return 43;
+  }
+};
+assertEq(f(), 43);
+
+f = function() {
+  // F.[[type]] is throw
+  try {
+    return 42;
+  } finally {
+    throw 43;
+  }
+};
+var caught = false;
+try {
+  f();
+} catch (e) {
+  assertEq(e, 43);
+  caught = true;
+}
+assertEq(caught, true);
+
+f = function() {
+  // F.[[type]] is break
+  do try {
+    return 42;
+  } finally {
+    break;
+  } while (false);
+  return 43;
+};
+assertEq(f(), 43);
+
+f = function() {
+  // F.[[type]] is break
+  L: try {
+    return 42;
+  } finally {
+    break L;
+  }
+  return 43;
+};
+assertEq(f(), 43);
+
+f = function() {
+  // F.[[type]] is continue
+  do try {
+    return 42;
+  } finally {
+    continue;
+  } while (false);
+  return 43;
+};
+assertEq(f(), 43);
+
+// ==== nested ====
+
+f = function() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is break
+    do try {
+      return 43;
+    } finally {
+      break;
+    } while (0);
+  }
+};
+assertEq(f(), 42);
+
+f = function() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is break
+    L: try {
+      return 43;
+    } finally {
+      break L;
+    }
+  }
+}
+assertEq(f(), 42);
+
+f = function() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is continue
+    do try {
+      return 43;
+    } finally {
+      continue;
+    } while (0);
+  }
+};
+assertEq(f(), 42);
+
+f = function() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is normal
+    // B.[[type]] is normal
+    try {
+      // F.[[type]] is throw
+      try {
+        return 43;
+      } finally {
+        throw 9;
+      }
+    } catch (e) {
+    }
+  }
+};
+assertEq(f(), 42);
+
+f = function() {
+  // F.[[type]] is return
+  try {
+    return 41;
+  } finally {
+    // F.[[type]] is normal
+    // B.[[type]] is return
+    try {
+      return 42;
+    } finally {
+      // F.[[type]] is break
+      do try {
+        return 43;
+      } finally {
+        break;
+      } while (0);
+    }
+  }
+};
+assertEq(f(), 42);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Generators/return-finally.js
@@ -0,0 +1,309 @@
+var BUGNUMBER = 1202134;
+var summary = "Return value should not be overwritten by finally block with normal execution.";
+
+print(BUGNUMBER + ": " + summary);
+
+// ==== single ====
+
+var f, g, v;
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    return 43;
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is throw
+  try {
+    return 42;
+  } finally {
+    throw 43;
+  }
+};
+var caught = false;
+g = f();
+try {
+  v = g.next();
+} catch (e) {
+  assertEq(e, 43);
+  caught = true;
+}
+assertEq(caught, true);
+
+f = function*() {
+  // F.[[type]] is break
+  do try {
+    return 42;
+  } finally {
+    break;
+  } while (false);
+  return 43;
+};
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is break
+  L: try {
+    return 42;
+  } finally {
+    break L;
+  }
+  return 43;
+};
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is continue
+  do try {
+    return 42;
+  } finally {
+    continue;
+  } while (false);
+  return 43;
+};
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, true);
+
+// ==== nested ====
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is break
+    do try {
+      return 43;
+    } finally {
+      break;
+    } while (0);
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is break
+    L: try {
+      return 43;
+    } finally {
+      break L;
+    }
+  }
+}
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is continue
+    do try {
+      return 43;
+    } finally {
+      continue;
+    } while (0);
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is normal
+    // B.[[type]] is normal
+    try {
+      // F.[[type]] is throw
+      try {
+        return 43;
+      } finally {
+        throw 9;
+      }
+    } catch (e) {
+    }
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is return
+  try {
+    return 41;
+  } finally {
+    // F.[[type]] is normal
+    // B.[[type]] is return
+    try {
+      return 42;
+    } finally {
+      // F.[[type]] is break
+      do try {
+        return 43;
+      } finally {
+        break;
+      } while (0);
+    }
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+// ==== with yield ====
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    yield 43;
+  }
+};
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, false);
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+// ==== throw() ====
+
+f = function*() {
+  // F.[[type]] is throw
+  try {
+    return 42;
+  } finally {
+    yield 43;
+  }
+};
+caught = false;
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, false);
+try {
+  v = g.throw(44);
+} catch (e) {
+  assertEq(e, 44);
+  caught = true;
+}
+assertEq(caught, true);
+
+f = function*() {
+  // F.[[type]] is normal
+  try {
+    return 42;
+  } finally {
+    // F.[[type]] is normal
+    // B.[[type]] is throw
+    try {
+      yield 43;
+    } catch (e) {
+    }
+  }
+};
+caught = false;
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, false);
+v = g.throw(44);
+assertEq(v.value, 42);
+assertEq(v.done, true);
+
+// ==== return() ====
+
+f = function*() {
+  // F.[[type]] is return
+  try {
+    return 42;
+  } finally {
+    yield 43;
+  }
+};
+caught = false;
+g = f();
+v = g.next();
+assertEq(v.value, 43);
+assertEq(v.done, false);
+v = g.return(44);
+assertEq(v.value, 44);
+assertEq(v.done, true);
+
+f = function*() {
+  // F.[[type]] is normal
+  // B.[[type]] is return
+  try {
+    yield 42;
+  } finally {
+    // F.[[type]] is continue
+    do try {
+      return 43;
+    } finally {
+      continue;
+    } while (0);
+  }
+};
+caught = false;
+g = f();
+v = g.next();
+assertEq(v.value, 42);
+assertEq(v.done, false);
+v = g.return(44);
+assertEq(v.value, 44);
+assertEq(v.done, true);
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -60,17 +60,16 @@
     macro(defineProperty, defineProperty, "defineProperty") \
     macro(defineGetter, defineGetter, "__defineGetter__") \
     macro(defineSetter, defineSetter, "__defineSetter__") \
     macro(delete, delete_, "delete") \
     macro(deleteProperty, deleteProperty, "deleteProperty") \
     macro(displayURL, displayURL, "displayURL") \
     macro(done, done, "done") \
     macro(dotGenerator, dotGenerator, ".generator") \
-    macro(dotGenRVal, dotGenRVal, ".genrval") \
     macro(each, each, "each") \
     macro(elementType, elementType, "elementType") \
     macro(empty, empty, "") \
     macro(emptyRegExp, emptyRegExp, "(?:)") \
     macro(encodeURI, encodeURI, "encodeURI") \
     macro(encodeURIComponent, encodeURIComponent, "encodeURIComponent") \
     macro(endTimestamp, endTimestamp, "endTimestamp") \
     macro(enumerable, enumerable, "enumerable") \
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -107,48 +107,38 @@ js::SetReturnValueForClosingGenerator(JS
 {
     CallObject& callObj = frame.callObj();
 
     // Get the generator object stored on the scope chain and close it.
     Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
     GeneratorObject& genObj = callObj.getSlot(shape->slot()).toObject().as<GeneratorObject>();
     genObj.setClosed();
 
-    Value v;
-    if (genObj.is<StarGeneratorObject>()) {
-        // The return value is stored in the .genrval slot.
-        shape = callObj.lookup(cx, cx->names().dotGenRVal);
-        v = callObj.getSlot(shape->slot());
-    } else {
-        // Legacy generator .close() always returns |undefined|.
-        MOZ_ASSERT(genObj.is<LegacyGeneratorObject>());
-        v = UndefinedValue();
-    }
+    // Return value is already set in GeneratorThrowOrClose.
+    if (genObj.is<StarGeneratorObject>())
+        return;
 
-    frame.setReturnValue(v);
+    // Legacy generator .close() always returns |undefined|.
+    MOZ_ASSERT(genObj.is<LegacyGeneratorObject>());
+    frame.setReturnValue(UndefinedValue());
 }
 
 bool
 js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj,
                           HandleValue arg, uint32_t resumeKind)
 {
     if (resumeKind == GeneratorObject::THROW) {
         cx->setPendingException(arg);
         genObj->setRunning();
     } else {
         MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
 
         if (genObj->is<StarGeneratorObject>()) {
-            // Store the return value in the frame's CallObject so that we can
-            // return it after executing finally blocks (and potentially
-            // yielding again).
             MOZ_ASSERT(arg.isObject());
-            CallObject& callObj = frame.callObj();
-            Shape* shape = callObj.lookup(cx, cx->names().dotGenRVal);
-            callObj.setSlot(shape->slot(), arg);
+            frame.setReturnValue(arg);
         } else {
             MOZ_ASSERT(arg.isUndefined());
         }
 
         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
         genObj->setClosing();
     }
     return false;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1792,17 +1792,16 @@ CASE(EnableInterruptsPseudoOpcode)
 
     /* Commence executing the actual opcode. */
     SANITY_CHECKS();
     DISPATCH_TO(op);
 }
 
 /* Various 1-byte no-ops. */
 CASE(JSOP_NOP)
-CASE(JSOP_UNUSED2)
 CASE(JSOP_UNUSED14)
 CASE(JSOP_BACKPATCH)
 CASE(JSOP_UNUSED171)
 CASE(JSOP_UNUSED172)
 CASE(JSOP_UNUSED173)
 CASE(JSOP_UNUSED174)
 CASE(JSOP_UNUSED175)
 CASE(JSOP_UNUSED176)
@@ -1904,16 +1903,20 @@ CASE(JSOP_DUPAT)
     PUSH_COPY(rref);
 }
 END_CASE(JSOP_DUPAT)
 
 CASE(JSOP_SETRVAL)
     POP_RETURN_VALUE();
 END_CASE(JSOP_SETRVAL)
 
+CASE(JSOP_GETRVAL)
+    PUSH_COPY(REGS.fp()->returnValue());
+END_CASE(JSOP_GETRVAL)
+
 CASE(JSOP_ENTERWITH)
 {
     ReservedRooted<Value> val(&rootValue0, REGS.sp[-1]);
     REGS.sp--;
     ReservedRooted<JSObject*> staticWith(&rootObject0, script->getObject(REGS.pc));
 
     if (!EnterWithOperation(cx, REGS.fp(), val, staticWith))
         goto error;
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -97,17 +97,24 @@ 1234567890123456789012345678901234567890
     /*
      * Pushes 'undefined' onto the stack.
      *   Category: Literals
      *   Type: Constants
      *   Operands:
      *   Stack: => undefined
      */ \
     macro(JSOP_UNDEFINED, 1,  js_undefined_str, "",       1,  0,  1, JOF_BYTE) \
-    macro(JSOP_UNUSED2,   2,  "unused2",    NULL,         1,  1,  0, JOF_BYTE) \
+    /*
+     * Pushes stack frame's 'rval' onto the stack.
+     *   Category: Statements
+     *   Type: Function
+     *   Operands:
+     *   Stack: => rval
+     */ \
+    macro(JSOP_GETRVAL,   2,  "getrval",    NULL,         1,  0,  1, JOF_BYTE) \
     /*
      * Pops the top of stack value, converts it to an object, and adds a
      * 'DynamicWithObject' wrapping that object to the scope chain.
      *
      * There is a matching JSOP_LEAVEWITH instruction later. All name
      * lookups between the two that may need to consult the With object
      * are deoptimized.
      *   Category: Statements
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 315;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 316;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 419,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");