Chromium Code Reviews| Index: runtime/tools/kernel-service.dart |
| diff --git a/runtime/tools/kernel-service.dart b/runtime/tools/kernel-service.dart |
| index f473c154775f0376174f49614e7eb7ace589ec48..d42bfd1ae6acde38b9a3799593fab2730c21635c 100644 |
| --- a/runtime/tools/kernel-service.dart |
| +++ b/runtime/tools/kernel-service.dart |
| @@ -1,22 +1,26 @@ |
| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| +library runtime.tools.kernel_service; |
| // This is an interface to the Dart Kernel parser and Kernel binary generator. |
| +// |
| // It is used by the kernel-isolate to load Dart source code and generate |
| // Kernel binary format. |
| +// |
| -import 'dart:isolate'; |
| import 'dart:async'; |
| +import 'dart:convert'; |
| import 'dart:io'; |
| +import 'dart:isolate'; |
| import 'dart:typed_data'; |
| -import 'package:kernel/binary/ast_to_binary.dart'; |
| import 'package:kernel/analyzer/loader.dart'; |
| +import 'package:kernel/binary/ast_to_binary.dart'; |
| import 'package:kernel/kernel.dart'; |
| import 'package:kernel/target/targets.dart'; |
| -const verbose = false; |
| +const bool verbose = const bool.fromEnvironment('DFE_VERBOSE') ?? false; |
| class DataSink implements Sink<List<int>> { |
| final BytesBuilder builder = new BytesBuilder(); |
| @@ -30,7 +34,72 @@ class DataSink implements Sink<List<int>> { |
| } |
| } |
| -Future<Uint8List> parseScript( |
| +// Note: this values must match Dart_KernelCompilationStatus in dart_api.h. |
|
siva
2017/01/24 02:38:02
these values
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| +const STATUS_OK = 0; // Compilation was successful. |
| +const STATUS_ERROR = 1; // Compilation failed with a compile time error. |
| +const STATUS_CRASH = 2; // Compiler crashed. |
|
kustermann
2017/01/30 12:14:23
more types: int?
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| + |
| +abstract class CompilationResult { |
| + List toResponse(); |
| +} |
| + |
| +class CompilationOk extends CompilationResult { |
| + final Uint8List binary; |
| + |
| + CompilationOk(this.binary); |
| + |
| + List toResponse() => [STATUS_OK, binary]; |
| + |
| + String toString() => "CompilationOk(${binary.length} bytes)"; |
| +} |
| + |
| +abstract class CompilationFail extends CompilationResult { |
| + String get errorString; |
| + |
| + Map<String, dynamic> toJson(); |
|
siva
2017/01/24 02:38:02
Sometimes there are blank lines between methods an
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| + static CompilationFail fromJson(Map m) { |
| + switch (m['status']) { |
| + case STATUS_ERROR: |
| + return new CompilationError(m['errors']); |
| + case STATUS_CRASH: |
| + return new CompilationCrash(m['exception'], m['stack']); |
| + } |
| + } |
|
kustermann
2017/01/30 12:14:23
The fromJson/toJson doesn't seem to be used in thi
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| +} |
| + |
| +class CompilationError extends CompilationFail { |
| + final List<String> errors; |
| + |
| + CompilationError(this.errors); |
| + |
| + List toResponse() => [STATUS_ERROR, errorString]; |
| + Map<String, dynamic> toJson() => { |
| + "status": STATUS_ERROR, |
| + "errors": errors, |
| + }; |
| + |
| + String get errorString => errors.take(10).join('\n'); |
| + String toString() => "CompilationError(${errorString})"; |
| +} |
| + |
| +class CompilationCrash extends CompilationFail { |
| + final String exception; |
| + final String stack; |
| + |
| + CompilationCrash(this.exception, this.stack); |
| + |
| + List toResponse() => [STATUS_CRASH, errorString]; |
| + Map<String, dynamic> toJson() => { |
| + "status": STATUS_CRASH, |
| + "exception": exception, |
| + "stack": stack, |
| + }; |
| + |
| + String get errorString => "${exception}\n${stack}"; |
| + String toString() => "CompilationCrash(${errorString})"; |
| +} |
| + |
| +Future<CompilationResult> parseScriptImpl(DartLoaderBatch batch_loader, |
| Uri fileName, String packageConfig, String sdkPath) async { |
| if (!FileSystemEntity.isFileSync(fileName.path)) { |
| throw "Input file '${fileName.path}' does not exist."; |
| @@ -49,12 +118,12 @@ Future<Uint8List> parseScript( |
| customUriMappings: const {}, |
| declaredVariables: const {}); |
| DartLoader loader = |
| - await new DartLoaderBatch().getLoader(new Repository(), dartOptions); |
| + await batch_loader.getLoader(new Repository(), dartOptions); |
| var program = loader.loadProgram(fileName, target: target); |
| var errors = loader.errors; |
| if (errors.isNotEmpty) { |
| - throw loader.errors.first; |
| + return new CompilationError(loader.errors.toList()); |
| } |
| // Link program into one file, cf. --link option in dartk. |
| @@ -63,33 +132,107 @@ Future<Uint8List> parseScript( |
| // Write the program to a list of bytes and return it. |
| var sink = new DataSink(); |
| new BinaryPrinter(sink).writeProgramFile(program); |
| - return sink.builder.takeBytes(); |
| + return new CompilationOk(sink.builder.takeBytes()); |
| } |
| -Future _processLoadRequest(request) async { |
| - if (verbose) { |
| - print("FROM DART KERNEL: load request: $request"); |
| - print("FROM DART KERNEL: package: ${Platform.packageConfig}"); |
| - print("FROM DART KERNEL: exec: ${Platform.resolvedExecutable}"); |
| +Future<CompilationResult> parseScript(DartLoaderBatch loader, Uri fileName, |
| + String packageConfig, String sdkPath) async { |
| + try { |
| + return await parseScriptImpl(loader, fileName, packageConfig, sdkPath); |
| + } catch (err, stack) { |
| + return new CompilationCrash(err.toString(), stack.toString()); |
| } |
| +} |
| - int tag = request[0]; |
| - SendPort port = request[1]; |
| - String inputFileUrl = request[2]; |
| +Future _processLoadRequestImpl(String inputFileUrl) async { |
| Uri scriptUri = Uri.parse(inputFileUrl); |
| - Uri packagesUri = Uri.parse(Platform.packageConfig ?? ".packages"); |
| + |
| + // Because we serve both Loader and bootstrapping requests we need to |
| + // duplicate the logic from _resolveScriptUri(...) here and attempt to |
| + // resolve schemaless uris using current working directory. |
| + if (scriptUri.scheme == '') { |
| + // Script does not have a scheme, assume that it is a path, |
| + // resolve it against the working directory. |
| + scriptUri = Directory.current.uri.resolveUri(scriptUri); |
| + } |
| + |
| + if (scriptUri.scheme != 'file') { |
| + // TODO: reuse loader code to support other schemes. |
| + throw "Expected 'file' scheme for a script uri: got ${scriptUri.scheme}"; |
| + } |
| + |
| + Uri packagesUri = (Platform.packageConfig != null) |
| + ? Uri.parse(Platform.packageConfig) |
| + : await _findPackagesFile(scriptUri); |
| + if (packagesUri == null) { |
| + throw "Could not find .packages"; |
| + } |
| + |
| Uri patchedSdk = |
|
kustermann
2017/01/30 12:14:24
final :)
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk"); |
| + if (verbose) { |
| + print("""DFE: Requesting compilation { |
| + scriptUri: ${scriptUri} |
| + packagesUri: ${packagesUri} |
| + patchedSdk: ${patchedSdk} |
| +}"""); |
| + } |
| + |
| + CompilationResult result; |
|
kustermann
2017/01/30 12:14:23
unused variable
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| + return await parseScript( |
| + new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path); |
| +} |
| + |
| +Future _processLoadRequest(request) async { |
|
kustermann
2017/01/30 12:14:23
Maybe add a comment describing where the other end
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| + if (verbose) { |
| + print("DFE: request: $request"); |
| + print("DFE: Platform.packageConfig: ${Platform.packageConfig}"); |
| + print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}"); |
| + } |
| + |
| + final int tag = request[0]; |
| + final SendPort port = request[1]; |
| + final String inputFileUrl = request[2]; |
| + |
| var result; |
| try { |
| - result = await parseScript(scriptUri, packagesUri.path, patchedSdk.path); |
| - } catch (error) { |
| - tag = -tag; // Mark reply as an exception. |
| - result = error.toString(); |
| + result = await _processLoadRequestImpl(inputFileUrl); |
| + } catch (error, stack) { |
| + result = new CompilationCrash(error.toString(), stack.toString()); |
| } |
| - port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
| + if (verbose) { |
| + print("DFE:> ${result}"); |
| + } |
| + |
| + final isBootstrapRequest = tag == null; |
| + if (isBootstrapRequest) { |
| + port.send(result.toResponse()); |
| + } else { |
| + if (result is CompilationOk) { |
| + port.send([tag, inputFileUrl, inputFileUrl, null, result]); |
| + } else { |
| + port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]); |
|
kustermann
2017/01/30 12:14:23
Also add a pointer here where in c++ this is recei
Vyacheslav Egorov (Google)
2017/01/30 19:34:13
Done.
|
| + } |
| + } |
| } |
| main() => new RawReceivePort()..handler = _processLoadRequest; |
| + |
| +// This duplicates functionality from the Loader which we can't easily |
| +// access from here. |
| +Uri _findPackagesFile(Uri base) async { |
| + var dir = new File.fromUri(base).parent; |
| + while (true) { |
| + final packagesFile = dir.uri.resolve(".packages"); |
| + if (await new File.fromUri(packagesFile).exists()) { |
| + return packagesFile; |
| + } |
| + if (dir.parent == dir) { |
| + break; |
| + } |
| + dir = dir.parent; |
| + } |
| + return null; |
| +} |