Skip to content

Commit df693c2

Browse files
sstricklCommit Queue
authored andcommitted
[vm,dyn_modules] Add flags to bytecode source positions.
There are two possible flags for each source position currently: a flag that marks the source position as synthetic and a flag that marks the source position as within a yield point. Synthetic source positions in bytecode are treated the same as synthetic source positions in compiled code. That is, they encode the source position in the text that caused them to be synthesized, but denote that the covered instructions are internal and not to be used for debugger pause points or for call site/branch coverage information. Adding these flags allow us to mark appropriate parts of the async machinery as synthetic, and also allow us to mark all the bytecode involved in yield points as having the same token position. The latter fixes tests where the code would step over a previous expression, thus being paused at the start of the await bytecode, and would record the fp and token position there as the ones to ignore. However, since a new source position wasn't emitted until the direct call to the await method, the recorded token position would be the token position prior to the await call, and so the change in token position at the await call would trigger an early pause. TEST=pkg/vm_service/test/async_single_step_exception_test pkg/vm_service/test/async_single_step_into_test pkg/vm_service/test/async_single_step_out_test pkg/vm_service/test/async_star_single_step_into_test pkg/vm_service/test/async_step_out_test pkg/vm_service/test/positive_token_pos_test pkg/vm_service/test/step_into_async_no_await_test Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try,vm-dyn-mac-debug-arm64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-product-x64-try Change-Id: Ic7642a74fb76227a473f461f360e84dd3d5a45a1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/453322 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Tess Strickland <[email protected]>
1 parent bdf620c commit df693c2

File tree

10 files changed

+392
-242
lines changed

10 files changed

+392
-242
lines changed

pkg/dart2bytecode/docs/bytecode.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -850,9 +850,27 @@ the 'sourcePositions' section of BytecodeFile.
850850
```
851851
type SourcePositions {
852852
UInt numEntries;
853-
// Encoded list of pairs (PC, FileOffset), ordered by PC (offset of bytecode instruction).
853+
// Encoded list of pairs (PC, encodedFileOffset), ordered by PC
854+
// (offset of bytecode instruction).
855+
//
856+
// An encodedFileOffset encodes a fileOffset and a set of flags.
857+
//
858+
// If the encodedFileOffset is non-negative, then the encodedFileOffset
859+
// is the same numeric value as the fileOffset.
860+
//
861+
// If the encodedFileOffset is -1, then there is no source position
862+
// associated with the given PC.
863+
//
864+
// Otherwise, encodedFileOffset = -((fileOffset << n) | flags) -1,
865+
// where n is the number of flags below:
866+
// - synthetic (bit 0): set if the source position corresponds to
867+
// synthetic code (e.g., a source position that should not be used
868+
// for debugger breakpoints or pausing or for code coverage).
869+
// - yieldPoint (bit 1): set if the source position corresponds to
870+
// bytecode that implements a yield point.
871+
//
854872
// For two consecutive entries (PC1, FileOffset1), (PC2, FileOffset2) all
855-
// bytecode instructions in range [PC1,PC2) correspond to a source
873+
// bytecode instructions in range [PC1,PC2) correspond to an encoded source
856874
// position FileOffset1.
857875
SourcePositionEntry[numEntries] entries
858876
}

pkg/dart2bytecode/lib/assembler.dart

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class BytecodeAssembler {
6363
final bool _emitSourcePositions;
6464
bool isUnreachable = false;
6565
int currentSourcePosition = TreeNode.noOffset;
66+
int currentSourcePositionFlags = 0;
6667

6768
BytecodeAssembler(BytecodeOptions options)
6869
: _emitSourcePositions = options.emitSourcePositions;
@@ -81,12 +82,22 @@ class BytecodeAssembler {
8182
}
8283
}
8384

85+
@pragma('vm:prefer-inline')
86+
void _emitSourcePosition() {
87+
if (_emitSourcePositions && !isUnreachable) {
88+
sourcePositions.add(
89+
offset,
90+
currentSourcePosition == TreeNode.noOffset
91+
? SourcePositions.noSourcePosition
92+
: currentSourcePosition,
93+
currentSourcePositionFlags);
94+
}
95+
}
96+
8497
@pragma('vm:prefer-inline')
8598
void emitSourcePosition() {
86-
if (_emitSourcePositions &&
87-
!isUnreachable &&
88-
currentSourcePosition != TreeNode.noOffset) {
89-
sourcePositions.add(offset, currentSourcePosition);
99+
if (currentSourcePosition != TreeNode.noOffset) {
100+
_emitSourcePosition();
90101
}
91102
}
92103

@@ -95,15 +106,7 @@ class BytecodeAssembler {
95106
// source position to distinguish these calls and avoid stopping at them
96107
// while single stepping.
97108
@pragma('vm:prefer-inline')
98-
void emitSourcePositionForCall() {
99-
if (_emitSourcePositions && !isUnreachable) {
100-
sourcePositions.add(
101-
offset,
102-
currentSourcePosition == TreeNode.noOffset
103-
? SourcePositions.syntheticCodeMarker
104-
: currentSourcePosition);
105-
}
106-
}
109+
void emitSourcePositionForCall() => _emitSourcePosition();
107110

108111
void _grow() {
109112
final newSize = _buffer.length << 1;

pkg/dart2bytecode/lib/bytecode_generator.dart

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,11 @@ class BytecodeGenerator extends RecursiveVisitor {
10211021
// variable 'foo' has type 'dynamic'.
10221022
late final implicitCallName = Name('implicit:call');
10231023

1024-
void _recordSourcePosition(int fileOffset) {
1024+
void _recordSourcePosition(int fileOffset, [int? flags]) {
10251025
asm.currentSourcePosition = fileOffset;
1026+
if (flags != null) {
1027+
asm.currentSourcePositionFlags = flags;
1028+
}
10261029
maxSourcePosition = math.max(maxSourcePosition, fileOffset);
10271030
}
10281031

@@ -1031,9 +1034,11 @@ class BytecodeGenerator extends RecursiveVisitor {
10311034
return;
10321035
}
10331036
final savedSourcePosition = asm.currentSourcePosition;
1034-
_recordSourcePosition(node.fileOffset);
1037+
final savedFlags = asm.currentSourcePositionFlags;
1038+
_recordSourcePosition(node.fileOffset, 0);
10351039
node.accept(this);
10361040
asm.currentSourcePosition = savedSourcePosition;
1041+
asm.currentSourcePositionFlags = savedFlags;
10371042
}
10381043

10391044
void _generateNodeList(List<TreeNode> nodes) {
@@ -1216,6 +1221,9 @@ class BytecodeGenerator extends RecursiveVisitor {
12161221
break;
12171222
}
12181223
if (returnMethod != null) {
1224+
// Unlike other async machinery, this can't be marked synthetic
1225+
// as the method may return directly from the direct call and so
1226+
// the debugger needs to pause at it, not the following return.
12191227
asm.emitPopLocal(locals.returnVarIndexInFrame);
12201228
asm.emitPush(locals.suspendStateVarIndexInFrame);
12211229
asm.emitPush(locals.returnVarIndexInFrame);
@@ -1633,17 +1641,19 @@ class BytecodeGenerator extends RecursiveVisitor {
16331641
savedAssemblers = null;
16341642
currentLoopDepth = 0;
16351643
savedMaxSourcePositions = <int>[];
1636-
if (node is Procedure) {
1637-
maxSourcePosition = node.fileStartOffset;
1638-
} else if (node is Constructor) {
1639-
maxSourcePosition = node.startFileOffset;
1640-
} else {
1641-
maxSourcePosition = node.fileOffset;
1642-
}
16431644

16441645
locals = new LocalVariables(node, options, staticTypeContext);
16451646
locals.enterScope(node);
16461647

1648+
final int startPosition;
1649+
if (node is Procedure) {
1650+
startPosition = node.fileStartOffset;
1651+
} else if (node is Constructor) {
1652+
startPosition = node.startFileOffset;
1653+
} else {
1654+
startPosition = node.fileOffset;
1655+
}
1656+
_recordSourcePosition(startPosition, SourcePositions.syntheticFlag);
16471657
_genPrologue(node, node.function);
16481658
_setupInitialContext(node.function);
16491659
_emitFirstDebugCheck(node, node.function);
@@ -1677,6 +1687,9 @@ class BytecodeGenerator extends RecursiveVisitor {
16771687
if (!locals.isSuspendableFunction) {
16781688
return;
16791689
}
1690+
1691+
final savedFlags = asm.currentSourcePositionFlags;
1692+
asm.currentSourcePositionFlags |= SourcePositions.syntheticFlag;
16801693
Procedure initMethod;
16811694
switch (function!.dartAsyncMarker) {
16821695
case AsyncMarker.Async:
@@ -1696,6 +1709,10 @@ class BytecodeGenerator extends RecursiveVisitor {
16961709
asm.emitPopLocal(locals.suspendStateVarIndexInFrame);
16971710

16981711
if (function.dartAsyncMarker != AsyncMarker.Async) {
1712+
final savedFlags = asm.currentSourcePositionFlags;
1713+
// Mark all of the code in this block as within the yield point.
1714+
asm.currentSourcePositionFlags |= SourcePositions.yieldPointFlag;
1715+
asm.emitSourcePositionForCall();
16991716
// Suspend async* and sync* functions after prologue is finished.
17001717
Label done = Label();
17011718
asm.emitSuspend(done);
@@ -1710,6 +1727,7 @@ class BytecodeGenerator extends RecursiveVisitor {
17101727

17111728
asm.bind(done);
17121729
asm.emitDrop1(); // Discard result of Suspend.
1730+
asm.currentSourcePositionFlags = savedFlags;
17131731
}
17141732

17151733
if (function.dartAsyncMarker == AsyncMarker.SyncStar &&
@@ -1729,6 +1747,7 @@ class BytecodeGenerator extends RecursiveVisitor {
17291747
asyncTryBlock.needsStackTrace = true;
17301748
asyncTryBlock.types.add(cp.addType(const DynamicType()));
17311749
}
1750+
asm.currentSourcePositionFlags = savedFlags;
17321751
}
17331752

17341753
void _endSuspendableFunction(FunctionNode? function) {
@@ -1744,6 +1763,8 @@ class BytecodeGenerator extends RecursiveVisitor {
17441763
// Exception handlers are reachable although there are no labels or jumps.
17451764
asm.isUnreachable = false;
17461765

1766+
final savedFlags = asm.currentSourcePositionFlags;
1767+
asm.currentSourcePositionFlags |= SourcePositions.syntheticFlag;
17471768
asm.emitSetFrame(locals.frameSize);
17481769

17491770
final rethrowException = Label();
@@ -1765,6 +1786,7 @@ class BytecodeGenerator extends RecursiveVisitor {
17651786
asm.emitMoveSpecial(SpecialIndex.stackTrace, temp);
17661787
asm.emitPush(temp);
17671788
asm.emitThrow(1);
1789+
asm.currentSourcePositionFlags = savedFlags;
17681790
}
17691791
}
17701792

@@ -1954,9 +1976,11 @@ class BytecodeGenerator extends RecursiveVisitor {
19541976
}
19551977
}
19561978

1979+
// The CheckStack below is the instruction which should be used for function
1980+
// entry breakpoints.
1981+
_recordInitialSourcePositionForFunction(node, function);
19571982
// CheckStack must see a properly initialized context when stress-testing
19581983
// stack trace collection.
1959-
_recordInitialSourcePositionForFunction(node, function);
19601984
asm.emitCheckStack(0);
19611985

19621986
if (locals.hasFunctionTypeArgsVar && isClosure) {
@@ -2063,7 +2087,8 @@ class BytecodeGenerator extends RecursiveVisitor {
20632087

20642088
void _recordInitialSourcePositionForFunction(
20652089
TreeNode node, FunctionNode? function) {
2066-
_recordSourcePosition(_initialSourcePositionForFunction(node, function));
2090+
final position = _initialSourcePositionForFunction(node, function);
2091+
_recordSourcePosition(position, 0);
20672092
}
20682093

20692094
void _emitFirstDebugCheck(TreeNode node, FunctionNode? function) {
@@ -2417,7 +2442,7 @@ class BytecodeGenerator extends RecursiveVisitor {
24172442

24182443
final int closureFunctionIndex = cp.addClosureFunction(closureIndex);
24192444

2420-
maxSourcePosition = math.max(maxSourcePosition, function.fileOffset);
2445+
_recordSourcePosition(function.fileOffset, SourcePositions.syntheticFlag);
24212446
_genPrologue(node, function);
24222447
_setupInitialContext(function);
24232448
_emitFirstDebugCheck(node, function);
@@ -2695,7 +2720,8 @@ class BytecodeGenerator extends RecursiveVisitor {
26952720
if (options.emitSourcePositions) {
26962721
asm.emitSourcePosition();
26972722
}
2698-
if (options.emitDebuggerStops) {
2723+
if (options.emitDebuggerStops &&
2724+
(asm.currentSourcePositionFlags & SourcePositions.syntheticFlag) == 0) {
26992725
asm.emitDebugCheck();
27002726
}
27012727
}
@@ -3413,8 +3439,7 @@ class BytecodeGenerator extends RecursiveVisitor {
34133439
tryCatches![tryCatch]!.needsStackTrace = true;
34143440

34153441
// Allow breakpoint on explicit rethrow statement.
3416-
_emitSourcePosition();
3417-
_genRethrow(tryCatch);
3442+
_genRethrow(tryCatch, isSynthetic: false);
34183443
}
34193444

34203445
bool _hasNonTrivialInitializer(Field field) {
@@ -4147,7 +4172,13 @@ class BytecodeGenerator extends RecursiveVisitor {
41474172
}
41484173
}
41494174

4150-
void _genRethrow(TreeNode node) {
4175+
void _genRethrow(TreeNode node, {required bool isSynthetic}) {
4176+
final savedFlags = asm.currentSourcePositionFlags;
4177+
if (isSynthetic) {
4178+
asm.currentSourcePositionFlags |= SourcePositions.syntheticFlag;
4179+
} else {
4180+
asm.currentSourcePositionFlags &= ~SourcePositions.syntheticFlag;
4181+
}
41514182
final capturedExceptionVar = locals.capturedExceptionVar(node);
41524183
if (capturedExceptionVar != null) {
41534184
assert(locals.isCaptured(capturedExceptionVar));
@@ -4165,6 +4196,7 @@ class BytecodeGenerator extends RecursiveVisitor {
41654196
}
41664197

41674198
asm.emitThrow(1);
4199+
asm.currentSourcePositionFlags = savedFlags;
41684200
}
41694201

41704202
@override
@@ -4241,7 +4273,7 @@ class BytecodeGenerator extends RecursiveVisitor {
42414273

42424274
if (!hasCatchAll) {
42434275
tryBlock.needsStackTrace = true;
4244-
_genRethrow(node);
4276+
_genRethrow(node, isSynthetic: true);
42454277
}
42464278

42474279
asm.bind(done);
@@ -4277,7 +4309,7 @@ class BytecodeGenerator extends RecursiveVisitor {
42774309
_generateNode(node.finalizer);
42784310

42794311
tryBlock.needsStackTrace = true; // For rethrowing.
4280-
_genRethrow(node);
4312+
_genRethrow(node, isSynthetic: true);
42814313

42824314
for (var finallyBlock in finallyBlocks[node]!) {
42834315
asm.bind(finallyBlock.entry);
@@ -4457,6 +4489,14 @@ class BytecodeGenerator extends RecursiveVisitor {
44574489
void visitAwaitExpression(AwaitExpression node) {
44584490
_generateNode(node.operand);
44594491

4492+
// The rest of the await expression bytecode is the yield point.
4493+
// Emit a source position at the start to ensure that if the debugger
4494+
// is paused anywhere in this bytecode, a request to step over the await
4495+
// doesn't pause at either the direct call or the return, which have the
4496+
// same source position information.
4497+
final savedFlags = asm.currentSourcePositionFlags;
4498+
asm.currentSourcePositionFlags |= SourcePositions.yieldPointFlag;
4499+
asm.emitSourcePositionForCall();
44604500
final int temp = locals.tempIndexInFrame(node);
44614501
asm.emitPopLocal(temp);
44624502

@@ -4478,6 +4518,7 @@ class BytecodeGenerator extends RecursiveVisitor {
44784518
_genDirectCall(_await, objectTable.getArgDescHandle(2), 2);
44794519
}
44804520
asm.emitReturnTOS();
4521+
asm.currentSourcePositionFlags = savedFlags;
44814522

44824523
asm.bind(done);
44834524
}

0 commit comments

Comments
 (0)