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

Unified Diff: pkg/front_end/example/incremental_reload/compiler_with_invalidation.dart

Issue 2928483005: Add an incremental reloader example and a utility tool to trigger a reload by (Closed)
Patch Set: CL comments Created 3 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: pkg/front_end/example/incremental_reload/compiler_with_invalidation.dart
diff --git a/pkg/front_end/example/incremental_reload/compiler_with_invalidation.dart b/pkg/front_end/example/incremental_reload/compiler_with_invalidation.dart
new file mode 100644
index 0000000000000000000000000000000000000000..3951e00712dceea6ef441fd305fccdbfbecd2bee
--- /dev/null
+++ b/pkg/front_end/example/incremental_reload/compiler_with_invalidation.dart
@@ -0,0 +1,185 @@
+// Copyright (c) 2017, 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.
+
+/// A wrapper on top of the [IncrementalKernelGenerator] that tracks
+/// file modifications between subsequent compilation requests and only
+/// invalidates those files that appear to be modified.
+library front_end.example.incremental_reload.compiler_with_invalidation;
+
+import 'dart:io';
+import 'dart:async';
+import 'dart:convert' show JSON;
+
+import 'package:front_end/compiler_options.dart';
+import 'package:front_end/incremental_kernel_generator.dart';
+import 'package:front_end/src/incremental/file_byte_store.dart';
+import 'package:front_end/src/incremental/byte_store.dart';
+import 'package:kernel/ast.dart';
+import 'package:kernel/binary/limited_ast_to_binary.dart';
+
+/// Create an instance of an [IncrementalCompiler] to compile a program whose
+/// main entry point file is [entry]. This uses some default options
+/// for the location of the sdk and temporary folder to save intermediate
+/// results.
+// TODO(sigmund): make this example work outside of the SDK repo.
+Future<IncrementalCompiler> createIncrementalCompiler(String entry,
+ {bool persistent: true}) {
+ var entryUri = Uri.base.resolve(entry);
+ var dartVm = Uri.base.resolve(Platform.resolvedExecutable);
+ var sdkRoot = dartVm.resolve("patched_sdk/");
+ var tmpDir = Directory.systemTemp.createTempSync('ikg_cache');
+ var options = new CompilerOptions()
+ ..sdkRoot = sdkRoot
+ ..packagesFileUri = Uri.base.resolve('.packages')
+ ..strongMode = false
+ ..dartLibraries = loadDartLibraries(sdkRoot)
+ ..byteStore =
+ persistent ? new FileByteStore(tmpDir.path) : new MemoryByteStore();
+ return IncrementalCompiler.create(options, entryUri);
+}
+
+/// Reads the `libraries.json` file for an SDK to provide the location of the
+/// SDK files.
+// TODO(sigmund): this should be handled by package:front_end internally.
+Map<String, Uri> loadDartLibraries(Uri sdkRoot) {
+ var libraries = sdkRoot.resolve('lib/libraries.json');
+ var map =
+ JSON.decode(new File.fromUri(libraries).readAsStringSync())['libraries'];
+ var dartLibraries = <String, Uri>{};
+ map.forEach((k, v) => dartLibraries[k] = libraries.resolve(v));
+ return dartLibraries;
+}
+
+/// An incremental compiler that monitors file modifications on disk and
+/// invalidates only files that have been modified since the previous time the
+/// compiler was invoked.
+class IncrementalCompiler {
+ /// Underlying incremental compiler implementation.
+ IncrementalKernelGenerator _generator;
+
+ /// Last modification for each tracked input file.
+ Map<Uri, DateTime> lastModified = {};
+
+ /// Create an instance of [IncrementalCompiler].
+ static Future<IncrementalCompiler> create(
+ CompilerOptions options, Uri entryUri) async {
+ return new IncrementalCompiler._internal(
+ await IncrementalKernelGenerator.newInstance(options, entryUri));
+ }
+
+ IncrementalCompiler._internal(this._generator);
+
+ /// Callback for the [IncrementalKernelGenerator] to keep track of relevant
+ /// files.
+ Future _watch(Uri uri, bool used) {
+ if (used) {
+ lastModified[uri] ??= new File.fromUri(uri).lastModifiedSync();
+ } else {
+ lastModified.remove(uri);
+ }
+ return new Future.value(null);
+ }
+
+ /// How many files changed during the last call to [recompile].
+ int changed;
+
+ /// Time spent updating time-stamps from disk during the last call to
+ /// [recompile].
+ int invalidateTime;
+
+ /// Time actually spent compiling the code in the incremental compiler during
+ /// the last call to [recompile].
+ int compileTime;
+
+ /// Determine which files have been modified, and recompile the program
+ /// incrementally based on that information.
+ Future<Program> recompile() async {
+ changed = 0;
+ invalidateTime = 0;
+ compileTime = 0;
+
+ var invalidateTimer = new Stopwatch()..start();
+ for (var uri in lastModified.keys.toList()) {
+ var last = lastModified[uri];
+ var current = new File.fromUri(uri).lastModifiedSync();
+ if (last != current) {
+ lastModified[uri] = current;
+ _generator.invalidate(uri);
+ changed++;
+ }
+ }
+ invalidateTimer.stop();
+ invalidateTime = invalidateTimer.elapsedMilliseconds;
+ if (changed == 0 && lastModified.isNotEmpty) return null;
+
+ var compileTimer = new Stopwatch()..start();
+ var delta = await _generator.computeDelta(watch: _watch);
+ compileTimer.stop();
+ compileTime = compileTimer.elapsedMilliseconds;
+ var program = delta.newProgram;
+ return program;
+ }
+}
+
+/// The result of an incremental compile and metrics collected during the
+/// the compilation.
+class CompilationResult {
+ /// How many files were modified by the time we invoked the compiler again.
+ int changed = 0;
+
+ /// How many files are currently being tracked for modifications.
+ int totalFiles = 0;
+
+ /// How long it took to invalidate files that have been modified.
+ int invalidateTime = 0;
+
+ /// How long it took to build the incremental program.
+ int compileTime = 0;
+
+ /// How long it took to do the hot-reload in the VM.
+ int reloadTime = 0;
+
+ /// Whether we saw errors during compilation or reload.
+ bool errorSeen = false;
+
+ /// Error message when [errorSeen] is true.
+ String errorDetails;
+
+ /// The program that was generated by the incremental compiler.
+ Program program;
+}
+
+/// Request a recompile and possibly a reload, and gather timing metrics.
+Future<CompilationResult> rebuild(
+ IncrementalCompiler compiler, Uri outputUri) async {
+ var result = new CompilationResult();
+ try {
+ var program = result.program = await compiler.recompile();
+ if (program != null && !program.libraries.isEmpty) {
+ // TODO(sigmund): the incremental generator should set the main method
+ // directly.
+ var mainLib = program.libraries.last;
+ program.mainMethod =
+ mainLib.procedures.firstWhere((p) => p.name.name == 'main');
+ var sink = new File.fromUri(outputUri).openWrite();
+
+ // TODO(sigmund): should the incremental generator always filter these
+ // libraries instead?
+ new LimitedBinaryPrinter(
+ sink, (library) => library.importUri.scheme != 'dart')
+ .writeProgramFile(program);
+ await sink.close();
+ }
+ } catch (e, t) {
+ result.errorDetails = 'compilation error: $e, $t';
+ result.errorSeen = true;
+ }
+
+ result.changed = compiler.changed;
+ result.totalFiles = compiler.lastModified.length;
+ result.invalidateTime = compiler.invalidateTime;
+ result.compileTime = compiler.compileTime;
+ result.reloadTime = 0;
+ return result;
+}

Powered by Google App Engine
This is Rietveld 408576698