Chromium Code Reviews| 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 |
| index b7d41e0090fd5a160c335473171e34bc9382cb1a..737dd202d51f32faddc510bb4ed8ad05a71ac52b 100644 |
| --- a/dart/pkg/dart2js_incremental/lib/library_updater.dart |
| +++ b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
| @@ -83,6 +83,9 @@ import 'diff.dart' show |
| Difference, |
| computeDifference; |
| +import 'dart2js_incremental.dart' show |
| + IncrementalCompiler; |
| + |
| typedef void Logger(message); |
| typedef bool Reuser( |
| @@ -103,8 +106,45 @@ class FailedUpdate { |
| } |
| } |
| -// TODO(ahe): Generalize this class. For now only works for Compiler.mainApp, |
| -// and only if that library has exactly one compilation unit. |
| +abstract class _IncrementalCompilerContext { |
| + IncrementalCompiler incrementalCompiler; |
| + |
| + Set<ClassElementX> _emittedClasses; |
| + |
| + Set<ClassElementX> _directlyInstantiatedClasses; |
| + |
| + Set<ConstantValue> _compiledConstants; |
| +} |
| + |
| +class IncrementalCompilerContext extends _IncrementalCompilerContext { |
| + final Set<Uri> _uriWithUpdates = new Set<Uri>(); |
| + |
| + void set incrementalCompiler(IncrementalCompiler value) { |
| + if (super.incrementalCompiler != null) { |
| + throw new StateError("Can't set [incrementalCompiler] more than once."); |
| + } |
|
Johnni Winther
2014/12/16 11:22:52
You need to do
super.incrementalCompiler = value
ahe
2014/12/16 12:04:31
Done.
|
| + } |
| + |
| + void registerUriWithUpdates(Iterable<Uri> uris) { |
| + _uriWithUpdates.addAll(uris); |
| + } |
| + |
| + void _captureState(Compiler compiler) { |
| + _emittedClasses = new Set.from(compiler.backend.emitter.neededClasses); |
| + |
| + _directlyInstantiatedClasses = |
| + new Set.from(compiler.codegenWorld.directlyInstantiatedClasses); |
| + |
| + List<ConstantValue> constants = |
| + compiler.backend.emitter.outputConstantLists[ |
| + compiler.deferredLoadTask.mainOutputUnit]; |
| + if (constants == null) constants = <ConstantValue>[]; |
| + _compiledConstants = new Set<ConstantValue>.identity()..addAll(constants); |
| + } |
| + |
| + bool _uriHasUpdate(Uri uri) => _uriWithUpdates.contains(uri); |
| +} |
| + |
| class LibraryUpdater extends JsFeatures { |
| final Compiler compiler; |
| @@ -114,10 +154,6 @@ class LibraryUpdater extends JsFeatures { |
| 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>[]; |
| final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; |
| @@ -129,50 +165,37 @@ class LibraryUpdater extends JsFeatures { |
| final Set<ClassElementX> _classesWithSchemaChanges = |
| new Set<ClassElementX>(); |
| - final Set<ClassElementX> _emittedClasses; |
| - |
| - final Set<ClassElementX> _directlyInstantiatedClasses; |
| - |
| - final Set<ConstantValue> _compiledConstants; |
| + final IncrementalCompilerContext _context; |
| bool _hasComputedNeeds = false; |
| + bool _hasCapturedCompilerState = false; |
| + |
| LibraryUpdater( |
| - Compiler compiler, |
| + this.compiler, |
| this.inputProvider, |
| - this.uri, |
| this.logTime, |
| - this.logVerbose) |
| - : this.compiler = compiler, |
| - _emittedClasses = _getEmittedClasses(compiler), |
| - _directlyInstantiatedClasses = |
| - _getDirectlyInstantiatedClasses(compiler), |
| - _compiledConstants = _getEmittedConstants(compiler); |
| + this.logVerbose, |
| + this._context) { |
| + // TODO(ahe): Would like to remove this from the constructor. However, the |
| + // state must be captured before calling [reuseCompiler]. |
| + // Proper solution might be: [reuseCompiler] should not clear the sets that |
| + // are captured in [IncrementalCompilerContext._captureState]. |
| + _ensureCompilerStateCaptured(); |
| + } |
| /// Returns the classes emitted by [compiler]. |
| - static Set<ClassElementX> _getEmittedClasses(Compiler compiler) { |
| - if (compiler == null) return null; |
| - return new Set.from(compiler.backend.emitter.neededClasses); |
| - } |
| + Set<ClassElementX> get _emittedClasses => _context._emittedClasses; |
| - /// Returns the directly instantantiated classes seen by [compiler]. |
| - static Set<ClassElementX> _getDirectlyInstantiatedClasses(Compiler compiler) { |
| - if (compiler == null) return null; |
| - return new Set.from(compiler.codegenWorld.directlyInstantiatedClasses); |
| + /// Returns the directly instantantiated classes seen by [compiler] (this |
| + /// includes interfaces and may be different from [_emittedClasses] that only |
| + /// includes interfaces used in type tests). |
| + Set<ClassElementX> get _directlyInstantiatedClasses { |
| + return _context._directlyInstantiatedClasses; |
| } |
| /// Returns the constants emitted by [compiler]. |
| - static Set<ConstantValue> _getEmittedConstants(Compiler compiler) { |
| - if (compiler != null) { |
| - List<ConstantValue> constants = |
| - compiler.backend.emitter.outputConstantLists[ |
| - compiler.deferredLoadTask.mainOutputUnit]; |
| - if (constants != null) { |
| - return new Set<ConstantValue>.identity()..addAll(constants); |
| - } |
| - } |
| - return null; |
| - } |
| + Set<ConstantValue> get _compiledConstants => _context._compiledConstants; |
| /// When [true], updates must be applied (using [applyUpdates]) before the |
| /// [compiler]'s state correctly reflects the updated program. |
| @@ -182,16 +205,38 @@ class LibraryUpdater extends JsFeatures { |
| /// Used as tear-off passed to [LibraryLoaderTask.resetAsync]. |
| Future<bool> reuseLibrary(LibraryElement library) { |
| + _ensureCompilerStateCaptured(); |
| assert(compiler != null); |
| - if (library.isPlatformLibrary || library.isPackageLibrary) { |
| - logTime('Reusing $library.'); |
| + if (library.isPlatformLibrary) { |
| + logTime('Reusing $library (assumed read-only).'); |
| return new Future.value(true); |
| - } else if (library != compiler.mainApp) { |
| - return new Future.value(false); |
| } |
| - return inputProvider(uri).then((bytes) { |
| - return canReuseLibrary(library, bytes); |
| - }); |
| + for (CompilationUnitElementX unit in library.compilationUnits) { |
| + Uri uri = unit.script.resourceUri; |
| + if (_context._uriHasUpdate(uri)) { |
| + if (!library.compilationUnits.tail.isEmpty) { |
| + // TODO(ahe): Remove this restriction. |
| + cannotReuse(library, "Multiple compilation units not supported."); |
| + return new Future.value(true); |
| + } |
| + return inputProvider(uri).then((bytes) { |
| + return canReuseLibrary(library, bytes); |
| + }); |
| + } |
| + } |
| + |
| + logTime("Reusing $library, source didn't change."); |
| + // Source code of [library] wasn't changed. |
| + return new Future.value(true); |
| + } |
| + |
| + void _ensureCompilerStateCaptured() { |
| + // TODO(ahe): [compiler] shouldn't be null, remove the following line. |
| + if (compiler == null) return; |
| + |
| + if (_hasCapturedCompilerState) return; |
| + _context._captureState(compiler); |
| + _hasCapturedCompilerState = true; |
| } |
| /// Returns true if [library] can be reused. |
| @@ -199,19 +244,11 @@ class LibraryUpdater extends JsFeatures { |
| /// This methods also computes the [updates] (patches) needed to have |
| /// [library] reflect the modifications in [bytes]. |
| bool canReuseLibrary(LibraryElement library, bytes) { |
| - logTime('Attempting to reuse mainApp.'); |
| + logTime('Attempting to reuse ${library}.'); |
| String newSource = bytes is String ? bytes : 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"); |
| + Uri uri = library.entryCompilationUnit.script.resourceUri; |
| Script sourceScript = new Script( |
| uri, uri, new StringSourceFile('$uri', newSource)); |
| var dartPrivacyIsBroken = compiler.libraryLoader; |