Skip to content

Commit

Permalink
[ResidentFrontendServer] Add 'replaceCachedDill' endpoint
Browse files Browse the repository at this point in the history
TEST=test case added to
`pkg/frontend_server/test/src/resident_frontend_server_test.dart`

Change-Id: I45a4b28c8c89714dcb93fa7f64874e158a0d69df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/394763
Reviewed-by: Ben Konyi <[email protected]>
Commit-Queue: Derek Xu <[email protected]>
  • Loading branch information
derekxu16 authored and Commit Queue committed Dec 11, 2024
1 parent e1faaad commit 0ce6724
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 6 deletions.
24 changes: 22 additions & 2 deletions pkg/dartdev/lib/src/commands/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:front_end/src/api_prototype/compiler_options.dart'
show Verbosity;
import 'package:frontend_server/resident_frontend_server_utils.dart'
show invokeReplaceCachedDill;
import 'package:path/path.dart';
import 'package:pub/pub.dart';

Expand Down Expand Up @@ -428,8 +430,26 @@ class RunCommand extends DartdevCommand {
}

final executableFile = File(executable.executable);
if (!await isFileKernelFile(executableFile) &&
!await isFileAppJitSnapshot(executableFile) &&
if (await isFileKernelFile(executableFile)) {
// If the file is a kernel file, we do not need to compile it, but we do
// need to replace the file in the resident frontend compiler kernel
// cache associated with this executable, because the cached kernel file
// may be used to populate context for expression evaluation later.
await ensureCompilationServerIsRunning(residentCompilerInfoFile);
final succeeded = await invokeReplaceCachedDill(
replacementDillPath: executableFile.absolute.path,
serverInfoFile: residentCompilerInfoFile,
);
if (!succeeded) {
log.stderr(
'Error: Encountered a problem accessing the Resident Frontend '
"Compiler's kernel file cache. Please try re-running the same "
'command again. If the error persists, please file an issue at '
'https://github.com/dart-lang/sdk/issues/new.',
);
return errorExitCode;
}
} else if (!await isFileAppJitSnapshot(executableFile) &&
!await isFileAotSnapshot(executableFile)) {
final compiledKernelFile = await _compileToKernelUsingResidentCompiler(
executable: executable,
Expand Down
21 changes: 20 additions & 1 deletion pkg/frontend_server/lib/resident_frontend_server_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 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:convert' show jsonDecode;
import 'dart:convert' show jsonDecode, jsonEncode;
import 'dart:io' show Directory, File, InternetAddress, Socket;

import 'package:path/path.dart' as path;
Expand Down Expand Up @@ -112,3 +112,22 @@ Future<Map<String, dynamic>> sendAndReceiveResponse(
client?.destroy();
return jsonResponse;
}

/// Sends a 'replaceCachedDill' request with [replacementDillPath] as the lone
/// argument to the resident frontend compiler associated with [serverInfoFile],
/// and returns a boolean indicating whether or not replacement succeeded.
///
/// Throws a [FileSystemException] if [serverInfoFile] cannot be accessed.
Future<bool> invokeReplaceCachedDill({
required String replacementDillPath,
required File serverInfoFile,
}) async {
final Map<String, dynamic> response = await sendAndReceiveResponse(
jsonEncode({
'command': 'replaceCachedDill',
'replacementDillPath': replacementDillPath,
}),
serverInfoFile,
);
return response['success'];
}
50 changes: 48 additions & 2 deletions pkg/frontend_server/lib/src/resident_frontend_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import 'dart:typed_data' show Uint8List;
import 'package:args/args.dart';
import 'package:front_end/src/api_unstable/vm.dart';
import 'package:kernel/binary/tag.dart' show expectedSdkHash;
import 'package:kernel/kernel.dart' show Component, loadComponentFromBytes;
import 'package:path/path.dart' as path;

import '../frontend_server.dart';
Expand Down Expand Up @@ -89,6 +90,10 @@ class ResidentCompiler {
updateState(_compileOptions);
}

void resetStateToWaitingForFirstCompile() {
_state = _ResidentState.waitingForFirstCompile;
}

/// The [ResidentCompiler] will use the [newOptions] for future compilation
/// requests.
void updateState(ArgResults newOptions) {
Expand All @@ -99,7 +104,7 @@ class ResidentCompiler {
// Refresh the compiler's output for the next compile
_compilerOutput.clear();
_formattedOutput.clear();
_state = _ResidentState.waitingForFirstCompile;
resetStateToWaitingForFirstCompile();
}

/// The current compiler options are outdated when any option has changed
Expand Down Expand Up @@ -170,7 +175,7 @@ class ResidentCompiler {
..resetIncrementalCompiler();
_state = _ResidentState.waitingForRecompile;
} else {
_state = _ResidentState.waitingForFirstCompile;
resetStateToWaitingForFirstCompile();
}

return _createResponseMap(
Expand Down Expand Up @@ -256,6 +261,8 @@ class ResidentCompiler {
/// residentListenAndCompile method.
class ResidentFrontendServer {
static const String _commandString = 'command';
static const String _replaceCachedDillString = 'replaceCachedDill';
static const String _replacementDillPathString = 'replacementDillPath';
static const String _compileString = 'compile';
static const String _executableString = 'executable';
static const String _packageString = 'packages';
Expand Down Expand Up @@ -301,6 +308,43 @@ class ResidentFrontendServer {
return residentCompiler;
}

static Future<String> _handleReplaceCachedDillRequest(
Map<String, dynamic> request,
) async {
if (request[_replacementDillPathString] == null) {
return _encodeErrorMessage(
"'$_replaceCachedDillString' requests must include a "
"'$_replacementDillPathString' property.",
);
}

final File replacementDillFile =
new File(request[_replacementDillPathString]);

final String canonicalizedLibraryPath;
try {
final Component component =
loadComponentFromBytes(replacementDillFile.readAsBytesSync());
canonicalizedLibraryPath = path.canonicalize(
component.mainMethod!.enclosingLibrary.fileUri.toFilePath(),
);

final String cachedDillPath =
computeCachedDillPath(canonicalizedLibraryPath);
replacementDillFile.copySync(cachedDillPath);
} catch (e) {
return _encodeErrorMessage('Failed to replace cached dill');
}

if (compilers[canonicalizedLibraryPath] != null) {
compilers[canonicalizedLibraryPath]!.resetStateToWaitingForFirstCompile();
}

return jsonEncode({
"success": true,
});
}

static Future<String> _handleCompileRequest(
Map<String, dynamic> request,
) async {
Expand Down Expand Up @@ -365,6 +409,8 @@ class ResidentFrontendServer {
}

switch (request[_commandString]) {
case _replaceCachedDillString:
return _handleReplaceCachedDillRequest(request);
case _compileString:
return _handleCompileRequest(request);
case _shutdownString:
Expand Down
67 changes: 66 additions & 1 deletion pkg/frontend_server/test/src/resident_frontend_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,72 @@ void main() async {
});
});

group('Resident Frontend Server: compile tests: ', () {
group("Resident Frontend Server: 'replaceCachedDill' command tests: ", () {
late Directory d;
late File executable, outputDill;

setUp(() async {
d = Directory.systemTemp.createTempSync();
executable = new File(path.join(d.path, 'src.dart'))
..createSync()
..writeAsStringSync('void main() {print("hello " "there");}');
outputDill = new File(path.join(d.path, 'src.dart.dill'));
});

tearDown(() async {
d.deleteSync(recursive: true);
ResidentFrontendServer.compilers.clear();
});

test('basic', () async {
final File cachedDillFile =
new File(computeCachedDillPath(executable.path));
expect(cachedDillFile.existsSync(), false);

final Map<String, dynamic> compileResult =
jsonDecode(await ResidentFrontendServer.handleRequest(
ResidentFrontendServer.createCompileJSON(
executable: executable.path,
outputDill: outputDill.path,
),
));
expect(compileResult['success'], true);

expect(cachedDillFile.existsSync(), true);
// Delete the kernel file associated with [executable.path] from the
// resident frontend compiler kernel cache.
cachedDillFile.deleteSync();

final Map<String, dynamic> replaceCachedDillResult = jsonDecode(
await ResidentFrontendServer.handleRequest(
jsonEncode({
'command': 'replaceCachedDill',
'replacementDillPath': outputDill.path,
}),
),
);
expect(replaceCachedDillResult['success'], true);
// Calling 'replaceCachedDill' with [outputDill] as the replacement dill
// should make [outputDill] the kernel file associated with
// [executable.path] in the resident frontend compiler kernel cache.
expect(cachedDillFile.existsSync(), true);
cachedDillFile.deleteSync();
});

test("invalid 'replacementDillPath' property in request", () async {
final Map<String, dynamic> replaceCachedDillResult = jsonDecode(
await ResidentFrontendServer.handleRequest(
jsonEncode({
'command': 'replaceCachedDill',
'replacementDillPath': path.join(d.path, 'nonexistent'),
}),
),
);
expect(replaceCachedDillResult['success'], false);
});
});

group("Resident Frontend Server: 'compile' command tests: ", () {
late Directory d;
late File executable, package, outputDill;

Expand Down

0 comments on commit 0ce6724

Please sign in to comment.