| 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 |