[VM/Service] Use the resident frontend server for expression evaluation when it's available
TEST=tests added to pkg/vm_service/test
CoreLibraryReviewExempt: This CL does not include any core library API
changes, only VM Service implementation changes within
sdk/lib/vmservice/.
Change-Id: I191bb7f3ec3abf1f42405a43ce72016796bc43f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394523
Reviewed-by: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index 3bcc305..e2df849 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -1018,6 +1018,20 @@
});
});
+ test('running dartdev is a prerequisite for passing --resident', () async {
+ p = project(mainSrc: 'void main() {}');
+ final result = await p.run(['--resident', p.relativeFilePath]);
+
+ expect(result.exitCode, 255);
+ expect(
+ result.stderr,
+ contains(
+ 'Passing the `--resident` flag to `dart` is invalid. It must be passed '
+ 'to `dart run`.',
+ ),
+ );
+ });
+
test(
'passing --resident is a prerequisite for passing --resident-compiler-info-file',
() async {
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index 2b1387c..c699f7f 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -115,6 +115,7 @@
bool pauseOnUnhandledExceptions,
bool testeeControlsServer,
bool useAuthToken,
+ bool shouldTesteeBeLaunchedWithDartRunResident,
List<String>? experiments,
List<String>? extraArgs,
) {
@@ -124,6 +125,7 @@
pauseOnUnhandledExceptions,
testeeControlsServer,
useAuthToken,
+ shouldTesteeBeLaunchedWithDartRunResident,
experiments,
extraArgs,
);
@@ -135,6 +137,7 @@
bool pauseOnUnhandledExceptions,
bool testeeControlsServer,
bool useAuthToken,
+ bool shouldTesteeBeLaunchedWithDartRunResident,
List<String>? experiments,
List<String>? extraArgs,
) {
@@ -165,7 +168,12 @@
if (!testeeControlsServer) {
fullArgs.add('--enable-vm-service:0');
}
+
+ if (shouldTesteeBeLaunchedWithDartRunResident) {
+ fullArgs.addAll(['run', '--resident']);
+ }
fullArgs.addAll(args);
+
return _spawnCommon(dartExecutable, fullArgs, <String, String>{});
}
@@ -194,6 +202,7 @@
bool pauseOnUnhandledExceptions,
bool testeeControlsServer,
bool useAuthToken,
+ bool shouldTesteeBeLaunchedWithDartRunResident,
List<String>? experiments,
List<String>? extraArgs,
) {
@@ -203,6 +212,7 @@
pauseOnUnhandledExceptions,
testeeControlsServer,
useAuthToken,
+ shouldTesteeBeLaunchedWithDartRunResident,
experiments,
extraArgs,
).then((p) {
@@ -272,6 +282,7 @@
bool pauseOnUnhandledExceptions = false,
bool testeeControlsServer = false,
bool useAuthToken = false,
+ bool shouldTesteeBeLaunchedWithDartRunResident = false,
bool allowForNonZeroExitCode = false,
VmServiceFactory serviceFactory = VmService.defaultFactory,
}) async {
@@ -286,6 +297,7 @@
pauseOnUnhandledExceptions,
testeeControlsServer,
useAuthToken,
+ shouldTesteeBeLaunchedWithDartRunResident,
experiments,
extraArgs,
)
@@ -410,6 +422,9 @@
bool pauseOnUnhandledExceptions = false,
bool testeeControlsServer = false,
bool useAuthToken = false,
+
+ /// If [true], `dart run --resident` will be used to launch the testee.
+ bool shouldTesteeBeLaunchedWithDartRunResident = false,
bool allowForNonZeroExitCode = false,
List<String>? experiments,
List<String>? extraArgs,
@@ -435,6 +450,8 @@
pauseOnUnhandledExceptions: pauseOnUnhandledExceptions,
testeeControlsServer: testeeControlsServer,
useAuthToken: useAuthToken,
+ shouldTesteeBeLaunchedWithDartRunResident:
+ shouldTesteeBeLaunchedWithDartRunResident,
allowForNonZeroExitCode: allowForNonZeroExitCode,
);
}
diff --git a/pkg/vm_service/test/eval_test.dart b/pkg/vm_service/test/eval_test.dart
index 798ef56..60c65fb 100644
--- a/pkg/vm_service/test/eval_test.dart
+++ b/pkg/vm_service/test/eval_test.dart
@@ -2,118 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:developer';
-import 'package:test/test.dart';
-import 'package:vm_service/vm_service.dart';
-import 'common/service_test_common.dart';
import 'common/test_helper.dart';
-
-int globalVar = 100;
-
-class MyClass {
- static int staticVar = 1000;
-
- static void method(int value) {
- debugger();
- }
-}
-
-class _MyClass {
- void foo() {
- debugger();
- }
-}
-
-void testFunction() {
- int i = 0;
- while (true) {
- if (++i % 100000000 == 0) {
- MyClass.method(10000);
- _MyClass().foo();
- }
- }
-}
-
-final tests = <IsolateTest>[
- hasStoppedAtBreakpoint,
-
-// Evaluate against library, class, and instance.
- (VmService service, IsolateRef isolateRef) async {
- final isolateId = isolateRef.id!;
- final isolate = await service.getIsolate(isolateId);
- final stack = await service.getStack(isolateId);
-
- // Make sure we are in the right place.
- expect(stack.frames!.length, greaterThanOrEqualTo(2));
- expect(stack.frames![0].function!.name, 'method');
- expect((stack.frames![0].function!.owner as ClassRef).name, 'MyClass');
-
- final LibraryRef lib = isolate.rootLib!;
- final ClassRef cls = stack.frames![0].function!.owner;
- final InstanceRef instance = stack.frames![0].vars![0].value;
-
- dynamic result =
- await service.evaluate(isolateId, lib.id!, 'globalVar + 5');
- print(result);
- expect(result.valueAsString, '105');
-
- await expectError(
- () => service.evaluate(isolateId, lib.id!, 'globalVar + staticVar + 5'),
- );
-
- result =
- await service.evaluate(isolateId, cls.id!, 'globalVar + staticVar + 5');
- print(result);
- expect(result.valueAsString, '1105');
-
- await expectError(() => service.evaluate(isolateId, cls.id!, 'this + 5'));
-
- result = await service.evaluate(isolateId, instance.id!, 'this + 5');
- print(result);
- expect(result.valueAsString, '10005');
-
- await expectError(
- () => service.evaluate(isolateId, instance.id!, 'this + frog'),
- );
- },
- resumeIsolate,
- hasStoppedAtBreakpoint,
- (VmService service, IsolateRef isolate) async {
- final isolateId = isolate.id!;
- final stack = await service.getStack(isolateId);
-
- // Make sure we are in the right place.
- expect(stack.frames!.length, greaterThanOrEqualTo(2));
- expect(stack.frames![0].function!.name, 'foo');
- expect((stack.frames![0].function!.owner as ClassRef).name, '_MyClass');
-
- final ClassRef cls = stack.frames![0].function!.owner;
-
- final InstanceRef result =
- await service.evaluate(isolateId, cls.id!, '1+1') as InstanceRef;
- print(result);
- expect(result.valueAsString, '2');
- }
-];
-
-Future<void> expectError(func) async {
- bool gotException = false;
- dynamic result;
- try {
- result = await func();
- fail('Failed to throw');
- } on RPCError catch (e) {
- expect(e.code, 113); // Compile time error.
- gotException = true;
- }
- if (result?.type != 'Error') {
- expect(gotException, true); // dart2 semantics
- }
-}
+import 'eval_test_common.dart';
void main([args = const <String>[]]) => runIsolateTests(
args,
- tests,
+ evalTests,
'eval_test.dart',
- testeeConcurrent: testFunction,
+ testeeConcurrent: testeeMain,
);
diff --git a/pkg/vm_service/test/eval_test_common.dart b/pkg/vm_service/test/eval_test_common.dart
new file mode 100644
index 0000000..7b3b33a
--- /dev/null
+++ b/pkg/vm_service/test/eval_test_common.dart
@@ -0,0 +1,105 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:developer';
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+import 'common/service_test_common.dart';
+
+int globalVar = 100;
+
+class MyClass {
+ static int staticVar = 1000;
+
+ static void method(int value) {
+ debugger();
+ }
+}
+
+class _MyClass {
+ void foo() {
+ debugger();
+ }
+}
+
+void testeeMain() {
+ int i = 0;
+ while (true) {
+ if (++i % 100000000 == 0) {
+ MyClass.method(10000);
+ _MyClass().foo();
+ }
+ }
+}
+
+final evalTests = <IsolateTest>[
+ hasStoppedAtBreakpoint,
+
+ // Evaluate against library, class, and instance.
+ (VmService service, IsolateRef isolateRef) async {
+ final isolateId = isolateRef.id!;
+ final isolate = await service.getIsolate(isolateId);
+ final stack = await service.getStack(isolateId);
+
+ // Make sure we are in the right place.
+ expect(stack.frames!.length, greaterThanOrEqualTo(2));
+ expect(stack.frames![0].function!.name, 'method');
+ expect((stack.frames![0].function!.owner as ClassRef).name, 'MyClass');
+
+ final LibraryRef lib = isolate.rootLib!;
+ final ClassRef cls = stack.frames![0].function!.owner;
+ final InstanceRef instance = stack.frames![0].vars![0].value;
+
+ dynamic result =
+ await service.evaluate(isolateId, lib.id!, 'globalVar + 5');
+ print(result);
+ expect(result.valueAsString, '105');
+
+ await expectError(
+ () => service.evaluate(isolateId, lib.id!, 'globalVar + staticVar + 5'),
+ );
+
+ result =
+ await service.evaluate(isolateId, cls.id!, 'globalVar + staticVar + 5');
+ print(result);
+ expect(result.valueAsString, '1105');
+
+ await expectError(() => service.evaluate(isolateId, cls.id!, 'this + 5'));
+
+ result = await service.evaluate(isolateId, instance.id!, 'this + 5');
+ print(result);
+ expect(result.valueAsString, '10005');
+
+ await expectError(
+ () => service.evaluate(isolateId, instance.id!, 'this + frog'),
+ );
+ },
+ resumeIsolate,
+ hasStoppedAtBreakpoint,
+ (VmService service, IsolateRef isolate) async {
+ final isolateId = isolate.id!;
+ final stack = await service.getStack(isolateId);
+
+ // Make sure we are in the right place.
+ expect(stack.frames!.length, greaterThanOrEqualTo(2));
+ expect(stack.frames![0].function!.name, 'foo');
+ expect((stack.frames![0].function!.owner as ClassRef).name, '_MyClass');
+
+ final ClassRef cls = stack.frames![0].function!.owner;
+
+ final InstanceRef result =
+ await service.evaluate(isolateId, cls.id!, '1+1') as InstanceRef;
+ print(result);
+ expect(result.valueAsString, '2');
+ }
+];
+
+Future<void> expectError(Future<Response> Function() func) async {
+ try {
+ await func();
+ fail('Failed to throw');
+ } on RPCError catch (e) {
+ expect(e.code, 113); // Compile time error.
+ }
+}
diff --git a/pkg/vm_service/test/eval_with_resident_compiler_test.dart b/pkg/vm_service/test/eval_with_resident_compiler_test.dart
new file mode 100644
index 0000000..e09565a
--- /dev/null
+++ b/pkg/vm_service/test/eval_with_resident_compiler_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'common/test_helper.dart';
+import 'eval_test_common.dart';
+
+void main([args = const <String>[]]) => runIsolateTests(
+ args,
+ evalTests,
+ 'eval_with_resident_compiler_test.dart',
+ testeeConcurrent: testeeMain,
+ shouldTesteeBeLaunchedWithDartRunResident: true,
+ );
diff --git a/pkg/vm_service/test/evaluate_in_frame_rpc_test.dart b/pkg/vm_service/test/evaluate_in_frame_rpc_test.dart
index f6a68a4..4287d6f 100644
--- a/pkg/vm_service/test/evaluate_in_frame_rpc_test.dart
+++ b/pkg/vm_service/test/evaluate_in_frame_rpc_test.dart
@@ -2,48 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:developer';
-
-import 'package:vm_service/vm_service.dart';
-
-import 'common/service_test_common.dart';
import 'common/test_helper.dart';
+import 'evaluate_in_frame_rpc_test_common.dart';
-void method(int value, _) {
- debugger();
-}
-
-void testFunction() {
- int i = 0;
- while (true) {
- if (++i % 100000000 == 0) {
- method(10000, 50);
- }
- }
-}
-
-final tests = <IsolateTest>[
- hasStoppedAtBreakpoint,
-
-// Evaluate against library, class, and instance.
- (VmService service, IsolateRef isolateRef) async {
- final isolateId = isolateRef.id!;
- await evaluateInFrameAndExpect(service, isolateId, 'value', '10000');
- await evaluateInFrameAndExpect(service, isolateId, '_', '50');
- await evaluateInFrameAndExpect(service, isolateId, 'value + _', '10050');
- await evaluateInFrameAndExpect(
- service,
- isolateId,
- 'i',
- '100000000',
- topFrame: 1,
- );
- },
-];
-
-void main([args = const <String>[]]) => runIsolateTests(
+Future<void> main([args = const <String>[]]) => runIsolateTests(
args,
- tests,
+ evaluateInFrameRpcTests,
'evaluate_in_frame_rpc_test.dart',
- testeeConcurrent: testFunction,
+ testeeConcurrent: testeeMain,
);
diff --git a/pkg/vm_service/test/evaluate_in_frame_rpc_test_common.dart b/pkg/vm_service/test/evaluate_in_frame_rpc_test_common.dart
new file mode 100644
index 0000000..7f4c01b
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_in_frame_rpc_test_common.dart
@@ -0,0 +1,41 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:developer';
+
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+
+void method(int value, _) {
+ debugger();
+}
+
+void testeeMain() {
+ int i = 0;
+ while (true) {
+ if (++i % 100000000 == 0) {
+ method(10000, 50);
+ }
+ }
+}
+
+final evaluateInFrameRpcTests = <IsolateTest>[
+ hasStoppedAtBreakpoint,
+
+// Evaluate against library, class, and instance.
+ (VmService service, IsolateRef isolateRef) async {
+ final isolateId = isolateRef.id!;
+ await evaluateInFrameAndExpect(service, isolateId, 'value', '10000');
+ await evaluateInFrameAndExpect(service, isolateId, '_', '50');
+ await evaluateInFrameAndExpect(service, isolateId, 'value + _', '10050');
+ await evaluateInFrameAndExpect(
+ service,
+ isolateId,
+ 'i',
+ '100000000',
+ topFrame: 1,
+ );
+ },
+];
diff --git a/pkg/vm_service/test/evaluate_in_frame_rpc_with_resident_compiler_test.dart b/pkg/vm_service/test/evaluate_in_frame_rpc_with_resident_compiler_test.dart
new file mode 100644
index 0000000..57f9623
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_in_frame_rpc_with_resident_compiler_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'common/test_helper.dart';
+import 'evaluate_in_frame_rpc_test_common.dart';
+
+Future<void> main([args = const <String>[]]) => runIsolateTests(
+ args,
+ evaluateInFrameRpcTests,
+ 'evaluate_in_frame_rpc_with_resident_compiler_test.dart',
+ testeeConcurrent: testeeMain,
+ shouldTesteeBeLaunchedWithDartRunResident: true,
+ );
diff --git a/pkg/vm_service/test/evaluate_in_frame_with_scope_test.dart b/pkg/vm_service/test/evaluate_in_frame_with_scope_test.dart
index 80a3243..02eddd4 100644
--- a/pkg/vm_service/test/evaluate_in_frame_with_scope_test.dart
+++ b/pkg/vm_service/test/evaluate_in_frame_with_scope_test.dart
@@ -2,140 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'dart:developer';
-
-import 'package:test/test.dart';
-import 'package:vm_service/vm_service.dart';
-
-import 'common/service_test_common.dart';
import 'common/test_helper.dart';
-
-// AUTOGENERATED START
-//
-// Update these constants by running:
-//
-// dart pkg/vm_service/test/update_line_numbers.dart <test.dart>
-//
-const LINE_A = 33;
-// AUTOGENERATED END
-
-late int thing1;
-late int thing2;
-
-void testeeMain() {
- thing1 = 3;
- thing2 = 4;
- foo(42, 1984);
-}
-
-int foo(x, y) {
- final local = x + y;
- debugger(); // LINE_A
- return local;
-}
-
-final tests = <IsolateTest>[
- hasStoppedAtBreakpoint,
- stoppedAtLine(LINE_A),
- (VmService service, IsolateRef isolateRef) async {
- final isolateId = isolateRef.id!;
- final isolate = await service.getIsolate(isolateId);
- final rootLibId = isolate.rootLib!.id!;
- final rootLib = await service.getObject(
- isolateId,
- rootLibId,
- ) as Library;
-
- Future<Field> findField(String name) async {
- final fieldRef = rootLib.variables!.singleWhere(
- (v) => v.name == name,
- );
- return await service.getObject(isolateId, fieldRef.id!) as Field;
- }
-
- final thing1Field = await findField('thing1');
- final thing1 = thing1Field.staticValue! as InstanceRef;
- print(thing1);
-
- final thing2Field = await findField('thing2');
- final thing2 = thing2Field.staticValue! as InstanceRef;
- print(thing2);
-
- await evaluateInFrameAndExpect(
- service,
- isolateId,
- 'x + y + a + b',
- '2033',
- scope: {
- 'a': thing1.id!,
- 'b': thing2.id!,
- },
- );
-
- await evaluateInFrameAndExpect(
- service,
- isolateId,
- 'local + a + b',
- '2033',
- scope: {
- 'a': thing1.id!,
- 'b': thing2.id!,
- },
- );
-
- await evaluateInFrameAndExpect(
- service,
- isolateId,
- 'x + y',
- '7',
- scope: {
- 'x': thing1.id!,
- 'y': thing2.id!,
- },
- );
-
- try {
- await service.evaluate(
- isolateId,
- rootLibId,
- 'x + y',
- scope: {
- 'x': rootLibId,
- 'y': rootLibId,
- },
- );
- fail('Evaluated against a VM-internal object');
- } on RPCError catch (e) {
- expect(e.code, RPCErrorKind.kExpressionCompilationError.code);
- expect(
- e.details,
- contains('Cannot evaluate against a VM-internal object'),
- );
- }
-
- try {
- await service.evaluate(
- isolateId,
- rootLibId,
- 'x + y',
- scope: {
- 'not&an&identifier': thing1.id!,
- },
- );
- fail('Evaluated with an invalid identifier');
- } on RPCError catch (e) {
- expect(e.code, RPCErrorKind.kExpressionCompilationError.code);
- expect(
- e.details,
- contains("invalid 'scope' parameter"),
- );
- }
- },
-];
+import 'evaluate_in_frame_with_scope_test_common.dart';
void main([args = const <String>[]]) => runIsolateTests(
args,
- tests,
+ evaluateInFrameWithScopeTests,
'evaluate_in_frame_with_scope_test.dart',
testeeConcurrent: testeeMain,
);
diff --git a/pkg/vm_service/test/evaluate_in_frame_with_scope_test_common.dart b/pkg/vm_service/test/evaluate_in_frame_with_scope_test_common.dart
new file mode 100644
index 0000000..a39dca3
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_in_frame_with_scope_test_common.dart
@@ -0,0 +1,138 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:developer';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/service_test_common.dart';
+
+// AUTOGENERATED START
+//
+// Update these constants by running:
+//
+// dart pkg/vm_service/test/update_line_numbers.dart <test.dart>
+//
+const LINE_A = 32;
+// AUTOGENERATED END
+
+late int thing1;
+late int thing2;
+
+void testeeMain() {
+ thing1 = 3;
+ thing2 = 4;
+ foo(42, 1984);
+}
+
+int foo(x, y) {
+ final local = x + y;
+ debugger(); // LINE_A
+ return local;
+}
+
+final evaluateInFrameWithScopeTests = <IsolateTest>[
+ hasStoppedAtBreakpoint,
+ stoppedAtLine(LINE_A),
+ (VmService service, IsolateRef isolateRef) async {
+ final isolateId = isolateRef.id!;
+ final isolate = await service.getIsolate(isolateId);
+ // [isolate.rootLib] refers to the test entrypoint library, which is a
+ // library that imports this file's library. We want a [Library] object that
+ // refers to this file's library itself.
+ final libId = isolate.libraries!
+ .singleWhere(
+ (l) =>
+ l.uri!.endsWith('evaluate_in_frame_with_scope_test_common.dart'),
+ )
+ .id!;
+ final Library lib = (await service.getObject(isolateId, libId)) as Library;
+
+ Future<Field> findField(String name) async {
+ final fieldRef = lib.variables!.singleWhere(
+ (v) => v.name == name,
+ );
+ return await service.getObject(isolateId, fieldRef.id!) as Field;
+ }
+
+ final thing1Field = await findField('thing1');
+ final thing1 = thing1Field.staticValue! as InstanceRef;
+ print(thing1);
+
+ final thing2Field = await findField('thing2');
+ final thing2 = thing2Field.staticValue! as InstanceRef;
+ print(thing2);
+
+ await evaluateInFrameAndExpect(
+ service,
+ isolateId,
+ 'x + y + a + b',
+ '2033',
+ scope: {
+ 'a': thing1.id!,
+ 'b': thing2.id!,
+ },
+ );
+
+ await evaluateInFrameAndExpect(
+ service,
+ isolateId,
+ 'local + a + b',
+ '2033',
+ scope: {
+ 'a': thing1.id!,
+ 'b': thing2.id!,
+ },
+ );
+
+ await evaluateInFrameAndExpect(
+ service,
+ isolateId,
+ 'x + y',
+ '7',
+ scope: {
+ 'x': thing1.id!,
+ 'y': thing2.id!,
+ },
+ );
+
+ try {
+ await service.evaluate(
+ isolateId,
+ libId,
+ 'x + y',
+ scope: {
+ 'x': libId,
+ 'y': libId,
+ },
+ );
+ fail('Evaluated against a VM-internal object');
+ } on RPCError catch (e) {
+ expect(e.code, RPCErrorKind.kExpressionCompilationError.code);
+ expect(
+ e.details,
+ contains('Cannot evaluate against a VM-internal object'),
+ );
+ }
+
+ try {
+ await service.evaluate(
+ isolateId,
+ libId,
+ 'x + y',
+ scope: {
+ 'not&an&identifier': thing1.id!,
+ },
+ );
+ fail('Evaluated with an invalid identifier');
+ } on RPCError catch (e) {
+ expect(e.code, RPCErrorKind.kExpressionCompilationError.code);
+ expect(
+ e.details,
+ contains("invalid 'scope' parameter"),
+ );
+ }
+ },
+];
diff --git a/pkg/vm_service/test/evaluate_in_frame_with_scope_with_resident_compiler_test.dart b/pkg/vm_service/test/evaluate_in_frame_with_scope_with_resident_compiler_test.dart
new file mode 100644
index 0000000..23a7163
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_in_frame_with_scope_with_resident_compiler_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'common/test_helper.dart';
+import 'evaluate_in_frame_with_scope_test_common.dart';
+
+void main([args = const <String>[]]) => runIsolateTests(
+ args,
+ evaluateInFrameWithScopeTests,
+ 'evaluate_in_frame_with_scope_with_resident_compiler_test.dart',
+ testeeConcurrent: testeeMain,
+ shouldTesteeBeLaunchedWithDartRunResident: true,
+ );
diff --git a/pkg/vm_service/test/evaluate_with_scope_test.dart b/pkg/vm_service/test/evaluate_with_scope_test.dart
index 126ce0a..cb81c22 100644
--- a/pkg/vm_service/test/evaluate_with_scope_test.dart
+++ b/pkg/vm_service/test/evaluate_with_scope_test.dart
@@ -2,82 +2,12 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:test/test.dart';
-import 'package:vm_service/vm_service.dart';
-
import 'common/test_helper.dart';
-
-int? thing1;
-int? thing2;
-
-void testeeMain() {
- thing1 = 3;
- thing2 = 4;
-}
-
-Future<InstanceRef> evaluate(VmService service, isolate, target, x, y) async =>
- await service.evaluate(
- isolate!.id!!,
- target.id!,
- 'x + y',
- scope: {'x': x.id!, 'y': y.id!},
- ) as InstanceRef;
-
-final tests = <IsolateTest>[
- (VmService service, IsolateRef isolateRef) async {
- final isolateId = isolateRef.id!;
- final isolate = await service.getIsolate(isolateId);
- final Library lib =
- (await service.getObject(isolateId, isolate.rootLib!.id!)) as Library;
-
- final Field field1 = (await service.getObject(
- isolateId,
- lib.variables!.singleWhere((v) => v.name == 'thing1').id!,
- )) as Field;
- final thing1 = await service.getObject(isolateId, field1.staticValue!.id!);
-
- final Field field2 = (await service.getObject(
- isolateId,
- lib.variables!.singleWhere((v) => v.name == 'thing2').id!,
- )) as Field;
- final thing2 = await service.getObject(isolateId, field2.staticValue!.id!);
-
- var result = await evaluate(service, isolate, lib, thing1, thing2);
- expect(result.valueAsString, equals('7'));
-
- bool didThrow = false;
- try {
- result = await evaluate(service, isolate, lib, lib, lib);
- print(result);
- } catch (e) {
- didThrow = true;
- expect(
- e.toString(),
- contains('Cannot evaluate against a VM-internal object'),
- );
- }
- expect(didThrow, isTrue);
-
- didThrow = false;
- try {
- result = await service.evaluate(
- isolateId,
- lib.id!,
- 'x + y',
- scope: <String, String>{'not&an&id!entifier': thing1.id!},
- ) as InstanceRef;
- print(result);
- } catch (e) {
- didThrow = true;
- expect(e.toString(), contains("invalid 'scope' parameter"));
- }
- expect(didThrow, isTrue);
- }
-];
+import 'evaluate_with_scope_test_common.dart';
Future<void> main([args = const <String>[]]) => runIsolateTests(
args,
- tests,
+ evaluteWithScopeTests,
'evaluate_with_scope_test.dart',
testeeBefore: testeeMain,
);
diff --git a/pkg/vm_service/test/evaluate_with_scope_test_common.dart b/pkg/vm_service/test/evaluate_with_scope_test_common.dart
new file mode 100644
index 0000000..cf4afde
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_with_scope_test_common.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+int? thing1;
+int? thing2;
+
+void testeeMain() {
+ thing1 = 3;
+ thing2 = 4;
+}
+
+Future<InstanceRef> evaluate(VmService service, isolate, target, x, y) async =>
+ await service.evaluate(
+ isolate!.id!!,
+ target.id!,
+ 'x + y',
+ scope: {'x': x.id!, 'y': y.id!},
+ ) as InstanceRef;
+
+final evaluteWithScopeTests = <IsolateTest>[
+ (VmService service, IsolateRef isolateRef) async {
+ final isolateId = isolateRef.id!;
+ final isolate = await service.getIsolate(isolateId);
+ // [isolate.rootLib] refers to the test entrypoint library, which is a
+ // library that imports this file's library. We want a [Library] object that
+ // refers to this file's library itself.
+ final libId = isolate.libraries!
+ .singleWhere(
+ (l) => l.uri!.endsWith('evaluate_with_scope_test_common.dart'),
+ )
+ .id!;
+ final Library lib = (await service.getObject(isolateId, libId)) as Library;
+
+ final Field field1 = (await service.getObject(
+ isolateId,
+ lib.variables!.singleWhere((v) => v.name == 'thing1').id!,
+ )) as Field;
+ final thing1 = await service.getObject(isolateId, field1.staticValue!.id!);
+
+ final Field field2 = (await service.getObject(
+ isolateId,
+ lib.variables!.singleWhere((v) => v.name == 'thing2').id!,
+ )) as Field;
+ final thing2 = await service.getObject(isolateId, field2.staticValue!.id!);
+
+ var result = await evaluate(service, isolate, lib, thing1, thing2);
+ expect(result.valueAsString, equals('7'));
+
+ bool didThrow = false;
+ try {
+ result = await evaluate(service, isolate, lib, lib, lib);
+ print(result);
+ } catch (e) {
+ didThrow = true;
+ expect(
+ e.toString(),
+ contains('Cannot evaluate against a VM-internal object'),
+ );
+ }
+ expect(didThrow, isTrue);
+
+ didThrow = false;
+ try {
+ result = await service.evaluate(
+ isolateId,
+ lib.id!,
+ 'x + y',
+ scope: <String, String>{'not&an&id!entifier': thing1.id!},
+ ) as InstanceRef;
+ print(result);
+ } catch (e) {
+ didThrow = true;
+ expect(e.toString(), contains("invalid 'scope' parameter"));
+ }
+ expect(didThrow, isTrue);
+ }
+];
diff --git a/pkg/vm_service/test/evaluate_with_scope_with_resident_compiler_test.dart b/pkg/vm_service/test/evaluate_with_scope_with_resident_compiler_test.dart
new file mode 100644
index 0000000..a7eb33f
--- /dev/null
+++ b/pkg/vm_service/test/evaluate_with_scope_with_resident_compiler_test.dart
@@ -0,0 +1,14 @@
+// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'common/test_helper.dart';
+import 'evaluate_with_scope_test_common.dart';
+
+Future<void> main([args = const <String>[]]) => runIsolateTests(
+ args,
+ evaluteWithScopeTests,
+ 'evaluate_with_scope_with_resident_compiler_test.dart',
+ testeeBefore: testeeMain,
+ shouldTesteeBeLaunchedWithDartRunResident: true,
+ );
diff --git a/runtime/bin/dart_embedder_api_impl.cc b/runtime/bin/dart_embedder_api_impl.cc
index 496ab23..a9d666b 100644
--- a/runtime/bin/dart_embedder_api_impl.cc
+++ b/runtime/bin/dart_embedder_api_impl.cc
@@ -102,15 +102,16 @@
Dart_EnterScope();
// Load embedder specific bits and return.
- if (!bin::VmService::Setup(config.ip, config.port, config.dev_mode,
- config.disable_auth_codes,
- config.write_service_info_filename,
- /*trace_loading=*/false, config.deterministic,
- /*enable_service_port_fallback=*/false,
- /*wait_for_dds_to_advertise_service=*/false,
- /*serve_devtools=*/false,
- /*serve_observatory=*/true,
- /*print_dtd=*/false)) {
+ if (!bin::VmService::Setup(
+ config.ip, config.port, config.dev_mode, config.disable_auth_codes,
+ config.write_service_info_filename,
+ /*trace_loading=*/false, config.deterministic,
+ /*enable_service_port_fallback=*/false,
+ /*wait_for_dds_to_advertise_service=*/false,
+ /*serve_devtools=*/false,
+ /*serve_observatory=*/true,
+ /*print_dtd=*/false, /*should_use_resident_compiler=*/false,
+ /*resident_compiler_info_file_path=*/nullptr)) {
*error = Utils::StrDup(bin::VmService::GetErrorMessage());
return nullptr;
}
@@ -149,7 +150,9 @@
/*wait_for_dds_to_advertise_service=*/false,
/*serve_devtools=*/false,
/*serve_observatory=*/true,
- /*print_dtd=*/false)) {
+ /*print_dtd=*/false,
+ /*should_use_resident_compiler=*/false,
+ /*resident_compiler_info_file_path=*/nullptr)) {
*error = Utils::StrDup(bin::VmService::GetErrorMessage());
return nullptr;
}
diff --git a/runtime/bin/main_impl.cc b/runtime/bin/main_impl.cc
index 49522df..cd4fab2 100644
--- a/runtime/bin/main_impl.cc
+++ b/runtime/bin/main_impl.cc
@@ -568,7 +568,11 @@
Options::vm_write_service_info_filename(), Options::trace_loading(),
Options::deterministic(), Options::enable_service_port_fallback(),
wait_for_dds_to_advertise_service, serve_devtools,
- Options::enable_observatory(), Options::print_dtd())) {
+ Options::enable_observatory(), Options::print_dtd(),
+ Options::resident(),
+ Options::resident_compiler_info_file_path() != nullptr
+ ? Options::resident_compiler_info_file_path()
+ : Options::resident_server_info_file_path())) {
*error = Utils::StrDup(VmService::GetErrorMessage());
return nullptr;
}
@@ -1432,6 +1436,16 @@
"command.\n");
Platform::Exit(kErrorExitCode);
}
+
+ if (!ran_dart_dev &&
+ (Options::resident() ||
+ Options::resident_compiler_info_file_path() != nullptr ||
+ Options::resident_server_info_file_path() != nullptr)) {
+ Syslog::PrintErr(
+ "Passing the `--resident` flag to `dart` is invalid. It must be passed "
+ "to `dart run`.\n");
+ Platform::Exit(kErrorExitCode);
+ }
#endif // !defined(DART_PRECOMPILED_RUNTIME)
if (should_run_user_program) {
diff --git a/runtime/bin/main_options.h b/runtime/bin/main_options.h
index 7478e87..5734a09 100644
--- a/runtime/bin/main_options.h
+++ b/runtime/bin/main_options.h
@@ -26,7 +26,11 @@
V(root_certs_file, root_certs_file) \
V(root_certs_cache, root_certs_cache) \
V(namespace, namespc) \
- V(write_service_info, vm_write_service_info_filename)
+ V(write_service_info, vm_write_service_info_filename) \
+ /* The purpose of these flags is documented in */ \
+ /* pkg/dartdev/lib/src/commands/compilation_server.dart. */ \
+ V(resident_server_info_file, resident_server_info_file_path) \
+ V(resident_compiler_info_file, resident_compiler_info_file_path)
// As STRING_OPTIONS_LIST but for boolean valued options. The default value is
// always false, and the presence of the flag switches the value to true.
@@ -53,7 +57,10 @@
V(serve_devtools, enable_devtools) \
V(no_serve_observatory, disable_observatory) \
V(serve_observatory, enable_observatory) \
- V(print_dtd, print_dtd)
+ V(print_dtd, print_dtd) \
+ /* The purpose of this flag is documented in */ \
+ /* pkg/dartdev/lib/src/commands/run.dart. */ \
+ V(resident, resident)
// Boolean flags that have a short form.
#define SHORT_BOOL_OPTIONS_LIST(V) \
diff --git a/runtime/bin/run_vm_tests.cc b/runtime/bin/run_vm_tests.cc
index ae1fb32..833e62b 100644
--- a/runtime/bin/run_vm_tests.cc
+++ b/runtime/bin/run_vm_tests.cc
@@ -156,7 +156,9 @@
/*wait_for_dds_to_advertise_service=*/false,
/*serve_devtools=*/false,
/*serve_observatory=*/true,
- /*print_dtd=*/false)) {
+ /*print_dtd=*/false,
+ /*should_use_resident_compiler=*/false,
+ /*resident_compiler_info_file_path=*/nullptr)) {
*error = Utils::StrDup(bin::VmService::GetErrorMessage());
return nullptr;
}
diff --git a/runtime/bin/vmservice_impl.cc b/runtime/bin/vmservice_impl.cc
index 7b7559b..3d6a0c3 100644
--- a/runtime/bin/vmservice_impl.cc
+++ b/runtime/bin/vmservice_impl.cc
@@ -125,7 +125,9 @@
bool wait_for_dds_to_advertise_service,
bool serve_devtools,
bool serve_observatory,
- bool print_dtd) {
+ bool print_dtd,
+ bool should_use_resident_compiler,
+ const char* resident_compiler_info_file_path) {
Dart_Isolate isolate = Dart_CurrentIsolate();
ASSERT(isolate != nullptr);
SetServerAddress("");
@@ -226,6 +228,20 @@
print_dtd ? Dart_True() : Dart_False());
SHUTDOWN_ON_ERROR(result);
+ if (should_use_resident_compiler) {
+ Dart_Handle resident_compiler_info_file_path_dart_string = nullptr;
+ if (resident_compiler_info_file_path == nullptr) {
+ resident_compiler_info_file_path_dart_string = Dart_Null();
+ } else {
+ resident_compiler_info_file_path_dart_string =
+ DartUtils::NewString(resident_compiler_info_file_path);
+ }
+ result = Dart_Invoke(
+ library, DartUtils::NewString("_populateResidentCompilerInfoFile"), 1,
+ &resident_compiler_info_file_path_dart_string);
+ SHUTDOWN_ON_ERROR(result);
+ }
+
// Are we running on Windows?
#if defined(DART_HOST_OS_WINDOWS)
Dart_Handle is_windows = Dart_True();
diff --git a/runtime/bin/vmservice_impl.h b/runtime/bin/vmservice_impl.h
index 76b6e0b..71a3309 100644
--- a/runtime/bin/vmservice_impl.h
+++ b/runtime/bin/vmservice_impl.h
@@ -26,7 +26,9 @@
bool wait_for_dds_to_advertise_service,
bool serve_devtools,
bool serve_observatory,
- bool print_dtd) {
+ bool print_dtd,
+ bool should_use_resident_compiler,
+ const char* resident_compiler_info_file_path) {
return false;
}
@@ -42,18 +44,25 @@
private:
#else // defined(PRODUCT)
- static bool Setup(const char* server_ip,
- intptr_t server_port,
- bool dev_mode_server,
- bool auth_codes_disabled,
- const char* write_service_info_filename,
- bool trace_loading,
- bool deterministic,
- bool enable_service_port_fallback,
- bool wait_for_dds_to_advertise_service,
- bool serve_devtools,
- bool serve_observatory,
- bool print_dtd);
+ static bool Setup(
+ const char* server_ip,
+ intptr_t server_port,
+ bool dev_mode_server,
+ bool auth_codes_disabled,
+ const char* write_service_info_filename,
+ bool trace_loading,
+ bool deterministic,
+ bool enable_service_port_fallback,
+ bool wait_for_dds_to_advertise_service,
+ bool serve_devtools,
+ bool serve_observatory,
+ bool print_dtd,
+ bool should_use_resident_compiler,
+ // If either --resident-compiler-info-file or --resident-server-info-file
+ // was supplied on the command line, the CLI argument should be forwarded
+ // as the argument to this parameter. If neither option was supplied, the
+ // argument to this parameter should be null.
+ const char* resident_compiler_info_file_path);
static void SetNativeResolver();
diff --git a/sdk/lib/_internal/vm/bin/vmservice_io.dart b/sdk/lib/_internal/vm/bin/vmservice_io.dart
index fbc0175..b56a4d1 100644
--- a/sdk/lib/_internal/vm/bin/vmservice_io.dart
+++ b/sdk/lib/_internal/vm/bin/vmservice_io.dart
@@ -69,6 +69,23 @@
@pragma('vm:entry-point', !const bool.fromEnvironment('dart.vm.product'))
bool _printDtd = false;
+File? _residentCompilerInfoFile = null;
+
+@pragma('vm:entry-point', !const bool.fromEnvironment('dart.vm.product'))
+void _populateResidentCompilerInfoFile(
+ /// If either `--resident-compiler-info-file` or `--resident-server-info-file`
+ /// was supplied on the command line, the CLI argument should be forwarded as
+ /// the argument to this parameter. If neither option was supplied, the
+ /// argument to this parameter should be [null].
+ final String? residentCompilerInfoFilePathArgumentFromCli,
+) {
+ _residentCompilerInfoFile = getResidentCompilerInfoFileConsideringArgsImpl(
+ residentCompilerInfoFilePathArgumentFromCli,
+ );
+}
+
+File? _getResidentCompilerInfoFile() => _residentCompilerInfoFile;
+
// HTTP server.
late final Server server;
@@ -270,6 +287,9 @@
VMServiceEmbedderHooks.acceptNewWebSocketConnections =
webServerAcceptNewWebSocketConnections;
VMServiceEmbedderHooks.serveObservatory = serveObservatoryCallback;
+ VMServiceEmbedderHooks.getResidentCompilerInfoFile =
+ _getResidentCompilerInfoFile;
+
server = Server(
// Always instantiate the vmservice object so that the exit message
// can be delivered and waiting loaders can be cancelled.
diff --git a/sdk/lib/vmservice/running_isolates.dart b/sdk/lib/vmservice/running_isolates.dart
index d20cdce..e6b8ca7 100644
--- a/sdk/lib/vmservice/running_isolates.dart
+++ b/sdk/lib/vmservice/running_isolates.dart
@@ -80,6 +80,60 @@
void routeResponse(Message message) {}
}
+// NOTE: The following class is a duplicate of one in
+// 'package:frontend_server/resident_frontend_server_utils.dart'. We are forced
+// to duplicate it because `dart:_vmservice` is not allowed to import
+// `package:frontend_server`.
+
+final class _ResidentCompilerInfo {
+ final String? _sdkHash;
+ final InternetAddress _address;
+ final int _port;
+
+ /// The SDK hash that kernel files compiled using the Resident Frontend
+ /// Compiler associated with this object will be stamped with.
+ String? get sdkHash => _sdkHash;
+
+ /// The address that the Resident Frontend Compiler associated with this
+ /// object is listening from.
+ InternetAddress get address => _address;
+
+ /// The port number that the Resident Frontend Compiler associated with this
+ /// object is listening on.
+ int get port => _port;
+
+ /// Extracts the value associated with a key from [entries], where [entries]
+ /// is a [String] with the format '$key1:$value1 $key2:$value2 $key3:$value3 ...'.
+ static String _extractValueAssociatedWithKey(String entries, String key) =>
+ RegExp(
+ '$key:'
+ r'(\S+)(\s|$)',
+ ).allMatches(entries).first[1]!;
+
+ static _ResidentCompilerInfo fromFile(File file) {
+ final fileContents = file.readAsStringSync();
+
+ return _ResidentCompilerInfo._(
+ sdkHash:
+ fileContents.contains('sdkHash:')
+ ? _extractValueAssociatedWithKey(fileContents, 'sdkHash')
+ : null,
+ address: InternetAddress(
+ _extractValueAssociatedWithKey(fileContents, 'address'),
+ ),
+ port: int.parse(_extractValueAssociatedWithKey(fileContents, 'port')),
+ );
+ }
+
+ _ResidentCompilerInfo._({
+ required String? sdkHash,
+ required int port,
+ required InternetAddress address,
+ }) : _sdkHash = sdkHash,
+ _port = port,
+ _address = address;
+}
+
/// Class that knows how to orchestrate expression evaluation in dart2 world.
class _Evaluator {
_Evaluator(this._message, this._isolate, this._service);
@@ -156,13 +210,49 @@
throw _CompileExpressionErrorDetails(data['details']);
}
+ // NOTE: The following function is a duplicate of one in
+ // 'package:frontend_server/resident_frontend_server_utils.dart'. We are
+ // forced to duplicate it because `dart:_vmservice` is not allowed to import
+ // `package:frontend_server`.
+
+ /// Sends a compilation [request] to the resident frontend compiler associated
+ /// with [serverInfoFile], and returns the compiler's JSON response.
+ ///
+ /// Throws a [FileSystemException] if [serverInfoFile] cannot be accessed.
+ static Future<Map<String, dynamic>>
+ _sendRequestToResidentFrontendCompilerAndRecieveResponse(
+ String request,
+ File serverInfoFile,
+ ) async {
+ Socket? client;
+ Map<String, dynamic> jsonResponse;
+ final residentCompilerInfo = _ResidentCompilerInfo.fromFile(serverInfoFile);
+
+ try {
+ client = await Socket.connect(
+ residentCompilerInfo.address,
+ residentCompilerInfo.port,
+ );
+ client.write(request);
+ final data = String.fromCharCodes(await client.first);
+ jsonResponse = jsonDecode(data);
+ } catch (e) {
+ jsonResponse = <String, dynamic>{
+ 'success': false,
+ 'errorMessage': e.toString(),
+ };
+ }
+ client?.destroy();
+ return jsonResponse;
+ }
+
/// If compilation fails, this method will throw a
/// [_CompileExpressionErrorDetails] object that will be used to populate the
/// 'details' field of the response to the evaluation RPC that requested this
/// compilation to happen.
Future<String> _compileExpression(
Map<String, dynamic> buildScopeResponseResult,
- ) {
+ ) async {
Client? externalClient = _service._findFirstClientThatHandlesService(
'compileExpression',
);
@@ -192,6 +282,7 @@
compileParams['method'] = method;
}
if (externalClient != null) {
+ // Let the external client handle expression compilation.
final compileExpression = Message.forMethod('compileExpression');
compileExpression.client = externalClient;
compileExpression.params.addAll(compileParams);
@@ -221,6 +312,38 @@
json as Map<String, dynamic>,
),
);
+ } else if (VMServiceEmbedderHooks.getResidentCompilerInfoFile!() != null) {
+ // Compile the expression using the resident compiler.
+ final response =
+ await _sendRequestToResidentFrontendCompilerAndRecieveResponse(
+ jsonEncode({
+ 'command': 'compileExpression',
+ 'expression': compileParams['expression'],
+ 'definitions': compileParams['definitions'],
+ 'definitionTypes': compileParams['definitionTypes'],
+ 'typeDefinitions': compileParams['typeDefinitions'],
+ 'typeBounds': compileParams['typeBounds'],
+ 'typeDefaults': compileParams['typeDefaults'],
+ 'libraryUri': compileParams['libraryUri'],
+ 'offset': compileParams['tokenPos'],
+ 'isStatic': compileParams['isStatic'],
+ 'class': compileParams['klass'],
+ 'scriptUri': compileParams['scriptUri'],
+ 'method': compileParams['method'],
+ 'rootLibraryUri': buildScopeResponseResult['rootLibraryUri'],
+ }),
+ VMServiceEmbedderHooks.getResidentCompilerInfoFile!()!,
+ );
+
+ if (response['success'] == true) {
+ return response['kernelBytes'];
+ } else if (response['errorMessage'] != null) {
+ throw _CompileExpressionErrorDetails(response['errorMessage']);
+ } else {
+ final compilerOutputLines =
+ (response['compilerOutputLines'] as List<dynamic>).cast<String>();
+ throw _CompileExpressionErrorDetails(compilerOutputLines.join('\n'));
+ }
} else {
// fallback to compile using kernel service
final compileExpressionParams = <String, dynamic>{
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 5bba53e..d12644f 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -7,6 +7,7 @@
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
+import 'dart:io' show File, InternetAddress, Socket;
import 'dart:math';
import 'dart:typed_data';
@@ -172,6 +173,10 @@
/// Called when a client wants the service to serve Observatory.
typedef void ServeObservatoryCallback();
+/// Called when we want to get the appropriate resident compiler info file for
+/// the current program execution.
+typedef File? getResidentCompilerInfoFileCallback();
+
/// Hooks that are setup by the embedder.
class VMServiceEmbedderHooks {
static DdsConnectedCallback? ddsConnected;
@@ -188,6 +193,7 @@
static WebServerAcceptNewWebSocketConnectionsCallback?
acceptNewWebSocketConnections;
static ServeObservatoryCallback? serveObservatory;
+ static getResidentCompilerInfoFileCallback? getResidentCompilerInfoFile;
}
class VMService extends MessageRouter {