| Index: pkg/analyzer_cli/lib/src/build_mode.dart
|
| diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
|
| index aa7afd476ad83e75815682dafde9dad89c3a15e6..ab6f1cd8440eb975e56738572622dd0f66b66103 100644
|
| --- a/pkg/analyzer_cli/lib/src/build_mode.dart
|
| +++ b/pkg/analyzer_cli/lib/src/build_mode.dart
|
| @@ -29,8 +29,11 @@ import 'package:analyzer_cli/src/error_formatter.dart';
|
| import 'package:analyzer_cli/src/error_severity.dart';
|
| import 'package:analyzer_cli/src/options.dart';
|
| import 'package:bazel_worker/bazel_worker.dart';
|
| +import 'package:collection/collection.dart';
|
| +import 'package:convert/convert.dart';
|
| import 'package:front_end/src/base/performace_logger.dart';
|
| import 'package:front_end/src/byte_store/byte_store.dart';
|
| +import 'package:front_end/src/byte_store/cache.dart';
|
|
|
| /**
|
| * Persistent Bazel worker.
|
| @@ -39,13 +42,17 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
|
| final ResourceProvider resourceProvider;
|
| final PerformanceLog logger = new PerformanceLog(null);
|
| final String dartSdkPath;
|
| + WorkerPackageBundleCache packageBundleCache;
|
|
|
| final StringBuffer errorBuffer = new StringBuffer();
|
| final StringBuffer outBuffer = new StringBuffer();
|
|
|
| AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection,
|
| {this.dartSdkPath})
|
| - : super(connection: connection);
|
| + : super(connection: connection) {
|
| + packageBundleCache = new WorkerPackageBundleCache(
|
| + resourceProvider, logger, 256 * 1024 * 1024);
|
| + }
|
|
|
| factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider,
|
| {io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) {
|
| @@ -58,10 +65,13 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
|
| /**
|
| * Performs analysis with given [options].
|
| */
|
| - Future<Null> analyze(CommandLineOptions options) async {
|
| + Future<Null> analyze(
|
| + CommandLineOptions options, Map<String, WorkerInput> inputs) async {
|
| + var packageBundleProvider =
|
| + new WorkerPackageBundleProvider(packageBundleCache, inputs);
|
| var buildMode = new BuildMode(
|
| resourceProvider, options, new AnalysisStats(),
|
| - logger: logger);
|
| + logger: logger, packageBundleProvider: packageBundleProvider);
|
| await buildMode.analyze();
|
| AnalysisEngine.instance.clearCaches();
|
| }
|
| @@ -75,6 +85,12 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
|
| errorBuffer.clear();
|
| outBuffer.clear();
|
| try {
|
| + // Prepare inputs with their digests.
|
| + Map<String, WorkerInput> inputs = {};
|
| + for (var input in request.inputs) {
|
| + inputs[input.path] = new WorkerInput(input.path, input.digest);
|
| + }
|
| +
|
| // Add in the dart-sdk argument if `dartSdkPath` is not null,
|
| // otherwise it will try to find the currently installed sdk.
|
| var arguments = request.arguments.toList();
|
| @@ -82,13 +98,15 @@ class AnalyzerWorkerLoop extends AsyncWorkerLoop {
|
| !arguments.any((arg) => arg.startsWith('--dart-sdk'))) {
|
| arguments.add('--dart-sdk=$dartSdkPath');
|
| }
|
| +
|
| // Prepare options.
|
| CommandLineOptions options =
|
| CommandLineOptions.parse(arguments, printAndFail: (String msg) {
|
| throw new ArgumentError(msg);
|
| });
|
| +
|
| // Analyze and respond.
|
| - await analyze(options);
|
| + await analyze(options, inputs);
|
| String msg = _getErrorOutputBuffersText();
|
| return new WorkResponse()
|
| ..exitCode = EXIT_CODE_OK
|
| @@ -136,6 +154,7 @@ class BuildMode {
|
| final CommandLineOptions options;
|
| final AnalysisStats stats;
|
| final PerformanceLog logger;
|
| + final PackageBundleProvider packageBundleProvider;
|
|
|
| SummaryDataStore summaryDataStore;
|
| AnalysisOptions analysisOptions;
|
| @@ -150,8 +169,10 @@ class BuildMode {
|
| final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{};
|
|
|
| BuildMode(this.resourceProvider, this.options, this.stats,
|
| - {PerformanceLog logger})
|
| - : logger = logger ?? new PerformanceLog(null);
|
| + {PerformanceLog logger, PackageBundleProvider packageBundleProvider})
|
| + : logger = logger ?? new PerformanceLog(null),
|
| + packageBundleProvider = packageBundleProvider ??
|
| + new DirectPackageBundleProvider(resourceProvider);
|
|
|
| bool get _shouldOutputSummary =>
|
| options.buildSummaryOutput != null ||
|
| @@ -303,8 +324,7 @@ class BuildMode {
|
|
|
| // Adds a bundle at `path` to `summaryDataStore`.
|
| PackageBundle addBundle(String path) {
|
| - var bundle =
|
| - new PackageBundle.fromBuffer(new io.File(path).readAsBytesSync());
|
| + PackageBundle bundle = packageBundleProvider.get(path);
|
| summaryDataStore.addBundle(path, bundle);
|
| return bundle;
|
| }
|
| @@ -467,6 +487,21 @@ class BuildMode {
|
| }
|
| }
|
|
|
| +/**
|
| + * [PackageBundleProvider] that always reads from the [ResourceProvider].
|
| + */
|
| +class DirectPackageBundleProvider implements PackageBundleProvider {
|
| + final ResourceProvider resourceProvider;
|
| +
|
| + DirectPackageBundleProvider(this.resourceProvider);
|
| +
|
| + @override
|
| + PackageBundle get(String path) {
|
| + var bytes = new io.File(path).readAsBytesSync();
|
| + return new PackageBundle.fromBuffer(bytes);
|
| + }
|
| +}
|
| +
|
| /**
|
| * Instances of the class [ExplicitSourceResolver] map URIs to files on disk
|
| * using a fixed mapping provided at construction time.
|
| @@ -510,3 +545,109 @@ class ExplicitSourceResolver extends UriResolver {
|
| return pathToUriMap;
|
| }
|
| }
|
| +
|
| +/**
|
| + * Provider for [PackageBundle]s by file paths.
|
| + */
|
| +abstract class PackageBundleProvider {
|
| + /**
|
| + * Return the [PackageBundle] for the file with the given [path].
|
| + */
|
| + PackageBundle get(String path);
|
| +}
|
| +
|
| +/**
|
| + * Worker input.
|
| + *
|
| + * Bazel does not specify the format of the digest, so we cannot assume that
|
| + * the digest itself is enough to uniquely identify inputs. So, we use a pair
|
| + * of path + digest.
|
| + */
|
| +class WorkerInput {
|
| + static const _digestEquality = const ListEquality<int>();
|
| +
|
| + final String path;
|
| + final List<int> digest;
|
| +
|
| + WorkerInput(this.path, this.digest);
|
| +
|
| + @override
|
| + int get hashCode => _digestEquality.hash(digest);
|
| +
|
| + @override
|
| + bool operator ==(Object other) {
|
| + return other is WorkerInput &&
|
| + other.path == path &&
|
| + _digestEquality.equals(other.digest, digest);
|
| + }
|
| +
|
| + @override
|
| + String toString() => '$path @ ${hex.encode(digest)}';
|
| +}
|
| +
|
| +/**
|
| + * Value object for [WorkerPackageBundleCache].
|
| + */
|
| +class WorkerPackageBundle {
|
| + final List<int> bytes;
|
| + final PackageBundle bundle;
|
| +
|
| + WorkerPackageBundle(this.bytes, this.bundle);
|
| +
|
| + /**
|
| + * Approximation of a bundle size in memory.
|
| + */
|
| + int get size => bytes.length * 3;
|
| +}
|
| +
|
| +/**
|
| + * Cache of [PackageBundle]s.
|
| + */
|
| +class WorkerPackageBundleCache {
|
| + final ResourceProvider resourceProvider;
|
| + final PerformanceLog logger;
|
| + final Cache<WorkerInput, WorkerPackageBundle> _cache;
|
| +
|
| + WorkerPackageBundleCache(this.resourceProvider, this.logger, int maxSizeBytes)
|
| + : _cache = new Cache<WorkerInput, WorkerPackageBundle>(
|
| + maxSizeBytes, (value) => value.size);
|
| +
|
| + /**
|
| + * Get the [PackageBundle] from the file with the given [path] in the context
|
| + * of the given worker [inputs].
|
| + */
|
| + PackageBundle get(Map<String, WorkerInput> inputs, String path) {
|
| + WorkerInput input = inputs[path];
|
| +
|
| + // The input must be not null, otherwise we're not expected to read
|
| + // this file, but we check anyway to be safe.
|
| + if (input == null) {
|
| + logger.writeln('Read $path outside of the inputs.');
|
| + var bytes = resourceProvider.getFile(path).readAsBytesSync();
|
| + return new PackageBundle.fromBuffer(bytes);
|
| + }
|
| +
|
| + return _cache.get(input, () {
|
| + logger.writeln('Read $input.');
|
| + var bytes = resourceProvider.getFile(path).readAsBytesSync();
|
| + var bundle = new PackageBundle.fromBuffer(bytes);
|
| + return new WorkerPackageBundle(bytes, bundle);
|
| + }).bundle;
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * [PackageBundleProvider] that reads from [WorkerPackageBundleCache] using
|
| + * the request specific [inputs].
|
| + */
|
| +class WorkerPackageBundleProvider implements PackageBundleProvider {
|
| + final WorkerPackageBundleCache cache;
|
| + final Map<String, WorkerInput> inputs;
|
| +
|
| + WorkerPackageBundleProvider(this.cache, this.inputs);
|
| +
|
| + @override
|
| + PackageBundle get(String path) {
|
| + return cache.get(inputs, path);
|
| + }
|
| +}
|
|
|