Skip to content

Commit 9468f60

Browse files
natebiggsCommit Queue
authored andcommitted
[dart2js, ddc] Fix nested exception handling.
In the flattened control flow state machine generated for dart2js and ddc async functions, there is a single error state variable per function. However, functions can have nested error handling that may try to access stacked errors separately. When we enter a nested try/catch we should avoid clobbering the outer exception as the outer handler may need it later. To fix this we maintain a stack of errors that we 'push' to when we enter a new try/catch and which we 'pop' from at the end of executing that try/catch. Fixes: #57046 Change-Id: Ib96a44ab4152f6f9e444f2ee959ec9aa252800f9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395580 Reviewed-by: Mayank Patke <[email protected]> Commit-Queue: Nate Biggs <[email protected]>
1 parent f27e78a commit 9468f60

File tree

4 files changed

+110
-62
lines changed

4 files changed

+110
-62
lines changed

pkg/compiler/lib/src/js/rewrite_async.dart

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,12 @@ abstract class AsyncRewriterBase extends js.NodeVisitor<Object?> {
139139
js.VariableUse get returnValue => js.VariableUse(returnValueName);
140140
late final String returnValueName;
141141

142-
/// Stores the current error when we are in the process of handling an error.
143-
js.VariableUse get currentError => js.VariableUse(currentErrorName);
144-
late final String currentErrorName;
142+
/// Stores a stack of the current set of errors when we are in the process of
143+
/// handling an error. Errors are pushed onto this stack when error handling
144+
/// begins and the current error is popped off when error handling ends. This
145+
/// prevents nested error handling from overwriting state.
146+
js.VariableUse get errorStack => js.VariableUse(errorStackName);
147+
late final String errorStackName;
145148

146149
/// The label of the outer loop.
147150
///
@@ -199,7 +202,7 @@ abstract class AsyncRewriterBase extends js.NodeVisitor<Object?> {
199202
handlerName = freshName("handler");
200203
nextName = freshName("next");
201204
returnValueName = freshName("returnValue");
202-
currentErrorName = freshName("currentError");
205+
errorStackName = freshName("errorStack");
203206
outerLabelName = freshName("outer");
204207
selfName = freshName("self");
205208
// Initialize names specific to the subclass.
@@ -742,8 +745,8 @@ abstract class AsyncRewriterBase extends js.NodeVisitor<Object?> {
742745
if (hasHandlerLabels) {
743746
variables.add(_makeVariableInitializer(
744747
handler, js.number(rethrowLabel), bodySourceInformation));
745-
variables.add(
746-
_makeVariableInitializer(currentError, null, bodySourceInformation));
748+
variables.add(_makeVariableInitializer(
749+
errorStack, js.ArrayInitializer(const []), bodySourceInformation));
747750
}
748751
if (analysis.hasFinally || (isAsyncStar && analysis.hasYield)) {
749752
variables.add(_makeVariableInitializer(
@@ -1615,7 +1618,8 @@ abstract class AsyncRewriterBase extends js.NodeVisitor<Object?> {
16151618
String errorRename = freshName(catchPart.declaration.name);
16161619
localVariables.add(js.VariableDeclaration(errorRename));
16171620
variableRenamings.add((catchPart.declaration.name, errorRename));
1618-
addStatement(js.js.statement("# = #;", [errorRename, currentError]));
1621+
addStatement(
1622+
js.js.statement("# = #.pop();", [errorRename, errorStack]));
16191623
visitStatement(catchPart.body);
16201624
variableRenamings.removeLast();
16211625
if (finallyPart != null) {
@@ -1837,10 +1841,10 @@ class AsyncRewriter extends AsyncRewriterBase {
18371841
void addErrorExit(js.JavaScriptNodeSourceInformation? sourceInformation) {
18381842
if (!hasHandlerLabels) return; // rethrow handled in method boilerplate.
18391843
beginLabel(rethrowLabel);
1840-
js.Expression thenHelperCall = js.js(
1841-
"#thenHelper(#currentError, #completer)", {
1844+
js.Expression thenHelperCall =
1845+
js.js("#thenHelper(#errorStack.at(-1), #completer)", {
18421846
"thenHelper": asyncRethrow,
1843-
"currentError": currentError,
1847+
"errorStack": errorStack,
18441848
"completer": completer
18451849
}).withSourceInformation(sourceInformation);
18461850
addStatement(
@@ -1917,12 +1921,12 @@ class AsyncRewriter extends AsyncRewriterBase {
19171921
if (hasHandlerLabels) {
19181922
errorCheck = js.js.statement("""
19191923
if (#errorCode === #ERROR) {
1920-
#currentError = #result;
1924+
#errorStack.push(#result);
19211925
#goto = #handler;
19221926
}""", {
19231927
"errorCode": errorCodeName,
19241928
"ERROR": js.number(status_codes.ERROR),
1925-
"currentError": currentError,
1929+
"errorStack": errorStack,
19261930
"result": resultName,
19271931
"goto": goto,
19281932
"handler": handler,
@@ -2066,22 +2070,22 @@ class SyncStarRewriter extends AsyncRewriterBase {
20662070
js.VariableDeclarationList copyParameters =
20672071
js.VariableDeclarationList(declarations);
20682072

2069-
js.Expression setCurrentError = js.js("#currentError = #result", {
2073+
js.Expression pushError = js.js("#errorStack.push(#result)", {
20702074
"result": resultName,
2071-
"currentError": currentErrorName,
2075+
"errorStack": errorStackName,
20722076
}).withSourceInformation(bodySourceInformation);
20732077
js.Expression setGoto = js.js("#goto = #handler", {
20742078
"goto": goto,
20752079
"handler": handler,
20762080
}).withSourceInformation(bodySourceInformation);
20772081
js.Statement checkErrorCode = js.js.statement("""
20782082
if (#errorCode === #ERROR) {
2079-
#setCurrentError;
2083+
#pushError;
20802084
#setGoto;
20812085
}""", {
20822086
"errorCode": errorCodeName,
20832087
"ERROR": js.number(status_codes.ERROR),
2084-
"setCurrentError": setCurrentError,
2088+
"pushError": pushError,
20852089
"setGoto": setGoto,
20862090
}).withSourceInformation(bodySourceInformation);
20872091
js.Expression innerInnerFunction = js.js("""
@@ -2140,7 +2144,7 @@ class SyncStarRewriter extends AsyncRewriterBase {
21402144
// SYNC_STAR_UNCAUGHT_EXCEPTION status code.
21412145
final store = js.Assignment(
21422146
js.PropertyAccess(js.VariableUse(iteratorName), iteratorDatumProperty),
2143-
currentError);
2147+
js.js('#.at(-1)', [errorStack]));
21442148
addStatement(js.Return(js.Binary(
21452149
',', store, js.number(status_codes.SYNC_STAR_UNCAUGHT_EXCEPTION)))
21462150
.withSourceInformation(sourceInformation));
@@ -2303,8 +2307,8 @@ class AsyncStarRewriter extends AsyncRewriterBase {
23032307
"goto": goto,
23042308
"callPop": callPop,
23052309
}).withSourceInformation(bodySourceInformation);
2306-
js.Expression updateError = js.js("#currentError = #result", {
2307-
"currentError": currentError,
2310+
js.Expression pushError = js.js("#errorStack.push(#result)", {
2311+
"errorStack": errorStack,
23082312
"result": resultName,
23092313
}).withSourceInformation(bodySourceInformation);
23102314
js.Expression gotoError = js.js("#goto = #handler", {
@@ -2320,7 +2324,7 @@ class AsyncStarRewriter extends AsyncRewriterBase {
23202324
#gotoCancelled;
23212325
#break;
23222326
case #ERROR:
2323-
#updateError;
2327+
#pushError;
23242328
#gotoError;
23252329
}""", {
23262330
"errorCode": errorCodeName,
@@ -2329,17 +2333,17 @@ class AsyncStarRewriter extends AsyncRewriterBase {
23292333
"gotoCancelled": gotoCancelled,
23302334
"break": breakStatement,
23312335
"ERROR": js.number(status_codes.ERROR),
2332-
"updateError": updateError,
2336+
"pushError": pushError,
23332337
"gotoError": gotoError,
23342338
}).withSourceInformation(bodySourceInformation);
23352339
js.Statement ifError = js.js.statement("""
23362340
if (#errorCode === #ERROR) {
2337-
#updateError;
2341+
#pushError;
23382342
#gotoError;
23392343
}""", {
23402344
"errorCode": errorCodeName,
23412345
"ERROR": js.number(status_codes.ERROR),
2342-
"updateError": updateError,
2346+
"pushError": pushError,
23432347
"gotoError": gotoError,
23442348
}).withSourceInformation(bodySourceInformation);
23452349
js.Statement ifHasYield = js.js.statement("""
@@ -2399,10 +2403,10 @@ class AsyncStarRewriter extends AsyncRewriterBase {
23992403
hasHandlerLabels = true;
24002404
beginLabel(rethrowLabel);
24012405
js.Expression asyncHelperCall =
2402-
js.js("#asyncHelper(#currentError, #errorCode, #controller)", {
2406+
js.js("#asyncHelper(#errorStack.at(-1), #errorCode, #controller)", {
24032407
"asyncHelper": asyncStarHelper,
24042408
"errorCode": js.number(status_codes.ERROR),
2405-
"currentError": currentError,
2409+
"errorStack": errorStack,
24062410
"controller": controllerName
24072411
}).withSourceInformation(sourceInformation);
24082412
addStatement(

pkg/compiler/test/async_await/async_await_js_transform_test.dart

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,10 @@ function(a) {
154154
function(b) {
155155
var __goto = 0,
156156
__completer = NewCompleter(CompleterType),
157-
__returnValue, __handler = 2, __currentError, __next = [], __helper;
157+
__returnValue, __handler = 2, __errorStack = [], __next = [], __helper;
158158
var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
159159
if (__errorCode === 1) {
160-
__currentError = __result;
160+
__errorStack.push(__result);
161161
__goto = __handler;
162162
}
163163
while (true)
@@ -247,7 +247,7 @@ function(b) {
247247
return returnHelper(__returnValue, __completer);
248248
case 2:
249249
// rethrow
250-
return rethrowHelper(__currentError, __completer);
250+
return rethrowHelper(__errorStack.at(-1), __completer);
251251
}
252252
});
253253
return startHelper(body, __completer);
@@ -764,10 +764,10 @@ function(c, i) async {
764764
function(c, i) {
765765
var __goto = 0,
766766
__completer = NewCompleter(CompleterType),
767-
__handler = 1, __currentError, __next = [], x, y, __error, __error1;
767+
__handler = 1, __errorStack = [], __next = [], x, y, __error, __error1;
768768
var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
769769
if (__errorCode === 1) {
770-
__currentError = __result;
770+
__errorStack.push(__result);
771771
__goto = __handler;
772772
}
773773
while (true)
@@ -800,7 +800,7 @@ function(c, i) {
800800
case 3:
801801
// catch
802802
__handler = 2;
803-
__error = __currentError;
803+
__error = __errorStack.pop();
804804
__handler = 11;
805805
__goto = c ? 14 : 16;
806806
break;
@@ -826,7 +826,7 @@ function(c, i) {
826826
case 11:
827827
// catch
828828
__handler = 10;
829-
__error1 = __currentError;
829+
__error1 = __errorStack.pop();
830830
y.x = foo(__error1);
831831
__next.push(13);
832832
// goto finally
@@ -858,7 +858,7 @@ function(c, i) {
858858
return returnHelper(null, __completer);
859859
case 1:
860860
// rethrow
861-
return rethrowHelper(__currentError, __completer);
861+
return rethrowHelper(__errorStack.at(-1), __completer);
862862
}
863863
});
864864
return startHelper(body, __completer);
@@ -1225,10 +1225,10 @@ function(b, l) {
12251225
function(m) {
12261226
var __goto = 0,
12271227
__completer = NewCompleter(CompleterType),
1228-
__handler = 1, __currentError, exception, __exception;
1228+
__handler = 1, __errorStack = [], exception, __exception;
12291229
var body = _wrapJsFunctionForAsync(function(__errorCode, __result) {
12301230
if (__errorCode === 1) {
1231-
__currentError = __result;
1231+
__errorStack.push(__result);
12321232
__goto = __handler;
12331233
}
12341234
while (true)
@@ -1249,7 +1249,7 @@ function(m) {
12491249
case 3:
12501250
// catch
12511251
__handler = 2;
1252-
__exception = __currentError;
1252+
__exception = __errorStack.pop();
12531253
__goto = 7;
12541254
return awaitHelper(10, body);
12551255
case 7:
@@ -1280,7 +1280,7 @@ function(m) {
12801280
return returnHelper(null, __completer);
12811281
case 1:
12821282
// rethrow
1283-
return rethrowHelper(__currentError, __completer);
1283+
return rethrowHelper(__errorStack.at(-1), __completer);
12841284
}
12851285
});
12861286
return startHelper(body, __completer);
@@ -1295,10 +1295,10 @@ function(a) sync* {
12951295
function(__a) {
12961296
return function() {
12971297
var a = __a;
1298-
var __goto = 0, __handler = 2, __currentError;
1298+
var __goto = 0, __handler = 2, __errorStack = [];
12991299
return function body(__iterator, __errorCode, __result) {
13001300
if (__errorCode === 1) {
1301-
__currentError = __result;
1301+
__errorStack.push(__result);
13021302
__goto = __handler;
13031303
}
13041304
while (true)
@@ -1314,7 +1314,7 @@ function(__a) {
13141314
return 0;
13151315
case 2:
13161316
// rethrow
1317-
return __iterator._datum = __currentError, 3;
1317+
return __iterator._datum = __errorStack.at(-1), 3;
13181318
}
13191319
};
13201320
};

0 commit comments

Comments
 (0)