| Index: runtime/tools/kernel-service.dart
|
| diff --git a/runtime/tools/kernel-service.dart b/runtime/tools/kernel-service.dart
|
| index f473c154775f0376174f49614e7eb7ace589ec48..b1b441db2ed5e7d1f6efe595150f09d973bb1c93 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,55 @@ class DataSink implements Sink<List<int>> {
|
| }
|
| }
|
|
|
| -Future<Uint8List> parseScript(
|
| +// Note: these values must match Dart_KernelCompilationStatus in dart_api.h.
|
| +const int STATUS_OK = 0; // Compilation was successful.
|
| +const int STATUS_ERROR = 1; // Compilation failed with a compile time error.
|
| +const int STATUS_CRASH = 2; // Compiler crashed.
|
| +
|
| +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;
|
| +}
|
| +
|
| +class CompilationError extends CompilationFail {
|
| + final List<String> errors;
|
| +
|
| + CompilationError(this.errors);
|
| +
|
| + List toResponse() => [STATUS_ERROR, errorString];
|
| +
|
| + 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];
|
| +
|
| + 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 +101,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 +115,111 @@ 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");
|
| - Uri patchedSdk =
|
| +
|
| + // 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}";
|
| + }
|
| +
|
| + final Uri packagesUri = (Platform.packageConfig != null)
|
| + ? Uri.parse(Platform.packageConfig)
|
| + : await _findPackagesFile(scriptUri);
|
| + if (packagesUri == null) {
|
| + throw "Could not find .packages";
|
| + }
|
| +
|
| + final Uri patchedSdk =
|
| Uri.parse(Platform.resolvedExecutable).resolve("patched_sdk");
|
|
|
| + if (verbose) {
|
| + print("""DFE: Requesting compilation {
|
| + scriptUri: ${scriptUri}
|
| + packagesUri: ${packagesUri}
|
| + patchedSdk: ${patchedSdk}
|
| +}""");
|
| + }
|
| +
|
| + return await parseScript(
|
| + new DartLoaderBatch(), scriptUri, packagesUri.path, patchedSdk.path);
|
| +}
|
| +
|
| +// Process a request from the runtime. See KernelIsolate::CompileToKernel in
|
| +// kernel_isolate.cc and Loader::SendKernelRequest in loader.cc.
|
| +Future _processLoadRequest(request) async {
|
| + 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}");
|
| + }
|
| +
|
| + // Check whether this is a Loader request or a bootstrapping request from
|
| + // KernelIsolate::CompileToKernel.
|
| + final isBootstrapRequest = tag == null;
|
| + if (isBootstrapRequest) {
|
| + port.send(result.toResponse());
|
| + } else {
|
| + // See loader.cc for the code that handles these replies.
|
| + if (result is CompilationOk) {
|
| + port.send([tag, inputFileUrl, inputFileUrl, null, result]);
|
| + } else {
|
| + port.send([-tag, inputFileUrl, inputFileUrl, null, result.errorString]);
|
| + }
|
| + }
|
| }
|
|
|
| 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;
|
| +}
|
|
|