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 14034c6ddfe50679935c8651c0ed0e6255226a61..6ea459f02e10b12f99cf138025d0e44732a078e1 100644 |
--- a/dart/pkg/dart2js_incremental/lib/library_updater.dart |
+++ b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
@@ -19,6 +19,7 @@ import 'package:compiler/src/dart2jslib.dart' show |
import 'package:compiler/src/elements/elements.dart' show |
ClassElement, |
+ CompilationUnitElement, |
Element, |
FunctionElement, |
LibraryElement, |
@@ -89,6 +90,9 @@ import 'package:compiler/src/universe/universe.dart' show |
import 'package:compiler/src/constants/values.dart' show |
ConstantValue; |
+import 'package:compiler/src/library_loader.dart' show |
+ TagState; |
+ |
import 'diff.dart' show |
Difference, |
computeDifference; |
@@ -181,6 +185,14 @@ class LibraryUpdater extends JsFeatures { |
final Map<Uri, Future> _sources = <Uri, Future>{}; |
+ /// Cached tokens of entry compilation units. |
+ final Map<LibraryElementX, Token> _entryUnitTokens = |
+ <LibraryElementX, Token>{}; |
+ |
+ /// Cached source files for entry compilation units. |
+ final Map<LibraryElementX, SourceFile> _entrySourceFiles = |
+ <LibraryElementX, SourceFile>{}; |
+ |
bool _hasComputedNeeds = false; |
bool _hasCapturedCompilerState = false; |
@@ -233,43 +245,62 @@ class LibraryUpdater extends JsFeatures { |
" supported."); |
return true; |
} |
+ |
+ bool isChanged = false; |
+ List<Future<Script>> futureScripts = <Future<Script>>[]; |
+ |
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" |
- " (${library.compilationUnits})."); |
- return true; |
- } |
- return _readUri(uri).then((bytes) { |
- return canReuseLibrary(library, bytes); |
- }); |
+ isChanged = true; |
+ futureScripts.add(_updatedScript(unit.script, library)); |
+ } else { |
+ futureScripts.add(new Future.value(unit.script)); |
} |
} |
- logTime("Reusing $library, source didn't change."); |
- // Source code of [library] wasn't changed. |
- return true; |
+ if (!isChanged) { |
+ logTime("Reusing $library, source didn't change."); |
+ return true; |
+ } |
+ |
+ return Future.wait(futureScripts).then( |
+ (List<Script> scripts) => canReuseLibrary(library, scripts)); |
+ }).whenComplete(() => _cleanUp(library)); |
+ } |
+ |
+ void _cleanUp(LibraryElementX library) { |
+ _entryUnitTokens.remove(library); |
+ _entrySourceFiles.remove(library); |
+ } |
+ |
+ Future<Script> _updatedScript(Script before, LibraryElementX library) { |
+ if (before == library.entryCompilationUnit.script && |
+ _entrySourceFiles.containsKey(library)) { |
+ return new Future.value(before.copyWithFile(_entrySourceFiles[library])); |
+ } |
+ |
+ return _readUri(before.resourceUri).then((bytes) { |
+ String filename = before.file.filename; |
+ SourceFile sourceFile = bytes is String |
+ ? new StringSourceFile(filename, bytes) |
+ : new CachingUtf8BytesSourceFile(filename, bytes); |
+ return before.copyWithFile(sourceFile); |
}); |
} |
Future<bool> _haveTagsChanged(LibraryElement library) { |
- Uri uri = library.entryCompilationUnit.script.resourceUri; |
- if (!_context._uriHasUpdate(uri)) { |
+ Script before = library.entryCompilationUnit.script; |
+ if (!_context._uriHasUpdate(before.resourceUri)) { |
// The entry compilation unit hasn't been updated. So the tags aren't |
// changed. |
return new Future<bool>.value(false); |
} |
- return _readUri(uri).then((bytes) { |
- String filename = '$uri'; |
- SourceFile sourceFile = bytes is String |
- ? new StringSourceFile(filename, bytes) |
- : new CachingUtf8BytesSourceFile(filename, bytes); |
- Token token = new Scanner(sourceFile).tokenize(); |
+ return _updatedScript(before, library).then((Script script) { |
+ _entrySourceFiles[library] = script.file; |
+ Token token = new Scanner(_entrySourceFiles[library]).tokenize(); |
+ _entryUnitTokens[library] = token; |
// Using two parsers to only create the nodes we want ([LibraryTag]). |
Parser parser = new Parser(new Listener()); |
NodeListener listener = new NodeListener( |
@@ -313,18 +344,59 @@ class LibraryUpdater extends JsFeatures { |
/// 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, bytes) { |
+ /// [library] reflect the modifications in [scripts]. |
+ bool canReuseLibrary(LibraryElement library, List<Script> scripts) { |
logTime('Attempting to reuse ${library}.'); |
- String newSource = bytes is String ? bytes : UTF8.decode(bytes); |
- logTime('Decoded UTF8'); |
- |
- Uri uri = library.entryCompilationUnit.script.resourceUri; |
- Script sourceScript = new Script( |
- uri, uri, new StringSourceFile('$uri', newSource)); |
- var dartPrivacyIsBroken = compiler.libraryLoader; |
- LibraryElement newLibrary = dartPrivacyIsBroken.createLibrarySync( |
- null, sourceScript, uri); |
+ |
+ Uri entryUri = library.entryCompilationUnit.script.resourceUri; |
+ Script entryScript = |
+ scripts.singleWhere((Script script) => script.resourceUri == entryUri); |
+ LibraryElement newLibrary = |
+ new LibraryElementX(entryScript, library.canonicalUri); |
+ if (_entryUnitTokens.containsKey(library)) { |
+ compiler.dietParser.dietParse( |
+ newLibrary.entryCompilationUnit, _entryUnitTokens[library]); |
+ } else { |
+ compiler.scanner.scanLibrary(newLibrary); |
+ } |
+ |
+ TagState tagState = new TagState(); |
+ for (LibraryTag tag in newLibrary.tags) { |
+ if (tag.isImport) { |
+ tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler); |
+ } else if (tag.isExport) { |
+ tagState.checkTag(TagState.IMPORT_OR_EXPORT, tag, compiler); |
+ } else if (tag.isLibraryName) { |
+ tagState.checkTag(TagState.LIBRARY, tag, compiler); |
+ if (newLibrary.libraryTag == null) { |
+ // Use the first if there are multiple (which is reported as an |
+ // error in [TagState.checkTag]). |
+ newLibrary.libraryTag = tag; |
+ } |
+ } else if (tag.isPart) { |
+ tagState.checkTag(TagState.PART, tag, compiler); |
+ } |
+ } |
+ |
+ // TODO(ahe): Process tags using TagState, not |
+ // LibraryLoaderTask.processLibraryTags. |
+ Link<CompilationUnitElement> units = library.compilationUnits; |
+ for (Script script in scripts) { |
+ CompilationUnitElementX unit = units.head; |
+ units = units.tail; |
+ if (script != entryScript) { |
+ // TODO(ahe): Copied from library_loader. |
+ CompilationUnitElement newUnit = |
+ new CompilationUnitElementX(script, newLibrary); |
+ compiler.withCurrentElement(newUnit, () { |
+ compiler.scanner.scan(newUnit); |
+ if (unit.partTag == null) { |
+ compiler.reportError(unit, MessageKind.MISSING_PART_OF_TAG); |
+ } |
+ }); |
+ } |
+ } |
+ |
logTime('New library synthesized.'); |
return canReuseScopeContainerElement(library, newLibrary); |
} |