Files
sdk/pkg/frontend_server/lib/resident_frontend_server_utils.dart
T
Paul Berry 7a5c71ba05 [frontend_server] Migrate to new constructor decl syntax.
(Part of https://github.com/dart-lang/sdk/issues/63288)

This change migrates the frontend_server package to use the new
constructor declaration syntax, described in
https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations.

This change was performed in an automated fashion, by (a) bumping the
package's SDK constraint to `3.13.0-0`, (b) enabling the lints
`unnecessary_type_name_in_constructor` and
`unnecessary_const_in_enum_constructor`, (c) fixing the resulting lint
failures using `dart fix`, and then (d) reformatting the affected
files.

To ease code review, I've reverted unrelated formatting changes.

Change-Id: Icbc924a1faae2f1c8fe0d5fa07bb9bd66a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508369
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
2026-06-01 17:48:27 -07:00

287 lines
9.1 KiB
Dart

// 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:convert' show jsonDecode, jsonEncode;
import 'dart:io'
show Directory, File, FileSystemException, InternetAddress, Socket;
import 'package:path/path.dart' as path;
final class ResidentCompilerInfo {
/// The SDK hash that kernel files compiled using the Resident Frontend
/// Compiler associated with this object will be stamped with.
final String? sdkHash;
/// The address that the Resident Frontend Compiler associated with this
/// object is listening from.
final InternetAddress address;
/// The port number that the Resident Frontend Compiler associated with this
/// object is listening on.
final int port;
/// Extracts the value associated with a key from [entries], where [entries]
/// is a [String] with the format '$key1:$value1 $key2:$value2 ...'.
static String _extractValueAssociatedWithKey(String entries, String key) =>
new RegExp(
'$key:'
r'(\S+)(\s|$)',
).allMatches(entries).first[1]!;
static ResidentCompilerInfo fromFile(File file) {
final String fileContents = file.readAsStringSync();
return new ResidentCompilerInfo._(
sdkHash: fileContents.contains('sdkHash:')
? _extractValueAssociatedWithKey(fileContents, 'sdkHash')
: null,
address: new InternetAddress(
_extractValueAssociatedWithKey(fileContents, 'address'),
),
port: int.parse(_extractValueAssociatedWithKey(fileContents, 'port')),
);
}
new _({required this.sdkHash, required this.port, required this.address});
}
typedef CachedDillAndCompilerOptionsPaths = ({
String cachedDillPath,
String cachedCompilerOptionsPath,
});
/// Returns the absolute paths to the cached kernel file and the cached compiler
/// options file associated with [canonicalizedLibraryPath].
CachedDillAndCompilerOptionsPaths computeCachedDillAndCompilerOptionsPaths(
String canonicalizedLibraryPath,
) {
final String dirname = path.dirname(canonicalizedLibraryPath);
final String basename = path.basename(canonicalizedLibraryPath);
final String cachedKernelDirectoryPath = path.join(
path.join(Directory.systemTemp.path, 'dart_resident_compiler_kernel_cache'),
dirname.replaceAll(new RegExp(r':|\\|\/'), '_'),
);
try {
new Directory(cachedKernelDirectoryPath).createSync(recursive: true);
} catch (e) {
throw new Exception(
'Failed to create directory for storing cached kernel files',
);
}
final String cachedDillPath = path.join(
cachedKernelDirectoryPath,
'$basename.dill',
);
final String cachedCompilerOptionsPath = path.join(
cachedKernelDirectoryPath,
'${basename}_options.json',
);
return (
cachedDillPath: cachedDillPath,
cachedCompilerOptionsPath: cachedCompilerOptionsPath,
);
}
/// 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, Object?>> sendAndReceiveResponse(
String request,
File serverInfoFile,
) async {
Socket? client;
Map<String, Object?> jsonResponse;
final ResidentCompilerInfo residentCompilerInfo =
ResidentCompilerInfo.fromFile(serverInfoFile);
try {
client = await Socket.connect(
residentCompilerInfo.address,
residentCompilerInfo.port,
);
client.write(request);
final String data = new String.fromCharCodes(await client.first);
jsonResponse = (jsonDecode(data) as Map<String, Object?>);
} catch (e) {
jsonResponse = <String, Object?>{
'success': false,
'errorMessage': e.toString(),
};
}
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, Object?> response = await sendAndReceiveResponse(
jsonEncode({
'command': 'replaceCachedDill',
'replacementDillPath': replacementDillPath,
}),
serverInfoFile,
);
return response['success'] == true;
}
/// The result of a successful compilation request sent to a resident
/// frontend compiler.
final class CompileResult {
/// The absolute path to the kernel file produced by the compiler.
final String outputDill;
/// The number of errors produced by the compiler.
final int errorCount;
/// The output lines produced by the compiler, if any.
final List<String> compilerOutputLines;
new({
required this.outputDill,
required this.errorCount,
this.compilerOutputLines = const [],
});
}
/// The result of a successful expression compilation request sent to a
/// resident frontend compiler.
final class CompileExpressionResult {
/// The base64 encoded kernel bytes produced by the compiler.
final String kernelBytes;
/// The number of errors produced by the compiler.
final int errorCount;
/// The output lines produced by the compiler, if any.
final List<String> compilerOutputLines;
new({
required this.kernelBytes,
required this.errorCount,
this.compilerOutputLines = const [],
});
}
/// The exception thrown when a compilation request to the resident frontend
/// compiler fails.
final class CompileException implements Exception {
/// The error message from the compiler.
final String message;
new(this.message);
@override
String toString() => 'CompileException: $message';
}
/// Sends a 'compile' request to the resident frontend compiler associated with
/// [serverInfoFile], and returns a [CompileResult] on success.
///
/// Throws a [CompileException] if compilation fails.
/// Throws a [FileSystemException] if [serverInfoFile] cannot be accessed.
Future<CompileResult> invokeCompile({
required String executable,
required String outputDill,
required File serverInfoFile,
}) async {
final Map<String, Object?> response = await sendAndReceiveResponse(
jsonEncode({
'command': 'compile',
'executable': executable,
'output-dill': outputDill,
'useCachedCompilerOptionsAsBase': true,
}),
serverInfoFile,
);
if (response['success'] != true) {
final String errorMessage = switch (response) {
{'errorMessage': final String errorMessage} => errorMessage,
{'compilerOutputLines': final List<Object?> lines} => lines.join('\n'),
_ => 'Unknown error: $response',
};
throw new CompileException(errorMessage);
}
return new CompileResult(
outputDill: response['output-dill'] as String,
errorCount: response['errorCount'] as int,
compilerOutputLines:
(response['compilerOutputLines'] as List<Object?>?)?.cast<String>() ??
const [],
);
}
/// Sends a 'compileExpression' request to the resident frontend compiler
/// associated with [serverInfoFile], and returns a [CompileExpressionResult]
/// on success.
///
/// Throws a [CompileException] if compilation fails.
/// Throws a [FileSystemException] if [serverInfoFile] cannot be accessed.
Future<CompileExpressionResult> invokeCompileExpression({
required String expression,
required List<String> definitions,
required List<String> definitionTypes,
required List<String> typeDefinitions,
required List<String> typeBounds,
required List<String> typeDefaults,
required String libraryUri,
required String? klass,
required String? method,
required int offset,
required String? scriptUri,
required bool isStatic,
String? rootLibraryUri,
required File serverInfoFile,
}) async {
final Map<String, Object?> response = await sendAndReceiveResponse(
jsonEncode({
'command': 'compileExpression',
'expression': expression,
'definitions': definitions,
'definitionTypes': definitionTypes,
'typeDefinitions': typeDefinitions,
'typeBounds': typeBounds,
'typeDefaults': typeDefaults,
'libraryUri': libraryUri,
if (klass != null) 'class': klass,
if (method != null) 'method': method,
'offset': offset,
if (scriptUri != null) 'scriptUri': scriptUri,
'isStatic': isStatic,
if (rootLibraryUri != null) 'rootLibraryUri': rootLibraryUri,
'useCachedCompilerOptionsAsBase': true,
}),
serverInfoFile,
);
if (response['success'] != true) {
final String errorMessage = switch (response) {
{'errorMessage': final String errorMessage} => errorMessage,
{'compilerOutputLines': final List<Object?> lines} => lines.join('\n'),
_ => 'Unknown error: $response',
};
throw new CompileException(errorMessage);
}
return new CompileExpressionResult(
kernelBytes: response['kernelBytes'] as String,
errorCount: response['errorCount'] as int,
compilerOutputLines:
(response['compilerOutputLines'] as List<Object?>?)?.cast<String>() ??
const [],
);
}