diff --git a/pkg/analysis_server_client/lib/analysis_server_client.dart b/pkg/analysis_server_client/lib/analysis_server_client.dart deleted file mode 100644 index 484397baf5f..00000000000 --- a/pkg/analysis_server_client/lib/analysis_server_client.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2017, 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'; - -/// Type of callbacks used to process notification. -typedef void NotificationProcessor(String event, Map params); - -/// Instances of the class [AnalysisServerClient] manage a connection to an -/// [AnalysisServer] process, and facilitate communication to and from the -/// client/user. -class AnalysisServerClient { - /// AnalysisServer process object, or null if the server has been shut down. - final Process _process; - - /// Commands that have been sent to the server but not yet acknowledged, - /// and the [Completer] objects which should be completed when - /// acknowledgement is received. - final Map _pendingCommands = {}; - - /// Number which should be used to compute the 'id' to send to the next - /// command sent to the server. - int _nextId = 0; - - AnalysisServerClient(this._process); - - /// Return a future that will complete when all commands that have been - /// sent to the server so far have been flushed to the OS buffer. - Future flushCommands() { - return _process.stdin.flush(); - } - - /// Force kill the server. Returns exit code future. - Future kill() { - _process.kill(); - return _process.exitCode; - } - - void listenToOutput({NotificationProcessor notificationProcessor}) { - _process.stdout - .transform((new Utf8Codec()).decoder) - .transform(new LineSplitter()) - .listen((String line) { - String trimmedLine = line.trim(); - if (trimmedLine.startsWith('Observatory listening on ')) { - return; - } - final result = json.decoder.convert(trimmedLine) as Map; - if (result.containsKey('id')) { - final id = result['id'] as String; - final completer = _pendingCommands.remove(id); - - if (result.containsKey('error')) { - completer.completeError(new ServerErrorMessage(result['error'])); - } else { - completer.complete(result['result']); - } - } else if (notificationProcessor != null && result.containsKey('event')) { - // Message is a notification. It should have an event and possibly - // params. - notificationProcessor(result['event'], result['params']); - } - }); - } - - /// Sends 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. - Future send(String method, Map params) { - String id = '${_nextId++}'; - Map command = { - 'id': id, - 'method': method - }; - if (params != null) { - command['params'] = params; - } - Completer completer = new Completer(); - _pendingCommands[id] = completer; - String commandAsJson = json.encode(command); - _process.stdin.add(utf8.encoder.convert('$commandAsJson\n')); - return completer.future; - } -} - -class ServerErrorMessage { - final Map errorJson; - - ServerErrorMessage(this.errorJson); - - String get code => errorJson['code'].toString(); - String get message => errorJson['message']; - String get stackTrace => errorJson['stackTrace']; -} diff --git a/pkg/analysis_server_client/lib/recording_server.dart b/pkg/analysis_server_client/lib/recording_server.dart new file mode 100644 index 00000000000..1dc21226d43 --- /dev/null +++ b/pkg/analysis_server_client/lib/recording_server.dart @@ -0,0 +1,55 @@ +// 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 'package:analysis_server_client/server.dart'; + +/// A subclass of [Server] that caches all messages exchanged with the server. +/// This is primarily used when testing and debugging the analysis server. +/// Most clients will want to use [Server] rather than this class. +class RecordingServer extends Server { + /// True if we are currently printing out messages exchanged with the server. + bool _echoMessages = false; + + /// Messages which have been exchanged with the server; we buffer these + /// up until the test finishes, so that they can be examined in the debugger + /// or printed out in response to a call to [echoMessages]. + final _messages = []; + + /// Print out any messages exchanged with the server. If some messages have + /// already been exchanged with the server, they are printed out immediately. + void echoMessages() { + if (_echoMessages) { + return; + } + _echoMessages = true; + for (String line in _messages) { + print(line); + } + } + + @override + Future kill([String reason = 'none']) { + echoMessages(); + return super.kill(reason); + } + + @override + void logBadDataFromServer(String details, {bool silent: false}) { + echoMessages(); + super.logBadDataFromServer(details, silent: silent); + } + + /// Record a message that was exchanged with the server, + /// and print it out if [echoMessages] has been called. + @override + void logMessage(String prefix, String details) { + String line = '$currentElapseTime: $prefix $details'; + if (_echoMessages) { + print(line); + } + _messages.add(line); + } +} diff --git a/pkg/analysis_server_client/lib/server.dart b/pkg/analysis_server_client/lib/server.dart new file mode 100644 index 00000000000..79a39b3527f --- /dev/null +++ b/pkg/analysis_server_client/lib/server.dart @@ -0,0 +1,275 @@ +// 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:path/path.dart'; + +/// Type of callbacks used to process notifications. +typedef void NotificationProcessor(String event, Map params); + +/// Instances of the class [Server] manage a connection to a server process, +/// and facilitate communication to and from the server. +class Server { + /// Server process object, or `null` if server hasn't been started yet. + Process _process; + + /// Commands that have been sent to the server but not yet acknowledged, + /// and the [Completer] objects which should be completed + /// when acknowledgement is received. + final _pendingCommands = >>{}; + + /// Number which should be used to compute the 'id' + /// to send in the next command sent to the server. + int _nextId = 0; + + /// True if we've received bad data from the server. + bool _receivedBadDataFromServer = false; + + /// Stopwatch that we use to generate timing information for debug output. + Stopwatch _time = new Stopwatch(); + + /// The [currentElapseTime] at which the last communication was received from + /// the server or `null` if no communication has been received. + double lastCommunicationTime; + + Server([Process process]) : this._process = process; + + /// The current elapse time (seconds) since the server was started. + double get currentElapseTime => _time.elapsedTicks / _time.frequency; + + /// Future that completes when the server process exits. + Future get exitCode => _process.exitCode; + + /// Return a future that will complete when all commands that have been sent + /// to the server so far have been flushed to the OS buffer. + Future flushCommands() => _process.stdin.flush(); + + /// Force kill the server. Returns exit code future. + Future kill([String reason = 'none']) { + logMessage('FORCIBLY TERMINATING PROCESS: ', reason); + _process.kill(); + return _process.exitCode; + } + + /// Start listening to output from the server, + /// and deliver notifications to [notificationProcessor]. + void listenToOutput({NotificationProcessor notificationProcessor}) { + _process.stdout + .transform(utf8.decoder) + .transform(new LineSplitter()) + .listen((String line) { + lastCommunicationTime = currentElapseTime; + String trimmedLine = line.trim(); + + // Guard against lines like: + // {"event":"server.connected","params":{...}}Observatory listening on ... + const observatoryMessage = 'Observatory listening on '; + if (trimmedLine.contains(observatoryMessage)) { + trimmedLine = trimmedLine + .substring(0, trimmedLine.indexOf(observatoryMessage)) + .trim(); + } + if (trimmedLine.isEmpty) { + return; + } + + logMessage('<== ', trimmedLine); + Map message; + try { + message = json.decoder.convert(trimmedLine); + } catch (exception) { + logBadDataFromServer('JSON decode failure: $exception'); + return; + } + + final id = message['id']; + if (id != null) { + // Handle response + final completer = _pendingCommands.remove(id); + if (completer == null) { + throw 'Unexpected response from server: id=$id'; + } + if (message.containsKey('error')) { + completer.completeError(new ServerErrorMessage(message)); + } else { + completer.complete(message['result']); + } + } else { + // Handle notification + final String event = message['event']; + if (event != null) { + if (notificationProcessor != null) { + notificationProcessor(event, message['params']); + } + } else { + logBadDataFromServer('Unexpected message from server'); + } + } + }); + + _process.stderr + .transform(utf8.decoder) + .transform(new LineSplitter()) + .listen((String line) { + String trimmedLine = line.trim(); + logMessage('ERR: ', trimmedLine); + logBadDataFromServer('Message received on stderr', silent: true); + }); + } + + /// 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. + Future> send( + String method, Map params) { + String id = '${_nextId++}'; + Map command = { + 'id': id, + 'method': method + }; + if (params != null) { + command['params'] = params; + } + final completer = new Completer>(); + _pendingCommands[id] = completer; + String line = json.encode(command); + logMessage('==> ', line); + _process.stdin.add(utf8.encoder.convert("$line\n")); + return completer.future; + } + + /** + * 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. + */ + Future start({ + int diagnosticPort, + String instrumentationLogFile, + bool profileServer: false, + String sdkPath, + String serverPath, + int servicesPort, + bool suppressAnalytics: true, + bool useAnalysisHighlight2: false, + }) async { + if (_process != null) { + throw new Exception('Process already started'); + } + _time.start(); + String 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')); + } + } + + List arguments = []; + // + // Add VM arguments. + // + if (profileServer) { + if (servicesPort == null) { + arguments.add('--observe'); + } else { + arguments.add('--observe=$servicesPort'); + } + arguments.add('--pause-isolates-on-exit'); + } else if (servicesPort != null) { + arguments.add('--enable-vm-service=$servicesPort'); + } + if (Platform.packageConfig != null) { + arguments.add('--packages=${Platform.packageConfig}'); + } + // + // Add the server executable. + // + arguments.add(serverPath); + // + // Add server arguments. + // + if (suppressAnalytics) { + arguments.add('--suppress-analytics'); + } + if (diagnosticPort != null) { + arguments.add('--port'); + arguments.add(diagnosticPort.toString()); + } + if (instrumentationLogFile != null) { + arguments.add('--instrumentation-log-file=$instrumentationLogFile'); + } + if (sdkPath != null) { + arguments.add('--sdk=$sdkPath'); + } + if (useAnalysisHighlight2) { + arguments.add('--useAnalysisHighlight2'); + } + logMessage( + 'Starting analysis server: ', '$dartBinary ${arguments.join(' ')}'); + _process = await Process.start(dartBinary, arguments); + _process.exitCode.then((int code) { + if (code != 0) { + logBadDataFromServer('server terminated with exit code $code'); + } + }); + } + + /// Deal with bad data received from the server. + void logBadDataFromServer(String details, {bool silent: false}) { + if (!silent) { + logMessage('BAD DATA FROM SERVER: ', details); + } + if (_receivedBadDataFromServer) { + // We're already dealing with it. + return; + } + _receivedBadDataFromServer = true; + // Give the server 1 second to continue outputting bad data + // such as outputting a stacktrace. + new Future.delayed(new Duration(seconds: 1), () { + throw 'Bad data received from server: $details'; + }); + } + + /// Log a message that was exchanged with the server. + /// Subclasses may override as needed. + void logMessage(String prefix, String details) { + // no-op + } +} + +/// An error result from a server request. +class ServerErrorMessage { + final Map message; + + ServerErrorMessage(this.message); + + Map get error => message['error']; + get errorCode => error['code']; + get errorMessage => error['message']; + get stackTrace => error['stackTrace']; + + String toString() => message.toString(); +} diff --git a/pkg/analysis_server_client/test/all.dart b/pkg/analysis_server_client/test/all.dart new file mode 100644 index 00000000000..c2ecd6b3c0e --- /dev/null +++ b/pkg/analysis_server_client/test/all.dart @@ -0,0 +1,9 @@ +// 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 'server_test.dart' as server_test; + +main() { + server_test.main(); +} diff --git a/pkg/analysis_server_client/test/analysis_server_client_test.dart b/pkg/analysis_server_client/test/server_test.dart similarity index 79% rename from pkg/analysis_server_client/test/analysis_server_client_test.dart rename to pkg/analysis_server_client/test/server_test.dart index c07ea61b9b8..ff2be0e1d22 100644 --- a/pkg/analysis_server_client/test/analysis_server_client_test.dart +++ b/pkg/analysis_server_client/test/server_test.dart @@ -6,23 +6,24 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:analysis_server_client/analysis_server_client.dart'; +import 'package:analysis_server_client/server.dart'; import 'package:test/test.dart'; void main() { MockProcess process; - AnalysisServerClient serverWrapper; + Server server; setUp(() async { process = new MockProcess(); - serverWrapper = new AnalysisServerClient(process); + server = new Server(process); }); test('test_listenToOutput_good', () async { process.stdout = _goodMessage(); + process.stderr = _noMessage(); - final future = serverWrapper.send('blahMethod', null); - serverWrapper.listenToOutput(); + final future = server.send('blahMethod', null); + server.listenToOutput(); final response = await future; expect(response, const TypeMatcher()); @@ -32,19 +33,22 @@ void main() { test('test_listenToOutput_error', () async { process.stdout = _badMessage(); - final future = serverWrapper.send('blahMethod', null); + process.stderr = _noMessage(); + + final future = server.send('blahMethod', null); future.catchError((e) { expect(e, const TypeMatcher()); - final e2 = e as ServerErrorMessage; - expect(e2.code, 'someErrorCode'); - expect(e2.message, 'something went wrong'); - expect(e2.stackTrace, 'some long stack trace'); + final error = e as ServerErrorMessage; + expect(error.errorCode, 'someErrorCode'); + expect(error.errorMessage, 'something went wrong'); + expect(error.stackTrace, 'some long stack trace'); }); - serverWrapper.listenToOutput(); + server.listenToOutput(); }); test('test_listenToOutput_event', () async { process.stdout = _eventMessage(); + process.stderr = _noMessage(); void eventHandler(String event, Map params) { expect(event, 'fooEvent'); @@ -53,8 +57,8 @@ void main() { expect(params['baz'] as String, 'bang'); } - serverWrapper.send('blahMethod', null); - serverWrapper.listenToOutput(notificationProcessor: eventHandler); + server.send('blahMethod', null); + server.listenToOutput(notificationProcessor: eventHandler); }); } @@ -88,6 +92,10 @@ Stream> _goodMessage() async* { yield utf8.encoder.convert(json.encode(sampleJson)); } +Stream> _noMessage() async* { + yield utf8.encoder.convert(''); +} + class MockProcess implements Process { @override Stream> stderr; diff --git a/pkg/dartfix/lib/src/driver.dart b/pkg/dartfix/lib/src/driver.dart index 4dc2680e06d..4cdae40ccac 100644 --- a/pkg/dartfix/lib/src/driver.dart +++ b/pkg/dartfix/lib/src/driver.dart @@ -6,10 +6,11 @@ import 'dart:async'; import 'dart:io' show File, Platform; import 'package:analysis_server_client/protocol.dart'; +import 'package:analysis_server_client/server.dart'; import 'package:cli_util/cli_logging.dart'; import 'package:dartfix/src/context.dart'; +import 'package:dartfix/src/verbose_server.dart'; import 'package:dartfix/src/options.dart'; -import 'package:dartfix/src/server.dart'; import 'package:path/path.dart' as path; class Driver { @@ -25,11 +26,24 @@ class Driver { Ansi get ansi => logger.ansi; - bool get runAnalysisServerFromSource { - // Automatically run analysis server from source - // if this command line tool is being run from source - // within the source tree. - return Server.findRoot() != null; + /// Return the analysis_server executable by proceeding upward + /// until finding the Dart SDK repository root then returning + /// the analysis_server executable within the repository. + /// Return `null` if it cannot be found. + String findServerPath() { + String pathname = Platform.script.toFilePath(); + while (true) { + String parent = path.dirname(pathname); + if (parent.length >= pathname.length) { + return null; + } + String serverPath = + path.join(parent, 'pkg', 'analysis_server', 'bin', 'server.dart'); + if (new File(serverPath).existsSync()) { + return serverPath; + } + pathname = parent; + } } Future start(List args) async { @@ -66,20 +80,22 @@ class Driver { } Future startServer(Options options) async { - server = new Server(logger); + server = logger.isVerbose ? new VerboseServer(logger) : new Server(); const connectTimeout = const Duration(seconds: 15); serverConnected = new Completer(); if (options.verbose) { - server.debugStdio(); logger.trace('Dart SDK version ${Platform.version}'); logger.trace(' ${Platform.resolvedExecutable}'); logger.trace('dartfix'); logger.trace(' ${Platform.script.toFilePath()}'); } - final runFromSource = runAnalysisServerFromSource; - logger.trace(runFromSource ? 'Starting from source...' : 'Starting...'); - await server.start(sdkPath: options.sdkPath, useSnapshot: !runFromSource); - server.listenToOutput(dispatchNotification); + // Automatically run analysis server from source + // if this command line tool is being run from source within the SDK repo. + String serverPath = findServerPath(); + logger + .trace(serverPath != null ? 'Starting from source...' : 'Starting...'); + await server.start(sdkPath: options.sdkPath, serverPath: serverPath); + server.listenToOutput(notificationProcessor: handleEvent); await serverConnected.future.timeout(connectTimeout, onTimeout: () { logger.stderr('Failed to connect to server'); context.exit(15); @@ -192,7 +208,7 @@ class Driver { /// Dispatch the notification named [event], and containing parameters /// [params], to the appropriate stream. - void dispatchNotification(String event, params) { + void handleEvent(String event, params) { ResponseDecoder decoder = new ResponseDecoder(null); switch (event) { case SERVER_NOTIFICATION_CONNECTED: diff --git a/pkg/dartfix/lib/src/server.dart b/pkg/dartfix/lib/src/server.dart deleted file mode 100644 index 525610e1b5d..00000000000 --- a/pkg/dartfix/lib/src/server.dart +++ /dev/null @@ -1,360 +0,0 @@ -// 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:cli_util/cli_logging.dart'; -import 'package:path/path.dart'; - -/** - * Type of callbacks used to process notifications. - */ -typedef void NotificationProcessor(String event, params); - -/** - * Instances of the class [Server] manage a connection to a server process, and - * facilitate communication to and from the server. - */ -class Server { - /** - * Server process object, or null if server hasn't been started yet. - */ - Process _process; - - /** - * Commands that have been sent to the server but not yet acknowledged, and - * the [Completer] objects which should be completed when acknowledgement is - * received. - */ - final Map>> _pendingCommands = - >>{}; - - /** - * Number which should be used to compute the 'id' to send in the next command - * sent to the server. - */ - int _nextId = 0; - - /** - * Messages which have been exchanged with the server; we buffer these - * up until the test finishes, so that they can be examined in the debugger - * or printed out in response to a call to [debugStdio]. - */ - final List _recordedStdio = []; - - /** - * True if we are currently printing out messages exchanged with the server. - */ - bool _debuggingStdio = false; - - /** - * True if we've received bad data from the server, and we are aborting the - * test. - */ - bool _receivedBadDataFromServer = false; - - /** - * Stopwatch that we use to generate timing information for debug output. - */ - Stopwatch _time = new Stopwatch(); - - /** - * The [currentElapseTime] at which the last communication was received from the server - * or `null` if no communication has been received. - */ - double lastCommunicationTime; - - /** - * The current elapse time (seconds) since the server was started. - */ - double get currentElapseTime => _time.elapsedTicks / _time.frequency; - - /** - * Future that completes when the server process exits. - */ - Future get exitCode => _process.exitCode; - - final Logger logger; - - Server(this.logger); - - /** - * Print out any messages exchanged with the server. If some messages have - * already been exchanged with the server, they are printed out immediately. - */ - void debugStdio() { - if (_debuggingStdio) { - return; - } - _debuggingStdio = true; - for (String line in _recordedStdio) { - logger.trace(line); - } - } - - /** - * Find the root directory of the analysis_server package by proceeding - * upward until finding the Dart SDK repository root then returning - * the analysis_server package root within the repository. - * Return `null` if it cannot be found. - */ - static String findRoot([String pathname]) { - pathname ??= Platform.script.toFilePath(windows: Platform.isWindows); - while (true) { - String parent = dirname(pathname); - if (parent.length >= pathname.length) { - return null; - } - String root = normalize(join(parent, 'pkg', 'analysis_server')); - String server = join(root, 'bin', 'server.dart'); - if (new File(server).existsSync()) { - return root; - } - pathname = parent; - } - } - - /** - * Return a future that will complete when all commands that have been sent - * to the server so far have been flushed to the OS buffer. - */ - Future flushCommands() { - return _process.stdin.flush(); - } - - /** - * Stop the server. - */ - Future kill(String reason) { - debugStdio(); - _recordStdio('FORCIBLY TERMINATING PROCESS: $reason'); - _process.kill(); - return _process.exitCode; - } - - /** - * Start listening to output from the server, and deliver notifications to - * [notificationProcessor]. - */ - void listenToOutput(NotificationProcessor notificationProcessor) { - _process.stdout - .transform(utf8.decoder) - .transform(new LineSplitter()) - .listen((String line) { - lastCommunicationTime = currentElapseTime; - String trimmedLine = line.trim(); - - // Guard against lines like: - // {"event":"server.connected","params":{...}}Observatory listening on ... - final String observatoryMessage = 'Observatory listening on '; - if (trimmedLine.contains(observatoryMessage)) { - trimmedLine = trimmedLine - .substring(0, trimmedLine.indexOf(observatoryMessage)) - .trim(); - } - if (trimmedLine.isEmpty) { - return; - } - - _recordStdio('<== $trimmedLine'); - var message; - try { - message = json.decoder.convert(trimmedLine); - } catch (exception) { - _badDataFromServer('JSON decode failure: $exception'); - return; - } - Map messageAsMap = message; - if (messageAsMap.containsKey('id')) { - String id = message['id']; - Completer> completer = _pendingCommands[id]; - if (completer == null) { - throw 'Unexpected response from server: id=$id'; - } else { - _pendingCommands.remove(id); - } - if (messageAsMap.containsKey('error')) { - completer.completeError(new ServerErrorMessage(messageAsMap)); - } else { - Map result = messageAsMap['result']; - completer.complete(result); - } - } else { - String event = messageAsMap['event']; - notificationProcessor(event, messageAsMap['params']); - } - }); - _process.stderr - .transform((new Utf8Codec()).decoder) - .transform(new LineSplitter()) - .listen((String line) { - String trimmedLine = line.trim(); - _recordStdio('ERR: $trimmedLine'); - _badDataFromServer('Message received on stderr', silent: true); - }); - } - - /** - * 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. - */ - Future> send( - String method, Map params) { - String id = '${_nextId++}'; - Map command = { - 'id': id, - 'method': method - }; - if (params != null) { - command['params'] = params; - } - Completer> completer = - new Completer>(); - _pendingCommands[id] = completer; - String line = json.encode(command); - _recordStdio('==> $line'); - _process.stdin.add(utf8.encoder.convert("$line\n")); - return completer.future; - } - - /** - * 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. - */ - Future start({ - int diagnosticPort, - String instrumentationLogFile, - bool profileServer: false, - String sdkPath, - int servicesPort, - bool useAnalysisHighlight2: false, - bool useSnapshot: true, - }) async { - if (_process != null) { - throw new Exception('Process already started'); - } - _time.start(); - String dartBinary = Platform.executable; - - String serverPath; - - // The integration tests run 3x faster when run from snapshots (you need to - // run test.py with --use-sdk). - if (useSnapshot) { - // 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')); - } - } else { - String rootDir = Server.findRoot(); - if (rootDir == null) { - throw new Exception("Can't find analysis server root directory"); - } - serverPath = normalize(join(rootDir, 'bin', 'server.dart')); - } - - List arguments = []; - // - // Add VM arguments. - // - if (profileServer) { - if (servicesPort == null) { - arguments.add('--observe'); - } else { - arguments.add('--observe=$servicesPort'); - } - arguments.add('--pause-isolates-on-exit'); - } else if (servicesPort != null) { - arguments.add('--enable-vm-service=$servicesPort'); - } - if (Platform.packageConfig != null) { - arguments.add('--packages=${Platform.packageConfig}'); - } - // - // Add the server executable. - // - arguments.add(serverPath); - // - // Add server arguments. - // - arguments.add('--suppress-analytics'); - if (diagnosticPort != null) { - arguments.add('--port'); - arguments.add(diagnosticPort.toString()); - } - if (instrumentationLogFile != null) { - arguments.add('--instrumentation-log-file=$instrumentationLogFile'); - } - if (sdkPath != null) { - arguments.add('--sdk=$sdkPath'); - } - if (useAnalysisHighlight2) { - arguments.add('--useAnalysisHighlight2'); - } - _process = await Process.start(dartBinary, arguments); - _process.exitCode.then((int code) { - if (code != 0) { - _badDataFromServer('server terminated with exit code $code'); - } - }); - } - - /** - * Deal with bad data received from the server. - */ - void _badDataFromServer(String details, {bool silent: false}) { - if (!silent) { - _recordStdio('BAD DATA FROM SERVER: $details'); - } - if (_receivedBadDataFromServer) { - // We're already dealing with it. - return; - } - _receivedBadDataFromServer = true; - debugStdio(); - // Give the server 1 second to continue outputting bad data - // such as outputting a stacktrace. - new Future.delayed(new Duration(seconds: 1), () { - throw 'Bad data received from server: $details'; - }); - } - - /** - * Record a message that was exchanged with the server, and print it out if - * [debugStdio] has been called. - */ - void _recordStdio(String line) { - double elapsedTime = currentElapseTime; - line = "$elapsedTime: $line"; - if (_debuggingStdio) { - logger.trace(line); - } - _recordedStdio.add(line); - } -} - -/** - * An error result from a server request. - */ -class ServerErrorMessage { - final Map message; - - ServerErrorMessage(this.message); - - dynamic get error => message['error']; - - String toString() => message.toString(); -} diff --git a/pkg/dartfix/lib/src/verbose_server.dart b/pkg/dartfix/lib/src/verbose_server.dart new file mode 100644 index 00000000000..1b09d3dff94 --- /dev/null +++ b/pkg/dartfix/lib/src/verbose_server.dart @@ -0,0 +1,17 @@ +// 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 'package:analysis_server_client/server.dart'; +import 'package:cli_util/cli_logging.dart'; + +class VerboseServer extends Server { + final Logger logger; + + VerboseServer(this.logger); + + @override + void logMessage(String prefix, String details) { + logger.trace('$currentElapseTime: $prefix $details'); + } +}