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 1be2076d8923774e9c7b55debd734c439ccde498..b5b20fc728ae231f6ddc6e8a5c18df1c663c65b0 100644 |
--- a/dart/pkg/dart2js_incremental/lib/library_updater.dart |
+++ b/dart/pkg/dart2js_incremental/lib/library_updater.dart |
@@ -64,6 +64,19 @@ typedef bool Reuser( |
PartialElement before, |
PartialElement after); |
+class FailedUpdate { |
+ /// Either an [Element] or a [Difference]. |
+ final context; |
+ final String message; |
+ |
+ FailedUpdate(this.context, this.message); |
+ |
+ String toString() { |
+ if (context == null) return '$message'; |
+ return 'In $context:\n $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 { |
@@ -79,14 +92,10 @@ class LibraryUpdater { |
// changed. |
final Uri uri; |
- // When [true], updates must be applied (using [applyUpdates]) before the |
- // [compiler]'s state correctly reflects the updated program. |
- bool hasPendingUpdates = false; |
- |
- bool onlySimpleUpdates = true; |
- |
final List<Update> updates = <Update>[]; |
+ final List<FailedUpdate> _failedUpdates = <FailedUpdate>[]; |
+ |
LibraryUpdater( |
this.compiler, |
this.inputProvider, |
@@ -94,6 +103,12 @@ class LibraryUpdater { |
this.logTime, |
this.logVerbose); |
+ /// When [true], updates must be applied (using [applyUpdates]) before the |
+ /// [compiler]'s state correctly reflects the updated program. |
+ bool get hasPendingUpdates => !updates.isEmpty; |
+ |
+ bool get failed => !_failedUpdates.isEmpty; |
+ |
JavaScriptBackend get backend => compiler.backend; |
Namer get namer => backend.namer; |
@@ -141,6 +156,12 @@ class LibraryUpdater { |
return canReuseScopeContainerElement(library, newLibrary); |
} |
+ bool cannotReuse(context, String message) { |
+ _failedUpdates.add(new FailedUpdate(context, message)); |
+ logVerbose(message); |
+ return false; |
+ } |
+ |
bool canReuseScopeContainerElement( |
ScopeContainerElement element, |
ScopeContainerElement newElement) { |
@@ -149,23 +170,18 @@ class LibraryUpdater { |
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. |
- onlySimpleUpdates = false; |
- return false; |
+ cannotReuse(difference, "Can't reuse; Scope changed."); |
+ continue; |
} |
Token diffToken = difference.token; |
if (diffToken == null) { |
- logVerbose('No token stored in difference.'); |
- onlySimpleUpdates = false; |
- return false; |
+ cannotReuse(difference, "No difference token."); |
+ continue; |
} |
if (difference.after is! PartialElement && |
difference.before is! PartialElement) { |
- logVerbose('Not a PartialElement: $difference'); |
- // Don't know how to recompile element. |
- onlySimpleUpdates = false; |
- return false; |
+ cannotReuse(difference, "Don't know how to recompile."); |
+ continue; |
} |
PartialElement before = difference.before; |
PartialElement after = difference.after; |
@@ -178,16 +194,15 @@ class LibraryUpdater { |
after is PartialClassElement) { |
reuser = canReuseClass; |
} else { |
- reuser = cannotReuse; |
+ reuser = unableToReuse; |
} |
if (!reuser(diffToken, before, after)) { |
- onlySimpleUpdates = false; |
- return false; |
+ assert(!_failedUpdates.isEmpty); |
+ continue; |
} |
} |
- hasPendingUpdates = true; |
- return true; |
+ return _failedUpdates.isEmpty; |
} |
/// Returns true if function [before] can be reused to reflect the changes in |
@@ -201,16 +216,14 @@ class LibraryUpdater { |
FunctionExpression node = |
after.parseNode(compiler).asFunctionExpression(); |
if (node == null) { |
- logVerbose('Not a function expression.'); |
- return false; |
+ return cannotReuse(after, "Not a function expression: '$node'"); |
} |
Token last = after.endToken; |
if (node.body != null) { |
last = node.body.getBeginToken(); |
} |
if (isTokenBetween(diffToken, after.beginToken, last)) { |
- logVerbose('Signature changed.'); |
- return false; |
+ return cannotReuse(after, 'Signature changed.'); |
} |
logVerbose('Simple modification of ${after} detected'); |
updates.add(new FunctionUpdate(compiler, before, after)); |
@@ -223,17 +236,14 @@ class LibraryUpdater { |
PartialClassElement after) { |
ClassNode node = after.parseNode(compiler).asClassNode(); |
if (node == null) { |
- logVerbose('Not a ClassNode.'); |
- return false; |
+ return cannotReuse(after, "Not a ClassNode: '$node'"); |
} |
NodeList body = node.body; |
if (body == null) { |
- logVerbose('Class has no body.'); |
- return false; |
+ return cannotReuse(after, "Class has no body."); |
} |
if (isTokenBetween(diffToken, node.beginToken, body.beginToken)) { |
- logVerbose('Class header changed.'); |
- return false; |
+ return cannotReuse(after, "Class header changed."); |
} |
logVerbose('Simple modification of ${after} detected'); |
return canReuseScopeContainerElement(before, after); |
@@ -250,19 +260,20 @@ class LibraryUpdater { |
return false; |
} |
- bool cannotReuse( |
+ bool unableToReuse( |
Token diffToken, |
PartialElement before, |
PartialElement after) { |
- logVerbose( |
+ return cannotReuse( |
+ after, |
'Unhandled change:' |
' ${before} (${before.runtimeType} -> ${after.runtimeType}).'); |
- return false; |
} |
List<Element> applyUpdates() { |
- if (!onlySimpleUpdates) { |
- throw new StateError("Can't compute update."); |
+ if (!_failedUpdates.isEmpty) { |
+ throw new StateError( |
+ "Can't compute update.\n\n${_failedUpdates.join('\n\n')}"); |
} |
return updates.map((Update update) => update.apply()).toList(); |
} |