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 | 5 |
5 // 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 // |
6 // 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 |
7 // Kernel binary format. | 9 // Kernel binary format. |
| 10 // |
8 | 11 |
| 12 import 'dart:async'; |
| 13 import 'dart:convert'; |
| 14 import 'dart:io'; |
9 import 'dart:isolate'; | 15 import 'dart:isolate'; |
10 import 'dart:async'; | |
11 import 'dart:io'; | |
12 import 'dart:typed_data'; | 16 import 'dart:typed_data'; |
13 | 17 |
| 18 import 'package:kernel/analyzer/loader.dart'; |
14 import 'package:kernel/binary/ast_to_binary.dart'; | 19 import 'package:kernel/binary/ast_to_binary.dart'; |
15 import 'package:kernel/analyzer/loader.dart'; | |
16 import 'package:kernel/kernel.dart'; | 20 import 'package:kernel/kernel.dart'; |
17 import 'package:kernel/target/targets.dart'; | 21 import 'package:kernel/target/targets.dart'; |
18 | 22 |
19 const verbose = false; | 23 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false; |
20 | 24 |
21 class DataSink implements Sink<List<int>> { | 25 class DataSink implements Sink<List<int>> { |
22 final BytesBuilder builder = new BytesBuilder(); | 26 final BytesBuilder builder = new BytesBuilder(); |
23 | 27 |
24 void add(List<int> data) { | 28 void add(List<int> data) { |
25 builder.add(data); | 29 builder.add(data); |
26 } | 30 } |
27 | 31 |
28 void close() { | 32 void close() { |
29 // Nothing to do. | 33 // Nothing to do. |
30 } | 34 } |
31 } | 35 } |
32 | 36 |
33 Future<Uint8List> parseScript( | 37 // Note: these values must match Dart_KernelCompilationStatus in dart_api.h. |
| 38 const int STATUS_OK = 0; // Compilation was successful. |
| 39 const int STATUS_ERROR = 1; // Compilation failed with a compile time error. |
| 40 const int STATUS_CRASH = 2; // Compiler crashed. |
| 41 |
| 42 abstract class CompilationResult { |
| 43 List toResponse(); |
| 44 } |
| 45 |
| 46 class CompilationOk extends CompilationResult { |
| 47 final Uint8List binary; |
| 48 |
| 49 CompilationOk(this.binary); |
| 50 |
| 51 List toResponse() => [STATUS_OK, binary]; |
| 52 |
| 53 String toString() => "CompilationOk(${binary.length} bytes)"; |
| 54 } |
| 55 |
| 56 abstract class CompilationFail extends CompilationResult { |
| 57 String get errorString; |
| 58 } |
| 59 |
| 60 class CompilationError extends CompilationFail { |
| 61 final List<String> errors; |
| 62 |
| 63 CompilationError(this.errors); |
| 64 |
| 65 List toResponse() => [STATUS_ERROR, errorString]; |
| 66 |
| 67 String get errorString => errors.take(10).join('\n'); |
| 68 |
| 69 String toString() => "CompilationError(${errorString})"; |
| 70 } |
| 71 |
| 72 class CompilationCrash extends CompilationFail { |
| 73 final String exception; |
| 74 final String stack; |
| 75 |
| 76 CompilationCrash(this.exception, this.stack); |
| 77 |
| 78 List toResponse() => [STATUS_CRASH, errorString]; |
| 79 |
| 80 String get errorString => "${exception}\n${stack}"; |
| 81 |
| 82 String toString() => "CompilationCrash(${errorString})"; |
| 83 } |
| 84 |
| 85 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, |
34 Uri fileName, String packageConfig, String sdkPath) async { | 86 Uri fileName, String packageConfig, String sdkPath) async { |
35 if (!FileSystemEntity.isFileSync(fileName.path)) { | 87 if (!FileSystemEntity.isFileSync(fileName.path)) { |
36 throw "Input file '${fileName.path}' does not exist."; | 88 throw "Input file '${fileName.path}' does not exist."; |
37 } | 89 } |
38 | 90 |
39 if (!FileSystemEntity.isDirectorySync(sdkPath)) { | 91 if (!FileSystemEntity.isDirectorySync(sdkPath)) { |
40 throw "Patched sdk directory not found at $sdkPath"; | 92 throw "Patched sdk directory not found at $sdkPath"; |
41 } | 93 } |
42 | 94 |
43 Target target = getTarget("vm", new TargetFlags(strongMode: false)); | 95 Target target = getTarget("vm", new TargetFlags(strongMode: false)); |
44 DartOptions dartOptions = new DartOptions( | 96 DartOptions dartOptions = new DartOptions( |
45 strongMode: false, | 97 strongMode: false, |
46 strongModeSdk: false, | 98 strongModeSdk: false, |
47 sdk: sdkPath, | 99 sdk: sdkPath, |
48 packagePath: packageConfig, | 100 packagePath: packageConfig, |
49 customUriMappings: const {}, | 101 customUriMappings: const {}, |
50 declaredVariables: const {}); | 102 declaredVariables: const {}); |
51 DartLoader loader = | 103 DartLoader loader = |
52 await new DartLoaderBatch().getLoader(new Repository(), dartOptions); | 104 await batch_loader.getLoader(new Repository(), dartOptions); |
53 var program = loader.loadProgram(fileName, target: target); | 105 var program = loader.loadProgram(fileName, target: target); |
54 | 106 |
55 var errors = loader.errors; | 107 var errors = loader.errors; |
56 if (errors.isNotEmpty) { | 108 if (errors.isNotEmpty) { |
57 throw loader.errors.first; | 109 return new CompilationError(loader.errors.toList()); |
58 } | 110 } |
59 | 111 |
60 // Link program into one file, cf. --link option in dartk. | 112 // Link program into one file, cf. --link option in dartk. |
61 target.transformProgram(program); | 113 target.transformProgram(program); |
62 | 114 |
63 // Write the program to a list of bytes and return it. | 115 // Write the program to a list of bytes and return it. |
64 var sink = new DataSink(); | 116 var sink = new DataSink(); |
65 new BinaryPrinter(sink).writeProgramFile(program); | 117 new BinaryPrinter(sink).writeProgramFile(program); |
66 return sink.builder.takeBytes(); | 118 return new CompilationOk(sink.builder.takeBytes()); |
67 } | 119 } |
68 | 120 |
| 121 Future<CompilationResult> parseScript(DartLoaderBatch loader, Uri fileName, |
| 122 String packageConfig, String sdkPath) async { |
| 123 try { |
| 124 return await parseScriptImpl(loader, fileName, packageConfig, sdkPath); |
| 125 } catch (err, stack) { |
| 126 return new CompilationCrash(err.toString(), stack.toString()); |
| 127 } |
| 128 } |
| 129 |
| 130 Future _processLoadRequestImpl(String inputFileUrl) async { |
| 131 Uri scriptUri = Uri.parse(inputFileUrl); |
| 132 |
| 133 // Because we serve both Loader and bootstrapping requests we need to |
| 134 // duplicate the logic from _resolveScriptUri(...) here and attempt to |
| 135 // resolve schemaless uris using current working directory. |
| 136 if (scriptUri.scheme == '') { |
| 137 // Script does not have a scheme, assume that it is a path, |
| 138 // resolve it against the working directory. |
| 139 scriptUri = Directory.current.uri.resolveUri(scriptUri); |
| 140 } |
| 141 |
| 142 if (scriptUri.scheme != 'file') { |
| 143 // TODO: reuse loader code to support other schemes. |
| 144 throw "Expected 'file' scheme for a script uri: got ${scriptUri.scheme}"; |
| 145 } |
| 146 |
| 147 final Uri packagesUri = (Platform.packageConfig != null) |
| 148 ? Uri.parse(Platform.packageConfig) |
| 149 : await _findPackagesFile(scriptUri); |
| 150 if (packagesUri == null) { |
| 151 throw "Could not find .packages"; |
| 152 } |
| 153 |
| 154 final Uri patchedSdk = |
| 155 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); |
| 156 |
| 157 if (verbose) { |
| 158 print("""DFE: Requesting compilation { |
| 159 scriptUri: ${scriptUri} |
| 160 packagesUri: ${packagesUri} |
| 161 patchedSdk: ${patchedSdk} |
| 162 }"""); |
| 163 } |
| 164 |
| 165 return await parseScript( |
| 166 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); |
| 167 } |
| 168 |
| 169 // Process a request from the runtime. See KernelIsolate::CompileToKernel in |
| 170 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc. |
69 Future _processLoadRequest(request) async { | 171 Future _processLoadRequest(request) async { |
70 if (verbose) { | 172 if (verbose) { |
71 print("FROM DART KERNEL: load request: $request"); | 173 print("DFE: request: $request"); |
72 print("FROM DART KERNEL: package: ${Platform.packageConfig}"); | 174 print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
73 print("FROM DART KERNEL: exec: ${Platform.resolvedExecutable}"); | 175 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
74 } | 176 } |
75 | 177 |
76 int tag = request[0]; | 178 final int tag = request[0]; |
77 SendPort port = request[1]; | 179 final SendPort port = request[1]; |
78 String inputFileUrl = request[2]; | 180 final String inputFileUrl = request[2]; |
79 Uri scriptUri = Uri.parse(inputFileUrl); | |
80 Uri packagesUri = Uri.parse(Platform.packageConfig ?? ".packages"); | |
81 Uri patchedSdk = | |
82 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); | |
83 | 181 |
84 var result; | 182 var result; |
85 try { | 183 try { |
86 result = await parseScript(scriptUri, packagesUri.path, patchedSdk.path); | 184 result = await _processLoadRequestImpl(inputFileUrl); |
87 } catch (error) { | 185 } catch (error, stack) { |
88 tag = -tag; // Mark reply as an exception. | 186 result = new CompilationCrash(error.toString(), stack.toString()); |
89 result = error.toString(); | |
90 } | 187 } |
91 | 188 |
92 port.send([tag, inputFileUrl, inputFileUrl, null, result]); | 189 if (verbose) { |
| 190 print("DFE:> ${result}"); |
| 191 } |
| 192 |
| 193 // Check whether this is a Loader request or a bootstrapping request from |
| 194 // KernelIsolate::CompileToKernel. |
| 195 final isBootstrapRequest = tag == null; |
| 196 if (isBootstrapRequest) { |
| 197 port.send(result.toResponse()); |
| 198 } else { |
| 199 // See loader.cc for the code that handles these replies. |
| 200 if (result is CompilationOk) { |
| 201 port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
| 202 } else { |
| 203 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); |
| 204 } |
| 205 } |
93 } | 206 } |
94 | 207 |
95 main() => new RawReceivePort()..handler = _processLoadRequest; | 208 main() => new RawReceivePort()..handler = _processLoadRequest; |
| 209 |
| 210 // This duplicates functionality from the Loader which we can't easily |
| 211 // access from here. |
| 212 Uri _findPackagesFile(Uri base) async { |
| 213 var dir = new File.fromUri(base).parent; |
| 214 while (true) { |
| 215 final packagesFile = dir.uri.resolve(".packages"); |
| 216 if (await new File.fromUri(packagesFile).exists()) { |
| 217 return packagesFile; |
| 218 } |
| 219 if (dir.parent == dir) { |
| 220 break; |
| 221 } |
| 222 dir = dir.parent; |
| 223 } |
| 224 return null; |
| 225 } |
OLD | NEW |