Index: pkg/analyzer_cli/lib/src/build_mode.dart |
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart |
index 22562473d38804b0136364c79617a8efd2a4848e..b04535f2b34d88d43d679eb721f2949b0ae44f42 100644 |
--- a/pkg/analyzer_cli/lib/src/build_mode.dart |
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart |
@@ -4,10 +4,11 @@ |
library analyzer_cli.src.build_mode; |
-import 'dart:convert'; |
import 'dart:core' hide Resource; |
import 'dart:io' as io; |
+import 'package:protobuf/protobuf.dart'; |
+ |
import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit; |
import 'package:analyzer/dart/element/element.dart'; |
import 'package:analyzer/file_system/file_system.dart'; |
@@ -30,6 +31,9 @@ import 'package:analyzer_cli/src/driver.dart'; |
import 'package:analyzer_cli/src/error_formatter.dart'; |
import 'package:analyzer_cli/src/options.dart'; |
+import 'message_grouper.dart'; |
+import 'worker_protocol.pb.dart'; |
+ |
/** |
* Analyzer used when the "--build-mode" option is supplied. |
*/ |
@@ -284,28 +288,18 @@ class BuildMode { |
} |
/** |
- * Interface that every worker related data object has. |
- */ |
-abstract class WorkDataObject { |
- /** |
- * Translate the data in this class into a JSON map. |
- */ |
- Map<String, Object> toJson(); |
-} |
- |
-/** |
* Connection between a worker and input / output. |
*/ |
abstract class WorkerConnection { |
/** |
- * Read a new line. Block until a line is read. Return `null` if EOF. |
+ * Read a new [WorkRequest]. Returns [null] when there are no more requests. |
*/ |
- String readLineSync(); |
+ WorkRequest readRequest(); |
/** |
- * Write the given [json] as a new line to the output. |
+ * Write the given [response] as bytes to the output. |
*/ |
- void writeJson(Map<String, Object> json); |
+ void writeResponse(WorkResponse response); |
} |
/** |
@@ -322,8 +316,11 @@ class WorkerLoop { |
WorkerLoop(this.connection); |
- factory WorkerLoop.std() { |
- WorkerConnection connection = new _StdWorkerConnection(); |
+ factory WorkerLoop.std({io.Stdin stdinStream, io.Stdout stdoutStream}) { |
+ stdinStream ??= io.stdin; |
+ stdoutStream ??= io.stdout; |
+ WorkerConnection connection = |
+ new StdWorkerConnection(stdinStream, stdoutStream); |
return new WorkerLoop(connection); |
} |
@@ -339,7 +336,7 @@ class WorkerLoop { |
*/ |
bool performSingle() { |
try { |
- WorkRequest request = _readRequest(); |
+ WorkRequest request = connection.readRequest(); |
if (request == null) { |
return true; |
} |
@@ -351,11 +348,15 @@ class WorkerLoop { |
// Analyze and respond. |
analyze(options); |
String msg = _getErrorOutputBuffersText(); |
- _writeResponse(new WorkResponse(EXIT_CODE_OK, msg)); |
+ connection.writeResponse(new WorkResponse() |
+ ..exitCode = EXIT_CODE_OK |
+ ..output = msg); |
} catch (e, st) { |
String msg = _getErrorOutputBuffersText(); |
msg += '$e \n $st'; |
- _writeResponse(new WorkResponse(EXIT_CODE_ERROR, msg)); |
+ connection.writeResponse(new WorkResponse() |
+ ..exitCode = EXIT_CODE_ERROR |
+ ..output = msg); |
} |
return false; |
} |
@@ -389,182 +390,34 @@ class WorkerLoop { |
} |
return msg; |
} |
- |
- /** |
- * Read a new [WorkRequest]. Return `null` if EOF. |
- * Throw [ArgumentError] if cannot be parsed. |
- */ |
- WorkRequest _readRequest() { |
- String line = connection.readLineSync(); |
- if (line == null) { |
- return null; |
- } |
- Object json = JSON.decode(line); |
- if (json is Map) { |
- return new WorkRequest.fromJson(json); |
- } else { |
- throw new ArgumentError('The request line is not a JSON object: $line'); |
- } |
- } |
- |
- void _writeResponse(WorkResponse response) { |
- Map<String, Object> json = response.toJson(); |
- connection.writeJson(json); |
- } |
} |
/** |
- * Input file. |
+ * Default implementation of [WorkerConnection] that works with stdio. |
*/ |
-class WorkInput implements WorkDataObject { |
- final String path; |
- final List<int> digest; |
+class StdWorkerConnection implements WorkerConnection { |
+ final MessageGrouper _messageGrouper; |
+ final io.Stdout _stdoutStream; |
- WorkInput(this.path, this.digest); |
- |
- factory WorkInput.fromJson(Map<String, Object> json) { |
- // Parse path. |
- Object path2 = json['path']; |
- if (path2 == null) { |
- throw new ArgumentError('The field "path" is missing.'); |
- } |
- if (path2 is! String) { |
- throw new ArgumentError('The field "path" must be a string.'); |
- } |
- // Parse digest. |
- List<int> digest = const <int>[]; |
- { |
- Object digestJson = json['digest']; |
- if (digestJson != null) { |
- if (digestJson is List && digestJson.every((e) => e is int)) { |
- digest = digestJson; |
- } else { |
- throw new ArgumentError( |
- 'The field "digest" should be a list of int.'); |
- } |
- } |
- } |
- // OK |
- return new WorkInput(path2, digest); |
- } |
+ StdWorkerConnection(io.Stdin stdinStream, this._stdoutStream) |
+ : _messageGrouper = new MessageGrouper(stdinStream); |
@override |
- Map<String, Object> toJson() { |
- Map<String, Object> json = <String, Object>{}; |
- if (path != null) { |
- json['path'] = path; |
- } |
- if (digest != null) { |
- json['digest'] = digest; |
- } |
- return json; |
- } |
-} |
+ WorkRequest readRequest() { |
+ var buffer = _messageGrouper.next; |
+ if (buffer == null) return null; |
-/** |
- * Single work unit that Bazel sends to the worker. |
- */ |
-class WorkRequest implements WorkDataObject { |
- /** |
- * Command line arguments for this request. |
- */ |
- final List<String> arguments; |
- |
- /** |
- * Input files that the worker is allowed to read during execution of this |
- * request. |
- */ |
- final List<WorkInput> inputs; |
- |
- WorkRequest(this.arguments, this.inputs); |
- |
- factory WorkRequest.fromJson(Map<String, Object> json) { |
- // Parse arguments. |
- List<String> arguments = const <String>[]; |
- { |
- Object argumentsJson = json['arguments']; |
- if (argumentsJson != null) { |
- if (argumentsJson is List && argumentsJson.every((e) => e is String)) { |
- arguments = argumentsJson; |
- } else { |
- throw new ArgumentError( |
- 'The field "arguments" should be a list of strings.'); |
- } |
- } |
- } |
- // Parse inputs. |
- List<WorkInput> inputs = const <WorkInput>[]; |
- { |
- Object inputsJson = json['inputs']; |
- if (inputsJson != null) { |
- if (inputsJson is List && |
- inputsJson.every((e) { |
- return e is Map && e.keys.every((key) => key is String); |
- })) { |
- inputs = inputsJson |
- .map((Map input) => new WorkInput.fromJson(input)) |
- .toList(); |
- } else { |
- throw new ArgumentError( |
- 'The field "inputs" should be a list of objects.'); |
- } |
- } |
- } |
- // No inputs. |
- if (arguments.isEmpty && inputs.isEmpty) { |
- throw new ArgumentError('Both "arguments" and "inputs" cannot be empty.'); |
- } |
- // OK |
- return new WorkRequest(arguments, inputs); |
+ return new WorkRequest.fromBuffer(buffer); |
} |
@override |
- Map<String, Object> toJson() { |
- Map<String, Object> json = <String, Object>{}; |
- if (arguments != null) { |
- json['arguments'] = arguments; |
- } |
- if (inputs != null) { |
- json['inputs'] = inputs.map((input) => input.toJson()).toList(); |
- } |
- return json; |
- } |
-} |
+ void writeResponse(WorkResponse response) { |
+ var responseBuffer = response.writeToBuffer(); |
-/** |
- * Result that the worker sends back to Bazel when it finished its work on a |
- * [WorkRequest] message. |
- */ |
-class WorkResponse implements WorkDataObject { |
- final int exitCode; |
- final String output; |
- |
- WorkResponse(this.exitCode, this.output); |
- |
- @override |
- Map<String, Object> toJson() { |
- Map<String, Object> json = <String, Object>{}; |
- if (exitCode != null) { |
- json['exit_code'] = exitCode; |
- } |
- if (output != null) { |
- json['output'] = output; |
- } |
- return json; |
- } |
-} |
+ var writer = new CodedBufferWriter(); |
+ writer.writeInt32NoTag(responseBuffer.length); |
+ writer.writeRawBytes(responseBuffer); |
-/** |
- * Default implementation of [WorkerConnection] that works with stdio. |
- */ |
-class _StdWorkerConnection implements WorkerConnection { |
- @override |
- String readLineSync() { |
- return io.stdin.readLineSync(); |
- } |
- |
- @override |
- void writeJson(Map<String, Object> json) { |
- io.stdout.writeln(JSON.encode(json)); |
+ _stdoutStream.add(writer.toBuffer()); |
} |
} |