Index: dart/pkg/dart2js_incremental/lib/library_updater.dart |
diff --git a/dart/pkg/dart2js_incremental/lib/library_updater.dart b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..78e6cc922b87378356f2190c2bc101ffc0bf3878 |
--- /dev/null |
+++ b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
@@ -0,0 +1,196 @@ |
+// Copyright (c) 2014, 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 dart2js_incremental.library_updater; |
+ |
+import 'dart:async' show |
+ Future; |
+ |
+import 'dart:convert' show |
+ UTF8; |
+ |
+import 'package:compiler/compiler.dart' as api; |
+ |
+import 'package:compiler/implementation/dart2jslib.dart' show |
+ Compiler, |
+ Script; |
+ |
+import 'package:compiler/implementation/elements/elements.dart' show |
+ LibraryElement; |
+ |
+import 'package:compiler/implementation/scanner/scannerlib.dart' show |
+ EOF_TOKEN, |
+ PartialElement, |
+ PartialFunctionElement, |
+ Token; |
+ |
+import 'package:compiler/implementation/source_file.dart' show |
+ StringSourceFile; |
+ |
+import 'package:compiler/implementation/tree/tree.dart' show |
+ FunctionExpression; |
+ |
+import 'diff.dart' show |
+ Difference, |
+ computeDifference; |
+ |
+typedef void Logger(message); |
+ |
+// TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, |
+// and only if that library has exactly one compilation unit. |
+class LibraryUpdater { |
+ final Compiler compiler; |
+ |
+ final api.CompilerInputProvider inputProvider; |
+ |
+ final Logger logTime; |
+ |
+ final Logger logVerbose; |
+ |
+ // TODO(ahe): Get rid of this field. It assumes that only one library has |
+ // changed. |
+ final Uri uri; |
+ |
+ final List<Update> updates = <Update>[]; |
+ |
+ LibraryUpdater( |
+ this.compiler, |
+ this.inputProvider, |
+ this.uri, |
+ this.logTime, |
+ this.logVerbose); |
+ |
+ /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. |
+ Future<bool> reuseLibrary(LibraryElement library) { |
+ assert(compiler != null); |
+ if (library.isPlatformLibrary || library.isPackageLibrary) { |
+ logTime('Reusing $library.'); |
+ return new Future.value(true); |
+ } else if (library != compiler.mainApp) { |
+ return new Future.value(false); |
+ } |
+ return inputProvider(uri).then((List<int> bytes) { |
+ if (canReuseLibrary(library, bytes)) { |
+ // TODO(ahe): Temporary. Since we don't yet apply the updates, the |
+ // library cannot be reused if there are updates. |
+ return updates.isEmpty; |
+ } else { |
+ return false; |
+ } |
+ }); |
+ } |
+ |
+ /// Returns true if [library] can be reused. |
+ /// |
+ /// This methods also computes the [updates] (patches) needed to have |
+ /// [library] reflect the modifications in [bytes]. |
+ bool canReuseLibrary(LibraryElement library, List<int> bytes) { |
+ logTime('Attempting to reuse mainApp.'); |
+ String newSource = UTF8.decode(bytes); |
+ logTime('Decoded UTF8'); |
+ |
+ // TODO(ahe): Can't use compiler.mainApp in general. |
+ if (false && newSource == compiler.mainApp.compilationUnit.script.text) { |
+ // TODO(ahe): Need to update the compilationUnit's source code when |
+ // doing incremental analysis for this to work. |
+ logTime("Source didn't change"); |
+ return true; |
+ } |
+ |
+ logTime("Source did change"); |
+ Script sourceScript = new Script( |
+ uri, uri, new StringSourceFile('$uri', newSource)); |
+ var dartPrivacyIsBroken = compiler.libraryLoader; |
+ LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( |
+ null, sourceScript, uri); |
+ logTime('New library synthesized.'); |
+ List<Difference> differences = computeDifference(library, newLibrary); |
+ logTime('Differences computed.'); |
+ for (Difference difference in differences) { |
+ logTime('Looking at difference: $difference'); |
+ if (difference.before == null || difference.after == null) { |
+ logVerbose('Scope changed in $difference'); |
+ // Scope changed, don't reuse library. |
+ return false; |
+ } |
+ Token diffToken = difference.token; |
+ if (diffToken == null) { |
+ logVerbose('No token stored in difference.'); |
+ return false; |
+ } |
+ if (difference.after is! PartialElement && |
+ difference.before is! PartialElement) { |
+ logVerbose('Not a PartialElement: $difference'); |
+ // Don't know how to recompile element. |
+ return false; |
+ } |
+ PartialElement before = difference.before; |
+ PartialElement after = difference.after; |
+ |
+ if (before is PartialFunctionElement && after is PartialFunctionElement) { |
+ if (!canReuseFunction(diffToken, before, after)) { |
+ return false; |
+ } |
+ } else { |
+ // Unhandled kind of element. |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
+ } |
+ |
+ /// Returns true if function [before] can be reused to reflect the changes in |
+ /// [after]. |
+ /// |
+ /// If [before] can be reused, an update (patch) is added to [updates]. |
+ bool canReuseFunction( |
+ Token diffToken, |
+ PartialFunctionElement before, |
+ PartialFunctionElement after) { |
+ FunctionExpression node = |
+ after.parseNode(compiler).asFunctionExpression(); |
+ if (node == null) { |
+ print('Not a function expression.'); |
+ return false; |
+ } |
+ Token last = after.endToken; |
+ if (node.body != null) { |
+ last = node.body.getBeginToken(); |
+ } |
+ Token token = after.beginToken; |
+ while (token != last && token.kind != EOF_TOKEN) { |
+ if (token == diffToken) { |
+ logVerbose('Signature changed'); |
+ return false; |
+ } |
+ token = token.next; |
+ } |
+ print('Simple modification of ${after} detected'); |
+ updates.add(new FunctionUpdate(compiler, before, after)); |
+ return true; |
+ } |
+} |
+ |
+/// Represents an update (aka patch) of [before] to [after]. We use the word |
+/// "update" to avoid confusion with the compiler feature of "patch" methods. |
+abstract class Update { |
+ final Compiler compiler; |
+ |
+ PartialElement get before; |
+ |
+ PartialElement get after; |
+ |
+ Update(this.compiler); |
+} |
+ |
+/// Represents an update of a function element. |
+class FunctionUpdate extends Update { |
+ final PartialFunctionElement before; |
+ |
+ final PartialFunctionElement after; |
+ |
+ FunctionUpdate(Compiler compiler, this.before, this.after) |
+ : super(compiler); |
+} |