Index: pkg/analyzer/lib/src/generated/engine.dart |
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart |
index 1e1c3b437b746d59cdc59b91f34075950aaa22f1..a63861f16b7ac18f4c231a0910cee985ea6391e1 100644 |
--- a/pkg/analyzer/lib/src/generated/engine.dart |
+++ b/pkg/analyzer/lib/src/generated/engine.dart |
@@ -1739,11 +1739,8 @@ class DartEntryImpl extends SourceEntryImpl implements DartEntry { |
} |
} |
- /** |
- * Invalidate all of the information associated with the compilation unit. |
- */ |
void invalidateAllInformation() { |
- setState(SourceEntry.LINE_INFO, CacheState.INVALID); |
+ super.invalidateAllInformation(); |
_sourceKind = SourceKind.UNKNOWN; |
_sourceKindState = CacheState.INVALID; |
_parseErrors = AnalysisError.NO_ERRORS; |
@@ -2579,11 +2576,8 @@ class HtmlEntryImpl extends SourceEntryImpl implements HtmlEntry { |
return copy; |
} |
- /** |
- * Invalidate all of the information associated with the HTML file. |
- */ |
void invalidateAllInformation() { |
- setState(SourceEntry.LINE_INFO, CacheState.INVALID); |
+ super.invalidateAllInformation(); |
_parseErrors = AnalysisError.NO_ERRORS; |
_parseErrorsState = CacheState.INVALID; |
_parsedUnit = null; |
@@ -2849,6 +2843,14 @@ abstract class SourceEntryImpl implements SourceEntry { |
} |
/** |
+ * Invalidate all of the information associated with this source. |
+ */ |
+ void invalidateAllInformation() { |
+ _lineInfo = null; |
+ _lineInfoState = CacheState.INVALID; |
+ } |
+ |
+ /** |
* Set the most recent time at which the state of the source matched the state represented by this |
* entry to the given time. |
* |
@@ -3069,6 +3071,12 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
Map<Source, ChangeNoticeImpl> _pendingNotices = new Map<Source, ChangeNoticeImpl>(); |
/** |
+ * A set containing information about the tasks that have been performed since the last change |
+ * notification. Used to detect infinite loops in [performAnalysisTask]. |
+ */ |
+ Set<String> _recentTasks = new Set<String>(); |
+ |
+ /** |
* The object used to synchronize access to all of the caches. The rules related to the use of |
* this lock object are |
* |
@@ -3110,6 +3118,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
return; |
} |
{ |
+ _recentTasks.clear(); |
// |
// First, compute the list of sources that have been removed. |
// |
@@ -3137,6 +3146,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
// that might have been referencing the not-yet-existing source that was just added. Longer |
// term we need to keep track of which libraries are referencing non-existing sources and |
// only re-analyze those libraries. |
+ logInformation("Added Dart sources, invalidating all resolution information"); |
for (MapEntry<Source, SourceEntry> mapEntry in _cache.entrySet()) { |
SourceEntry sourceEntry = mapEntry.getValue(); |
if (!mapEntry.getKey().isInSystemLibrary && sourceEntry is DartEntry) { |
@@ -3714,10 +3724,18 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
int getStart = JavaSystem.currentTimeMillis(); |
AnalysisTask task = nextTaskAnalysisTask; |
int getEnd = JavaSystem.currentTimeMillis(); |
+ if (task == null && validateCacheConsistency()) { |
+ task = nextTaskAnalysisTask; |
+ } |
if (task == null) { |
return new AnalysisResult(getChangeNotices(true), getEnd - getStart, null, -1); |
} |
- //System.out.println(task); |
+ String taskDescriptor = task.toString(); |
+ if (_recentTasks.add(taskDescriptor)) { |
+ logInformation("Performing task: ${taskDescriptor}"); |
+ } else { |
+ logInformation("*** Performing repeated task: ${taskDescriptor}"); |
+ } |
int performStart = JavaSystem.currentTimeMillis(); |
try { |
task.perform(_resultRecorder); |
@@ -3819,6 +3837,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
void setChangedContents(Source source, String contents, int offset, int oldLength, int newLength) { |
{ |
+ _recentTasks.clear(); |
String originalContents = _sourceFactory.setContents(source, contents); |
if (contents != null) { |
if (contents != originalContents) { |
@@ -3836,6 +3855,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
void setContents(Source source, String contents) { |
{ |
+ _recentTasks.clear(); |
String originalContents = _sourceFactory.setContents(source, contents); |
if (contents != null) { |
if (contents != originalContents) { |
@@ -3954,11 +3974,14 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
} |
} else { |
+ PrintStringWriter writer = new PrintStringWriter(); |
+ writer.println("Library resolution results discarded for"); |
for (Library library in resolvedLibraries) { |
for (Source source in library.compilationUnitSources) { |
DartEntry dartEntry = getReadableDartEntry(source); |
if (dartEntry != null) { |
int resultTime = library.getModificationTime(source); |
+ writer.println(" ${debuggingString(source)}; sourceTime = ${source.modificationStamp}, resultTime = ${resultTime}, cacheTime = ${dartEntry.modificationTime}"); |
DartEntryImpl dartCopy = dartEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -3979,9 +4002,12 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
if (source == unitSource) { |
unitEntry = dartCopy; |
} |
+ } else { |
+ writer.println(" ${debuggingString(source)}; sourceTime = ${source.modificationStamp}, no entry"); |
} |
} |
} |
+ logInformation(writer.toString()); |
} |
} |
} |
@@ -4289,6 +4315,15 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
/** |
+ * Return a string with debugging information about the given source (the full name and |
+ * modification stamp of the source). |
+ * |
+ * @param source the source for which a debugging string is to be produced |
+ * @return debugging information about the given source |
+ */ |
+ String debuggingString(Source source) => "'${source.fullName}' [${source.modificationStamp}]"; |
+ |
+ /** |
* Return an array containing all of the change notices that are waiting to be returned. If there |
* are no notices, then return either `null` or an empty array, depending on the value of |
* the argument. |
@@ -4845,8 +4880,9 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
* <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock]. |
* |
* @param librarySource the source of the library being invalidated |
+ * @param writer the writer to which debugging information should be written |
*/ |
- void invalidateLibraryResolution(Source librarySource) { |
+ void invalidateLibraryResolution(Source librarySource, PrintStringWriter writer) { |
// TODO(brianwilkerson) This could be optimized. There's no need to flush all of these caches if |
// the public namespace hasn't changed, which will be a fairly common case. The question is |
// whether we can afford the time to compute the namespace to look for differences. |
@@ -4854,15 +4890,23 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
if (libraryEntry != null) { |
List<Source> includedParts = libraryEntry.getValue(DartEntry.INCLUDED_PARTS); |
DartEntryImpl libraryCopy = libraryEntry.writableCopy; |
+ int oldTime = libraryCopy.modificationTime; |
libraryCopy.invalidateAllResolutionInformation(); |
libraryCopy.setState(DartEntry.INCLUDED_PARTS, CacheState.INVALID); |
_cache.put(librarySource, libraryCopy); |
+ if (writer != null) { |
+ writer.println(" Invalidated library source: ${debuggingString(librarySource)} (previously modified at ${oldTime})"); |
+ } |
for (Source partSource in includedParts) { |
SourceEntry partEntry = _cache.get(partSource); |
if (partEntry is DartEntry) { |
DartEntryImpl partCopy = partEntry.writableCopy; |
+ oldTime = partCopy.modificationTime; |
partCopy.invalidateAllResolutionInformation(); |
_cache.put(partSource, partCopy); |
+ if (writer != null) { |
+ writer.println(" Invalidated part source: ${debuggingString(partSource)} (previously modified at ${oldTime})"); |
+ } |
} |
} |
} |
@@ -4898,6 +4942,29 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
/** |
+ * Log the given debugging information. |
+ * |
+ * @param message the message to be added to the log |
+ */ |
+ void logInformation(String message) { |
+ AnalysisEngine.instance.logger.logInformation(message); |
+ } |
+ |
+ /** |
+ * Log the given debugging information. |
+ * |
+ * @param message the message to be added to the log |
+ * @param exception the exception to be included in the log entry |
+ */ |
+ void logInformation2(String message, Exception exception) { |
+ if (exception == null) { |
+ AnalysisEngine.instance.logger.logInformation(message); |
+ } else { |
+ AnalysisEngine.instance.logger.logInformation3(message, exception); |
+ } |
+ } |
+ |
+ /** |
* Given a cache entry and a library element, record the library element and other information |
* gleaned from the element in the cache entry. |
* |
@@ -4963,6 +5030,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(source, dartCopy); |
dartEntry = dartCopy; |
} else { |
+ logInformation2("Generated errors discarded for ${debuggingString(source)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${dartEntry.modificationTime}", thrownException); |
DartEntryImpl dartCopy = dartEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -5060,6 +5128,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(unitSource, dartCopy); |
dartEntry = dartCopy; |
} else { |
+ logInformation2("Generated hints discarded for ${debuggingString(unitSource)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${dartEntry.modificationTime}", thrownException); |
if (identical(dartEntry.getState2(DartEntry.HINTS, librarySource), CacheState.IN_PROCESS)) { |
DartEntryImpl dartCopy = dartEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
@@ -5166,6 +5235,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(source, dartCopy); |
dartEntry = dartCopy; |
} else { |
+ logInformation2("Parse results discarded for ${debuggingString(source)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${dartEntry.modificationTime}", thrownException); |
DartEntryImpl dartCopy = dartEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -5241,6 +5311,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(source, htmlCopy); |
htmlEntry = htmlCopy; |
} else { |
+ logInformation2("Parse results discarded for ${debuggingString(source)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${htmlEntry.modificationTime}", thrownException); |
HtmlEntryImpl htmlCopy = (sourceEntry as HtmlEntry).writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -5320,6 +5391,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(unitSource, dartCopy); |
dartEntry = dartCopy; |
} else { |
+ logInformation2("Resolution results discarded for ${debuggingString(unitSource)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${dartEntry.modificationTime}", thrownException); |
DartEntryImpl dartCopy = dartEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -5394,6 +5466,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
_cache.put(source, htmlCopy); |
htmlEntry = htmlCopy; |
} else { |
+ logInformation2("Resolution results discarded for ${debuggingString(source)}; sourceTime = ${sourceTime}, resultTime = ${resultTime}, cacheTime = ${htmlEntry.modificationTime}", thrownException); |
HtmlEntryImpl htmlCopy = htmlEntry.writableCopy; |
if (thrownException == null || resultTime >= 0) { |
// |
@@ -5438,10 +5511,14 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
SourceEntry sourceEntry = _cache.get(source); |
if (sourceEntry == null) { |
sourceEntry = createSourceEntry(source); |
+ logInformation("Added new source: ${debuggingString(source)}"); |
} else { |
SourceEntryImpl sourceCopy = sourceEntry.writableCopy; |
+ int oldTime = sourceCopy.modificationTime; |
sourceCopy.modificationTime = source.modificationStamp; |
+ // TODO(brianwilkerson) Understand why we're not invalidating the cache. |
_cache.put(source, sourceCopy); |
+ logInformation("Added new source: ${debuggingString(source)} (previously modified at ${oldTime})"); |
} |
return sourceEntry is DartEntry; |
} |
@@ -5456,13 +5533,20 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
if (sourceEntry == null || sourceEntry.modificationTime == source.modificationStamp) { |
// Either we have removed this source, in which case we don't care that it is changed, or we |
// have already invalidated the cache and don't need to invalidate it again. |
+ if (sourceEntry == null) { |
+ logInformation("Modified source, but there is no entry: ${debuggingString(source)}"); |
+ } else { |
+ logInformation("Modified source, but modification time matches: ${debuggingString(source)}"); |
+ } |
return; |
} |
if (sourceEntry is HtmlEntry) { |
HtmlEntryImpl htmlCopy = sourceEntry.writableCopy; |
+ int oldTime = htmlCopy.modificationTime; |
htmlCopy.modificationTime = source.modificationStamp; |
htmlCopy.invalidateAllInformation(); |
_cache.put(source, htmlCopy); |
+ logInformation("Modified HTML source: ${debuggingString(source)} (previously modified at ${oldTime})"); |
} else if (sourceEntry is DartEntry) { |
List<Source> containingLibraries = getLibrariesContaining(source); |
Set<Source> librariesToInvalidate = new Set<Source>(); |
@@ -5472,14 +5556,18 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
librariesToInvalidate.add(dependentLibrary); |
} |
} |
+ PrintStringWriter writer = new PrintStringWriter(); |
+ int oldTime = sourceEntry.modificationTime; |
+ writer.println("Modified Dart source: ${debuggingString(source)} (previously modified at ${oldTime})"); |
for (Source library in librariesToInvalidate) { |
// for (Source library : containingLibraries) { |
- invalidateLibraryResolution(library); |
+ invalidateLibraryResolution(library, writer); |
} |
DartEntryImpl dartCopy = sourceEntry.writableCopy; |
dartCopy.modificationTime = source.modificationStamp; |
dartCopy.invalidateAllInformation(); |
_cache.put(source, dartCopy); |
+ logInformation(writer.toString()); |
} |
} |
@@ -5489,6 +5577,8 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
* @param source the source that has been deleted |
*/ |
void sourceRemoved(Source source) { |
+ PrintStringWriter writer = new PrintStringWriter(); |
+ writer.println("Removed source: ${debuggingString(source)}"); |
SourceEntry sourceEntry = _cache.get(source); |
if (sourceEntry is DartEntry) { |
Set<Source> libraries = new Set<Source>(); |
@@ -5499,10 +5589,39 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
} |
for (Source librarySource in libraries) { |
- invalidateLibraryResolution(librarySource); |
+ invalidateLibraryResolution(librarySource, writer); |
} |
} |
_cache.remove(source); |
+ logInformation(writer.toString()); |
+ } |
+ |
+ /** |
+ * Check the cache for any invalid entries (entries whose modification time does not match the |
+ * modification time of the source associated with the entry). Invalid entries will be marked as |
+ * invalid so that the source will be re-analyzed. |
+ * |
+ * <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock]. |
+ * |
+ * @return `true` if at least one entry was invalid |
+ */ |
+ bool validateCacheConsistency() { |
+ int consistencyCheckStart = JavaSystem.nanoTime(); |
+ int inconsistentCount = 0; |
+ { |
+ for (MapEntry<Source, SourceEntry> entry in _cache.entrySet()) { |
+ Source source = entry.getKey(); |
+ SourceEntry sourceEntry = entry.getValue(); |
+ int sourceTime = source.modificationStamp; |
+ if (sourceTime != sourceEntry.modificationTime) { |
+ sourceChanged(source); |
+ inconsistentCount++; |
+ } |
+ } |
+ } |
+ int consistencyCheckEnd = JavaSystem.nanoTime(); |
+ logInformation("Consistency check found ${inconsistentCount} inconsistent entries in ${((consistencyCheckEnd - consistencyCheckStart) / 1000000.0)} ms"); |
+ return inconsistentCount > 0; |
} |
} |
@@ -7185,7 +7304,7 @@ abstract class AnalysisTask { |
safelyPerform(); |
} on AnalysisException catch (exception) { |
_thrownException = exception; |
- AnalysisEngine.instance.logger.logInformation2("Task failed: ${taskDescription}", exception); |
+ AnalysisEngine.instance.logger.logInformation3("Task failed: ${taskDescription}", exception); |
} |
return accept(visitor); |
} |
@@ -8330,7 +8449,7 @@ abstract class Logger { |
* @param message an explanation of why the error occurred or what it means |
* @param exception the exception being logged |
*/ |
- void logInformation2(String message, Exception exception); |
+ void logInformation3(String message, Exception exception); |
} |
/** |
@@ -8349,6 +8468,6 @@ class Logger_NullLogger implements Logger { |
void logInformation(String message) { |
} |
- void logInformation2(String message, Exception exception) { |
+ void logInformation3(String message, Exception exception) { |
} |
} |