2
0
Эх сурвалжийг харах

Few iterator close - array destructuring.

Array destructuring was wrapped with try..finally as it was assumed that we will get abrupt completion due to throw. But 'yield' makes it possible to abrupt completion with 'return'. Fixed that by extend that to try.catch.finally.
Also we were emitting reference of the LHS after emitting of RHS. However that is supposed to happen first. Fixed that by Emit reference of LHS first.
Added unittest.
Akrosh Gandhi 9 жил өмнө
parent
commit
bbc92c0c3a

+ 5 - 5
lib/Parser/Parse.cpp

@@ -12197,11 +12197,6 @@ ParseNodePtr Parser::ParseDestructuredArrayLiteral(tokens declarationType, bool
     {
         while (true)
         {
-            if (seenRest) // Rest must be in the last position.
-            {
-                Error(ERRDestructRestLast);
-            }
-
             ParseNodePtr pnodeElem = ParseDestructuredVarDecl<buildAST>(declarationType, isDecl, &seenRest, topLevel);
             if (buildAST)
             {
@@ -12224,6 +12219,11 @@ ParseNodePtr Parser::ParseDestructuredArrayLiteral(tokens declarationType, bool
                 Error(ERRDestructNoOper);
             }
 
+            if (seenRest) // Rest must be in the last position.
+            {
+                Error(ERRDestructRestLast);
+            }
+
             m_pscan->Scan();
 
             // break if we have the trailing comma as well, eg. [a,]

+ 185 - 142
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -2779,7 +2779,7 @@ void ByteCodeGenerator::EmitInitCapturedNewTarget(FuncInfo* funcInfo, Scope* sco
 }
 
 void EmitDestructuredObject(ParseNode *lhs, Js::RegSlot rhsLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
-void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode, Js::RegSlot rhsLocation, ParseNodePtr initializer, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
+void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode, Js::RegSlot rhsLocation, ParseNodePtr initializer, bool isNonPatternAssignmentTarget, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo);
 
 void ByteCodeGenerator::PopulateFormalsScope(uint beginOffset, FuncInfo *funcInfo, ParseNode *pnode)
 {
@@ -2827,7 +2827,12 @@ void ByteCodeGenerator::EmitDefaultArgs(FuncInfo *funcInfo, ParseNode *pnode)
             {
                 Assert(pnode1->nop == knopAsg);
                 Assert(pnode1->sxBin.pnode1->IsPattern());
-                EmitDestructuredValueOrInitializer(pnode1->sxBin.pnode1, pnodeArg->sxParamPattern.location, pnode1->sxBin.pnode2, this, funcInfo);
+                EmitDestructuredValueOrInitializer(pnode1->sxBin.pnode1,
+                    pnodeArg->sxParamPattern.location,
+                    pnode1->sxBin.pnode2,
+                    false /*isNonPatternAssignmentTarget*/,
+                    this,
+                    funcInfo);
             }
             this->EndStatement(pnodeArg);
             return;
@@ -6054,9 +6059,25 @@ void EmitDestructuredElement(ParseNode *elem, Js::RegSlot sourceLocation, ByteCo
     funcInfo->ReleaseReference(elem);
 }
 
-void EmitDestructuredRestArray(ParseNode *elem, Js::RegSlot iteratorLocation, Js::RegSlot shouldCallReturnFunctionLocation, ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
+void EmitDestructuredRestArray(ParseNode *elem,
+    Js::RegSlot iteratorLocation,
+    Js::RegSlot shouldCallReturnFunctionLocation,
+    Js::RegSlot shouldCallReturnFunctionLocationFinally,
+    ByteCodeGenerator *byteCodeGenerator,
+    FuncInfo *funcInfo)
 {
     Js::RegSlot restArrayLocation = funcInfo->AcquireTmpRegister();
+    bool isAssignmentTarget = !(elem->sxUni.pnode1->IsPattern() || elem->sxUni.pnode1->IsVarLetOrConst());
+
+    if (isAssignmentTarget)
+    {
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
+        EmitReference(elem->sxUni.pnode1, byteCodeGenerator, funcInfo);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
+    }
+
     byteCodeGenerator->Writer()->Reg1Unsigned1(
         Js::OpCode::NewScArray,
         restArrayLocation,
@@ -6089,6 +6110,7 @@ void EmitDestructuredRestArray(ParseNode *elem, Js::RegSlot iteratorLocation, Js
     EmitIteratorValue(valueLocation, itemLocation, byteCodeGenerator, funcInfo);
 
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
 
     byteCodeGenerator->Writer()->Element(
         ByteCodeGenerator::GetStElemIOpCode(funcInfo),
@@ -6100,6 +6122,7 @@ void EmitDestructuredRestArray(ParseNode *elem, Js::RegSlot iteratorLocation, Js
     byteCodeGenerator->Writer()->Reg2(Js::OpCode::Incr_A, counterLocation, counterLocation);
 
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
 
     byteCodeGenerator->Writer()->Br(loopTop);
 
@@ -6107,7 +6130,15 @@ void EmitDestructuredRestArray(ParseNode *elem, Js::RegSlot iteratorLocation, Js
     byteCodeGenerator->Writer()->MarkLabel(iteratorDone);
 
     ParseNode *restElem = elem->sxUni.pnode1;
-    EmitDestructuredElement(restElem, restArrayLocation, byteCodeGenerator, funcInfo);
+    if (isAssignmentTarget)
+    {
+        EmitAssignment(nullptr, restElem, restArrayLocation, byteCodeGenerator, funcInfo);
+        funcInfo->ReleaseReference(restElem);
+    }
+    else
+    {
+        EmitDestructuredElement(restElem, restArrayLocation, byteCodeGenerator, funcInfo);
+    }
 
     funcInfo->ReleaseTmpRegister(restArrayLocation);
 }
@@ -6157,6 +6188,7 @@ void EmitDestructuredArrayCore(
     ParseNode *list,
     Js::RegSlot iteratorLocation,
     Js::RegSlot shouldCallReturnFunctionLocation,
+    Js::RegSlot shouldCallReturnFunctionLocationFinally,
     ByteCodeGenerator *byteCodeGenerator,
     FuncInfo *funcInfo
     )
@@ -6202,7 +6234,17 @@ void EmitDestructuredArrayCore(
 
         byteCodeGenerator->StartStatement(elem);
 
+        bool isAssignmentTarget = !(elem->IsPattern() || elem->IsVarLetOrConst());
+
+        if (isAssignmentTarget)
+        {
+            byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
+            byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
+            EmitReference(elem, byteCodeGenerator, funcInfo);
+        }
+
         byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
 
         Js::RegSlot itemLocation = funcInfo->AcquireTmpRegister();
         EmitIteratorNext(itemLocation, iteratorLocation, Js::Constants::NoRegister, byteCodeGenerator, funcInfo);
@@ -6239,6 +6281,7 @@ void EmitDestructuredArrayCore(
         Js::ByteCodeLabel beforeDefaultAssign = byteCodeGenerator->Writer()->DefineLabel();
 
         byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocation);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdTrue, shouldCallReturnFunctionLocationFinally);
         byteCodeGenerator->Writer()->Br(beforeDefaultAssign);
 
         // iteratorAlreadyDone:
@@ -6324,10 +6367,11 @@ void EmitDestructuredArrayCore(
         }
         else
         {
-            EmitDestructuredValueOrInitializer(elem, valueLocation, init, byteCodeGenerator, funcInfo);
+            EmitDestructuredValueOrInitializer(elem, valueLocation, init, isAssignmentTarget, byteCodeGenerator, funcInfo);
         }
 
         byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
+        byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
 
         if (list->nop != knopList)
         {
@@ -6338,6 +6382,11 @@ void EmitDestructuredArrayCore(
         funcInfo->ReleaseTmpRegister(doneLocation);
         funcInfo->ReleaseTmpRegister(itemLocation);
 
+        if (isAssignmentTarget)
+        {
+            funcInfo->ReleaseReference(elem);
+        }
+
         byteCodeGenerator->EndStatement(elem);
 
         if (list->nop == knopList)
@@ -6353,7 +6402,12 @@ void EmitDestructuredArrayCore(
     // If we saw a rest element, emit the rest array.
     if (elem != nullptr && elem->nop == knopEllipsis)
     {
-        EmitDestructuredRestArray(elem, iteratorLocation, shouldCallReturnFunctionLocation, byteCodeGenerator, funcInfo);
+        EmitDestructuredRestArray(elem,
+            iteratorLocation,
+            shouldCallReturnFunctionLocation,
+            shouldCallReturnFunctionLocationFinally,
+            byteCodeGenerator,
+            funcInfo);
     }
 }
 
@@ -6400,21 +6454,71 @@ struct ByteCodeGenerator::TryScopeRecord : public JsUtil::DoublyLinkedListElemen
     TryScopeRecord(Js::OpCode op, Js::ByteCodeLabel label, Js::RegSlot r1, Js::RegSlot r2) : op(op), label(label), reg1(r1), reg2(r2) { }
 };
 
+// Generating
+// catch(e) {
+//      if (shouldCallReturn)
+//          CallReturnWhichWrappedByTryCatch
+//      throw e;
+// }
+void EmitTopLevelCatch(Js::ByteCodeLabel catchLabel,
+    Js::RegSlot iteratorLocation,
+    Js::RegSlot shouldCallReturnLocation,
+    Js::RegSlot shouldCallReturnLocationFinally,
+    ByteCodeGenerator *byteCodeGenerator,
+    FuncInfo *funcInfo)
+{
+    Js::ByteCodeLabel afterCatchBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
+    byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
+    byteCodeGenerator->Writer()->Br(afterCatchBlockLabel);
+    byteCodeGenerator->Writer()->MarkLabel(catchLabel);
+
+    Js::RegSlot catchParamLocation = funcInfo->AcquireTmpRegister();
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::Catch, catchParamLocation);
+
+    ByteCodeGenerator::TryScopeRecord tryRecForCatch(Js::OpCode::ResumeCatch, catchLabel);
+    if (funcInfo->byteCodeFunction->IsGenerator())
+    {
+        byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForCatch);
+    }
+
+    Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
+
+    byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnLocationFinally);
+    EmitTryCatchAroundClose(iteratorLocation, skipCallCloseLabel, byteCodeGenerator, funcInfo);
+
+    byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
+
+    // Rethrow the exception.
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::Throw, catchParamLocation);
+
+    funcInfo->ReleaseTmpRegister(catchParamLocation);
+
+    if (funcInfo->byteCodeFunction->IsGenerator())
+    {
+        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
+    }
+
+    byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
+    byteCodeGenerator->Writer()->MarkLabel(afterCatchBlockLabel);
+}
+
 // Generating
 // finally {
-//    if (hasReturnFunction)
-//        EmitTryCatchAroundClose
+//      if (shouldCallReturn)
+//          CallReturn
 // }
 
-void EmitFinallyForClose(
+void EmitTopLevelFinally(Js::ByteCodeLabel finallyLabel,
     Js::RegSlot iteratorLocation,
-    Js::RegSlot returnLocation,
+    Js::RegSlot shouldCallReturnLocation,
     Js::RegSlot yieldExceptionLocation,
     Js::RegSlot yieldOffsetLocation,
-    Js::ByteCodeLabel finallyLabel,
     ByteCodeGenerator *byteCodeGenerator,
     FuncInfo *funcInfo)
 {
+    bool isGenerator = funcInfo->byteCodeFunction->IsGenerator();
+
     Js::ByteCodeLabel afterFinallyBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
     byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
 
@@ -6424,19 +6528,18 @@ void EmitFinallyForClose(
     byteCodeGenerator->Writer()->Br(afterFinallyBlockLabel);
     byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
 
-    bool isGenerator = funcInfo->byteCodeFunction->IsGenerator();
     ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, yieldExceptionLocation, yieldOffsetLocation);
     if (isGenerator)
     {
         byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForFinally);
     }
 
-    Js::ByteCodeLabel skipCallTryCatchLabel = byteCodeGenerator->Writer()->DefineLabel();
+    Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
 
-    byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallTryCatchLabel, returnLocation);
-    EmitTryCatchAroundClose(iteratorLocation, skipCallTryCatchLabel, byteCodeGenerator, funcInfo);
+    byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
+    EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
 
-    byteCodeGenerator->Writer()->MarkLabel(skipCallTryCatchLabel);
+    byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
 
     if (isGenerator)
     {
@@ -6450,6 +6553,47 @@ void EmitFinallyForClose(
     byteCodeGenerator->Writer()->MarkLabel(afterFinallyBlockLabel);
 }
 
+void EmitCatchAndFinallyBlocks(Js::ByteCodeLabel catchLabel,
+    Js::ByteCodeLabel finallyLabel,
+    Js::RegSlot iteratorLocation,
+    Js::RegSlot shouldCallReturnFunctionLocation,
+    Js::RegSlot shouldCallReturnFunctionLocationFinally,
+    Js::RegSlot yieldExceptionLocation,
+    Js::RegSlot yieldOffsetLocation,
+    ByteCodeGenerator *byteCodeGenerator,
+    FuncInfo *funcInfo
+    )
+{
+    bool isGenerator = funcInfo->byteCodeFunction->IsGenerator();
+    if (isGenerator)
+    {
+        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
+    }
+
+    EmitTopLevelCatch(catchLabel,
+        iteratorLocation,
+        shouldCallReturnFunctionLocation,
+        shouldCallReturnFunctionLocationFinally,
+        byteCodeGenerator,
+        funcInfo);
+
+    if (isGenerator)
+    {
+        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
+    }
+
+    EmitTopLevelFinally(finallyLabel,
+        iteratorLocation,
+        shouldCallReturnFunctionLocationFinally,
+        yieldExceptionLocation,
+        yieldOffsetLocation,
+        byteCodeGenerator,
+        funcInfo);
+
+    funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocationFinally);
+    funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocation);
+}
+
 // Emit a wrapper try..finaly block around the destructuring elements
 void EmitDestructuredArray(
     ParseNode *lhs,
@@ -6478,7 +6622,9 @@ void EmitDestructuredArray(
     // This variable facilitates on when to call the return function (which is Iterator close). When we are emitting bytecode for destructuring element
     // this variable will be set to true.
     Js::RegSlot shouldCallReturnFunctionLocation = funcInfo->AcquireTmpRegister();
+    Js::RegSlot shouldCallReturnFunctionLocationFinally = funcInfo->AcquireTmpRegister();
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
+    byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
 
     byteCodeGenerator->SetHasFinally(true);
     byteCodeGenerator->SetHasTry(true);
@@ -6496,9 +6642,9 @@ void EmitDestructuredArray(
 
     // Insert try node here 
     Js::ByteCodeLabel finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
+    Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
     byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
 
-    //byteCodeGenerator->Writer()->Br(Js::OpCode::TryFinally, finallyLabel);
     ByteCodeGenerator::TryScopeRecord tryRecForTryFinally(Js::OpCode::TryFinallyWithYield, finallyLabel);
 
     if (isGenerator)
@@ -6513,22 +6659,31 @@ void EmitDestructuredArray(
         byteCodeGenerator->Writer()->Br(Js::OpCode::TryFinally, finallyLabel);
     }
 
-    EmitDestructuredArrayCore(list, iteratorLocation, shouldCallReturnFunctionLocation, byteCodeGenerator, funcInfo);
+    byteCodeGenerator->Writer()->Br(Js::OpCode::TryCatch, catchLabel);
 
+    ByteCodeGenerator::TryScopeRecord tryRecForTry(Js::OpCode::TryCatch, catchLabel);
     if (isGenerator)
     {
-        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
+        byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForTry);
     }
 
-    EmitFinallyForClose(iteratorLocation,
+    EmitDestructuredArrayCore(list,
+        iteratorLocation,
         shouldCallReturnFunctionLocation,
+        shouldCallReturnFunctionLocationFinally,
+        byteCodeGenerator,
+        funcInfo);
+
+    EmitCatchAndFinallyBlocks(catchLabel,
+        finallyLabel,
+        iteratorLocation,
+        shouldCallReturnFunctionLocation,
+        shouldCallReturnFunctionLocationFinally,
         regException,
         regOffset,
-        finallyLabel,
         byteCodeGenerator,
         funcInfo);
 
-    funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocation);
     funcInfo->ReleaseTmpRegister(iteratorLocation);
 
     byteCodeGenerator->EndStatement(lhs);
@@ -6563,6 +6718,7 @@ void EmitNameInvoke(Js::RegSlot lhsLocation,
 void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode,
     Js::RegSlot rhsLocation,
     ParseNodePtr initializer,
+    bool isNonPatternAssignmentTarget,
     ByteCodeGenerator *byteCodeGenerator,
     FuncInfo *funcInfo)
 {
@@ -6600,6 +6756,10 @@ void EmitDestructuredValueOrInitializer(ParseNodePtr lhsElementNode,
     {
         EmitDestructuredObject(lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
     }
+    else if (isNonPatternAssignmentTarget)
+    {
+        EmitAssignment(nullptr, lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
+    }
     else
     {
         EmitDestructuredElement(lhsElementNode, rhsLocationTmp, byteCodeGenerator, funcInfo);
@@ -6636,7 +6796,7 @@ void EmitDestructuredObjectMember(ParseNodePtr memberNode,
         lhsElementNode = lhsElementNode->sxBin.pnode1;
     }
 
-    EmitDestructuredValueOrInitializer(lhsElementNode, nameLocation, init, byteCodeGenerator, funcInfo);
+    EmitDestructuredValueOrInitializer(lhsElementNode, nameLocation, init, false /*isNonPatternAssignmentTarget*/, byteCodeGenerator, funcInfo);
 
     funcInfo->ReleaseTmpRegister(nameLocation);
 }
@@ -8793,105 +8953,6 @@ void EmitIteratorValue(Js::RegSlot valueLocation, Js::RegSlot iteratorResultLoca
     byteCodeGenerator->Writer()->PatchableProperty(Js::OpCode::LdFld, valueLocation, iteratorResultLocation, cacheId);
 }
 
-// Generating
-// catch(e) {
-//      if (shouldCallReturn)
-//          CallReturnWhichWrappedByTryCatch
-//      throw e;
-// }
-void EmitTopLevelCatchForOf(Js::ByteCodeLabel catchLabel,
-    Js::RegSlot iteratorLocation,
-    Js::RegSlot shouldCallReturnLocation,
-    Js::RegSlot shouldCallReturnLocationFinally,
-    ByteCodeGenerator *byteCodeGenerator,
-    FuncInfo *funcInfo)
-{
-    Js::ByteCodeLabel afterCatchBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
-    byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
-    byteCodeGenerator->Writer()->Br(afterCatchBlockLabel);
-    byteCodeGenerator->Writer()->MarkLabel(catchLabel);
-
-    Js::RegSlot catchParamLocation = funcInfo->AcquireTmpRegister();
-    byteCodeGenerator->Writer()->Reg1(Js::OpCode::Catch, catchParamLocation);
-
-    ByteCodeGenerator::TryScopeRecord tryRecForCatch(Js::OpCode::ResumeCatch, catchLabel);
-    if (funcInfo->byteCodeFunction->IsGenerator())
-    {
-        byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForCatch);
-    }
-
-    Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
-
-    byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
-    byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnLocationFinally);
-    EmitTryCatchAroundClose(iteratorLocation, skipCallCloseLabel, byteCodeGenerator, funcInfo);
-
-    byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
-
-    // Rethrow the exception.
-    byteCodeGenerator->Writer()->Reg1(Js::OpCode::Throw, catchParamLocation);
-
-    funcInfo->ReleaseTmpRegister(catchParamLocation);
-
-    if (funcInfo->byteCodeFunction->IsGenerator())
-    {
-        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
-    }
-
-    byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
-    byteCodeGenerator->Writer()->MarkLabel(afterCatchBlockLabel);
-}
-
-// Generating
-// finally {
-//      if (shouldCallReturn)
-//          CallReturn
-// }
-
-void EmitTopLevelFinallyForOf(Js::ByteCodeLabel finallyLabel,
-    Js::RegSlot iteratorLocation,
-    Js::RegSlot shouldCallReturnLocation,
-    Js::RegSlot yieldExceptionLocation,
-    Js::RegSlot yieldOffsetLocation,
-    ByteCodeGenerator *byteCodeGenerator,
-    FuncInfo *funcInfo)
-{
-    bool isGenerator = funcInfo->byteCodeFunction->IsGenerator();
-
-    Js::ByteCodeLabel afterFinallyBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
-    byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
-
-    byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
-    byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
-
-    byteCodeGenerator->Writer()->Br(afterFinallyBlockLabel);
-    byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
-
-    ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, yieldExceptionLocation, yieldOffsetLocation);
-    if (isGenerator)
-    {
-        byteCodeGenerator->tryScopeRecordsList.LinkToEnd(&tryRecForFinally);
-    }
-
-    Js::ByteCodeLabel skipCallCloseLabel = byteCodeGenerator->Writer()->DefineLabel();
-
-    byteCodeGenerator->Writer()->BrReg1(Js::OpCode::BrFalse_A, skipCallCloseLabel, shouldCallReturnLocation);
-    EmitIteratorClose(iteratorLocation, byteCodeGenerator, funcInfo);
-
-    byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
-
-    if (isGenerator)
-    {
-        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
-        funcInfo->ReleaseTmpRegister(yieldOffsetLocation);
-        funcInfo->ReleaseTmpRegister(yieldExceptionLocation);
-    }
-
-    byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(false);
-    byteCodeGenerator->Writer()->Empty(Js::OpCode::LeaveNull);
-    byteCodeGenerator->Writer()->MarkLabel(afterFinallyBlockLabel);
-}
-
 void EmitForInOfLoopBody(ParseNode *loopNode,
     Js::ByteCodeLabel loopEntrance,
     Js::ByteCodeLabel continuePastLoop,
@@ -9146,34 +9207,16 @@ void EmitForInOrForOf(ParseNode *loopNode, ByteCodeGenerator *byteCodeGenerator,
 
     byteCodeGenerator->Writer()->ExitLoop(loopId);
 
-    if (isGenerator)
-    {
-        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
-    }
-
-    EmitTopLevelCatchForOf(catchLabel,
+    EmitCatchAndFinallyBlocks(catchLabel,
+        finallyLabel,
         loopNode->location,
         shouldCallReturnFunctionLocation,
         shouldCallReturnFunctionLocationFinally,
-        byteCodeGenerator,
-        funcInfo);
-
-    if (isGenerator)
-    {
-        byteCodeGenerator->tryScopeRecordsList.UnlinkFromEnd();
-    }
-
-    EmitTopLevelFinallyForOf(finallyLabel,
-        loopNode->location,
-        shouldCallReturnFunctionLocationFinally,
         regException,
         regOffset,
         byteCodeGenerator,
         funcInfo);
 
-    funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocationFinally);
-    funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocation);
-
     byteCodeGenerator->Writer()->MarkLabel(skipPastLoop);
 
     if (!byteCodeGenerator->IsES6ForLoopSemanticsEnabled())

+ 2 - 0
test/es6/destructuring.js

@@ -128,6 +128,8 @@ var tests = [
       assert.throws(function () { eval("const [...a, b] = [];"); },       SyntaxError, "Destructured const array declaration with rest parameter in non-last position throws", "Destructuring rest variables must be in the last position of the expression");
       assert.throws(function () { eval("var a, b; [...a, b] = [];"); },   SyntaxError, "Destructured var array assignment with rest parameter in non-last position throws",    "Destructuring rest variables must be in the last position of the expression");
       assert.throws(function () { eval("let a, b; [...a, b] = [];"); },   SyntaxError, "Destructured let array assignment with rest parameter in non-last position throws",    "Destructuring rest variables must be in the last position of the expression");
+      assert.throws(function () { eval("let [...a,] = [];"); },           SyntaxError, "Destructured array declaration has comma after rest parameter throws",                 "Destructuring rest variables must be in the last position of the expression");
+      assert.throws(function () { eval("let a; [...a,] = [];"); },        SyntaxError, "Destructured array assignment has comma after rest parameter throws",                  "Destructuring rest variables must be in the last position of the expression");
 
       // Default values
       assert.doesNotThrow(function () { eval("var [a = 1] = [];"); },    "Destructured var array declaration with all default values does not throw");

+ 94 - 0
test/es6/iteratorclose.js

@@ -397,6 +397,100 @@ var tests = [
             assert.areEqual(returnCalled, 1, "Destructuring elements exhaust and that should call return function");
 		}
 	},    
+    {
+		name : "Destructuring - yield in the pattern and inner return throws",
+		body : function () {
+            function *innerGen () { yield undefined; }
+
+            innerGen.prototype.return = function () {
+                throw new Error('Exception from return function');
+            };
+            
+            var x;
+
+            var iter = (function * () {
+                ([x = yield] = innerGen());
+                assert.fail('Unreachable code');
+            }());
+
+            iter.next();
+            assert.throws(function () {
+                iter.return();
+            }, Error, "calling return on the outer generator will call return on inner generator", 'Exception from return function');
+		}
+	},
+    {
+		name : "Destructuring - yield in the pattern and both caller and inner return throws",
+		body : function () {
+            function *innerGen () { yield undefined;}
+
+            var returnCalled = 0;
+            innerGen.prototype.return = function () {
+                returnCalled++;
+                throw new Error('Exception from return function');
+            };
+            
+            var x;
+
+            var iter = (function * () {
+                ([x = yield] = innerGen());
+                assert.fail('Unreachable code');
+            }());
+
+            iter.next();
+            assert.throws(function () {
+                iter.throw(new Error('Exception from outer throw'));
+            }, Error, "calling throw on the outer generator will call return on inner generator but outer exxception wins", 'Exception from outer throw');
+
+            assert.areEqual(returnCalled, 1, "ensuring that return function is called");
+		}
+	},
+    {
+		name : "Destructuring - .return will be called even before .next function is called if yield as return",
+		body : function () {
+            function *innerGen () { assert.fail('innerGen body is not executed');}
+
+            var returnCalled = 0;
+            innerGen.prototype.return = function () {
+                returnCalled++;
+                return {};
+            };
+            
+            var x;
+
+            var iter = (function * () {
+                ([{}[yield]] = innerGen());
+                assert.fail('Unreachable code');
+            }());
+
+            iter.next();
+            iter.return();
+            assert.areEqual(returnCalled, 1, "ensuring that return function is called");
+		}
+	},
+    {
+		name : "Destructuring - with rest - .return will be called even before .next function is called if yield as return",
+		body : function () {
+            function *innerGen () { assert.fail('innerGen body is not executed');}
+
+            var returnCalled = 0;
+            innerGen.prototype.return = function () {
+                returnCalled++;
+                return {};
+            };
+            
+            var x;
+
+            var iter = (function * () {
+                ([...{}[yield]] = innerGen());
+                assert.fail('Unreachable code');
+            }());
+
+            iter.next();
+            iter.return();
+            assert.areEqual(returnCalled, 1, "ensuring that return function is called");
+		}
+	},
     {
 		name : "For..of - validation of calling .return function on abrupt loop break",
 		body : function () {