Skip to content

Commit

Permalink
Fixing stack balance under generator/yield
Browse files Browse the repository at this point in the history
We were caching and restoring the sp in the processtryfinally in order to balance the stack when we are in the middle of call. However in the case of generator we can yield out of the call. In that case we actually haven't finished the try part.
So once we comeback after yield we don't need to cache the stack again. Introduced the flag to capture such state and fix the problem.
  • Loading branch information
akroshg committed Aug 26, 2016
1 parent cd950a7 commit 5cd9eed
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 3 deletions.
10 changes: 7 additions & 3 deletions lib/Runtime/Language/InterpreterStackFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ namespace Js
newInstance->m_flags = InterpreterStackFrameFlags_None;
newInstance->closureInitDone = false;
newInstance->isParamScopeDone = false;
newInstance->shouldCacheSP = true;
#if ENABLE_PROFILE_INFO
newInstance->switchProfileMode = false;
newInstance->isAutoProfiling = false;
Expand Down Expand Up @@ -6744,7 +6745,10 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
// mark the stackFrame as 'in try block'
this->m_flags |= InterpreterStackFrameFlags_WithinTryBlock;

CacheSp();
if (shouldCacheSP)
{
CacheSp();
}

if (this->IsInDebugMode())
{
Expand Down Expand Up @@ -6772,10 +6776,10 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
this->m_flags &= ~InterpreterStackFrameFlags_WithinTryBlock;
}

shouldCacheSP = !skipFinallyBlock;

if (skipFinallyBlock)
{
RestoreSp();

// A leave occurred due to a yield
return;
}
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Language/InterpreterStackFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ namespace Js

bool closureInitDone : 1;
bool isParamScopeDone : 1;
bool shouldCacheSP : 1; // Helps in determining if we need to cache the sp in ProcessTryFinally
#if ENABLE_PROFILE_INFO
bool switchProfileMode : 1;
bool isAutoProfiling : 1;
Expand Down
41 changes: 41 additions & 0 deletions test/es6/iteratorclose.js
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,47 @@ var tests = [
});
}
},
{
name : "BugFix : yielding in the call expression under generator function",
body : function () {
var val = 0;
function bar(a, b, c) { val = b; }
function *foo(d) {
for (var k of [2, 3]) {
bar(1, d ? yield : d, k);
}
}
var iter = foo(true);
iter.next();
iter.next();
iter.next(10);
assert.areEqual(val, 10, "yielding in the call expression under for..of is working correctly");
}
},
{
name : "BugFix : yielding in the call expression under try catch",
body : function () {
var val = 0;
var counter = 0;
function bar(a, b, c) { val = b; }
function *foo(d) {
try {
try {
bar(1, d ? yield : d, 11);
} finally {
counter++;
}
} finally {
counter++;
}
}
var iter = foo(true);
iter.next();
iter.next(10);
assert.areEqual(val, 10, "yielding in the call expression under try/catch is working correctly");
assert.areEqual(counter, 2, "both finally called after yielding");
}
},
];

testRunner.runTests(tests, {
Expand Down

0 comments on commit 5cd9eed

Please sign in to comment.