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 | 4 |
5 /// This is an interface to the Dart Kernel parser and Kernel binary generator. | 5 /// This is an interface to the Dart Kernel parser and Kernel binary generator. |
6 /// | 6 /// |
7 /// It is used by the kernel-isolate to load Dart source code and generate | 7 /// It is used by the kernel-isolate to load Dart source code and generate |
8 /// Kernel binary format. | 8 /// Kernel binary format. |
9 /// | 9 /// |
10 /// This is either invoked as the root script of the Kernel isolate when used | 10 /// This is either invoked as the root script of the Kernel isolate when used |
11 /// as a part of | 11 /// as a part of |
12 /// | 12 /// |
13 /// dart --dfe=utils/kernel-service/kernel-service.dart ... | 13 /// dart --dfe=utils/kernel-service/kernel-service.dart ... |
14 /// | 14 /// |
15 /// invocation or it is invoked as a standalone script to perform batch mode | 15 /// invocation or it is invoked as a standalone script to perform training for |
16 /// compilation requested via an HTTP interface | 16 /// the app-jit snapshot |
17 /// | 17 /// |
18 /// dart utils/kernel-service/kernel-service.dart --batch | 18 /// dart utils/kernel-service/kernel-service.dart --train <source-file> |
19 /// | 19 /// |
20 /// The port for the batch mode worker is controlled by DFE_WORKER_PORT | |
21 /// environment declarations (set by -DDFE_WORKER_PORT=... command line flag). | |
22 /// When not set (or set to 0) an ephemeral port returned by the OS is used | |
23 /// 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 /// | |
29 /// Set DFE_USE_FASTA environment declaration to true to use fasta front-end | |
30 /// instead of dartk. Note: we expect patched_sdk folder to contain | |
31 /// platform.dill file that contains patched SDK in the Kernel binary form. | |
32 /// This file can be created using the following command line: | |
33 /// | |
34 /// export DART_AOT_SDK=<path-to-patched_sdk> | |
35 /// dart pkg/front_end/lib/src/fasta/bin/compile_platform.dart \ | |
36 /// ${DART_AOT_SDK}/platform.dill | |
37 /// | 20 /// |
38 library runtime.tools.kernel_service; | 21 library runtime.tools.kernel_service; |
39 | 22 |
40 import 'dart:async'; | 23 import 'dart:async'; |
41 import 'dart:convert'; | 24 import 'dart:convert'; |
42 import 'dart:io'; | 25 import 'dart:io'; |
43 import 'dart:isolate'; | 26 import 'dart:isolate'; |
44 import 'dart:typed_data'; | 27 import 'dart:typed_data'; |
45 | 28 |
46 import 'package:kernel/analyzer/loader.dart'; | |
47 import 'package:kernel/binary/ast_to_binary.dart'; | 29 import 'package:kernel/binary/ast_to_binary.dart'; |
48 import 'package:kernel/kernel.dart'; | 30 import 'package:kernel/kernel.dart'; |
49 import 'package:kernel/target/targets.dart'; | 31 import 'package:kernel/target/targets.dart'; |
50 | 32 |
51 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget; | 33 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget; |
52 import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri; | 34 import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri; |
53 import 'package:front_end/src/fasta/ticker.dart' show Ticker; | 35 import 'package:front_end/src/fasta/ticker.dart' show Ticker; |
54 import 'package:front_end/src/fasta/kernel/kernel_target.dart' | 36 import 'package:front_end/src/fasta/kernel/kernel_target.dart' |
55 show KernelTarget; | 37 show KernelTarget; |
56 import 'package:front_end/src/fasta/ast_kind.dart' show AstKind; | 38 import 'package:front_end/src/fasta/ast_kind.dart' show AstKind; |
57 import 'package:front_end/src/fasta/errors.dart' show InputError; | 39 import 'package:front_end/src/fasta/errors.dart' show InputError; |
58 | 40 |
59 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE'); | 41 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE'); |
60 const int workerPort = const int.fromEnvironment('DFE_WORKER_PORT') ?? 0; | |
61 const bool useFasta = const bool.fromEnvironment('DFE_USE_FASTA'); | |
62 | 42 |
63 class DataSink implements Sink<List<int>> { | 43 class DataSink implements Sink<List<int>> { |
64 final BytesBuilder builder = new BytesBuilder(); | 44 final BytesBuilder builder = new BytesBuilder(); |
65 | 45 |
66 void add(List<int> data) { | 46 void add(List<int> data) { |
67 builder.add(data); | 47 builder.add(data); |
68 } | 48 } |
69 | 49 |
70 void close() { | 50 void close() { |
71 // Nothing to do. | 51 // Nothing to do. |
(...skipping 14 matching lines...) Expand all Loading... |
86 | 66 |
87 CompilationOk(this.binary); | 67 CompilationOk(this.binary); |
88 | 68 |
89 List toResponse() => [STATUS_OK, binary]; | 69 List toResponse() => [STATUS_OK, binary]; |
90 | 70 |
91 String toString() => "CompilationOk(${binary.length} bytes)"; | 71 String toString() => "CompilationOk(${binary.length} bytes)"; |
92 } | 72 } |
93 | 73 |
94 abstract class CompilationFail extends CompilationResult { | 74 abstract class CompilationFail extends CompilationResult { |
95 String get errorString; | 75 String get errorString; |
96 | |
97 Map<String, dynamic> toJson(); | |
98 | |
99 static CompilationFail fromJson(Map m) { | |
100 switch (m['status']) { | |
101 case STATUS_ERROR: | |
102 return new CompilationError(m['errors']); | |
103 case STATUS_CRASH: | |
104 return new CompilationCrash(m['exception'], m['stack']); | |
105 default: | |
106 throw "Can't deserialize CompilationFail from ${m}."; | |
107 } | |
108 } | |
109 } | 76 } |
110 | 77 |
111 class CompilationError extends CompilationFail { | 78 class CompilationError extends CompilationFail { |
112 final List<String> errors; | 79 final List<String> errors; |
113 | 80 |
114 CompilationError(this.errors); | 81 CompilationError(this.errors); |
115 | 82 |
116 List toResponse() => [STATUS_ERROR, errorString]; | 83 List toResponse() => [STATUS_ERROR, errorString]; |
117 | 84 |
118 Map<String, dynamic> toJson() => { | |
119 "status": STATUS_ERROR, | |
120 "errors": errors, | |
121 }; | |
122 | |
123 String get errorString => errors.take(10).join('\n'); | 85 String get errorString => errors.take(10).join('\n'); |
124 | 86 |
125 String toString() => "CompilationError(${errorString})"; | 87 String toString() => "CompilationError(${errorString})"; |
126 } | 88 } |
127 | 89 |
128 class CompilationCrash extends CompilationFail { | 90 class CompilationCrash extends CompilationFail { |
129 final String exception; | 91 final String exception; |
130 final String stack; | 92 final String stack; |
131 | 93 |
132 CompilationCrash(this.exception, this.stack); | 94 CompilationCrash(this.exception, this.stack); |
133 | 95 |
134 List toResponse() => [STATUS_CRASH, errorString]; | 96 List toResponse() => [STATUS_CRASH, errorString]; |
135 | 97 |
136 Map<String, dynamic> toJson() => { | |
137 "status": STATUS_CRASH, | |
138 "exception": exception, | |
139 "stack": stack, | |
140 }; | |
141 | |
142 String get errorString => "${exception}\n${stack}"; | 98 String get errorString => "${exception}\n${stack}"; |
143 | 99 |
144 String toString() => "CompilationCrash(${errorString})"; | 100 String toString() => "CompilationCrash(${errorString})"; |
145 } | 101 } |
146 | 102 |
147 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, | 103 Future<CompilationResult> parseScriptImpl( |
148 Uri fileName, String packageConfig, String sdkPath) async { | 104 Uri fileName, String packageConfig, String sdkPath) async { |
149 if (!FileSystemEntity.isFileSync(fileName.path)) { | 105 if (!FileSystemEntity.isFileSync(fileName.path)) { |
150 throw "Input file '${fileName.path}' does not exist."; | 106 throw "Input file '${fileName.path}' does not exist."; |
151 } | 107 } |
152 | 108 |
153 if (!FileSystemEntity.isDirectorySync(sdkPath)) { | 109 if (!FileSystemEntity.isDirectorySync(sdkPath)) { |
154 throw "Patched sdk directory not found at $sdkPath"; | 110 throw "Patched sdk directory not found at $sdkPath"; |
155 } | 111 } |
156 | 112 |
157 Target target = getTarget("vm", new TargetFlags(strongMode: false)); | 113 Target target = getTarget("vm", new TargetFlags(strongMode: false)); |
158 | 114 |
159 Program program; | 115 Program program; |
160 if (useFasta) { | 116 final uriTranslator = await TranslateUri.parse(new Uri.file(packageConfig)); |
161 final uriTranslator = await TranslateUri.parse(new Uri.file(packageConfig)); | 117 final Ticker ticker = new Ticker(isVerbose: verbose); |
162 final Ticker ticker = new Ticker(isVerbose: verbose); | 118 final DillTarget dillTarget = new DillTarget(ticker, uriTranslator); |
163 final DillTarget dillTarget = new DillTarget(ticker, uriTranslator); | 119 dillTarget.read(new Uri.directory(sdkPath).resolve('platform.dill')); |
164 dillTarget.read(new Uri.directory(sdkPath).resolve('platform.dill')); | 120 final KernelTarget kernelTarget = |
165 final KernelTarget kernelTarget = | 121 new KernelTarget(dillTarget, uriTranslator); |
166 new KernelTarget(dillTarget, uriTranslator); | 122 try { |
167 try { | 123 kernelTarget.read(fileName); |
168 kernelTarget.read(fileName); | 124 await dillTarget.writeOutline(null); |
169 await dillTarget.writeOutline(null); | 125 program = await kernelTarget.writeOutline(null); |
170 program = await kernelTarget.writeOutline(null); | 126 program = await kernelTarget.writeProgram(null, AstKind.Kernel); |
171 program = await kernelTarget.writeProgram(null, AstKind.Kernel); | 127 if (kernelTarget.errors.isNotEmpty) { |
172 if (kernelTarget.errors.isNotEmpty) { | 128 return new CompilationError(kernelTarget.errors |
173 return new CompilationError(kernelTarget.errors | 129 .map((err) => err.toString()) |
174 .map((err) => err.toString()) | 130 .toList(growable: false)); |
175 .toList(growable: false)); | |
176 } | |
177 } on InputError catch (e) { | |
178 return new CompilationError(<String>[e.format()]); | |
179 } | 131 } |
180 } else { | 132 } on InputError catch (e) { |
181 DartOptions dartOptions = new DartOptions( | 133 return new CompilationError(<String>[e.format()]); |
182 strongMode: false, | |
183 strongModeSdk: false, | |
184 sdk: sdkPath, | |
185 packagePath: packageConfig, | |
186 customUriMappings: const {}, | |
187 declaredVariables: const {}); | |
188 program = new Program(); | |
189 DartLoader loader = | |
190 await batch_loader.getLoader(program, dartOptions); | |
191 loader.loadProgram(fileName, target: target); | |
192 | |
193 if (loader.errors.isNotEmpty) { | |
194 return new CompilationError(loader.errors.toList(growable: false)); | |
195 } | |
196 } | 134 } |
197 | 135 |
198 // Perform target-specific transformations. | 136 // Perform target-specific transformations. |
199 target.performModularTransformations(program); | 137 target.performModularTransformations(program); |
200 target.performGlobalTransformations(program); | 138 target.performGlobalTransformations(program); |
201 | 139 |
202 // Write the program to a list of bytes and return it. | 140 // Write the program to a list of bytes and return it. |
203 var sink = new DataSink(); | 141 var sink = new DataSink(); |
204 new BinaryPrinter(sink).writeProgramFile(program); | 142 new BinaryPrinter(sink).writeProgramFile(program); |
205 return new CompilationOk(sink.builder.takeBytes()); | 143 return new CompilationOk(sink.builder.takeBytes()); |
206 } | 144 } |
207 | 145 |
208 Future<CompilationResult> parseScript(DartLoaderBatch loader, Uri fileName, | 146 Future<CompilationResult> parseScript( |
209 String packageConfig, String sdkPath) async { | 147 Uri fileName, String packageConfig, String sdkPath) async { |
210 try { | 148 try { |
211 return await parseScriptImpl(loader, fileName, packageConfig, sdkPath); | 149 return await parseScriptImpl(fileName, packageConfig, sdkPath); |
212 } catch (err, stack) { | 150 } catch (err, stack) { |
213 return new CompilationCrash(err.toString(), stack.toString()); | 151 return new CompilationCrash(err.toString(), stack.toString()); |
214 } | 152 } |
215 } | 153 } |
216 | 154 |
217 Future _processLoadRequestImpl(String inputFileUrl) async { | 155 Future _processLoadRequestImpl(String inputFileUrl) async { |
218 Uri scriptUri = Uri.parse(inputFileUrl); | 156 Uri scriptUri = Uri.parse(inputFileUrl); |
219 | 157 |
220 // Because we serve both Loader and bootstrapping requests we need to | 158 // Because we serve both Loader and bootstrapping requests we need to |
221 // duplicate the logic from _resolveScriptUri(...) here and attempt to | 159 // duplicate the logic from _resolveScriptUri(...) here and attempt to |
(...skipping 20 matching lines...) Expand all Loading... |
242 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); | 180 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); |
243 | 181 |
244 if (verbose) { | 182 if (verbose) { |
245 print("""DFE: Requesting compilation { | 183 print("""DFE: Requesting compilation { |
246 scriptUri: ${scriptUri} | 184 scriptUri: ${scriptUri} |
247 packagesUri: ${packagesUri} | 185 packagesUri: ${packagesUri} |
248 patchedSdk: ${patchedSdk} | 186 patchedSdk: ${patchedSdk} |
249 }"""); | 187 }"""); |
250 } | 188 } |
251 | 189 |
252 if (workerPort != 0) { | 190 return await parseScript(scriptUri, packagesUri.path, patchedSdk.path); |
253 return await requestParse(scriptUri, packagesUri, patchedSdk); | |
254 } else { | |
255 return await parseScript( | |
256 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); | |
257 } | |
258 } | 191 } |
259 | 192 |
260 // Process a request from the runtime. See KernelIsolate::CompileToKernel in | 193 // Process a request from the runtime. See KernelIsolate::CompileToKernel in |
261 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. | 194 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. |
262 Future _processLoadRequest(request) async { | 195 Future _processLoadRequest(request) async { |
263 if (verbose) { | 196 if (verbose) { |
264 print("DFE: request: $request"); | 197 print("DFE: request: $request"); |
265 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); | 198 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
266 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); | 199 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
267 } | 200 } |
(...skipping 21 matching lines...) Expand all Loading... |
289 } else { | 222 } else { |
290 // See loader.cc for the code that handles these replies. | 223 // See loader.cc for the code that handles these replies. |
291 if (result is CompilationOk) { | 224 if (result is CompilationOk) { |
292 port.send([tag, inputFileUrl, inputFileUrl, null, result]); | 225 port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
293 } else { | 226 } else { |
294 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); | 227 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); |
295 } | 228 } |
296 } | 229 } |
297 } | 230 } |
298 | 231 |
299 Future<CompilationResult> requestParse( | |
300 Uri scriptUri, Uri packagesUri, Uri patchedSdk) async { | |
301 if (verbose) { | |
302 print( | |
303 "DFE: forwarding request to worker at http://localhost:${workerPort}/"); | |
304 } | |
305 | |
306 HttpClient client = new HttpClient(); | |
307 final rq = await client | |
308 .postUrl(new Uri(host: 'localhost', port: workerPort, scheme: 'http')); | |
309 rq.headers.contentType = ContentType.JSON; | |
310 rq.write(JSON.encode({ | |
311 "inputFileUrl": scriptUri.toString(), | |
312 "packagesUri": packagesUri.toString(), | |
313 "patchedSdk": patchedSdk.toString(), | |
314 })); | |
315 final rs = await rq.close(); | |
316 try { | |
317 if (rs.statusCode == HttpStatus.OK) { | |
318 final BytesBuilder bb = new BytesBuilder(); | |
319 await rs.forEach(bb.add); | |
320 return new CompilationOk(bb.takeBytes()); | |
321 } else { | |
322 return CompilationFail.fromJson(JSON.decode(await UTF8.decodeStream(rs))); | |
323 } | |
324 } finally { | |
325 await client.close(); | |
326 } | |
327 } | |
328 | |
329 void startBatchServer() { | |
330 final loader = new DartLoaderBatch(); | |
331 HttpServer.bind('localhost', workerPort).then((server) { | |
332 print('READY ${server.port}'); | |
333 server.listen((HttpRequest request) async { | |
334 final rq = JSON.decode(await UTF8.decodeStream(request)); | |
335 | |
336 final Uri scriptUri = Uri.parse(rq['inputFileUrl']); | |
337 final Uri packagesUri = Uri.parse(rq['packagesUri']); | |
338 final Uri patchedSdk = Uri.parse(rq['patchedSdk']); | |
339 | |
340 final CompilationResult result = await parseScript( | |
341 loader, scriptUri, packagesUri.path, patchedSdk.path); | |
342 | |
343 if (result is CompilationOk) { | |
344 request.response.statusCode = HttpStatus.OK; | |
345 request.response.headers.contentType = ContentType.BINARY; | |
346 request.response.add(result.binary); | |
347 request.response.close(); | |
348 } else { | |
349 request.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR; | |
350 request.response.headers.contentType = ContentType.TEXT; | |
351 request.response.write(JSON.encode(result)); | |
352 request.response.close(); | |
353 } | |
354 }); | |
355 ProcessSignal.SIGTERM.watch().first.then((_) => server.close()); | |
356 }); | |
357 } | |
358 | |
359 train(String scriptUri) { | 232 train(String scriptUri) { |
360 // TODO(28532): Enable on Windows. | 233 // TODO(28532): Enable on Windows. |
361 if (Platform.isWindows) return; | 234 if (Platform.isWindows) return; |
362 | 235 |
363 var tag = 1; | 236 var tag = 1; |
364 var responsePort = new RawReceivePort(); | 237 var responsePort = new RawReceivePort(); |
365 responsePort.handler = (response) { | 238 responsePort.handler = (response) { |
366 if (response[0] == tag) { | 239 if (response[0] == tag) { |
367 // Success. | 240 // Success. |
368 responsePort.close(); | 241 responsePort.close(); |
369 } else if (response[0] == -tag) { | 242 } else if (response[0] == -tag) { |
370 // Compilation error. | 243 // Compilation error. |
371 throw response[4]; | 244 throw response[4]; |
372 } else { | 245 } else { |
373 throw "Unexpected response: $response"; | 246 throw "Unexpected response: $response"; |
374 } | 247 } |
375 }; | 248 }; |
376 var request = [tag, responsePort.sendPort, scriptUri]; | 249 var request = [tag, responsePort.sendPort, scriptUri]; |
377 _processLoadRequest(request); | 250 _processLoadRequest(request); |
378 } | 251 } |
379 | 252 |
380 main([args]) { | 253 main([args]) { |
381 if (args?.length == 1 && args[0] == '--batch') { | 254 if (args?.length == 2 && args[0] == '--train') { |
382 startBatchServer(); | |
383 } else if (args?.length == 2 && args[0] == '--train') { | |
384 // This entry point is used when creating an app snapshot. The argument | 255 // This entry point is used when creating an app snapshot. The argument |
385 // provides a script to compile to warm-up generated code. | 256 // provides a script to compile to warm-up generated code. |
386 train(args[1]); | 257 train(args[1]); |
387 } else { | 258 } else { |
388 // Entry point for the Kernel isolate. | 259 // Entry point for the Kernel isolate. |
389 return new RawReceivePort()..handler = _processLoadRequest; | 260 return new RawReceivePort()..handler = _processLoadRequest; |
390 } | 261 } |
391 } | 262 } |
392 | 263 |
393 // This duplicates functionality from the Loader which we can't easily | 264 // This duplicates functionality from the Loader which we can't easily |
394 // access from here. | 265 // access from here. |
395 Future<Uri> _findPackagesFile(Uri base) async { | 266 Future<Uri> _findPackagesFile(Uri base) async { |
396 var dir = new File.fromUri(base).parent; | 267 var dir = new File.fromUri(base).parent; |
397 while (true) { | 268 while (true) { |
398 final packagesFile = dir.uri.resolve(".packages"); | 269 final packagesFile = dir.uri.resolve(".packages"); |
399 if (await new File.fromUri(packagesFile).exists()) { | 270 if (await new File.fromUri(packagesFile).exists()) { |
400 return packagesFile; | 271 return packagesFile; |
401 } | 272 } |
402 if (dir.parent.path == dir.path) { | 273 if (dir.parent.path == dir.path) { |
403 break; | 274 break; |
404 } | 275 } |
405 dir = dir.parent; | 276 dir = dir.parent; |
406 } | 277 } |
407 return null; | 278 return null; |
408 } | 279 } |
OLD | NEW |