Format frontend_server_client

Change-Id: Ibfdf6a7afedaf9e1486624a85e7d04437db13e49
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494460
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Nicholas Shahan <nshahan@google.com>
Auto-Submit: Jonas Jensen <jonasfj@google.com>
This commit is contained in:
Jonas Finnemann Jensen
2026-04-10 09:53:29 -07:00
committed by Commit Queue
parent ef6054d688
commit 8ffc0dddd0
6 changed files with 169 additions and 143 deletions
@@ -174,8 +174,8 @@ void _print(String message) {
}
void _prompt() => stdout.write(
'Enter a new message to print and recompile, or type `quit` to exit:',
);
'Enter a new message to print and recompile, or type `quit` to exit:',
);
final app = 'example/app/main.dart';
final dartSdkJs = p.join('.dart_tool', 'out', 'dart_sdk.js');
@@ -41,16 +41,19 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
final String? _mainModuleJs;
DartDevcFrontendServerClient._(
this._frontendServerClient, this._entrypoint, String moduleFormat)
: _bootstrapJs = moduleFormat == 'amd'
? generateAmdBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'dart_stack_trace_mapper.js',
entrypoint: _entrypoint)
: null,
_mainModuleJs = moduleFormat == 'amd'
? generateAmdMainModule(entrypoint: _entrypoint)
: null {
this._frontendServerClient,
this._entrypoint,
String moduleFormat,
) : _bootstrapJs = moduleFormat == 'amd'
? generateAmdBootstrapScript(
requireUrl: 'require.js',
mapperUrl: 'dart_stack_trace_mapper.js',
entrypoint: _entrypoint,
)
: null,
_mainModuleJs = moduleFormat == 'amd'
? generateAmdMainModule(entrypoint: _entrypoint)
: null {
_resetAssets();
}
@@ -86,7 +89,10 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
verbose: verbose,
);
return DartDevcFrontendServerClient._(
feServer, Uri.parse(entrypoint).path, dartdevcModuleFormat);
feServer,
Uri.parse(entrypoint).path,
dartdevcModuleFormat,
);
}
/// Returns the current bytes for the asset at [path].
@@ -121,11 +127,15 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
for (final entry in manifest.entries) {
final metadata = entry.value as Map<String, dynamic>;
final sourceOffsets = metadata['code'] as List;
_assets[entry.key] =
sourceBytes.sublist(sourceOffsets[0] as int, sourceOffsets[1] as int);
_assets[entry.key] = sourceBytes.sublist(
sourceOffsets[0] as int,
sourceOffsets[1] as int,
);
final sourceMapOffsets = metadata['sourcemap'] as List;
_assets['${entry.key}.map'] = sourceMapBytes.sublist(
sourceMapOffsets[0] as int, sourceMapOffsets[1] as int);
sourceMapOffsets[0] as int,
sourceMapOffsets[1] as int,
);
}
}
@@ -142,9 +152,9 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
required String klass,
required String libraryUri,
required List<String> typeDefinitions,
}) =>
throw UnsupportedError(
'Use `compileExpressionToJs` for dartdevc based clients');
}) => throw UnsupportedError(
'Use `compileExpressionToJs` for dartdevc based clients',
);
@override
Future<CompileResult> compileExpressionToJs({
@@ -155,15 +165,15 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
required String libraryUri,
required int line,
required String moduleName,
}) =>
_frontendServerClient.compileExpressionToJs(
expression: expression,
column: column,
jsFrameValues: jsFrameValues,
jsModules: jsModules,
libraryUri: libraryUri,
line: line,
moduleName: moduleName);
}) => _frontendServerClient.compileExpressionToJs(
expression: expression,
column: column,
jsFrameValues: jsFrameValues,
jsModules: jsModules,
libraryUri: libraryUri,
line: line,
moduleName: moduleName,
);
@override
void accept() {
@@ -201,11 +211,13 @@ class DartDevcFrontendServerClient implements FrontendServerClient {
}
final mainModuleJs = _mainModuleJs;
if (mainModuleJs != null) {
_assets['$_entrypoint.bootstrap.js'] =
Uint8List.fromList(utf8.encode(mainModuleJs));
_assets['$_entrypoint.bootstrap.js'] = Uint8List.fromList(
utf8.encode(mainModuleJs),
);
}
}
}
final _dartdevcPlatformKernel =
p.toUri(p.join(sdkDir, 'lib', '_internal', 'ddc_sdk.dill')).toString();
final _dartdevcPlatformKernel = p
.toUri(p.join(sdkDir, 'lib', '_internal', 'ddc_sdk.dill'))
.toString();
@@ -21,10 +21,12 @@ class FrontendServerClient {
_ClientState _state;
FrontendServerClient._(
this._entrypoint, this._feServer, this._feServerStdoutLines,
{bool? verbose})
: _verbose = verbose ?? false,
_state = _ClientState.waitingForFirstCompile {
this._entrypoint,
this._feServer,
this._feServerStdoutLines, {
bool? verbose,
}) : _verbose = verbose ?? false,
_state = _ClientState.waitingForFirstCompile {
_feServer.stderr.transform(utf8.decoder).listen(stderr.write);
}
@@ -89,49 +91,39 @@ class FrontendServerClient {
if (enabledExperiments != null)
for (final experiment in enabledExperiments)
'--enable-experiment=$experiment',
for (final source in additionalSources) ...[
'--source',
source,
],
if (nativeAssets != null) ...[
'--native-assets',
nativeAssets,
],
for (final source in additionalSources) ...['--source', source],
if (nativeAssets != null) ...['--native-assets', nativeAssets],
];
late final Process feServer;
if (frontendServerPath != null) {
feServer = await Process.start(
Platform.resolvedExecutable,
<String>[
if (debug) '--observe',
frontendServerPath,
...commonArguments,
],
);
feServer = await Process.start(Platform.resolvedExecutable, <String>[
if (debug) '--observe',
frontendServerPath,
...commonArguments,
]);
} else if (File(_feServerAotSnapshotPath).existsSync()) {
if (debug) {
throw ArgumentError('The debug argument cannot be set to true when the '
'frontendServerPath argument is omitted.');
throw ArgumentError(
'The debug argument cannot be set to true when the '
'frontendServerPath argument is omitted.',
);
}
feServer = await Process.start(
_dartAotRuntimePath,
<String>[_feServerAotSnapshotPath, ...commonArguments],
);
feServer = await Process.start(_dartAotRuntimePath, <String>[
_feServerAotSnapshotPath,
...commonArguments,
]);
} else {
// AOT snapshots cannot be generated on IA32, so we need this fallback
// branch until support for IA32 is dropped (https://dartbug.com/49969).
feServer = await Process.start(
Platform.resolvedExecutable,
<String>[
if (debug) '--observe',
_feServerAppJitSnapshotPath,
...commonArguments,
],
);
feServer = await Process.start(Platform.resolvedExecutable, <String>[
if (debug) '--observe',
_feServerAppJitSnapshotPath,
...commonArguments,
]);
}
final feServerStdoutLines = StreamQueue(feServer.stdout
.transform(utf8.decoder)
.transform(const LineSplitter()));
final feServerStdoutLines = StreamQueue(
feServer.stdout.transform(utf8.decoder).transform(const LineSplitter()),
);
// The frontend_server doesn't appear to recursively create files, so we
// need to make sure the output dir already exists.
@@ -162,16 +154,20 @@ class FrontendServerClient {
break;
case _ClientState.waitingForAcceptOrReject:
throw StateError(
'Previous `CompileResult` must be accepted or rejected by '
'calling `accept` or `reject`.');
'Previous `CompileResult` must be accepted or rejected by '
'calling `accept` or `reject`.',
);
case _ClientState.compiling:
throw StateError(
'App is already being compiled, you must wait for that to '
'complete and `accept` or `reject` the result before compiling '
'again.');
'App is already being compiled, you must wait for that to '
'complete and `accept` or `reject` the result before compiling '
'again.',
);
case _ClientState.rejecting:
throw StateError('Still waiting for previous `reject` call to finish. '
'You must await that before compiling again.');
throw StateError(
'Still waiting for previous `reject` call to finish. '
'You must await that before compiling again.',
);
}
_state = _ClientState.compiling;
@@ -180,8 +176,9 @@ class FrontendServerClient {
if (action == 'recompile') {
if (invalidatedUris == null || invalidatedUris.isEmpty) {
throw StateError(
'Subsequent compile invocations must provide a non-empty list '
'of invalidated uris.');
'Subsequent compile invocations must provide a non-empty list '
'of invalidated uris.',
);
}
final boundaryKey = generateUuidV4();
command.writeln(' $boundaryKey');
@@ -199,8 +196,8 @@ class FrontendServerClient {
final compilerOutputLines = <String>[];
var errorCount = 0;
String? outputDillPath;
while (
state != _CompileState.done && await _feServerStdoutLines.hasNext) {
while (state != _CompileState.done &&
await _feServerStdoutLines.hasNext) {
final line = await _nextInputLine();
switch (state) {
case _CompileState.started:
@@ -230,8 +227,9 @@ class FrontendServerClient {
removedSources.add(diffUri);
} else {
throw StateError(
'unrecognized diff line, should start with a + or - '
'but got: $line');
'unrecognized diff line, should start with a + or - '
'but got: $line',
);
}
continue;
case _CompileState.done:
@@ -240,11 +238,12 @@ class FrontendServerClient {
}
return CompileResult._(
dillOutput: outputDillPath,
errorCount: errorCount,
newSources: newSources,
removedSources: removedSources,
compilerOutputLines: compilerOutputLines);
dillOutput: outputDillPath,
errorCount: errorCount,
newSources: newSources,
removedSources: removedSources,
compilerOutputLines: compilerOutputLines,
);
} finally {
_state = _ClientState.waitingForAcceptOrReject;
}
@@ -258,8 +257,7 @@ class FrontendServerClient {
required String klass,
required String libraryUri,
required List<String> typeDefinitions,
}) =>
throw UnimplementedError();
}) => throw UnimplementedError();
/// TODO: Document
Future<CompileResult> compileExpressionToJs({
@@ -270,8 +268,7 @@ class FrontendServerClient {
required String libraryUri,
required int line,
required String moduleName,
}) =>
throw UnimplementedError();
}) => throw UnimplementedError();
/// Should be invoked when results of compilation are accepted by the client.
///
@@ -279,7 +276,8 @@ class FrontendServerClient {
void accept() {
if (_state != _ClientState.waitingForAcceptOrReject) {
throw StateError(
'Called `accept` but there was no previous compile to accept.');
'Called `accept` but there was no previous compile to accept.',
);
}
_sendCommand('accept');
_state = _ClientState.waitingForRecompile;
@@ -294,7 +292,8 @@ class FrontendServerClient {
Future<void> reject() async {
if (_state != _ClientState.waitingForAcceptOrReject) {
throw StateError(
'Called `reject` but there was no previous compile to reject.');
'Called `reject` but there was no previous compile to reject.',
);
}
_state = _ClientState.rejecting;
_sendCommand('reject');
@@ -307,8 +306,9 @@ class FrontendServerClient {
case _RejectState.started:
if (!line.startsWith('result')) {
throw StateError(
'Expected a line like `result <boundary-key>` after a `reject` '
'command, but got:\n$line');
'Expected a line like `result <boundary-key>` after a `reject` '
'command, but got:\n$line',
);
}
boundaryKey = line.split(' ').last;
rejectState = _RejectState.waitingForKey;
@@ -332,8 +332,9 @@ class FrontendServerClient {
void reset() {
if (_state == _ClientState.compiling) {
throw StateError(
'Called `reset` during an active compile, you must wait for that to '
'complete first.');
'Called `reset` during an active compile, you must wait for that to '
'complete first.',
);
}
_sendCommand('reset');
_state = _ClientState.waitingForRecompile;
@@ -377,12 +378,13 @@ class FrontendServerClient {
/// The result of a compile call.
class CompileResult {
const CompileResult._(
{required this.dillOutput,
required this.compilerOutputLines,
required this.errorCount,
required this.newSources,
required this.removedSources});
const CompileResult._({
required this.dillOutput,
required this.compilerOutputLines,
required this.errorCount,
required this.newSources,
required this.removedSources,
});
/// The produced dill output file, this will either be a full dill file, an
/// incremental dill file, or `null` if no file was produced.
@@ -432,24 +434,23 @@ enum _ClientState {
}
/// Frontend server interaction states for a `compile` call.
enum _CompileState {
started,
waitingForKey,
gettingSourceDiffs,
done,
}
enum _CompileState { started, waitingForKey, gettingSourceDiffs, done }
/// Frontend server interaction states for a `reject` call.
enum _RejectState {
started,
waitingForKey,
done,
}
enum _RejectState { started, waitingForKey, done }
final _dartAotRuntimePath = p.join(sdkDir, 'bin', 'dartaotruntime');
final _feServerAppJitSnapshotPath =
p.join(sdkDir, 'bin', 'snapshots', 'frontend_server.dart.snapshot');
final _feServerAppJitSnapshotPath = p.join(
sdkDir,
'bin',
'snapshots',
'frontend_server.dart.snapshot',
);
final _feServerAotSnapshotPath =
p.join(sdkDir, 'bin', 'snapshots', 'frontend_server_aot.dart.snapshot');
final _feServerAotSnapshotPath = p.join(
sdkDir,
'bin',
'snapshots',
'frontend_server_aot.dart.snapshot',
);
@@ -18,16 +18,26 @@ void main() {
final exampleFilePath = await pathFromNearestPackageConfig(
'example/vm_client.dart',
);
final process = await TestProcess.start(
Platform.resolvedExecutable, ['run', exampleFilePath]);
await expectLater(process.stdout,
emitsThrough(contains('done compiling example/app/main.dart')));
final process = await TestProcess.start(Platform.resolvedExecutable, [
'run',
exampleFilePath,
]);
await expectLater(
process.stdout, emitsThrough(contains('APP -> hello/world')));
await expectLater(process.stdout,
emitsThrough(contains('done recompiling example/app/main.dart')));
process.stdout,
emitsThrough(contains('done compiling example/app/main.dart')),
);
await expectLater(
process.stdout, emitsThrough(contains('APP -> goodbye/world')));
process.stdout,
emitsThrough(contains('APP -> hello/world')),
);
await expectLater(
process.stdout,
emitsThrough(contains('done recompiling example/app/main.dart')),
);
await expectLater(
process.stdout,
emitsThrough(contains('APP -> goodbye/world')),
);
expect(await process.exitCode, 0);
});
}
@@ -18,15 +18,19 @@ void main() {
final exampleFilePath = await pathFromNearestPackageConfig(
'example/web_client.dart',
);
final process = await TestProcess.start(
Platform.resolvedExecutable, ['run', exampleFilePath]);
await expectLater(process.stdout,
emitsThrough(contains('done compiling example/app/main.dart')));
final process = await TestProcess.start(Platform.resolvedExecutable, [
'run',
exampleFilePath,
]);
await expectLater(
process.stdout,
emitsThrough(contains('done compiling example/app/main.dart')),
);
process.stdin.writeln('new message');
await expectLater(
process.stdout,
emitsThrough(
contains('Recompile succeeded for example/app/main.dart')));
process.stdout,
emitsThrough(contains('Recompile succeeded for example/app/main.dart')),
);
process.stdin.writeln('quit');
expect(await process.exitCode, 0);
});
@@ -52,15 +52,13 @@ String get message => p.join('hello', 'world');
]),
]).create();
packageRoot = p.join(d.sandbox, 'a');
await Process.run(
Platform.resolvedExecutable,
[
'pub',
'get',
],
workingDirectory: packageRoot);
await Process.run(Platform.resolvedExecutable, [
'pub',
'get',
], workingDirectory: packageRoot);
packageConfig = (await findPackageConfig(Directory(packageRoot)))!;
packagesJsonPath = findNearestPackageConfigPath(Directory(packageRoot)) ??
packagesJsonPath =
findNearestPackageConfigPath(Directory(packageRoot)) ??
p.join(packageRoot, '.dart_tool', 'package_config.json');
});
@@ -207,8 +205,9 @@ String get message => p.join('hello', 'world');
});
test('can compile and recompile a dartdevc app', () async {
final entrypoint =
p.toUri(p.join(packageRoot, 'bin', 'main.dart')).toString();
final entrypoint = p
.toUri(p.join(packageRoot, 'bin', 'main.dart'))
.toString();
final dartDevcClient = client = await DartDevcFrontendServerClient.start(
entrypoint,
p.join(packageRoot, 'out.dill'),