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 09cb6f4bd463fa1767e438752a1f833ef26c49a5..14034c6ddfe50679935c8651c0ed0e6255226a61 100644 |
--- a/dart/pkg/dart2js_incremental/lib/library_updater.dart |
+++ b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
@@ -27,19 +27,29 @@ import 'package:compiler/src/elements/elements.dart' show |
import 'package:compiler/src/scanner/scannerlib.dart' show |
EOF_TOKEN, |
+ Listener, |
+ NodeListener, |
+ Parser, |
PartialClassElement, |
PartialElement, |
PartialFieldList, |
PartialFunctionElement, |
+ Scanner, |
Token; |
import 'package:compiler/src/source_file.dart' show |
+ CachingUtf8BytesSourceFile, |
+ SourceFile, |
StringSourceFile; |
import 'package:compiler/src/tree/tree.dart' show |
ClassNode, |
FunctionExpression, |
- NodeList; |
+ LibraryTag, |
+ NodeList, |
+ Part, |
+ StringNode, |
+ unparse; |
import 'package:compiler/src/js/js.dart' show |
js; |
@@ -84,6 +94,7 @@ import 'diff.dart' show |
computeDifference; |
import 'dart2js_incremental.dart' show |
+ IncrementalCompilationFailed, |
IncrementalCompiler; |
typedef void Logger(message); |
@@ -168,6 +179,8 @@ class LibraryUpdater extends JsFeatures { |
final IncrementalCompilerContext _context; |
+ final Map<Uri, Future> _sources = <Uri, Future>{}; |
+ |
bool _hasComputedNeeds = false; |
bool _hasCapturedCompilerState = false; |
@@ -212,23 +225,80 @@ class LibraryUpdater extends JsFeatures { |
logTime('Reusing $library (assumed read-only).'); |
return new Future.value(true); |
} |
- 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 _haveTagsChanged(library).then((bool haveTagsChanged) { |
+ if (haveTagsChanged) { |
+ cannotReuse( |
+ library, |
+ "Changes to library, import, export, or part declarations not" |
+ " supported."); |
+ return true; |
+ } |
+ 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); |
+ }); |
} |
- return inputProvider(uri).then((bytes) { |
- return canReuseLibrary(library, bytes); |
- }); |
} |
+ |
+ logTime("Reusing $library, source didn't change."); |
+ // Source code of [library] wasn't changed. |
+ return true; |
+ }); |
+ } |
+ |
+ Future<bool> _haveTagsChanged(LibraryElement library) { |
+ Uri uri = library.entryCompilationUnit.script.resourceUri; |
+ if (!_context._uriHasUpdate(uri)) { |
+ // The entry compilation unit hasn't been updated. So the tags aren't |
+ // changed. |
+ return new Future<bool>.value(false); |
} |
- logTime("Reusing $library, source didn't change."); |
- // Source code of [library] wasn't changed. |
- return new Future.value(true); |
+ 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(); |
+ // Using two parsers to only create the nodes we want ([LibraryTag]). |
+ Parser parser = new Parser(new Listener()); |
+ NodeListener listener = new NodeListener( |
+ compiler, library.entryCompilationUnit); |
+ Parser nodeParser = new Parser(listener); |
+ Iterator<LibraryTag> tags = library.tags.iterator; |
+ while (token.kind != EOF_TOKEN) { |
+ token = parser.parseMetadataStar(token); |
+ if (parser.optional('library', token) || |
+ parser.optional('import', token) || |
+ parser.optional('export', token) || |
+ parser.optional('part', token)) { |
+ if (!tags.moveNext()) return true; |
+ token = nodeParser.parseTopLevelDeclaration(token); |
+ LibraryTag tag = listener.popNode(); |
+ assert(listener.nodes.isEmpty); |
+ if (unparse(tags.current) != unparse(tag)) { |
+ return true; |
+ } |
+ } else { |
+ break; |
+ } |
+ } |
+ return tags.moveNext(); |
+ }); |
+ } |
+ |
+ Future _readUri(Uri uri) { |
+ return _sources.putIfAbsent(uri, () => inputProvider(uri)); |
} |
void _ensureCompilerStateCaptured() { |
@@ -583,8 +653,7 @@ class LibraryUpdater extends JsFeatures { |
update.captureState(); |
} |
if (!_failedUpdates.isEmpty) { |
- throw new StateError( |
- "Can't compute update.\n\n${_failedUpdates.join('\n\n')}"); |
+ throw new IncrementalCompilationFailed(_failedUpdates.join('\n\n')); |
} |
for (ElementX element in _elementsToInvalidate) { |
compiler.forgetElement(element); |