[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 <bkonyi@google.com>
This commit is contained in:
Derek Xu
2025-01-08 14:51:12 -08:00
committed by Commit Queue
parent 8785c4e000
commit 337e07fab6
10 changed files with 497 additions and 282 deletions
+1
View File
@@ -191,6 +191,7 @@ vm_service/test/regress_55559_test: SkipByDesign # Spawns a child process from s
vm_service/test/regress_88104_test: SkipByDesign # Debugger is disabled in AOT mode.
vm_service/test/reload_sources_rpc_triggers_isolate_reload_event_test: SkipByDesign # Hot reload is disabled in AOT mode.
vm_service/test/reload_sources_test: SkipByDesign # Hot reload is disabled in AOT mode.
vm_service/test/reload_sources_with_resident_compiler_test: SkipByDesign # Hot reload is disabled in AOT mode.
vm_service/test/resume_shutdown_race_test: SkipByDesign # Debugger is disabled in AOT mode.
vm_service/test/rewind*: SkipByDesign # Debugger is disabled in AOT mode.
vm_service/test/sdk_break_with_mixin_test: SkipByDesign # Debugger is disabled in AOT mode.
@@ -2,182 +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' show debugger;
import 'dart:io' show Directory, File;
import 'dart:isolate' as i;
import 'package:path/path.dart' show join;
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'common/service_test_common.dart';
import 'breakpoint_resolution_after_reloading_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 = 78;
// AUTOGENERATED END
const v0Contents = '''
import 'dart:developer';
void f() {}
void main() {
debugger();
f();
f();
}
''';
const v1Contents = '''
import 'dart:developer';
void f() {
(() {
(() {
print('v1');
})();
})();
}
void main() {
f();
f();
}
''';
const v2Contents = '''
import 'dart:developer';
void f() {
(() {
print('v2.a');
print('v2.b');
})();
}
void main() {
f();
f();
}
''';
Future<void> testeeMain() async {
// Spawn the child isolate.
final tempDir = Directory.systemTemp.createTempSync();
try {
final rootLib = File(join(tempDir.path, 'main.dart'));
rootLib.writeAsStringSync(v0Contents);
await i.Isolate.spawnUri(rootLib.uri, [], null);
debugger(); // LINE_A
tempDir.deleteSync(recursive: true);
} catch (_) {
tempDir.deleteSync(recursive: true);
rethrow;
}
}
final tests = <IsolateTest>[
// Ensure that the main isolate has stopped at the [debugger] statement at the
// end of [testeeMain].
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
(VmService service, IsolateRef isolateRef) async {
final tempDir = Directory.systemTemp.createTempSync();
try {
// This test is a regression test against a bug caused by comparing script
// URLs instead of script pointers in the debugger. To produce a situation
// in which the bug used to occur, this test loads
// [spawnedIsolateRootLib], modifies [spawnedIsolateRootLib], and then
// reloads [spawnedIsolateRootLib].
final spawnedIsolateRootLib = File(join(tempDir.path, 'main.dart'));
// Find the spawned isolate.
final vm = await service.getVM();
final isolates = vm.isolates!;
expect(isolates.length, 2);
final spawnedIsolateRef = isolates.firstWhere(
(i) => i != isolateRef,
);
final spawnedIsolateId = spawnedIsolateRef.id!;
// Load [v1Contents] into the spawned isolate.
spawnedIsolateRootLib.writeAsStringSync(v1Contents);
await service.reloadSources(
spawnedIsolateId,
rootLibUri: spawnedIsolateRootLib.uri.toString(),
force: true,
);
Isolate spawnedIsolate = await service.getIsolate(spawnedIsolateId);
Library rootLib = await service.getObject(
spawnedIsolateId,
spawnedIsolate.rootLib!.id!,
) as Library;
String scriptId = rootLib.scripts![0].id!;
// Add a breakpoint at `print('v1');`.
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
// Resuming the spawned isolate should let it run until it gets paused at
// the breakpoint at `print('v1');`.
await resumeIsolate(service, spawnedIsolateRef);
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
await stoppedAtLine(6)(service, spawnedIsolateRef);
// Load [v2Contents] into the spawned isolate.
spawnedIsolateRootLib.writeAsStringSync(v2Contents);
await service.reloadSources(
spawnedIsolateId,
rootLibUri: spawnedIsolateRootLib.uri.toString(),
force: true,
);
spawnedIsolate = await service.getIsolate(spawnedIsolateId);
rootLib = await service.getObject(
spawnedIsolateId,
spawnedIsolate.rootLib!.id!,
) as Library;
scriptId = rootLib.scripts![0].id!;
// Add a breakpoint at `print('v2.a');`.
await service.addBreakpoint(spawnedIsolateId, scriptId, 5);
// Resuming the spawned isolate should let it run until it gets paused at
// the breakpoint at `print('v2.a');`.
await resumeIsolate(service, spawnedIsolateRef);
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
await stoppedAtLine(5)(service, spawnedIsolateRef);
// Add a breakpoint at `print('v2.b');`.
final breakpoint3 =
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
expect(breakpoint3.breakpointNumber, 3);
// We previously had a bug that would have made the breakpoint resolution
// code get confused by the old closure that was defined in [v1Contents].
// We prevent a reintroduction of that bug by ensuring that the newly set
// breakpoint has been resolved immediately.
expect(breakpoint3.resolved, true);
await resumeIsolate(service, spawnedIsolateRef);
tempDir.deleteSync(recursive: true);
} catch (_) {
tempDir.deleteSync(recursive: true);
rethrow;
}
},
];
void main([args = const <String>[]]) => runIsolateTests(
args,
tests,
breakpointResolutionAfterReloadingTests,
'breakpoint_resolution_after_reloading_test.dart',
testeeConcurrent: testeeMain,
);
@@ -0,0 +1,175 @@
// Copyright (c) 2025, 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' show debugger;
import 'dart:io' show Directory, File;
import 'dart:isolate' as i;
import 'package:path/path.dart' show join;
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 = 77;
// AUTOGENERATED END
const _v0Contents = '''
import 'dart:developer';
void f() {}
void main() {
debugger();
f();
f();
}
''';
const _v1Contents = '''
import 'dart:developer';
void f() {
(() {
(() {
print('v1');
})();
})();
}
void main() {
f();
f();
}
''';
const _v2Contents = '''
import 'dart:developer';
void f() {
(() {
print('v2.a');
print('v2.b');
})();
}
void main() {
f();
f();
}
''';
Future<void> testeeMain() async {
// Spawn the child isolate.
final tempDir = Directory.systemTemp.createTempSync();
try {
final rootLib = File(join(tempDir.path, 'main.dart'));
rootLib.writeAsStringSync(_v0Contents);
await i.Isolate.spawnUri(rootLib.uri, [], null);
debugger(); // LINE_A
tempDir.deleteSync(recursive: true);
} catch (_) {
tempDir.deleteSync(recursive: true);
rethrow;
}
}
final breakpointResolutionAfterReloadingTests = <IsolateTest>[
// Ensure that the main isolate has stopped at the [debugger] statement at the
// end of [testeeMain].
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
(VmService service, IsolateRef isolateRef) async {
final tempDir = Directory.systemTemp.createTempSync();
try {
// This test is a regression test against a bug caused by comparing script
// URLs instead of script pointers in the debugger. To produce a situation
// in which the bug used to occur, this test loads
// [spawnedIsolateRootLib], modifies [spawnedIsolateRootLib], and then
// reloads [spawnedIsolateRootLib].
final spawnedIsolateRootLib = File(join(tempDir.path, 'main.dart'));
// Find the spawned isolate.
final vm = await service.getVM();
final isolates = vm.isolates!;
expect(isolates.length, 2);
final spawnedIsolateRef = isolates.firstWhere(
(i) => i != isolateRef,
);
final spawnedIsolateId = spawnedIsolateRef.id!;
// Load [v1Contents] into the spawned isolate.
spawnedIsolateRootLib.writeAsStringSync(_v1Contents);
await service.reloadSources(
spawnedIsolateId,
rootLibUri: spawnedIsolateRootLib.uri.toString(),
force: true,
);
Isolate spawnedIsolate = await service.getIsolate(spawnedIsolateId);
Library rootLib = await service.getObject(
spawnedIsolateId,
spawnedIsolate.rootLib!.id!,
) as Library;
String scriptId = rootLib.scripts![0].id!;
// Add a breakpoint at `print('v1');`.
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
// Resuming the spawned isolate should let it run until it gets paused at
// the breakpoint at `print('v1');`.
await resumeIsolate(service, spawnedIsolateRef);
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
await stoppedAtLine(6)(service, spawnedIsolateRef);
// Load [v2Contents] into the spawned isolate.
spawnedIsolateRootLib.writeAsStringSync(_v2Contents);
await service.reloadSources(
spawnedIsolateId,
rootLibUri: spawnedIsolateRootLib.uri.toString(),
force: true,
);
spawnedIsolate = await service.getIsolate(spawnedIsolateId);
rootLib = await service.getObject(
spawnedIsolateId,
spawnedIsolate.rootLib!.id!,
) as Library;
scriptId = rootLib.scripts![0].id!;
// Add a breakpoint at `print('v2.a');`.
await service.addBreakpoint(spawnedIsolateId, scriptId, 5);
// Resuming the spawned isolate should let it run until it gets paused at
// the breakpoint at `print('v2.a');`.
await resumeIsolate(service, spawnedIsolateRef);
await hasStoppedAtBreakpoint(service, spawnedIsolateRef);
await stoppedAtLine(5)(service, spawnedIsolateRef);
// Add a breakpoint at `print('v2.b');`.
final breakpoint3 =
await service.addBreakpoint(spawnedIsolateId, scriptId, 6);
expect(breakpoint3.breakpointNumber, 3);
// We previously had a bug that would have made the breakpoint resolution
// code get confused by the old closure that was defined in [v1Contents].
// We prevent a reintroduction of that bug by ensuring that the newly set
// breakpoint has been resolved immediately.
expect(breakpoint3.resolved, true);
await resumeIsolate(service, spawnedIsolateRef);
tempDir.deleteSync(recursive: true);
} catch (_) {
tempDir.deleteSync(recursive: true);
rethrow;
}
},
];
@@ -0,0 +1,14 @@
// Copyright (c) 2025, 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 'breakpoint_resolution_after_reloading_test_common.dart';
import 'common/test_helper.dart';
void main([args = const <String>[]]) => runIsolateTests(
args,
breakpointResolutionAfterReloadingTests,
'breakpoint_resolution_after_reloading_with_resident_compiler_test.dart',
testeeConcurrent: testeeMain,
shouldTesteeBeLaunchedWithDartRunResident: true,
);
+3 -62
View File
@@ -2,71 +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 = 23;
// AUTOGENERATED END
void testMain() {
debugger(); // LINE_A.
print('1');
while (true) {}
}
Future<void> isolateIsRunning(VmService service, IsolateRef isolateRef) async {
final isolate = await service.getIsolate(isolateRef.id!);
final pauseEvent = isolate.pauseEvent;
final isPaused = pauseEvent == null
? false
: isolate.pauseEvent!.kind != EventKind.kResume;
final topFrame = pauseEvent?.topFrame;
expect(!isPaused && topFrame != null, true);
}
final tests = <IsolateTest>[
// Stopped at 'debugger' statement.
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
// Reload sources and request to pause post reload. The pause request will be
// ignored because we are already paused at a breakpoint.
reloadSources(pause: true),
// Ensure that we are still stopped at a breakpoint.
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
// Resume the isolate into the while loop.
resumeIsolate,
// Verify that it is running.
isolateIsRunning,
// Reload sources and request to pause post reload. The pause request will
// be respected because we are not already paused.
reloadSources(pause: true),
// Ensure that we are paused post reload request.
hasStoppedPostRequest,
// Resume the isolate.
resumeIsolate,
// Verify that it is running.
isolateIsRunning,
// Reload sources and do not request to pause post reload.
reloadSources(),
// Verify that it is running.
isolateIsRunning,
];
import 'reload_sources_test_common.dart' show reloadSourcesTests, testeeMain;
void main([args = const <String>[]]) => runIsolateTests(
args,
tests,
reloadSourcesTests,
'reload_sources_test.dart',
testeeConcurrent: testMain,
testeeConcurrent: testeeMain,
);
@@ -0,0 +1,64 @@
// Copyright (c) 2025, 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 = 22;
// AUTOGENERATED END
void testeeMain() {
debugger(); // LINE_A.
print('1');
while (true) {}
}
Future<void> isolateIsRunning(VmService service, IsolateRef isolateRef) async {
final isolate = await service.getIsolate(isolateRef.id!);
final pauseEvent = isolate.pauseEvent;
final isPaused = pauseEvent == null
? false
: isolate.pauseEvent!.kind != EventKind.kResume;
final topFrame = pauseEvent?.topFrame;
expect(!isPaused && topFrame != null, true);
}
final reloadSourcesTests = <IsolateTest>[
// Stopped at 'debugger' statement.
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
// Reload sources and request to pause post reload. The pause request will be
// ignored because we are already paused at a breakpoint.
reloadSources(pause: true),
// Ensure that we are still stopped at a breakpoint.
hasStoppedAtBreakpoint,
stoppedAtLine(LINE_A),
// Resume the isolate into the while loop.
resumeIsolate,
// Verify that it is running.
isolateIsRunning,
// Reload sources and request to pause post reload. The pause request will
// be respected because we are not already paused.
reloadSources(pause: true),
// Ensure that we are paused post reload request.
hasStoppedPostRequest,
// Resume the isolate.
resumeIsolate,
// Verify that it is running.
isolateIsRunning,
// Reload sources and do not request to pause post reload.
reloadSources(),
// Verify that it is running.
isolateIsRunning,
];
@@ -0,0 +1,14 @@
// Copyright (c) 2025, 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 'reload_sources_test_common.dart' show reloadSourcesTests, testeeMain;
void main([args = const <String>[]]) => runIsolateTests(
args,
reloadSourcesTests,
'reload_sources_with_resident_compiler_test.dart',
testeeConcurrent: testeeMain,
shouldTesteeBeLaunchedWithDartRunResident: true,
);
+85
View File
@@ -27,6 +27,7 @@
#include "vm/heap/safepoint.h"
#include "vm/isolate.h"
#include "vm/json_stream.h"
#include "vm/kernel.h"
#include "vm/kernel_isolate.h"
#include "vm/lockers.h"
#include "vm/message.h"
@@ -3953,6 +3954,88 @@ static void GetSourceReport(Thread* thread, JSONStream* js) {
#endif // !DART_PRECOMPILED_RUNTIME
}
static const MethodParameter* const reload_kernel_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
new BoolParameter("force", false),
new BoolParameter("pause", false),
new StringParameter("kernelFilePath", false),
nullptr,
};
/// Replaces the program running in the isolate group containing the isolate
/// specified by |js->LookupParam("isolateId")| with the program defined by
/// |js->LookupParam("kernelFilePath")|.
static void ReloadKernel(Thread* thread, JSONStream* js) {
#if defined(DART_PRECOMPILED_RUNTIME)
js->PrintError(kFeatureDisabled, "Compiler is disabled in AOT mode.");
#else
if (!js->HasParam("kernelFilePath")) {
PrintMissingParamError(js, "kernelFilePath");
return;
}
IsolateGroup* isolate_group = thread->isolate_group();
if (isolate_group->library_tag_handler() == nullptr) {
js->PrintError(kFeatureDisabled,
"A library tag handler must be installed.");
return;
}
Isolate* isolate = thread->isolate();
if ((isolate->sticky_error() != Error::null()) ||
(Thread::Current()->sticky_error() != Error::null())) {
js->PrintError(kIsolateReloadBarred,
"The specified isolate cannot reload from a kernel file "
"anymore because it encountered an unhandled exception. "
"Restart the isolate.");
return;
}
if (isolate_group->IsReloading()) {
js->PrintError(kIsolateIsReloading,
"The specified isolate group is already in the process of "
"being reloaded.");
return;
}
if (!isolate_group->CanReload()) {
js->PrintError(kFeatureDisabled,
"The specified isolate group cannot reload from a kernel "
"file right now.");
return;
}
Dart_FileOpenCallback file_open = Dart::file_open_callback();
Dart_FileReadCallback file_read = Dart::file_read_callback();
Dart_FileCloseCallback file_close = Dart::file_close_callback();
if ((file_open == nullptr) || (file_read == nullptr) ||
(file_close == nullptr)) {
js->PrintError(kInternalError,
"An internal error occurred when trying to read the "
"specified kernel file.");
return;
}
void* file = (*file_open)(js->LookupParam("kernelFilePath"), /*write=*/false);
if (file == nullptr) {
js->PrintError(kIsolateReloadBarred,
"The specified kernel file could not be read. Please ensure "
"that the provided 'kernelFilePath' argument is correct.");
return;
}
uint8_t* kernel_buffer = nullptr;
intptr_t kernel_buffer_size = -1;
(*file_read)(&kernel_buffer, &kernel_buffer_size, file);
const bool force_reload =
BoolParameter::Parse(js->LookupParam("force"), false);
isolate_group->ReloadKernel(js, force_reload, kernel_buffer,
kernel_buffer_size);
free(kernel_buffer);
Service::CheckForPause(isolate, js);
#endif // defined(DART_PRECOMPILED_RUNTIME)
}
static const MethodParameter* const reload_sources_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
new BoolParameter("force", false),
@@ -6207,6 +6290,8 @@ static const ServiceMethodDescriptor service_methods_[] = {
pause_params },
{ "removeBreakpoint", RemoveBreakpoint,
remove_breakpoint_params },
{ "_reloadKernel", ReloadKernel,
reload_kernel_params },
{ "reloadSources", ReloadSources,
reload_sources_params },
{ "_reloadSources", ReloadSources,
+138 -47
View File
@@ -10,7 +10,27 @@ final class _CompileExpressionErrorDetails {
_CompileExpressionErrorDetails(this.details);
}
/// The message in an error response from the resident frontend compiler can
/// either be in the 'errorMessage' property or the 'compilerOutputLines'
/// property of the response.
String _extractErrorMessageFromResidentFrontendCompilerResponse(
Map<String, dynamic> response,
) {
const errorMessageString = 'errorMessage';
if (response[errorMessageString] != null) {
return response[errorMessageString];
} else {
return (response['compilerOutputLines'] as List<dynamic>)
.cast<String>()
.join('\n');
}
}
class RunningIsolates implements MessageRouter {
static const _isolateIdString = 'isolateId';
static const _successString = 'success';
final isolates = <int, RunningIsolate>{};
int? _rootPortId;
@@ -31,6 +51,77 @@ class RunningIsolates implements MessageRouter {
(isolates.remove(portId))?.onIsolateExit();
}
Future<Response> _handleReloadSourcesRequest(
VMService service,
Message message,
RunningIsolate isolate,
) async {
if (VMServiceEmbedderHooks.getResidentCompilerInfoFile!() == null) {
// If there isn't a resident frontend compiler available, we let the VM
// take care of the request.
return isolate.routeRequest(service, message);
} else {
const rootLibUriString = 'rootLibUri';
final String rootLibUri;
if (message.params[rootLibUriString] == null) {
// If a 'rootLibUri' property was not included in the request, we have
// to ask the VM for [isolate]'s root library URI.
final getIsolateRequest = Message.forMethod('getIsolate');
getIsolateRequest.params[_isolateIdString] =
message.params[isolate.serviceId];
final getIsolateResponse = await isolate.routeRequest(
service,
getIsolateRequest,
);
final isolateJson =
(getIsolateResponse.decodeJson() as Map<String, dynamic>)['result']
as Map<String, dynamic>;
final rootLibJson = isolateJson['rootLib'] as Map<String, dynamic>;
rootLibUri = rootLibJson['uri'];
} else {
rootLibUri = message.params[rootLibUriString];
}
final tempDirectory = Directory.systemTemp.createTempSync();
final outputDill = File(
'${tempDirectory.path}${Platform.pathSeparator}for_hot_reload.dill',
);
final responseFromResidentCompiler =
await _sendRequestToResidentFrontendCompilerAndRecieveResponse(
jsonEncode(<String, Object?>{
'command': 'compile',
'executable': Uri.parse(rootLibUri).toFilePath(),
'output-dill': outputDill.path,
}),
VMServiceEmbedderHooks.getResidentCompilerInfoFile!()!,
);
if (responseFromResidentCompiler[_successString] == false) {
return Response.from(
encodeRpcError(
message,
kInternalError,
details: _extractErrorMessageFromResidentFrontendCompilerResponse(
responseFromResidentCompiler,
),
),
);
}
final reloadKernelRequest = Message.forMethod('_reloadKernel');
reloadKernelRequest.params[_isolateIdString] =
message.params[isolate.serviceId];
reloadKernelRequest.params['kernelFilePath'] =
outputDill.uri.toFilePath();
final response = isolate.routeRequest(service, message);
tempDirectory.deleteSync(recursive: true);
return response;
}
}
@override
Future<Response> routeRequest(VMService service, Message message) {
String isolateParam = message.params['isolateId']! as String;
@@ -71,6 +162,8 @@ class RunningIsolates implements MessageRouter {
if (message.method == 'evaluateInFrame' || message.method == 'evaluate') {
return _Evaluator(message, isolate, service).run();
} else if (message.method == 'reloadSources') {
return _handleReloadSourcesRequest(service, message, isolate);
} else {
return isolate.routeRequest(service, message);
}
@@ -128,11 +221,46 @@ final class _ResidentCompilerInfo {
});
}
// 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.
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;
}
/// Class that knows how to orchestrate expression evaluation in dart2 world.
class _Evaluator {
static const _successString = 'success';
static const _kernelBytesString = 'kernelBytes';
static const _compileExpressionString = 'compileExpression';
static const _isolateIdString = 'isolateId';
static const _expressionString = 'expression';
static const _definitionsString = 'definitions';
static const _definitionTypesString = 'definitionTypes';
@@ -212,7 +340,7 @@ class _Evaluator {
Map<String, dynamic> response,
) {
if (response['result'] != null) {
return (response['result'] as Map<String, dynamic>)['kernelBytes']
return (response['result'] as Map<String, dynamic>)[_kernelBytesString]
as String;
}
final error = response['error'] as Map<String, dynamic>;
@@ -220,42 +348,6 @@ class _Evaluator {
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>{
_successString: 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
@@ -268,7 +360,8 @@ class _Evaluator {
);
final compileParams = <String, dynamic>{
_isolateIdString: _message.params[_isolateIdString]!,
RunningIsolates._isolateIdString:
_message.params[RunningIsolates._isolateIdString]!,
_expressionString: _message.params[_expressionString]!,
_definitionsString: buildScopeResponseResult['param_names']!,
_definitionTypesString: buildScopeResponseResult['param_types']!,
@@ -346,14 +439,12 @@ class _Evaluator {
VMServiceEmbedderHooks.getResidentCompilerInfoFile!()!,
);
if (response[_successString] == true) {
return response['kernelBytes'];
} else if (response['errorMessage'] != null) {
throw _CompileExpressionErrorDetails(response['errorMessage']);
if (response[RunningIsolates._successString] == true) {
return response[_kernelBytesString];
} else {
final compilerOutputLines =
(response['compilerOutputLines'] as List<dynamic>).cast<String>();
throw _CompileExpressionErrorDetails(compilerOutputLines.join('\n'));
throw _CompileExpressionErrorDetails(
_extractErrorMessageFromResidentFrontendCompilerResponse(response),
);
}
} else {
// fallback to compile using kernel service
+1 -1
View File
@@ -7,7 +7,7 @@ library dart._vmservice;
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'dart:io' show File, InternetAddress, Socket;
import 'dart:io' show Directory, File, InternetAddress, Platform, Socket;
import 'dart:math';
import 'dart:typed_data';