Skip to content

Commit a2f785c

Browse files
nshahanCommit Queue
authored andcommitted
[ddc] Update source mapping for static invocations
when they have been rewritten to include hot reload checks. Appears to fix the issue detected in the dwds test failure. Issue: dart-lang/webdev#2617. The new mapping ensures breakpoints applied to the call site in the Dart source are the call site instead of the the generation check. Allows the tooling to set a breakpoint on the static method call site, and then when paused, a step-into will action will advance the program into the static method. In the future we will need to reevaluate how we generate code where a single branch of execution in Dart source produces multiple branches in JavaScript. I believe right now the tooling will not support creating breakpoints in both branches even when they both have a mapping to the same Dart source location. Change-Id: I1ea0548768d8213dc7eefc332814d3bda7c81535 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/427567 Reviewed-by: Nate Biggs <[email protected]> Commit-Queue: Nicholas Shahan <[email protected]> Reviewed-by: Srujan Gaddam <[email protected]>
1 parent c2fe957 commit a2f785c

File tree

4 files changed

+71
-13
lines changed

4 files changed

+71
-13
lines changed

pkg/dev_compiler/lib/src/js_ast/nodes.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,13 @@ class ArrayBindingPattern extends BindingPattern {
12131213
ArrayBindingPattern _clone() => ArrayBindingPattern(variables);
12141214
}
12151215

1216+
/// Entirely transparent wrapper only used to identify the node as being a
1217+
/// rewritten invocation that includes correctness checks in case of a hot
1218+
/// reload at runtime.
1219+
class InvocationWithHotReloadChecks extends Conditional {
1220+
InvocationWithHotReloadChecks(super.condition, super.then, super.otherwise);
1221+
}
1222+
12161223
class Conditional extends Expression {
12171224
final Expression condition;
12181225
final Expression then;

pkg/dev_compiler/lib/src/js_ast/printer.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ abstract class JavaScriptPrintingContext {
4444
/// Callback after printing the last character representing [node].
4545
void exitNode(Node node) {}
4646

47+
// Callback before branching the flow of code in an expression.
48+
void expressionBranch() {}
49+
50+
// Callback after returning to the original flow of code in an expression.
51+
void expressionJoin() {}
52+
4753
late Printer printer;
4854
}
4955

@@ -757,11 +763,13 @@ class Printer implements NodeVisitor {
757763
// The then part is allowed to have an 'in'.
758764
visitNestedExpression(cond.then, ASSIGNMENT,
759765
newInForInit: false, newAtStatementBegin: false);
766+
context.expressionBranch();
760767
spaceOut();
761768
out(':');
762769
spaceOut();
763770
visitNestedExpression(cond.otherwise, ASSIGNMENT,
764771
newInForInit: inForInit, newAtStatementBegin: false);
772+
context.expressionJoin();
765773
}
766774

767775
@override

pkg/dev_compiler/lib/src/js_ast/source_map_printer.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,22 @@ class SourceMapPrintingContext extends SimpleJavaScriptPrintingContext {
139139
}
140140
}
141141

142+
@override
143+
void expressionBranch() {
144+
// Only one branch of a conditional expression will be executed but the
145+
// expressions could be mapped to the same source location. To ensure
146+
// mappings are produced for both branches any pending locations should be
147+
// flushed when switching output from one branch to the other.
148+
_flushPendingMarks();
149+
}
150+
151+
@override
152+
void expressionJoin() {
153+
// To ensure mappings get output for all branches when nesting conditional
154+
// expressions they should be flushed after code flow joins after branching.
155+
_flushPendingMarks();
156+
}
157+
142158
/// Marks that we entered a Dart node at this offset.
143159
///
144160
/// Multiple nested Dart expressions will appear to start at the same Dart

pkg/dev_compiler/lib/src/kernel/compiler_new.dart

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4582,6 +4582,14 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
45824582
return _emitDebuggerCall(expr).toStatement();
45834583
}
45844584
}
4585+
var jsExpression = _visitExpression(expr);
4586+
if (jsExpression is js_ast.InvocationWithHotReloadChecks) {
4587+
// When the static invocation expression has been rewritten to include
4588+
// hot reload correctness checks the entire resulting node shouldn't
4589+
// get source-mapped to the original call. Instead the call sites in
4590+
// the sub-expressions will have the correct mapping applied.
4591+
return jsExpression.toStatement()..sourceInformation = continueSourceMap;
4592+
}
45854593
return _visitExpression(expr).toStatement();
45864594
}
45874595

@@ -6795,26 +6803,38 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
67956803

67966804
var fn = _emitStaticTarget(target);
67976805
var args = _emitArgumentList(node.arguments, target: target);
6798-
var staticCall = js_ast.Call(fn, args);
6799-
if (_inFunctionExpression &&
6800-
!usesJSInterop(node.target) &&
6801-
!_isBuildingSdk) {
6806+
var staticCall = js_ast.Call(fn, args)
6807+
..sourceInformation = _nodeStart(node);
6808+
if (_shouldRewriteInvocationWithHotReloadChecks(target)) {
6809+
var generationCheck = js.call('# === #', [
6810+
js.number(_hotReloadGeneration),
6811+
_runtimeCall('global.dartDevEmbedder.hotReloadGeneration')
6812+
]);
68026813
var checkedCall = _rewriteInvocationWithHotReloadChecks(
6803-
target, node.arguments, node.getStaticType(_staticTypeContext));
6814+
target,
6815+
node.arguments,
6816+
node.getStaticType(_staticTypeContext),
6817+
_nodeStart(node));
68046818
// As an optimization, avoid extra checks when the invocation code was
68056819
// compiled in the same generation that it is running.
6806-
return js.call('# === # ? # : #', [
6807-
js.number(_hotReloadGeneration),
6808-
_runtimeCall('global.dartDevEmbedder.hotReloadGeneration'),
6809-
staticCall,
6810-
checkedCall
6811-
]);
6820+
return js_ast.InvocationWithHotReloadChecks(
6821+
generationCheck, staticCall, checkedCall)
6822+
..sourceInformation = continueSourceMap;
68126823
}
68136824
return _isNullCheckableJsInterop(target)
68146825
? _wrapWithJsInteropNullCheck(staticCall)
68156826
: staticCall;
68166827
}
68176828

6829+
/// Returns `true` when an invocation of [target] should be rewritten to
6830+
/// include checks to preserve soundness in the presence of hot reloads at
6831+
/// runtime.
6832+
bool _shouldRewriteInvocationWithHotReloadChecks(Member target) =>
6833+
_inFunctionExpression &&
6834+
!_isBuildingSdk &&
6835+
!usesJSInterop(target) &&
6836+
target != _assertInteropMethod;
6837+
68186838
/// Rewrites an invocation of [target] that is statically sound at compile
68196839
/// time to include checks to preserve soundness in the presence of hot
68206840
/// reloads at runtime.
@@ -6826,8 +6846,14 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
68266846
///
68276847
/// Invokes `noSuchMethod` if the check fails. If that also returns a value,
68286848
/// it's cast to the [expectedReturnType].
6849+
///
6850+
/// The resulting expression for the validated call site will receive the
6851+
/// [originalCallSiteSourceLocation].
68296852
js_ast.Expression _rewriteInvocationWithHotReloadChecks(
6830-
Member target, Arguments arguments, DartType expectedReturnType) {
6853+
Member target,
6854+
Arguments arguments,
6855+
DartType expectedReturnType,
6856+
SourceLocation? originalCallSiteSourceLocation) {
68316857
var hoistedPositionalVariables = <js_ast.Expression>[];
68326858
var hoistedNamedVariables = <String, js_ast.Expression>{};
68336859
js_ast.Expression? letAssignments;
@@ -6906,7 +6932,8 @@ class LibraryCompiler extends ComputeOnceConstantVisitor<js_ast.Expression>
69066932
for (var e in hoistedNamedVariables.entries)
69076933
js_ast.Property(js.string(e.key), e.value)
69086934
])
6909-
]);
6935+
])
6936+
..sourceInformation = originalCallSiteSourceLocation;
69106937
// Cast the result of the checked call or the value returned from a
69116938
// `NoSuchMethod` invocation.
69126939
return js_ast.Binary(

0 commit comments

Comments
 (0)