| 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 training for | 15 /// invocation or it is invoked as a standalone script to perform training for |
| 16 /// the app-jit snapshot | 16 /// the app-jit snapshot |
| 17 /// | 17 /// |
| 18 /// dart utils/kernel-service/kernel-service.dart --train <source-file> | 18 /// dart utils/kernel-service/kernel-service.dart --train <source-file> |
| 19 /// | 19 /// |
| 20 /// | 20 /// |
| 21 library runtime.tools.kernel_service; | 21 library runtime.tools.kernel_service; |
| 22 | 22 |
| 23 import 'dart:async'; | 23 import 'dart:async'; |
| 24 import 'dart:io'; | 24 import 'dart:io'; |
| 25 import 'dart:isolate'; | 25 import 'dart:isolate'; |
| 26 import 'dart:typed_data'; | |
| 27 | 26 |
| 28 import 'package:kernel/binary/ast_to_binary.dart'; | 27 import 'package:front_end/src/fasta/vm.dart' |
| 29 import 'package:kernel/kernel.dart'; | 28 show CompilationResult, Status, parseScript; |
| 30 import 'package:kernel/target/targets.dart'; | |
| 31 | |
| 32 import 'package:front_end/src/fasta/dill/dill_target.dart' show DillTarget; | |
| 33 import 'package:front_end/src/fasta/translate_uri.dart' show TranslateUri; | |
| 34 import 'package:front_end/src/fasta/ticker.dart' show Ticker; | |
| 35 import 'package:front_end/src/fasta/kernel/kernel_target.dart' | |
| 36 show KernelTarget; | |
| 37 import 'package:front_end/src/fasta/errors.dart' show InputError; | |
| 38 | 29 |
| 39 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE'); | 30 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE'); |
| 40 | 31 |
| 41 class DataSink implements Sink<List<int>> { | 32 Future<CompilationResult> _processLoadRequestImpl(String inputFilePathOrUri) { |
| 42 final BytesBuilder builder = new BytesBuilder(); | 33 Uri scriptUri = Uri.parse(inputFilePathOrUri); |
| 43 | |
| 44 void add(List<int> data) { | |
| 45 builder.add(data); | |
| 46 } | |
| 47 | |
| 48 void close() { | |
| 49 // Nothing to do. | |
| 50 } | |
| 51 } | |
| 52 | |
| 53 // Note: these values must match Dart_KernelCompilationStatus in dart_api.h. | |
| 54 const int STATUS_OK = 0; // Compilation was successful. | |
| 55 const int STATUS_ERROR = 1; // Compilation failed with a compile time error. | |
| 56 const int STATUS_CRASH = 2; // Compiler crashed. | |
| 57 | |
| 58 abstract class CompilationResult { | |
| 59 List toResponse(); | |
| 60 } | |
| 61 | |
| 62 class CompilationOk extends CompilationResult { | |
| 63 final Uint8List binary; | |
| 64 | |
| 65 CompilationOk(this.binary); | |
| 66 | |
| 67 List toResponse() => [STATUS_OK, binary]; | |
| 68 | |
| 69 String toString() => "CompilationOk(${binary.length} bytes)"; | |
| 70 } | |
| 71 | |
| 72 abstract class CompilationFail extends CompilationResult { | |
| 73 String get errorString; | |
| 74 } | |
| 75 | |
| 76 class CompilationError extends CompilationFail { | |
| 77 final List<String> errors; | |
| 78 | |
| 79 CompilationError(this.errors); | |
| 80 | |
| 81 List toResponse() => [STATUS_ERROR, errorString]; | |
| 82 | |
| 83 String get errorString => errors.take(10).join('\n'); | |
| 84 | |
| 85 String toString() => "CompilationError(${errorString})"; | |
| 86 } | |
| 87 | |
| 88 class CompilationCrash extends CompilationFail { | |
| 89 final String exception; | |
| 90 final String stack; | |
| 91 | |
| 92 CompilationCrash(this.exception, this.stack); | |
| 93 | |
| 94 List toResponse() => [STATUS_CRASH, errorString]; | |
| 95 | |
| 96 String get errorString => "${exception}\n${stack}"; | |
| 97 | |
| 98 String toString() => "CompilationCrash(${errorString})"; | |
| 99 } | |
| 100 | |
| 101 Future<CompilationResult> parseScriptImpl( | |
| 102 Uri fileName, String packageConfig, String sdkPath) async { | |
| 103 if (!FileSystemEntity.isFileSync(fileName.path)) { | |
| 104 throw "Input file '${fileName.path}' does not exist."; | |
| 105 } | |
| 106 | |
| 107 if (!FileSystemEntity.isDirectorySync(sdkPath)) { | |
| 108 throw "Patched sdk directory not found at $sdkPath"; | |
| 109 } | |
| 110 | |
| 111 Target target = getTarget("vm", new TargetFlags(strongMode: false)); | |
| 112 | |
| 113 Program program; | |
| 114 final uriTranslator = | |
| 115 await TranslateUri.parse(null, new Uri.file(packageConfig)); | |
| 116 final Ticker ticker = new Ticker(isVerbose: verbose); | |
| 117 final DillTarget dillTarget = new DillTarget(ticker, uriTranslator); | |
| 118 dillTarget.read(new Uri.directory(sdkPath).resolve('platform.dill')); | |
| 119 final KernelTarget kernelTarget = new KernelTarget(dillTarget, uriTranslator); | |
| 120 try { | |
| 121 kernelTarget.read(fileName); | |
| 122 await dillTarget.writeOutline(null); | |
| 123 program = await kernelTarget.writeOutline(null); | |
| 124 program = await kernelTarget.writeProgram(null); | |
| 125 if (kernelTarget.errors.isNotEmpty) { | |
| 126 return new CompilationError(kernelTarget.errors | |
| 127 .map((err) => err.toString()) | |
| 128 .toList(growable: false)); | |
| 129 } | |
| 130 } on InputError catch (e) { | |
| 131 return new CompilationError(<String>[e.format()]); | |
| 132 } | |
| 133 | |
| 134 // Perform target-specific transformations. | |
| 135 target.performModularTransformations(program); | |
| 136 target.performGlobalTransformations(program); | |
| 137 | |
| 138 // Write the program to a list of bytes and return it. | |
| 139 var sink = new DataSink(); | |
| 140 new BinaryPrinter(sink).writeProgramFile(program); | |
| 141 return new CompilationOk(sink.builder.takeBytes()); | |
| 142 } | |
| 143 | |
| 144 Future<CompilationResult> parseScript( | |
| 145 Uri fileName, String packageConfig, String sdkPath) async { | |
| 146 try { | |
| 147 return await parseScriptImpl(fileName, packageConfig, sdkPath); | |
| 148 } catch (err, stack) { | |
| 149 return new CompilationCrash(err.toString(), stack.toString()); | |
| 150 } | |
| 151 } | |
| 152 | |
| 153 Future _processLoadRequestImpl(String inputFileUrl) async { | |
| 154 Uri scriptUri = Uri.parse(inputFileUrl); | |
| 155 | 34 |
| 156 // Because we serve both Loader and bootstrapping requests we need to | 35 // Because we serve both Loader and bootstrapping requests we need to |
| 157 // duplicate the logic from _resolveScriptUri(...) here and attempt to | 36 // duplicate the logic from _resolveScriptUri(...) here and attempt to |
| 158 // resolve schemaless uris using current working directory. | 37 // resolve schemaless uris using current working directory. |
| 159 if (scriptUri.scheme == '') { | 38 if (!scriptUri.hasScheme) { |
| 160 // Script does not have a scheme, assume that it is a path, | 39 // Script does not have a scheme, assume that it is a path, |
| 161 // resolve it against the working directory. | 40 // resolve it against the working directory. |
| 162 scriptUri = Directory.current.uri.resolveUri(scriptUri); | 41 scriptUri = Uri.base.resolveUri(new Uri.file(inputFilePathOrUri)); |
| 163 } | 42 } |
| 164 | 43 |
| 165 if (scriptUri.scheme != 'file') { | 44 if (!scriptUri.isScheme('file')) { |
| 166 // TODO: reuse loader code to support other schemes. | 45 // TODO(vegorov): Reuse loader code to support other schemes. |
| 167 throw "Expected 'file' scheme for a script uri: got ${scriptUri.scheme}"; | 46 return new Future<CompilationResult>.value(new CompilationResult.error( |
| 47 ["Expected 'file' scheme for a script uri: got ${scriptUri.scheme}"])); |
| 168 } | 48 } |
| 169 | 49 |
| 170 final Uri packagesUri = (Platform.packageConfig != null) | 50 return parseScript(scriptUri, verbose: verbose); |
| 171 ? Uri.parse(Platform.packageConfig) | |
| 172 : await _findPackagesFile(scriptUri); | |
| 173 if (packagesUri == null) { | |
| 174 throw "Could not find .packages"; | |
| 175 } | |
| 176 | |
| 177 final Uri patchedSdk = | |
| 178 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); | |
| 179 | |
| 180 if (verbose) { | |
| 181 print("""DFE: Requesting compilation { | |
| 182 scriptUri: ${scriptUri} | |
| 183 packagesUri: ${packagesUri} | |
| 184 patchedSdk: ${patchedSdk} | |
| 185 }"""); | |
| 186 } | |
| 187 | |
| 188 return await parseScript(scriptUri, packagesUri.path, patchedSdk.path); | |
| 189 } | 51 } |
| 190 | 52 |
| 191 // Process a request from the runtime. See KernelIsolate::CompileToKernel in | 53 // Process a request from the runtime. See KernelIsolate::CompileToKernel in |
| 192 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. | 54 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. |
| 193 Future _processLoadRequest(request) async { | 55 Future _processLoadRequest(request) async { |
| 194 if (verbose) { | 56 if (verbose) { |
| 195 print("DFE: request: $request"); | 57 print("DFE: request: $request"); |
| 196 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); | 58 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
| 197 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); | 59 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
| 198 } | 60 } |
| 199 | 61 |
| 200 final int tag = request[0]; | 62 int tag = request[0]; |
| 201 final SendPort port = request[1]; | 63 final SendPort port = request[1]; |
| 202 final String inputFileUrl = request[2]; | 64 final String inputFileUrl = request[2]; |
| 203 | 65 |
| 204 var result; | 66 CompilationResult result; |
| 205 try { | 67 try { |
| 206 result = await _processLoadRequestImpl(inputFileUrl); | 68 result = await _processLoadRequestImpl(inputFileUrl); |
| 207 } catch (error, stack) { | 69 } catch (error, stack) { |
| 208 result = new CompilationCrash(error.toString(), stack.toString()); | 70 result = new CompilationResult.crash(error, stack); |
| 209 } | 71 } |
| 210 | 72 |
| 211 if (verbose) { | 73 if (verbose) { |
| 212 print("DFE:> ${result}"); | 74 print("DFE:> ${result}"); |
| 213 } | 75 } |
| 214 | 76 |
| 215 // Check whether this is a Loader request or a bootstrapping request from | 77 // Check whether this is a Loader request or a bootstrapping request from |
| 216 // KernelIsolate::CompileToKernel. | 78 // KernelIsolate::CompileToKernel. |
| 217 final isBootstrapRequest = tag == null; | 79 final isBootstrapRequest = tag == null; |
| 218 if (isBootstrapRequest) { | 80 if (isBootstrapRequest) { |
| 219 port.send(result.toResponse()); | 81 port.send(result.toResponse()); |
| 220 } else { | 82 } else { |
| 221 // See loader.cc for the code that handles these replies. | 83 // See loader.cc for the code that handles these replies. |
| 222 if (result is CompilationOk) { | 84 if (result.status != Status.ok) { |
| 223 port.send([tag, inputFileUrl, inputFileUrl, null, result]); | 85 tag = -tag; |
| 224 } else { | |
| 225 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); | |
| 226 } | 86 } |
| 87 port.send([tag, inputFileUrl, inputFileUrl, null, result.payload]); |
| 227 } | 88 } |
| 228 } | 89 } |
| 229 | 90 |
| 230 train(String scriptUri) { | 91 train(String scriptUri) { |
| 231 // TODO(28532): Enable on Windows. | 92 // TODO(28532): Enable on Windows. |
| 232 if (Platform.isWindows) return; | 93 if (Platform.isWindows) return; |
| 233 | 94 |
| 234 var tag = 1; | 95 var tag = 1; |
| 235 var responsePort = new RawReceivePort(); | 96 var responsePort = new RawReceivePort(); |
| 236 responsePort.handler = (response) { | 97 responsePort.handler = (response) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 251 main([args]) { | 112 main([args]) { |
| 252 if (args?.length == 2 && args[0] == '--train') { | 113 if (args?.length == 2 && args[0] == '--train') { |
| 253 // This entry point is used when creating an app snapshot. The argument | 114 // This entry point is used when creating an app snapshot. The argument |
| 254 // provides a script to compile to warm-up generated code. | 115 // provides a script to compile to warm-up generated code. |
| 255 train(args[1]); | 116 train(args[1]); |
| 256 } else { | 117 } else { |
| 257 // Entry point for the Kernel isolate. | 118 // Entry point for the Kernel isolate. |
| 258 return new RawReceivePort()..handler = _processLoadRequest; | 119 return new RawReceivePort()..handler = _processLoadRequest; |
| 259 } | 120 } |
| 260 } | 121 } |
| 261 | |
| 262 // This duplicates functionality from the Loader which we can't easily | |
| 263 // access from here. | |
| 264 Future<Uri> _findPackagesFile(Uri base) async { | |
| 265 var dir = new File.fromUri(base).parent; | |
| 266 while (true) { | |
| 267 final packagesFile = dir.uri.resolve(".packages"); | |
| 268 if (await new File.fromUri(packagesFile).exists()) { | |
| 269 return packagesFile; | |
| 270 } | |
| 271 if (dir.parent.path == dir.path) { | |
| 272 break; | |
| 273 } | |
| 274 dir = dir.parent; | |
| 275 } | |
| 276 return null; | |
| 277 } | |
| OLD | NEW |