Files
sdk/pkg/analysis_server_client/lib/server.dart
T
Paul Berry a062462758 Bump developer experience packages to language version 3.12.
This CL is part of an effort to bump the SDK requirement to `3.12.0-0`
for all the packages in `pkg` that are not published to `pub`, so that
we can get better testing of the "private named parameters" feature.

(Packages that *are* published to `pub` can't be safely bumped yet,
because SDK 3.12 hasn't been released, and I don't want to block those
packages' ability to publish useful updates to customers.)

This change covers the following packages, which are owned by the
developer experience team:
- pkg/analysis_server_client
- pkg/linter
- pkg/server_plugin
- pkg/telemetry

Changes to `pubspec.yaml` files were made manually.

Changes to `.dart` files were made automatically (with a few
exceptions; see below), using `dart fix` to migrate to using private
named parameters where it is possible to do so without changing
semantics. Note that this migration is conservative; see
https://github.com/dart-lang/sdk/issues/58607 for details.

The exceptions are:
- pkg/analysis_server_client/lib/handler/notification_handler.dart
- pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
- pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart

These are code-generated files are checked by the trybots to make sure
they are correct. The code generator runs the formatter, and the
formatter's behavior depends on the current language version. To
minimize the risk of accidental behavioral changes, I addressed this
by manually running these files through `dart format` and then
verifying that the result matches what the code generator would
produce.

Change-Id: I5a0baa28904890a0b7e4234b12f587f66a6a6964
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/487621
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Auto-Submit: Paul Berry <paulberry@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2026-03-13 10:14:25 -07:00

197 lines
6.6 KiB
Dart

// Copyright (c) 2018, 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:async';
import 'dart:convert';
import 'dart:io';
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/src/server_base.dart';
import 'package:path/path.dart';
export 'package:analysis_server_client/src/server_base.dart'
show NotificationProcessor;
/// Instances of the class [Server] manage a server process,
/// and facilitate communication to and from the server.
///
/// Clients may not extend, implement or mix-in this class.
class Server extends ServerBase {
/// Server process object, or `null` if server hasn't been started yet
/// or if the server has already been stopped.
Process? _process;
/// The stderr subscription or `null` if either
/// [listenToOutput] has not been called or [stop] has been called.
StreamSubscription<String>? _stderrSubscription;
/// The stdout subscription or `null` if either
/// [listenToOutput] has not been called or [stop] has been called.
StreamSubscription<String>? _stdoutSubscription;
Server({super.listener, this._process, super.stdioPassthrough});
/// Force kill the server. Returns exit code future.
@override
Future<int> kill({String reason = 'none'}) {
listener?.killingServerProcess(reason);
final process = _process!;
_process = null;
process.kill();
return process.exitCode;
}
/// Start listening to output from the server,
/// and deliver notifications to [notificationProcessor].
@override
void listenToOutput({NotificationProcessor? notificationProcessor}) {
_stdoutSubscription = _process!.stdout
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((line) => outputProcessor(line, notificationProcessor));
_stderrSubscription = _process!.stderr
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((line) => errorProcessor(line, notificationProcessor));
}
/// Send a command to the server. An 'id' will be automatically assigned.
/// The returned [Future] will be completed when the server acknowledges
/// the command with a response.
/// If the server acknowledges the command with a normal (non-error) response,
/// the future will be completed with the 'result' field from the response.
/// If the server acknowledges the command with an error response,
/// the future will be completed with an error.
@override
Future<Map<String, Object?>?> send(
String method, Map<String, Object?>? params) =>
sendCommandWith(method, params, _process!.stdin.add);
/// Start the server.
///
/// If [profileServer] is `true`, the server will be started
/// with "--observe" and "--pause-isolates-on-exit", allowing the observatory
/// to be used.
///
/// If [serverPath] is specified, then that analysis server will be launched,
/// otherwise the analysis server snapshot in the SDK will be launched.
///
/// If [enableAsserts] is specified, then asserts will be enabled in the new
/// dart process for that server. This is typically just useful to enable
/// locally for debugging.
@override
Future start({
String? clientId,
String? clientVersion,
int? diagnosticPort,
String? instrumentationLogFile,
bool profileServer = false,
String? sdkPath,
String? serverPath,
int? servicePort,
bool suppressAnalytics = true,
bool useAnalysisHighlight2 = false,
bool enableAsserts = false,
String? dartBinary,
}) async {
if (_process != null) {
throw Exception('Process already started');
}
dartBinary ??= Platform.executable;
// The integration tests run 3x faster when run from snapshots
// (you need to run test.py with --use-sdk).
if (serverPath == null) {
// Look for snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'snapshots', 'analysis_server.dart.snapshot'));
if (!FileSystemEntity.isFileSync(serverPath)) {
// Look for dart-sdk/bin/snapshots/analysis_server.dart.snapshot.
serverPath = normalize(join(dirname(Platform.resolvedExecutable),
'dart-sdk', 'bin', 'snapshots', 'analysis_server.dart.snapshot'));
}
}
var arguments = <String>[];
//
// Add VM arguments.
//
if (profileServer) {
if (servicePort == null) {
arguments.add('--observe');
} else {
arguments.add('--observe=$servicePort');
}
arguments.add('--pause-isolates-on-exit');
} else if (servicePort != null) {
arguments.add('--enable-vm-service=$servicePort');
}
if (Platform.packageConfig != null) {
arguments.add('--packages=${Platform.packageConfig}');
}
if (enableAsserts) {
arguments.add('--enable-asserts');
}
//
// Add the server executable.
//
arguments.add(serverPath);
arguments.addAll(getServerArguments(
clientId: clientId,
clientVersion: clientVersion,
suppressAnalytics: suppressAnalytics,
diagnosticPort: diagnosticPort,
instrumentationLogFile: instrumentationLogFile,
sdkPath: sdkPath,
useAnalysisHighlight2: useAnalysisHighlight2));
listener?.startingServer(dartBinary, arguments);
final process = await Process.start(dartBinary, arguments);
_process = process;
// ignore: unawaited_futures
process.exitCode.then((int code) {
if (code != 0 && _process != null) {
// Report an error if server abruptly terminated
listener?.unexpectedStop(code);
}
});
}
/// Attempt to gracefully shutdown the server.
/// If that fails, then kill the process.
@override
Future<int> stop({Duration? timeLimit}) async {
timeLimit ??= const Duration(seconds: 5);
final process = _process;
if (process == null) {
// Process already exited
return -1;
}
final future = send(serverRequestShutdown, null);
_process = null;
await future
// fall through to wait for exit
.timeout(timeLimit, onTimeout: () {
return {};
}).whenComplete(() async {
await _stderrSubscription?.cancel();
_stderrSubscription = null;
await _stdoutSubscription?.cancel();
_stdoutSubscription = null;
});
return process.exitCode.timeout(
timeLimit,
onTimeout: () {
listener?.killingServerProcess('server failed to exit');
process.kill();
return process.exitCode;
},
);
}
}