Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: runtime/tools/kernel-service.dart

Issue 2665753002: Reapply "Create an app snapshot of the Dart front end." (Closed)
Patch Set: merge Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/platform/globals.h ('k') | runtime/vm/kernel_isolate.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4 library runtime.tools.kernel_service;
5
6 // This is an interface to the Dart Kernel parser and Kernel binary generator.
7 //
8 // It is used by the kernel-isolate to load Dart source code and generate
9 // Kernel binary format.
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 --batch
20 //
21 // The port for the batch mode worker is controlled by DFE_WORKER_PORT
22 // environment declarations (set by -DDFE_WORKER_PORT=... command line flag).
23 // When not set (or set to 0) an ephemeral port returned by the OS is used
24 // instead.
25 //
26 // When this script is used as a Kernel isolate root script and DFE_WORKER_PORT
27 // is set to non-zero value then Kernel isolate will forward all compilation
28 // requests it receives to the batch worker on the given port.
29 //
30
31 import 'dart:async';
32 import 'dart:convert';
33 import 'dart:io';
34 import 'dart:isolate';
35 import 'dart:typed_data';
36
37 import 'package:kernel/analyzer/loader.dart';
38 import 'package:kernel/binary/ast_to_binary.dart';
39 import 'package:kernel/kernel.dart';
40 import 'package:kernel/target/targets.dart';
41
42 const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false;
43 const int workerPort = const int.fromEnvironment('DFE_WORKER_PORT') ?? 0;
44
45 class DataSink implements Sink<List<int>> {
46 final BytesBuilder builder = new BytesBuilder();
47
48 void add(List<int> data) {
49 builder.add(data);
50 }
51
52 void close() {
53 // Nothing to do.
54 }
55 }
56
57 // Note: these values must match Dart_KernelCompilationStatus in dart_api.h.
58 const int STATUS_OK = 0; // Compilation was successful.
59 const int STATUS_ERROR = 1; // Compilation failed with a compile time error.
60 const int STATUS_CRASH = 2; // Compiler crashed.
61
62 abstract class CompilationResult {
63 List toResponse();
64 }
65
66 class CompilationOk extends CompilationResult {
67 final Uint8List binary;
68
69 CompilationOk(this.binary);
70
71 List toResponse() => [STATUS_OK, binary];
72
73 String toString() => "CompilationOk(${binary.length} bytes)";
74 }
75
76 abstract class CompilationFail extends CompilationResult {
77 String get errorString;
78
79 Map<String, dynamic> toJson();
80
81 static CompilationFail fromJson(Map m) {
82 switch (m['status']) {
83 case STATUS_ERROR:
84 return new CompilationError(m['errors']);
85 case STATUS_CRASH:
86 return new CompilationCrash(m['exception'], m['stack']);
87 }
88 }
89 }
90
91 class CompilationError extends CompilationFail {
92 final List<String> errors;
93
94 CompilationError(this.errors);
95
96 List toResponse() => [STATUS_ERROR, errorString];
97
98 Map<String, dynamic> toJson() => {
99 "status": STATUS_ERROR,
100 "errors": errors,
101 };
102
103 String get errorString => errors.take(10).join('\n');
104
105 String toString() => "CompilationError(${errorString})";
106 }
107
108 class CompilationCrash extends CompilationFail {
109 final String exception;
110 final String stack;
111
112 CompilationCrash(this.exception, this.stack);
113
114 List toResponse() => [STATUS_CRASH, errorString];
115
116 Map<String, dynamic> toJson() => {
117 "status": STATUS_CRASH,
118 "exception": exception,
119 "stack": stack,
120 };
121
122 String get errorString => "${exception}\n${stack}";
123
124 String toString() => "CompilationCrash(${errorString})";
125 }
126
127 Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader,
128 Uri fileName, String packageConfig, String sdkPath) async {
129 if (!FileSystemEntity.isFileSync(fileName.path)) {
130 throw "Input file '${fileName.path}' does not exist.";
131 }
132
133 if (!FileSystemEntity.isDirectorySync(sdkPath)) {
134 throw "Patched sdk directory not found at $sdkPath";
135 }
136
137 Target target = getTarget("vm", new TargetFlags(strongMode: false));
138 DartOptions dartOptions = new DartOptions(
139 strongMode: false,
140 strongModeSdk: false,
141 sdk: sdkPath,
142 packagePath: packageConfig,
143 customUriMappings: const {},
144 declaredVariables: const {});
145 DartLoader loader =
146 await batch_loader.getLoader(new Repository(), dartOptions);
147 var program = loader.loadProgram(fileName, target: target);
148
149 var errors = loader.errors;
150 if (errors.isNotEmpty) {
151 return new CompilationError(loader.errors.toList());
152 }
153
154 // Link program into one file, cf. --link option in dartk.
155 target.transformProgram(program);
156
157 // Write the program to a list of bytes and return it.
158 var sink = new DataSink();
159 new BinaryPrinter(sink).writeProgramFile(program);
160 return new CompilationOk(sink.builder.takeBytes());
161 }
162
163 Future<CompilationResult> parseScript(DartLoaderBatch loader, Uri fileName,
164 String packageConfig, String sdkPath) async {
165 try {
166 return await parseScriptImpl(loader, fileName, packageConfig, sdkPath);
167 } catch (err, stack) {
168 return new CompilationCrash(err.toString(), stack.toString());
169 }
170 }
171
172 Future _processLoadRequestImpl(String inputFileUrl) async {
173 Uri scriptUri = Uri.parse(inputFileUrl);
174
175 // Because we serve both Loader and bootstrapping requests we need to
176 // duplicate the logic from _resolveScriptUri(...) here and attempt to
177 // resolve schemaless uris using current working directory.
178 if (scriptUri.scheme == '') {
179 // Script does not have a scheme, assume that it is a path,
180 // resolve it against the working directory.
181 scriptUri = Directory.current.uri.resolveUri(scriptUri);
182 }
183
184 if (scriptUri.scheme != 'file') {
185 // TODO: reuse loader code to support other schemes.
186 throw "Expected 'file' scheme for a script uri: got ${scriptUri.scheme}";
187 }
188
189 final Uri packagesUri = (Platform.packageConfig != null)
190 ? Uri.parse(Platform.packageConfig)
191 : await _findPackagesFile(scriptUri);
192 if (packagesUri == null) {
193 throw "Could not find .packages";
194 }
195
196 final Uri patchedSdk =
197 Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk");
198
199 if (verbose) {
200 print("""DFE: Requesting compilation {
201 scriptUri: ${scriptUri}
202 packagesUri: ${packagesUri}
203 patchedSdk: ${patchedSdk}
204 }""");
205 }
206
207 if (workerPort != 0) {
208 return await requestParse(scriptUri, packagesUri, patchedSdk);
209 } else {
210 return await parseScript(
211 new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path);
212 }
213 }
214
215
216 // Process a request from the runtime. See KernelIsolate::CompileToKernel in
217 // kernel_isolate.cc and Loader::SendKernelRequest in loader.cc.
218 Future _processLoadRequest(request) async {
219 if (verbose) {
220 print("DFE: request: $request");
221 print("DFE: Platform.packageConfig: ${Platform.packageConfig}");
222 print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}");
223 }
224
225 final int tag = request[0];
226 final SendPort port = request[1];
227 final String inputFileUrl = request[2];
228
229 var result;
230 try {
231 result = await _processLoadRequestImpl(inputFileUrl);
232 } catch (error, stack) {
233 result = new CompilationCrash(error.toString(), stack.toString());
234 }
235
236 if (verbose) {
237 print("DFE:> ${result}");
238 }
239
240 // Check whether this is a Loader request or a bootstrapping request from
241 // KernelIsolate::CompileToKernel.
242 final isBootstrapRequest = tag == null;
243 if (isBootstrapRequest) {
244 port.send(result.toResponse());
245 } else {
246 // See loader.cc for the code that handles these replies.
247 if (result is CompilationOk) {
248 port.send([tag, inputFileUrl, inputFileUrl, null, result]);
249 } else {
250 port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]);
251 }
252 }
253 }
254
255 Future<CompilationResult> requestParse(
256 Uri scriptUri, Uri packagesUri, Uri patchedSdk) async {
257 if (verbose) {
258 print(
259 "DFE: forwarding request to worker at http://localhost:${workerPort}/");
260 }
261
262 HttpClient client = new HttpClient();
263 final rq = await client
264 .postUrl(new Uri(host: 'localhost', port: workerPort, scheme: 'http'));
265 rq.headers.contentType = ContentType.JSON;
266 rq.write(JSON.encode({
267 "inputFileUrl": scriptUri.toString(),
268 "packagesUri": packagesUri.toString(),
269 "patchedSdk": patchedSdk.toString(),
270 }));
271 final rs = await rq.close();
272 try {
273 if (rs.statusCode == HttpStatus.OK) {
274 final BytesBuilder bb = new BytesBuilder();
275 await rs.forEach(bb.add);
276 return new CompilationOk(bb.takeBytes());
277 } else {
278 return CompilationFail.fromJson(JSON.decode(await UTF8.decodeStream(rs)));
279 }
280 } finally {
281 await client.close();
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 {
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.statusCode = HttpStatus.OK;
301 request.response.headers.contentType = ContentType.BINARY;
302 request.response.add(result.binary);
303 request.response.close();
304 } else {
305 request.response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
306 request.response.headers.contentType = ContentType.TEXT;
307 request.response.write(JSON.encode(result.toJson()));
308 request.response.close();
309 }
310 });
311 ProcessSignal.SIGTERM.watch().first.then((_) => server.close());
312 });
313 }
314
315 main([args]) {
316 if (args?.length == 1 && args[0] == '--batch') {
317 startBatchServer();
318 } else {
319 // Entry point for the Kernel isolate.
320 return new RawReceivePort()..handler = _processLoadRequest;
321 }
322 }
323
324 // This duplicates functionality from the Loader which we can't easily
325 // access from here.
326 Uri _findPackagesFile(Uri base) async {
327 var dir = new File.fromUri(base).parent;
328 while (true) {
329 final packagesFile = dir.uri.resolve(".packages");
330 if (await new File.fromUri(packagesFile).exists()) {
331 return packagesFile;
332 }
333 if (dir.parent.path == dir.path) {
334 break;
335 }
336 dir = dir.parent;
337 }
338 return null;
339 }
OLDNEW
« no previous file with comments | « runtime/platform/globals.h ('k') | runtime/vm/kernel_isolate.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698