diff --git a/src/core/Microsoft.Dynamic/Ast/GeneratorRewriter.cs b/src/core/Microsoft.Dynamic/Ast/GeneratorRewriter.cs index ee93c8e3..013aab28 100644 --- a/src/core/Microsoft.Dynamic/Ast/GeneratorRewriter.cs +++ b/src/core/Microsoft.Dynamic/Ast/GeneratorRewriter.cs @@ -533,6 +533,17 @@ protected override Expression VisitExtension(Expression node) { return VisitYield(yield); } + // Lowered async nodes (AsyncExpression / AsyncEnumerableExpression) carry their own generator + // label and are reduced — with their await/yield points rewritten — in their own context so skip here. + // TODO: tracing *into* async bodies will instead require dedicated support here rather than this skip. + if (node is AsyncExpression +#if NET + or AsyncEnumerableExpression +#endif + ) { + return node; + } + if (node is FinallyFlowControlExpression ffc) { return Visit(node.ReduceExtensions()); } diff --git a/src/core/Microsoft.Dynamic/Debugging/DebugInfoRewriter.cs b/src/core/Microsoft.Dynamic/Debugging/DebugInfoRewriter.cs index e4826836..b660f509 100644 --- a/src/core/Microsoft.Dynamic/Debugging/DebugInfoRewriter.cs +++ b/src/core/Microsoft.Dynamic/Debugging/DebugInfoRewriter.cs @@ -120,6 +120,21 @@ protected override Expression VisitLambda(MSAst.Expression node) { return node; } + protected override Expression VisitExtension(Expression node) { + // Lowered async nodes (AsyncExpression / AsyncEnumerableExpression) are opaque boundaries to the + // debug transform, like nested lambdas (see VisitLambda). The async body is sliced into its own + // state machine, against its own generator label, when the node is reduced in its own context. + // TODO: make async bodies traceable via dedicated support here rather than skipping them. + if (node is Microsoft.Scripting.Ast.AsyncExpression +#if NET + or Microsoft.Scripting.Ast.AsyncEnumerableExpression +#endif + ) { + return node; + } + return base.VisitExtension(node); + } + // We remove all nested variables declared inside the block. protected override Expression VisitBlock(MSAst.BlockExpression node) { if (_transformToGenerator) { diff --git a/src/core/Microsoft.Dynamic/Debugging/LambdaWalker.cs b/src/core/Microsoft.Dynamic/Debugging/LambdaWalker.cs index c8ff7d71..d20ddad7 100644 --- a/src/core/Microsoft.Dynamic/Debugging/LambdaWalker.cs +++ b/src/core/Microsoft.Dynamic/Debugging/LambdaWalker.cs @@ -49,5 +49,18 @@ protected override MSAst.Expression VisitLambda(MSAst.Expression node) { // Explicitely don't walk nested lambdas. They should already have been transformed return node; } + + protected override MSAst.Expression VisitExtension(MSAst.Expression node) { + // Explicitely don't walk lowered async nodes (AsyncExpression / AsyncEnumerableExpression). + // Handle them separately (locals belong to their own context). + if (node is Microsoft.Scripting.Ast.AsyncExpression +#if NET + or Microsoft.Scripting.Ast.AsyncEnumerableExpression +#endif + ) { + return node; + } + return base.VisitExtension(node); + } } }