Skip to content

Commit a09538f

Browse files
committed
OpCode for Finally inside Generator or Async function
Was not implemented in the JIT, resulting in Abort when jitting a Generator or async function (or a loop inside a generator or async function) that contained a finally block. Note the for...of construction includes an implicit finally block hence triggered this error. Add handling in FlowGraph and Lowerer as well as simple test case.
1 parent c6a9549 commit a09538f

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

lib/Backend/FlowGraph.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//-------------------------------------------------------------------------------------------------------
22
// Copyright (C) Microsoft. All rights reserved.
3+
// Copyright (c) ChakraCore Project Contributors. All rights reserved.
34
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
45
//-------------------------------------------------------------------------------------------------------
56
#include "Backend.h"
@@ -339,6 +340,7 @@ FlowGraph::Build(void)
339340
break;
340341

341342
case Js::OpCode::TryFinally:
343+
case Js::OpCode::TryFinallyWithYield:
342344
if (this->finallyLabelStack)
343345
{
344346
AssertOrFailFast(!this->finallyLabelStack->Empty());
@@ -1778,6 +1780,7 @@ FlowGraph::Destroy(void)
17781780
{
17791781
case Js::OpCode::TryCatch:
17801782
case Js::OpCode::TryFinally:
1783+
case Js::OpCode::TryFinallyWithYield:
17811784
AssertMsg(region->GetParent() == predRegion, "Bad region prop on entry to try-catch/finally");
17821785
if (intermediateBlock->GetFirstInstr() == predBlock->GetLastInstr()->AsBranchInstr()->GetTarget())
17831786
{
@@ -2036,6 +2039,7 @@ FlowGraph::PropagateRegionFromPred(BasicBlock * block, BasicBlock * predBlock, R
20362039
break;
20372040

20382041
case Js::OpCode::TryFinally:
2042+
case Js::OpCode::TryFinallyWithYield:
20392043
// Entry to a try-finally. See whether we're entering the try or the finally
20402044
// by looking for the handler label.
20412045
Assert(predLastInstr->m_next->IsLabelInstr());

lib/Backend/Lower.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,6 +2605,8 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
26052605
instrPrev = this->LowerTry(instr, true /*try-catch*/);
26062606
break;
26072607

2608+
case Js::OpCode::TryFinallyWithYield:
2609+
instr->m_opcode = Js::OpCode::TryFinally;
26082610
case Js::OpCode::TryFinally:
26092611
instrPrev = this->LowerTry(instr, false /*try-finally*/);
26102612
break;

test/es6GeneratorJit/async-jit-bugs.js

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
55
//-------------------------------------------------------------------------------------------------------
66

7-
function main() {
7+
function testOne() {
88
const v2 = [13.37,13.37,13.37,13.37,13.37];
99
async function v4(v5,v6,v7,v8) {
1010
const v10 = 0;
@@ -26,8 +26,68 @@ function main() {
2626
const v38 = --v33;
2727
}
2828
const v39 = 128;
29-
print("pass")
3029
}
31-
v4("vEBD7ei78q");
30+
return v4("vEBD7ei78q");
3231
}
33-
main();
32+
33+
function testTwo() {
34+
let finallyCount = 0;
35+
let throwCount = 0;
36+
async function asyncFinally() {
37+
for (let i = 0; i < 1000; ++i){
38+
try {
39+
if (i > 170) {
40+
++throwCount;
41+
throw 1;
42+
}
43+
}
44+
finally {
45+
++finallyCount;
46+
}
47+
}
48+
}
49+
return asyncFinally ().catch((e) => {
50+
if (throwCount != 1) {
51+
throw new Error ("Wrong number of throws within async function expected 1 but received " + throwCount);
52+
}
53+
if (e != 1) {
54+
throw new Error ("Wrong value thrown from async function expected 1 but received " + e);
55+
}
56+
if (finallyCount != 172) {
57+
throw new Error ("Wrong number of finally calls from async function expected 172 but received " + finallyCount);
58+
}
59+
});
60+
}
61+
62+
function testThree() {
63+
let finallyCount = 0;
64+
let throwCount = 0;
65+
async function asyncFinallyAwait() {
66+
for (let i = 0; i < 1000; ++i){
67+
try {
68+
if (i > 170) {
69+
++throwCount;
70+
throw 1;
71+
}
72+
}
73+
finally {
74+
await 5;
75+
++finallyCount;
76+
}
77+
}
78+
}
79+
return asyncFinallyAwait().catch((e) => {
80+
if (throwCount != 1) {
81+
throw new Error ("Wrong number of throws within async function expected 1 but received " + throwCount);
82+
}
83+
if (e != 1) {
84+
throw new Error ("Wrong value thrown from async function expected 1 but received " + e);
85+
}
86+
if (finallyCount != 172) {
87+
throw new Error ("Wrong number of finally calls from async function expected 172 but received " + finallyCount);
88+
}
89+
});
90+
}
91+
92+
93+
Promise.all([testOne(), testTwo(), testThree()]).then(()=>{print("pass")}, (e)=>{print (e)});

0 commit comments

Comments
 (0)