[vm/bytecode] Support --causal_async_stacks in bytecode generator
Also, standalone_2/causal_async_stack_test is updated to actually
test --causal_async_stacks after Dart 2 sync-async.
Change-Id: I28a7a281963828707461652f19494ff54bdd21c8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106760
Commit-Queue: Alexander Markov <alexmarkov@google.com>
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/pkg/kernel/lib/transformations/continuation.dart b/pkg/kernel/lib/transformations/continuation.dart
index b599cdf..8cb5c3f 100644
--- a/pkg/kernel/lib/transformations/continuation.dart
+++ b/pkg/kernel/lib/transformations/continuation.dart
@@ -15,6 +15,7 @@
class ContinuationVariables {
static const awaitJumpVar = ':await_jump_var';
static const awaitContextVar = ':await_ctx_var';
+ static const asyncStackTraceVar = ':async_stack_trace';
static const exceptionParam = ':exception';
static const stackTraceParam = ':stack_trace';
@@ -270,7 +271,7 @@
final VariableDeclaration nestedClosureVariable =
new VariableDeclaration(":async_op");
final VariableDeclaration stackTraceVariable =
- new VariableDeclaration(":async_stack_trace");
+ new VariableDeclaration(ContinuationVariables.asyncStackTraceVar);
final VariableDeclaration thenContinuationVariable =
new VariableDeclaration(":async_op_then");
final VariableDeclaration catchErrorContinuationVariable =
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index d79d963..1e80cc7 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -59,6 +59,7 @@
void generateBytecode(
ast.Component component, {
bool enableAsserts: true,
+ bool causalAsyncStacks,
bool emitSourcePositions: false,
bool emitSourceFiles: false,
bool emitLocalVarInfo: false,
@@ -79,6 +80,8 @@
final constantsBackend = new VmConstantsBackend(coreTypes);
final errorReporter = new ForwardConstantEvaluationErrors();
libraries ??= component.libraries;
+ causalAsyncStacks ??=
+ environmentDefines['dart.developer.causal_async_stacks'] == 'true';
try {
final bytecodeGenerator = new BytecodeGenerator(
component,
@@ -88,6 +91,7 @@
constantsBackend,
environmentDefines,
enableAsserts,
+ causalAsyncStacks,
emitSourcePositions,
emitSourceFiles,
emitLocalVarInfo,
@@ -112,6 +116,7 @@
final ConstantsBackend constantsBackend;
final Map<String, String> environmentDefines;
final bool enableAsserts;
+ final bool causalAsyncStacks;
final bool emitSourcePositions;
final bool emitSourceFiles;
final bool emitLocalVarInfo;
@@ -167,6 +172,7 @@
this.constantsBackend,
this.environmentDefines,
this.enableAsserts,
+ this.causalAsyncStacks,
this.emitSourcePositions,
this.emitSourceFiles,
this.emitLocalVarInfo,
@@ -852,6 +858,15 @@
_asyncAwaitCompleterGetFuture ??= libraryIndex.getMember(
'dart:async', '_AsyncAwaitCompleter', 'get:future');
+ Procedure _setAsyncThreadStackTrace;
+ Procedure get setAsyncThreadStackTrace => _setAsyncThreadStackTrace ??=
+ libraryIndex.getTopLevelMember('dart:async', '_setAsyncThreadStackTrace');
+
+ Procedure _clearAsyncThreadStackTrace;
+ Procedure get clearAsyncThreadStackTrace =>
+ _clearAsyncThreadStackTrace ??= libraryIndex.getTopLevelMember(
+ 'dart:async', '_clearAsyncThreadStackTrace');
+
Library _dartFfiLibrary;
Library get dartFfiLibrary =>
_dartFfiLibrary ??= libraryIndex.tryGetLibrary('dart:ffi');
@@ -976,6 +991,15 @@
}
void _genReturnTOS() {
+ if (causalAsyncStacks &&
+ parentFunction != null &&
+ (parentFunction.dartAsyncMarker == AsyncMarker.Async ||
+ parentFunction.dartAsyncMarker == AsyncMarker.AsyncStar)) {
+ _genDirectCall(
+ clearAsyncThreadStackTrace, objectTable.getArgDescHandle(0), 0);
+ asm.emitDrop1();
+ }
+
asm.emitReturnTOS();
}
@@ -1345,7 +1369,7 @@
savedMaxSourcePositions = <int>[];
maxSourcePosition = node.fileOffset;
- locals = new LocalVariables(node, enableAsserts);
+ locals = new LocalVariables(node, enableAsserts, causalAsyncStacks);
locals.enterScope(node);
assert(!locals.isSyncYieldingFrame);
@@ -1891,6 +1915,17 @@
_recordSourcePosition(function.fileOffset);
_genPrologue(node, function);
+ if (causalAsyncStacks &&
+ parentFunction != null &&
+ (parentFunction.dartAsyncMarker == AsyncMarker.Async ||
+ parentFunction.dartAsyncMarker == AsyncMarker.AsyncStar)) {
+ _genLoadVar(locals.asyncStackTraceVar,
+ currentContextLevel: locals.contextLevelAtEntry);
+ _genDirectCall(
+ setAsyncThreadStackTrace, objectTable.getArgDescHandle(1), 1);
+ asm.emitDrop1();
+ }
+
Label continuationSwitchLabel;
int continuationSwitchVar;
if (locals.isSyncYieldingFrame) {
@@ -1903,8 +1938,6 @@
_setupInitialContext(function);
_checkArguments(function);
- // TODO(alexmarkov): support --causal_async_stacks.
-
_generateNode(function.body);
// BytecodeAssembler eliminates this bytecode if it is unreachable.
@@ -3279,12 +3312,12 @@
_getEnclosingTryFinallyBlocks(node, null);
if (tryFinallyBlocks.isEmpty) {
_generateNode(expr);
- asm.emitReturnTOS();
+ _genReturnTOS();
} else {
if (expr is BasicLiteral) {
_addFinallyBlocks(tryFinallyBlocks, () {
_generateNode(expr);
- asm.emitReturnTOS();
+ _genReturnTOS();
});
} else {
// Keep return value in a variable as try-catch statements
@@ -3294,7 +3327,7 @@
_addFinallyBlocks(tryFinallyBlocks, () {
asm.emitPush(locals.returnVarIndexInFrame);
- asm.emitReturnTOS();
+ _genReturnTOS();
});
}
}
@@ -3663,7 +3696,7 @@
// return <expression>
// Note: finally blocks are *not* executed on the way out.
_generateNode(node.expression);
- asm.emitReturnTOS();
+ _genReturnTOS();
asm.bind(continuationLabel);
diff --git a/pkg/vm/lib/bytecode/local_vars.dart b/pkg/vm/lib/bytecode/local_vars.dart
index c321b4e..14c5025 100644
--- a/pkg/vm/lib/bytecode/local_vars.dart
+++ b/pkg/vm/lib/bytecode/local_vars.dart
@@ -25,6 +25,7 @@
final Map<ForInStatement, VariableDeclaration> _capturedIteratorVars =
<ForInStatement, VariableDeclaration>{};
final bool enableAsserts;
+ final bool causalAsyncStacks;
Scope _currentScope;
Frame _currentFrame;
@@ -137,6 +138,13 @@
.getSyntheticVar(ContinuationVariables.awaitContextVar);
}
+ VariableDeclaration get asyncStackTraceVar {
+ assert(causalAsyncStacks);
+ assert(_currentFrame.isSyncYielding);
+ return _currentFrame.parent
+ .getSyntheticVar(ContinuationVariables.asyncStackTraceVar);
+ }
+
VariableDeclaration capturedSavedContextVar(TreeNode node) =>
_capturedSavedContextVars[node];
VariableDeclaration capturedExceptionVar(TreeNode node) =>
@@ -176,7 +184,7 @@
List<VariableDeclaration> get sortedNamedParameters =>
_currentFrame.sortedNamedParameters;
- LocalVariables(Member node, this.enableAsserts) {
+ LocalVariables(Member node, this.enableAsserts, this.causalAsyncStacks) {
final scopeBuilder = new _ScopeBuilder(this);
node.accept(scopeBuilder);
@@ -230,7 +238,7 @@
bool hasOptionalParameters = false;
bool hasCapturedParameters = false;
bool hasClosures = false;
- bool isDartSync = true;
+ AsyncMarker dartAsyncMarker = AsyncMarker.Sync;
bool isSyncYielding = false;
VariableDeclaration receiverVar;
VariableDeclaration capturedReceiverVar;
@@ -311,7 +319,7 @@
FunctionNode function = (node as dynamic).function;
assert(function != null);
- _currentFrame.isDartSync = function.dartAsyncMarker == AsyncMarker.Sync;
+ _currentFrame.dartAsyncMarker = function.dartAsyncMarker;
_currentFrame.isSyncYielding =
function.asyncMarker == AsyncMarker.SyncYielding;
@@ -363,6 +371,14 @@
.getSyntheticVar(ContinuationVariables.awaitJumpVar));
_useVariable(_currentFrame.parent
.getSyntheticVar(ContinuationVariables.awaitContextVar));
+
+ if (locals.causalAsyncStacks &&
+ (_currentFrame.parent.dartAsyncMarker == AsyncMarker.Async ||
+ _currentFrame.parent.dartAsyncMarker ==
+ AsyncMarker.AsyncStar)) {
+ _useVariable(_currentFrame.parent
+ .getSyntheticVar(ContinuationVariables.asyncStackTraceVar));
+ }
}
if (node is Constructor) {
@@ -535,7 +551,8 @@
visitVariableDeclaration(VariableDeclaration node) {
_declareVariable(node);
- if (!_currentFrame.isDartSync && node.name[0] == ':') {
+ if (_currentFrame.dartAsyncMarker != AsyncMarker.Sync &&
+ node.name[0] == ':') {
_currentFrame.syntheticVars ??= <String, VariableDeclaration>{};
assert(_currentFrame.syntheticVars[node.name] == null);
_currentFrame.syntheticVars[node.name] = node;
diff --git a/tests/standalone_2/causal_async_stack_test.dart b/tests/standalone_2/causal_async_stack_test.dart
index faebf3e..eab14af 100644
--- a/tests/standalone_2/causal_async_stack_test.dart
+++ b/tests/standalone_2/causal_async_stack_test.dart
@@ -5,7 +5,12 @@
import "package:expect/expect.dart";
+noop() async => Future.value(null);
+
baz() async {
+ // Throw exception after the first continuation, when there is no
+ // original stack trace.
+ await noop();
throw "Bad!";
}