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 | |
kustermann
2017/01/31 15:38:59
This should be:
dart runtime/tools/kernel-se
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
20 // | |
21 // The port for the batch mode worker is controlled by DFE_WORKER_PORT | |
22 // environment variable. When not set (or set to 0) an ephemeral port returned | |
kustermann
2017/01/31 15:38:59
"environment variable" aren't these -D... flags to
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
23 // by the OS is used instead. | |
24 // | |
25 // When this script is used as a Kernel isolate root script and DFE_WORKER_PORT | |
26 // is set to non-zero value then Kernel isolate will forward all compilation | |
27 // requests it receives to the batch worker on the given port. | |
28 // | |
11 | 29 |
12 import 'dart:async'; | 30 import 'dart:async'; |
13 import 'dart:convert'; | 31 import 'dart:convert'; |
14 import 'dart:io'; | 32 import 'dart:io'; |
15 import 'dart:isolate'; | 33 import 'dart:isolate'; |
16 import 'dart:typed_data'; | 34 import 'dart:typed_data'; |
17 | 35 |
18 import 'package:kernel/analyzer/loader.dart'; | 36 import 'package:kernel/analyzer/loader.dart'; |
19 import 'package:kernel/binary/ast_to_binary.dart'; | 37 import 'package:kernel/binary/ast_to_binary.dart'; |
20 import 'package:kernel/kernel.dart'; | 38 import 'package:kernel/kernel.dart'; |
21 import 'package:kernel/target/targets.dart'; | 39 import 'package:kernel/target/targets.dart'; |
22 | 40 |
23 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false; | 41 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false; |
42 const int workerPort = const int.fromEnvironment('DFE_WORKER_PORT') ?? 0; | |
24 | 43 |
25 class DataSink implements Sink<List<int>> { | 44 class DataSink implements Sink<List<int>> { |
26 final BytesBuilder builder = new BytesBuilder(); | 45 final BytesBuilder builder = new BytesBuilder(); |
27 | 46 |
28 void add(List<int> data) { | 47 void add(List<int> data) { |
29 builder.add(data); | 48 builder.add(data); |
30 } | 49 } |
31 | 50 |
32 void close() { | 51 void close() { |
33 // Nothing to do. | 52 // Nothing to do. |
(...skipping 14 matching lines...) Expand all Loading... | |
48 | 67 |
49 CompilationOk(this.binary); | 68 CompilationOk(this.binary); |
50 | 69 |
51 List toResponse() => [STATUS_OK, binary]; | 70 List toResponse() => [STATUS_OK, binary]; |
52 | 71 |
53 String toString() => "CompilationOk(${binary.length} bytes)"; | 72 String toString() => "CompilationOk(${binary.length} bytes)"; |
54 } | 73 } |
55 | 74 |
56 abstract class CompilationFail extends CompilationResult { | 75 abstract class CompilationFail extends CompilationResult { |
57 String get errorString; | 76 String get errorString; |
77 | |
78 Map<String, dynamic> toJson(); | |
79 | |
80 static CompilationFail fromJson(Map m) { | |
81 switch (m['status']) { | |
82 case STATUS_ERROR: | |
83 return new CompilationError(m['errors']); | |
84 case STATUS_CRASH: | |
85 return new CompilationCrash(m['exception'], m['stack']); | |
86 } | |
87 } | |
58 } | 88 } |
59 | 89 |
60 class CompilationError extends CompilationFail { | 90 class CompilationError extends CompilationFail { |
61 final List<String> errors; | 91 final List<String> errors; |
62 | 92 |
63 CompilationError(this.errors); | 93 CompilationError(this.errors); |
64 | 94 |
65 List toResponse() => [STATUS_ERROR, errorString]; | 95 List toResponse() => [STATUS_ERROR, errorString]; |
66 | 96 |
97 Map<String, dynamic> toJson() => { | |
98 "status": STATUS_ERROR, | |
99 "errors": errors, | |
100 }; | |
101 | |
67 String get errorString => errors.take(10).join('\n'); | 102 String get errorString => errors.take(10).join('\n'); |
68 | 103 |
69 String toString() => "CompilationError(${errorString})"; | 104 String toString() => "CompilationError(${errorString})"; |
70 } | 105 } |
71 | 106 |
72 class CompilationCrash extends CompilationFail { | 107 class CompilationCrash extends CompilationFail { |
73 final String exception; | 108 final String exception; |
74 final String stack; | 109 final String stack; |
75 | 110 |
76 CompilationCrash(this.exception, this.stack); | 111 CompilationCrash(this.exception, this.stack); |
77 | 112 |
78 List toResponse() => [STATUS_CRASH, errorString]; | 113 List toResponse() => [STATUS_CRASH, errorString]; |
79 | 114 |
115 Map<String, dynamic> toJson() => { | |
116 "status": STATUS_CRASH, | |
117 "exception": exception, | |
118 "stack": stack, | |
119 }; | |
120 | |
80 String get errorString => "${exception}\n${stack}"; | 121 String get errorString => "${exception}\n${stack}"; |
81 | 122 |
82 String toString() => "CompilationCrash(${errorString})"; | 123 String toString() => "CompilationCrash(${errorString})"; |
83 } | 124 } |
84 | 125 |
85 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, | 126 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, |
86 Uri fileName, String packageConfig, String sdkPath) async { | 127 Uri fileName, String packageConfig, String sdkPath) async { |
87 if (!FileSystemEntity.isFileSync(fileName.path)) { | 128 if (!FileSystemEntity.isFileSync(fileName.path)) { |
88 throw "Input file '${fileName.path}' does not exist."; | 129 throw "Input file '${fileName.path}' does not exist."; |
89 } | 130 } |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
155 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); | 196 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); |
156 | 197 |
157 if (verbose) { | 198 if (verbose) { |
158 print("""DFE: Requesting compilation { | 199 print("""DFE: Requesting compilation { |
159 scriptUri: ${scriptUri} | 200 scriptUri: ${scriptUri} |
160 packagesUri: ${packagesUri} | 201 packagesUri: ${packagesUri} |
161 patchedSdk: ${patchedSdk} | 202 patchedSdk: ${patchedSdk} |
162 }"""); | 203 }"""); |
163 } | 204 } |
164 | 205 |
165 return await parseScript( | 206 if (workerPort != 0) { |
166 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); | 207 return await requestParse(scriptUri, packagesUri, patchedSdk); |
208 } else { | |
209 return await parseScript( | |
210 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); | |
211 } | |
167 } | 212 } |
168 | 213 |
214 | |
169 // Process a request from the runtime. See KernelIsolate::CompileToKernel in | 215 // Process a request from the runtime. See KernelIsolate::CompileToKernel in |
170 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. | 216 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. |
171 Future _processLoadRequest(request) async { | 217 Future _processLoadRequest(request) async { |
172 if (verbose) { | 218 if (verbose) { |
173 print("DFE: request: $request"); | 219 print("DFE: request: $request"); |
174 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); | 220 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
175 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); | 221 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
176 } | 222 } |
177 | 223 |
178 final int tag = request[0]; | 224 final int tag = request[0]; |
(...skipping 19 matching lines...) Expand all Loading... | |
198 } else { | 244 } else { |
199 // See loader.cc for the code that handles these replies. | 245 // See loader.cc for the code that handles these replies. |
200 if (result is CompilationOk) { | 246 if (result is CompilationOk) { |
201 port.send([tag, inputFileUrl, inputFileUrl, null, result]); | 247 port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
202 } else { | 248 } else { |
203 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); | 249 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); |
204 } | 250 } |
205 } | 251 } |
206 } | 252 } |
207 | 253 |
208 main() => new RawReceivePort()..handler = _processLoadRequest; | 254 Future<CompilationResult> requestParse( |
255 Uri scriptUri, Uri packagesUri, Uri patchedSdk) async { | |
256 if (verbose) { | |
257 print( | |
258 "DFE: forwarding request to worker at http://localhost:${workerPort}/"); | |
259 } | |
260 | |
261 HttpClient client = new HttpClient(); | |
262 final rq = await client | |
263 .postUrl(new Uri(host: 'localhost', port: workerPort, scheme: 'http')); | |
264 rq.headers.contentType = | |
265 new ContentType("application", "json", charset: "utf-8"); | |
kustermann
2017/01/31 15:38:59
Isn't there a constant for it in dart:io?
Content
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
266 rq.write(JSON.encode({ | |
267 "inputFileUrl": scriptUri.toString(), | |
268 "packagesUri": packagesUri.toString(), | |
269 "patchedSdk": patchedSdk.toString(), | |
kustermann
2017/01/31 15:39:00
If these can be relative, you should Platform.curr
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
These are all absolute.
| |
270 })); | |
271 final rs = await rq.close(); | |
272 try { | |
273 if (rs.statusCode == HttpStatus.OK) { | |
274 final ds = new DataSink(); | |
275 await rs.forEach(ds.add); | |
kustermann
2017/01/31 15:38:59
Either use something like (and make DataSink be ab
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
276 return new CompilationOk(ds.builder.takeBytes()); | |
277 } else { | |
278 return CompilationFail.fromJson(JSON.decode(await UTF8.decodeStream(rs))); | |
279 } | |
280 } finally { | |
281 client.close(); | |
kustermann
2017/01/31 15:38:59
await client.close()
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
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 { | |
kustermann
2017/01/31 15:38:59
For a graceful shutdown:
ProcessSignal.SIGTERM.fi
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
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.headers.contentType = ContentType.BINARY; | |
301 request.response.add(result.binary); | |
302 request.response.close(); | |
303 } else { | |
304 request.response.headers.contentType = ContentType.TEXT; | |
305 request.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR; | |
kustermann
2017/01/31 15:38:59
Maybe set the statusCode to be OK in the other bra
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
306 request.response.write(JSON.encode(result.toJson())); | |
307 request.response.close(); | |
308 } | |
309 }); | |
310 }); | |
311 } | |
312 | |
313 main([args]) { | |
314 if (args?.length == 1 && args[0] == '--batch') { | |
315 return startBatchServer(); | |
316 } | |
317 | |
318 return new RawReceivePort()..handler = _processLoadRequest; | |
kustermann
2017/01/31 15:39:00
maybe use
if () {
....
return null;
} else {
Vyacheslav Egorov (Google)
2017/01/31 17:06:55
Done.
| |
319 } | |
209 | 320 |
210 // This duplicates functionality from the Loader which we can't easily | 321 // This duplicates functionality from the Loader which we can't easily |
211 // access from here. | 322 // access from here. |
212 Uri _findPackagesFile(Uri base) async { | 323 Uri _findPackagesFile(Uri base) async { |
213 var dir = new File.fromUri(base).parent; | 324 var dir = new File.fromUri(base).parent; |
214 while (true) { | 325 while (true) { |
215 final packagesFile = dir.uri.resolve(".packages"); | 326 final packagesFile = dir.uri.resolve(".packages"); |
216 if (await new File.fromUri(packagesFile).exists()) { | 327 if (await new File.fromUri(packagesFile).exists()) { |
217 return packagesFile; | 328 return packagesFile; |
218 } | 329 } |
219 if (dir.parent == dir) { | 330 if (dir.parent.path == dir.path) { |
220 break; | 331 break; |
221 } | 332 } |
222 dir = dir.parent; | 333 dir = dir.parent; |
223 } | 334 } |
224 return null; | 335 return null; |
225 } | 336 } |
OLD | NEW |