Skip to content

Commit 337e07f

Browse files
derekxu16Commit Queue
authored andcommitted
[VM/Service] Use the resident frontend server for hot reload when it's available
TEST=pkg/vm_service/test/reload_sources_with_resident_compiler_test.dart and pkg/vm_service/test/breakpoint_resolution_after_reloading_with_resident_compiler_test.dart CoreLibraryReviewExempt: This CL does not include any core library API changes, only VM Service implementation changes within sdk/lib/vmservice/. Change-Id: Ibc99cd37439ddd8aca97fa7e18a5112cbfc3b4cb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/401646 Reviewed-by: Ben Konyi <[email protected]>
1 parent 8785c4e commit 337e07f

10 files changed

+497
-282
lines changed

pkg/pkg.status

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ vm_service/test/regress_55559_test: SkipByDesign # Spawns a child process from s
191191
vm_service/test/regress_88104_test: SkipByDesign # Debugger is disabled in AOT mode.
192192
vm_service/test/reload_sources_rpc_triggers_isolate_reload_event_test: SkipByDesign # Hot reload is disabled in AOT mode.
193193
vm_service/test/reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
194+
vm_service/test/reload_sources_with_resident_compiler_test: SkipByDesign # Hot reload is disabled in AOT mode.
194195
vm_service/test/resume_shutdown_race_test: SkipByDesign # Debugger is disabled in AOT mode.
195196
vm_service/test/rewind*: SkipByDesign # Debugger is disabled in AOT mode.
196197
vm_service/test/sdk_break_with_mixin_test: SkipByDesign # Debugger is disabled in AOT mode.

pkg/vm_service/test/breakpoint_resolution_after_reloading_test.dart

Lines changed: 2 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -2,182 +2,12 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:developer' show debugger;
6-
import 'dart:io' show Directory, File;
7-
import 'dart:isolate' as i;
8-
9-
import 'package:path/path.dart' show join;
10-
import 'package:test/test.dart';
11-
import 'package:vm_service/vm_service.dart';
12-
13-
import 'common/service_test_common.dart';
5+
import 'breakpoint_resolution_after_reloading_test_common.dart';
146
import 'common/test_helper.dart';
157

16-
// AUTOGENERATED START
17-
//
18-
// Update these constants by running:
19-
//
20-
// dart pkg/vm_service/test/update_line_numbers.dart <test.dart>
21-
//
22-
const LINE_A = 78;
23-
// AUTOGENERATED END
24-
25-
const v0Contents = '''
26-
import 'dart:developer';
27-
28-
void f() {}
29-
30-
void main() {
31-
debugger();
32-
f();
33-
f();
34-
}
35-
''';
36-
37-
const v1Contents = '''
38-
import 'dart:developer';
39-
40-
void f() {
41-
(() {
42-
(() {
43-
print('v1');
44-
})();
45-
})();
46-
}
47-
48-
void main() {
49-
f();
50-
f();
51-
}
52-
''';
53-
54-
const v2Contents = '''
55-
import 'dart:developer';
56-
57-
void f() {
58-
(() {
59-
print('v2.a');
60-
print('v2.b');
61-
})();
62-
}
63-
64-
void main() {
65-
f();
66-
f();
67-
}
68-
''';
69-
70-
Future<void> testeeMain() async {
71-
// Spawn the child isolate.
72-
final tempDir = Directory.systemTemp.createTempSync();
73-
try {
74-
final rootLib = File(join(tempDir.path, 'main.dart'));
75-
rootLib.writeAsStringSync(v0Contents);
76-
77-
await i.Isolate.spawnUri(rootLib.uri, [], null);
78-
debugger(); // LINE_A
79-
tempDir.deleteSync(recursive: true);
80-
} catch (_) {
81-
tempDir.deleteSync(recursive: true);
82-
rethrow;
83-
}
84-
}
85-
86-
final tests = <IsolateTest>[
87-
// Ensure that the main isolate has stopped at the [debugger] statement at the
88-
// end of [testeeMain].
89-
hasStoppedAtBreakpoint,
90-
stoppedAtLine(LINE_A),
91-
(VmService service, IsolateRef isolateRef) async {
92-
final tempDir = Directory.systemTemp.createTempSync();
93-
try {
94-
// This test is a regression test against a bug caused by comparing script
95-
// URLs instead of script pointers in the debugger. To produce a situation
96-
// in which the bug used to occur, this test loads
97-
// [spawnedIsolateRootLib], modifies [spawnedIsolateRootLib], and then
98-
// reloads [spawnedIsolateRootLib].
99-
final spawnedIsolateRootLib = File(join(tempDir.path, 'main.dart'));
100-
101-
// Find the spawned isolate.
102-
final vm = await service.getVM();
103-
final isolates = vm.isolates!;
104-
expect(isolates.length, 2);
105-
final spawnedIsolateRef = isolates.firstWhere(
106-
(i) => i != isolateRef,
107-
);
108-
final spawnedIsolateId = spawnedIsolateRef.id!;
109-
110-
// Load [v1Contents] into the spawned isolate.
111-
spawnedIsolateRootLib.writeAsStringSync(v1Contents);
112-
await service.reloadSources(
113-
spawnedIsolateId,
114-
rootLibUri: spawnedIsolateRootLib.uri.toString(),
115-
force: true,
116-
);
117-
118-
Isolate spawnedIsolate = await service.getIsolate(spawnedIsolateId);
119-
Library rootLib = await service.getObject(
120-
spawnedIsolateId,
121-
spawnedIsolate.rootLib!.id!,
122-
) as Library;
123-
String scriptId = rootLib.scripts![0].id!;
124-
125-
// Add a breakpoint at `print('v1');`.
126-
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
127-
128-
// Resuming the spawned isolate should let it run until it gets paused at
129-
// the breakpoint at `print('v1');`.
130-
await resumeIsolate(service, spawnedIsolateRef);
131-
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
132-
await stoppedAtLine(6)(service, spawnedIsolateRef);
133-
134-
// Load [v2Contents] into the spawned isolate.
135-
spawnedIsolateRootLib.writeAsStringSync(v2Contents);
136-
await service.reloadSources(
137-
spawnedIsolateId,
138-
rootLibUri: spawnedIsolateRootLib.uri.toString(),
139-
force: true,
140-
);
141-
142-
spawnedIsolate = await service.getIsolate(spawnedIsolateId);
143-
rootLib = await service.getObject(
144-
spawnedIsolateId,
145-
spawnedIsolate.rootLib!.id!,
146-
) as Library;
147-
scriptId = rootLib.scripts![0].id!;
148-
149-
// Add a breakpoint at `print('v2.a');`.
150-
await service.addBreakpoint(spawnedIsolateId, scriptId, 5);
151-
152-
// Resuming the spawned isolate should let it run until it gets paused at
153-
// the breakpoint at `print('v2.a');`.
154-
await resumeIsolate(service, spawnedIsolateRef);
155-
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
156-
await stoppedAtLine(5)(service, spawnedIsolateRef);
157-
158-
// Add a breakpoint at `print('v2.b');`.
159-
final breakpoint3 =
160-
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
161-
expect(breakpoint3.breakpointNumber, 3);
162-
163-
// We previously had a bug that would have made the breakpoint resolution
164-
// code get confused by the old closure that was defined in [v1Contents].
165-
// We prevent a reintroduction of that bug by ensuring that the newly set
166-
// breakpoint has been resolved immediately.
167-
expect(breakpoint3.resolved, true);
168-
169-
await resumeIsolate(service, spawnedIsolateRef);
170-
tempDir.deleteSync(recursive: true);
171-
} catch (_) {
172-
tempDir.deleteSync(recursive: true);
173-
rethrow;
174-
}
175-
},
176-
];
177-
1788
void main([args = const <String>[]]) => runIsolateTests(
1799
args,
180-
tests,
10+
breakpointResolutionAfterReloadingTests,
18111
'breakpoint_resolution_after_reloading_test.dart',
18212
testeeConcurrent: testeeMain,
18313
);
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:developer' show debugger;
6+
import 'dart:io' show Directory, File;
7+
import 'dart:isolate' as i;
8+
9+
import 'package:path/path.dart' show join;
10+
import 'package:test/test.dart';
11+
import 'package:vm_service/vm_service.dart';
12+
13+
import 'common/service_test_common.dart';
14+
15+
// AUTOGENERATED START
16+
//
17+
// Update these constants by running:
18+
//
19+
// dart pkg/vm_service/test/update_line_numbers.dart <test.dart>
20+
//
21+
const LINE_A = 77;
22+
// AUTOGENERATED END
23+
24+
const _v0Contents = '''
25+
import 'dart:developer';
26+
27+
void f() {}
28+
29+
void main() {
30+
debugger();
31+
f();
32+
f();
33+
}
34+
''';
35+
36+
const _v1Contents = '''
37+
import 'dart:developer';
38+
39+
void f() {
40+
(() {
41+
(() {
42+
print('v1');
43+
})();
44+
})();
45+
}
46+
47+
void main() {
48+
f();
49+
f();
50+
}
51+
''';
52+
53+
const _v2Contents = '''
54+
import 'dart:developer';
55+
56+
void f() {
57+
(() {
58+
print('v2.a');
59+
print('v2.b');
60+
})();
61+
}
62+
63+
void main() {
64+
f();
65+
f();
66+
}
67+
''';
68+
69+
Future<void> testeeMain() async {
70+
// Spawn the child isolate.
71+
final tempDir = Directory.systemTemp.createTempSync();
72+
try {
73+
final rootLib = File(join(tempDir.path, 'main.dart'));
74+
rootLib.writeAsStringSync(_v0Contents);
75+
76+
await i.Isolate.spawnUri(rootLib.uri, [], null);
77+
debugger(); // LINE_A
78+
tempDir.deleteSync(recursive: true);
79+
} catch (_) {
80+
tempDir.deleteSync(recursive: true);
81+
rethrow;
82+
}
83+
}
84+
85+
final breakpointResolutionAfterReloadingTests = <IsolateTest>[
86+
// Ensure that the main isolate has stopped at the [debugger] statement at the
87+
// end of [testeeMain].
88+
hasStoppedAtBreakpoint,
89+
stoppedAtLine(LINE_A),
90+
(VmService service, IsolateRef isolateRef) async {
91+
final tempDir = Directory.systemTemp.createTempSync();
92+
try {
93+
// This test is a regression test against a bug caused by comparing script
94+
// URLs instead of script pointers in the debugger. To produce a situation
95+
// in which the bug used to occur, this test loads
96+
// [spawnedIsolateRootLib], modifies [spawnedIsolateRootLib], and then
97+
// reloads [spawnedIsolateRootLib].
98+
final spawnedIsolateRootLib = File(join(tempDir.path, 'main.dart'));
99+
100+
// Find the spawned isolate.
101+
final vm = await service.getVM();
102+
final isolates = vm.isolates!;
103+
expect(isolates.length, 2);
104+
final spawnedIsolateRef = isolates.firstWhere(
105+
(i) => i != isolateRef,
106+
);
107+
final spawnedIsolateId = spawnedIsolateRef.id!;
108+
109+
// Load [v1Contents] into the spawned isolate.
110+
spawnedIsolateRootLib.writeAsStringSync(_v1Contents);
111+
await service.reloadSources(
112+
spawnedIsolateId,
113+
rootLibUri: spawnedIsolateRootLib.uri.toString(),
114+
force: true,
115+
);
116+
117+
Isolate spawnedIsolate = await service.getIsolate(spawnedIsolateId);
118+
Library rootLib = await service.getObject(
119+
spawnedIsolateId,
120+
spawnedIsolate.rootLib!.id!,
121+
) as Library;
122+
String scriptId = rootLib.scripts![0].id!;
123+
124+
// Add a breakpoint at `print('v1');`.
125+
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
126+
127+
// Resuming the spawned isolate should let it run until it gets paused at
128+
// the breakpoint at `print('v1');`.
129+
await resumeIsolate(service, spawnedIsolateRef);
130+
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
131+
await stoppedAtLine(6)(service, spawnedIsolateRef);
132+
133+
// Load [v2Contents] into the spawned isolate.
134+
spawnedIsolateRootLib.writeAsStringSync(_v2Contents);
135+
await service.reloadSources(
136+
spawnedIsolateId,
137+
rootLibUri: spawnedIsolateRootLib.uri.toString(),
138+
force: true,
139+
);
140+
141+
spawnedIsolate = await service.getIsolate(spawnedIsolateId);
142+
rootLib = await service.getObject(
143+
spawnedIsolateId,
144+
spawnedIsolate.rootLib!.id!,
145+
) as Library;
146+
scriptId = rootLib.scripts![0].id!;
147+
148+
// Add a breakpoint at `print('v2.a');`.
149+
await service.addBreakpoint(spawnedIsolateId, scriptId, 5);
150+
151+
// Resuming the spawned isolate should let it run until it gets paused at
152+
// the breakpoint at `print('v2.a');`.
153+
await resumeIsolate(service, spawnedIsolateRef);
154+
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
155+
await stoppedAtLine(5)(service, spawnedIsolateRef);
156+
157+
// Add a breakpoint at `print('v2.b');`.
158+
final breakpoint3 =
159+
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
160+
expect(breakpoint3.breakpointNumber, 3);
161+
162+
// We previously had a bug that would have made the breakpoint resolution
163+
// code get confused by the old closure that was defined in [v1Contents].
164+
// We prevent a reintroduction of that bug by ensuring that the newly set
165+
// breakpoint has been resolved immediately.
166+
expect(breakpoint3.resolved, true);
167+
168+
await resumeIsolate(service, spawnedIsolateRef);
169+
tempDir.deleteSync(recursive: true);
170+
} catch (_) {
171+
tempDir.deleteSync(recursive: true);
172+
rethrow;
173+
}
174+
},
175+
];
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'breakpoint_resolution_after_reloading_test_common.dart';
6+
import 'common/test_helper.dart';
7+
8+
void main([args = const <String>[]]) => runIsolateTests(
9+
args,
10+
breakpointResolutionAfterReloadingTests,
11+
'breakpoint_resolution_after_reloading_with_resident_compiler_test.dart',
12+
testeeConcurrent: testeeMain,
13+
shouldTesteeBeLaunchedWithDartRunResident: true,
14+
);

0 commit comments

Comments
 (0)