OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library runtime.tools.kernel_service; | 4 library runtime.tools.kernel_service; |
5 | 5 |
6 // This is an interface to the Dart Kernel parser and Kernel binary generator. | 6 // This is an interface to the Dart Kernel parser and Kernel binary generator. |
7 // | 7 // |
8 // It is used by the kernel-isolate to load Dart source code and generate | 8 // It is used by the kernel-isolate to load Dart source code and generate |
9 // Kernel binary format. | 9 // Kernel binary format. |
10 // | 10 // |
| 11 // This is either invoked as the root script of the Kernel isolate when used |
| 12 // as a part of |
| 13 // |
| 14 // dart --dfe=runtime/tools/kernel-service.dart ... |
| 15 // |
| 16 // invocation or it is invoked as a standalone script to perform batch mode |
| 17 // compilation requested via an HTTP interface |
| 18 // |
| 19 // dart runtime/tools/kernel-service.dart --batch |
| 20 // |
| 21 // The port for the batch mode worker is controlled by DFE_WORKER_PORT |
| 22 // environment declarations (set by -DDFE_WORKER_PORT=... command line flag). |
| 23 // When not set (or set to 0) an ephemeral port returned by the OS is used |
| 24 // instead. |
| 25 // |
| 26 // When this script is used as a Kernel isolate root script and DFE_WORKER_PORT |
| 27 // is set to non-zero value then Kernel isolate will forward all compilation |
| 28 // requests it receives to the batch worker on the given port. |
| 29 // |
11 | 30 |
12 import 'dart:async'; | 31 import 'dart:async'; |
13 import 'dart:convert'; | 32 import 'dart:convert'; |
14 import 'dart:io'; | 33 import 'dart:io'; |
15 import 'dart:isolate'; | 34 import 'dart:isolate'; |
16 import 'dart:typed_data'; | 35 import 'dart:typed_data'; |
17 | 36 |
18 import 'package:kernel/analyzer/loader.dart'; | 37 import 'package:kernel/analyzer/loader.dart'; |
19 import 'package:kernel/binary/ast_to_binary.dart'; | 38 import 'package:kernel/binary/ast_to_binary.dart'; |
20 import 'package:kernel/kernel.dart'; | 39 import 'package:kernel/kernel.dart'; |
21 import 'package:kernel/target/targets.dart'; | 40 import 'package:kernel/target/targets.dart'; |
22 | 41 |
23 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false; | 42 const bool verbose = true || (const bool.fromEnvironment('DFE_VERBOSE') ?? false
); |
| 43 const int workerPort = const int.fromEnvironment('DFE_WORKER_PORT') ?? 0; |
24 | 44 |
25 class DataSink implements Sink<List<int>> { | 45 class DataSink implements Sink<List<int>> { |
26 final BytesBuilder builder = new BytesBuilder(); | 46 final BytesBuilder builder = new BytesBuilder(); |
27 | 47 |
28 void add(List<int> data) { | 48 void add(List<int> data) { |
29 builder.add(data); | 49 builder.add(data); |
30 } | 50 } |
31 | 51 |
32 void close() { | 52 void close() { |
33 // Nothing to do. | 53 // Nothing to do. |
(...skipping 14 matching lines...) Expand all Loading... |
48 | 68 |
49 CompilationOk(this.binary); | 69 CompilationOk(this.binary); |
50 | 70 |
51 List toResponse() => [STATUS_OK, binary]; | 71 List toResponse() => [STATUS_OK, binary]; |
52 | 72 |
53 String toString() => "CompilationOk(${binary.length} bytes)"; | 73 String toString() => "CompilationOk(${binary.length} bytes)"; |
54 } | 74 } |
55 | 75 |
56 abstract class CompilationFail extends CompilationResult { | 76 abstract class CompilationFail extends CompilationResult { |
57 String get errorString; | 77 String get errorString; |
| 78 |
| 79 Map<String, dynamic> toJson(); |
| 80 |
| 81 static CompilationFail fromJson(Map m) { |
| 82 switch (m['status']) { |
| 83 case STATUS_ERROR: |
| 84 return new CompilationError(m['errors']); |
| 85 case STATUS_CRASH: |
| 86 return new CompilationCrash(m['exception'], m['stack']); |
| 87 } |
| 88 } |
58 } | 89 } |
59 | 90 |
60 class CompilationError extends CompilationFail { | 91 class CompilationError extends CompilationFail { |
61 final List<String> errors; | 92 final List<String> errors; |
62 | 93 |
63 CompilationError(this.errors); | 94 CompilationError(this.errors); |
64 | 95 |
65 List toResponse() => [STATUS_ERROR, errorString]; | 96 List toResponse() => [STATUS_ERROR, errorString]; |
66 | 97 |
| 98 Map<String, dynamic> toJson() => { |
| 99 "status": STATUS_ERROR, |
| 100 "errors": errors, |
| 101 }; |
| 102 |
67 String get errorString => errors.take(10).join('\n'); | 103 String get errorString => errors.take(10).join('\n'); |
68 | 104 |
69 String toString() => "CompilationError(${errorString})"; | 105 String toString() => "CompilationError(${errorString})"; |
70 } | 106 } |
71 | 107 |
72 class CompilationCrash extends CompilationFail { | 108 class CompilationCrash extends CompilationFail { |
73 final String exception; | 109 final String exception; |
74 final String stack; | 110 final String stack; |
75 | 111 |
76 CompilationCrash(this.exception, this.stack); | 112 CompilationCrash(this.exception, this.stack); |
77 | 113 |
78 List toResponse() => [STATUS_CRASH, errorString]; | 114 List toResponse() => [STATUS_CRASH, errorString]; |
79 | 115 |
| 116 Map<String, dynamic> toJson() => { |
| 117 "status": STATUS_CRASH, |
| 118 "exception": exception, |
| 119 "stack": stack, |
| 120 }; |
| 121 |
80 String get errorString => "${exception}\n${stack}"; | 122 String get errorString => "${exception}\n${stack}"; |
81 | 123 |
82 String toString() => "CompilationCrash(${errorString})"; | 124 String toString() => "CompilationCrash(${errorString})"; |
83 } | 125 } |
84 | 126 |
85 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, | 127 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, |
86 Uri fileName, String packageConfig, String sdkPath) async { | 128 Uri fileName, String packageConfig, String sdkPath) async { |
87 if (!FileSystemEntity.isFileSync(fileName.path)) { | 129 if (!FileSystemEntity.isFileSync(fileName.path)) { |
88 throw "Input file '${fileName.path}' does not exist."; | 130 throw "Input file '${fileName.path}' does not exist."; |
89 } | 131 } |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); | 197 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); |
156 | 198 |
157 if (verbose) { | 199 if (verbose) { |
158 print("""DFE: Requesting compilation { | 200 print("""DFE: Requesting compilation { |
159 scriptUri: ${scriptUri} | 201 scriptUri: ${scriptUri} |
160 packagesUri: ${packagesUri} | 202 packagesUri: ${packagesUri} |
161 patchedSdk: ${patchedSdk} | 203 patchedSdk: ${patchedSdk} |
162 }"""); | 204 }"""); |
163 } | 205 } |
164 | 206 |
165 return await parseScript( | 207 if (workerPort != 0) { |
166 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); | 208 return await requestParse(scriptUri, packagesUri, patchedSdk); |
| 209 } else { |
| 210 return await parseScript( |
| 211 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); |
| 212 } |
167 } | 213 } |
168 | 214 |
| 215 |
169 // Process a request from the runtime. See KernelIsolate::CompileToKernel in | 216 // Process a request from the runtime. See KernelIsolate::CompileToKernel in |
170 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. | 217 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. |
171 Future _processLoadRequest(request) async { | 218 Future _processLoadRequest(request) async { |
172 if (verbose) { | 219 if (verbose) { |
173 print("DFE: request: $request"); | 220 print("DFE: request: $request"); |
174 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); | 221 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
175 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); | 222 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
176 } | 223 } |
177 | 224 |
178 final int tag = request[0]; | 225 final int tag = request[0]; |
(...skipping 19 matching lines...) Expand all Loading... |
198 } else { | 245 } else { |
199 // See loader.cc for the code that handles these replies. | 246 // See loader.cc for the code that handles these replies. |
200 if (result is CompilationOk) { | 247 if (result is CompilationOk) { |
201 port.send([tag, inputFileUrl, inputFileUrl, null, result]); | 248 port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
202 } else { | 249 } else { |
203 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); | 250 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); |
204 } | 251 } |
205 } | 252 } |
206 } | 253 } |
207 | 254 |
208 main() => new RawReceivePort()..handler = _processLoadRequest; | 255 Future<CompilationResult> requestParse( |
| 256 Uri scriptUri, Uri packagesUri, Uri patchedSdk) async { |
| 257 if (verbose) { |
| 258 print( |
| 259 "DFE: forwarding request to worker at http://localhost:${workerPort}/"); |
| 260 } |
| 261 |
| 262 HttpClient client = new HttpClient(); |
| 263 final rq = await client |
| 264 .postUrl(new Uri(host: 'localhost', port: workerPort, scheme: 'http')); |
| 265 rq.headers.contentType = ContentType.JSON; |
| 266 rq.write(JSON.encode({ |
| 267 "inputFileUrl": scriptUri.toString(), |
| 268 "packagesUri": packagesUri.toString(), |
| 269 "patchedSdk": patchedSdk.toString(), |
| 270 })); |
| 271 final rs = await rq.close(); |
| 272 try { |
| 273 if (rs.statusCode == HttpStatus.OK) { |
| 274 final BytesBuilder bb = new BytesBuilder(); |
| 275 await rs.forEach(bb.add); |
| 276 return new CompilationOk(bb.takeBytes()); |
| 277 } else { |
| 278 return CompilationFail.fromJson(JSON.decode(await UTF8.decodeStream(rs))); |
| 279 } |
| 280 } finally { |
| 281 await client.close(); |
| 282 } |
| 283 } |
| 284 |
| 285 void startBatchServer() { |
| 286 final loader = new DartLoaderBatch(); |
| 287 HttpServer.bind(InternetAddress.LOOPBACK_IP_V6, workerPort).then((server) { |
| 288 print('READY ${server.port}'); |
| 289 server.listen((HttpRequest request) async { |
| 290 final rq = JSON.decode(await UTF8.decodeStream(request)); |
| 291 |
| 292 final Uri scriptUri = Uri.parse(rq['inputFileUrl']); |
| 293 final Uri packagesUri = Uri.parse(rq['packagesUri']); |
| 294 final Uri patchedSdk = Uri.parse(rq['patchedSdk']); |
| 295 |
| 296 final CompilationResult result = await parseScript( |
| 297 loader, scriptUri, packagesUri.path, patchedSdk.path); |
| 298 |
| 299 if (result is CompilationOk) { |
| 300 request.response.statusCode = HttpStatus.OK; |
| 301 request.response.headers.contentType = ContentType.BINARY; |
| 302 request.response.add(result.binary); |
| 303 request.response.close(); |
| 304 } else { |
| 305 request.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR; |
| 306 request.response.headers.contentType = ContentType.TEXT; |
| 307 request.response.write(JSON.encode(result.toJson())); |
| 308 request.response.close(); |
| 309 } |
| 310 }); |
| 311 ProcessSignal.SIGTERM.watch().first.then((_) => server.close()); |
| 312 }); |
| 313 } |
| 314 |
| 315 main([args]) { |
| 316 if (args?.length == 1 && args[0] == '--batch') { |
| 317 startBatchServer(); |
| 318 } else { |
| 319 // Entry point for the Kernel isolate. |
| 320 return new RawReceivePort()..handler = _processLoadRequest; |
| 321 } |
| 322 } |
209 | 323 |
210 // This duplicates functionality from the Loader which we can't easily | 324 // This duplicates functionality from the Loader which we can't easily |
211 // access from here. | 325 // access from here. |
212 Uri _findPackagesFile(Uri base) async { | 326 Uri _findPackagesFile(Uri base) async { |
213 var dir = new File.fromUri(base).parent; | 327 var dir = new File.fromUri(base).parent; |
214 while (true) { | 328 while (true) { |
215 final packagesFile = dir.uri.resolve(".packages"); | 329 final packagesFile = dir.uri.resolve(".packages"); |
216 if (await new File.fromUri(packagesFile).exists()) { | 330 if (await new File.fromUri(packagesFile).exists()) { |
217 return packagesFile; | 331 return packagesFile; |
218 } | 332 } |
219 if (dir.parent == dir) { | 333 if (dir.parent.path == dir.path) { |
220 break; | 334 break; |
221 } | 335 } |
222 dir = dir.parent; | 336 dir = dir.parent; |
223 } | 337 } |
224 return null; | 338 return null; |
225 } | 339 } |
OLD | NEW |