Index: pkg/analyzer_experimental/lib/src/generated/engine.dart |
diff --git a/pkg/analyzer_experimental/lib/src/generated/engine.dart b/pkg/analyzer_experimental/lib/src/generated/engine.dart |
index 1570f8cfd14e6e79ee164b268ecc29efbe699794..2b2709ee9e1e70ad07fd6db429052e0b3c7159d2 100644 |
--- a/pkg/analyzer_experimental/lib/src/generated/engine.dart |
+++ b/pkg/analyzer_experimental/lib/src/generated/engine.dart |
@@ -800,6 +800,181 @@ class ChangeSet { |
} |
} |
/** |
+ * Instances of the class `AnalysisCache` implement an LRU cache of information related to |
+ * analysis. |
+ */ |
+class AnalysisCache { |
+ |
+ /** |
+ * A table mapping the sources known to the context to the information known about the source. |
+ */ |
+ Map<Source, SourceEntry> _sourceMap = new Map<Source, SourceEntry>(); |
+ |
+ /** |
+ * The maximum number of sources for which AST structures should be kept in the cache. |
+ */ |
+ int _maxCacheSize = 0; |
+ |
+ /** |
+ * A list containing the most recently accessed sources with the most recently used at the end of |
+ * the list. When more sources are added than the maximum allowed then the least recently used |
+ * source will be removed and will have it's cached AST structure flushed. |
+ */ |
+ List<Source> _recentlyUsed; |
+ |
+ /** |
+ * An array containing sources for which data should not be flushed. |
+ */ |
+ List<Source> _priorityOrder = Source.EMPTY_ARRAY; |
+ |
+ /** |
+ * The number of times that the flushing of information from the cache has been disabled without |
+ * being re-enabled. |
+ */ |
+ int _cacheRemovalCount = 0; |
+ |
+ /** |
+ * Initialize a newly created cache to maintain at most the given number of AST structures in the |
+ * cache. |
+ * |
+ * @param maxCacheSize the maximum number of sources for which AST structures should be kept in |
+ * the cache |
+ */ |
+ AnalysisCache(int maxCacheSize) { |
+ this._maxCacheSize = maxCacheSize; |
+ _recentlyUsed = new List<Source>(); |
+ } |
+ |
+ /** |
+ * Record that the given source was just accessed. |
+ * |
+ * @param source the source that was accessed |
+ */ |
+ void accessed(Source source) { |
+ if (_recentlyUsed.remove(source)) { |
+ _recentlyUsed.add(source); |
+ return; |
+ } |
+ if (_cacheRemovalCount == 0 && _recentlyUsed.length >= _maxCacheSize) { |
+ flushAstFromCache(); |
+ } |
+ _recentlyUsed.add(source); |
+ } |
+ |
+ /** |
+ * Disable flushing information from the cache until [enableCacheRemoval] has been |
+ * called. |
+ */ |
+ void disableCacheRemoval() { |
+ _cacheRemovalCount++; |
+ } |
+ |
+ /** |
+ * Re-enable flushing information from the cache. |
+ */ |
+ void enableCacheRemoval() { |
+ if (_cacheRemovalCount > 0) { |
+ _cacheRemovalCount--; |
+ } |
+ if (_cacheRemovalCount == 0) { |
+ while (_recentlyUsed.length > _maxCacheSize) { |
+ flushAstFromCache(); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Return a set containing all of the map entries mapping sources to cache entries. Clients should |
+ * not modify the returned set. |
+ * |
+ * @return a set containing all of the map entries mapping sources to cache entries |
+ */ |
+ Set<MapEntry<Source, SourceEntry>> entrySet() => getMapEntrySet(_sourceMap); |
+ |
+ /** |
+ * Return the entry associated with the given source. |
+ * |
+ * @param source the source whose entry is to be returned |
+ * @return the entry associated with the given source |
+ */ |
+ SourceEntry get(Source source) => _sourceMap[source]; |
+ |
+ /** |
+ * Return an array containing sources for which data should not be flushed. |
+ * |
+ * @return an array containing sources for which data should not be flushed |
+ */ |
+ List<Source> get priorityOrder => _priorityOrder; |
+ |
+ /** |
+ * Associate the given entry with the given source. |
+ * |
+ * @param source the source with which the entry is to be associated |
+ * @param entry the entry to be associated with the source |
+ */ |
+ void put(Source source, SourceEntry entry) { |
+ _sourceMap[source] = entry; |
+ } |
+ |
+ /** |
+ * Set the sources for which data should not be flushed to the given array. |
+ * |
+ * @param sources the sources for which data should not be flushed |
+ */ |
+ void set priorityOrder(List<Source> sources) { |
+ _priorityOrder = sources; |
+ } |
+ |
+ /** |
+ * Flush one AST structure from the cache. |
+ */ |
+ void flushAstFromCache() { |
+ Source removedSource = removeAstToFlush(); |
+ SourceEntry sourceEntry = _sourceMap[removedSource]; |
+ if (sourceEntry is HtmlEntry) { |
+ HtmlEntryImpl htmlCopy = ((sourceEntry as HtmlEntry)).writableCopy; |
+ htmlCopy.setState(HtmlEntry.PARSED_UNIT, CacheState.FLUSHED); |
+ _sourceMap[removedSource] = htmlCopy; |
+ } else if (sourceEntry is DartEntry) { |
+ DartEntryImpl dartCopy = ((sourceEntry as DartEntry)).writableCopy; |
+ dartCopy.flushAstStructures(); |
+ _sourceMap[removedSource] = dartCopy; |
+ } |
+ } |
+ |
+ /** |
+ * Return `true` if the given source is in the array of priority sources. |
+ * |
+ * @return `true` if the given source is in the array of priority sources |
+ */ |
+ bool isPrioritySource(Source source) { |
+ for (Source prioritySource in _priorityOrder) { |
+ if (source == prioritySource) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ /** |
+ * Remove and return one source from the list of recently used sources whose AST structure can be |
+ * flushed from the cache. The source that will be returned will be the source that has been |
+ * unreferenced for the longest period of time but that is not a priority for analysis. |
+ * |
+ * @return the source that was removed |
+ */ |
+ Source removeAstToFlush() { |
+ for (int i = 0; i < _recentlyUsed.length; i++) { |
+ Source source = _recentlyUsed[i]; |
+ if (!isPrioritySource(source)) { |
+ return _recentlyUsed.removeAt(i); |
+ } |
+ } |
+ AnalysisEngine.instance.logger.logError2("Internal error: The number of priority sources (${_priorityOrder.length}) is greater than the maximum cache size (${_maxCacheSize})", new JavaException()); |
+ return _recentlyUsed.removeAt(0); |
+ } |
+} |
+/** |
* The interface `DartEntry` defines the behavior of objects that maintain the information |
* cached by an analysis context about an individual Dart file. |
* |
@@ -1049,6 +1224,17 @@ class DartEntryImpl extends SourceEntryImpl implements DartEntry { |
* If the library is not "client code", then it is referenced as "server code". |
*/ |
static int _CLIENT_CODE = 1 << 2; |
+ |
+ /** |
+ * Flush any AST structures being maintained by this entry. |
+ */ |
+ void flushAstStructures() { |
+ if (identical(_parsedUnitState, CacheState.VALID)) { |
+ _parsedUnitState = CacheState.FLUSHED; |
+ _parsedUnit = null; |
+ } |
+ _resolutionState.flushAstStructures(); |
+ } |
List<AnalysisError> get allErrors { |
List<AnalysisError> errors = new List<AnalysisError>(); |
for (AnalysisError error in _parseErrors) { |
@@ -1633,6 +1819,19 @@ class DartEntryImpl_ResolutionState { |
} |
/** |
+ * Flush any AST structures being maintained by this state. |
+ */ |
+ void flushAstStructures() { |
+ if (identical(_resolvedUnitState, CacheState.VALID)) { |
+ _resolvedUnitState = CacheState.FLUSHED; |
+ _resolvedUnit = null; |
+ } |
+ if (_nextState != null) { |
+ _nextState.flushAstStructures(); |
+ } |
+ } |
+ |
+ /** |
* Invalidate all of the resolution information associated with the compilation unit. |
*/ |
void invalidateAllResolutionInformation() { |
@@ -2418,7 +2617,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
return null; |
} |
- CompilationUnit computeResolvableCompilationUnit(Source source) { |
+ ResolvableCompilationUnit computeResolvableCompilationUnit(Source source) { |
DartEntry dartEntry = getReadableDartEntry(source); |
if (dartEntry == null) { |
return null; |
@@ -2426,12 +2625,16 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
CompilationUnit unit = dartEntry.anyParsedCompilationUnit; |
if (unit == null) { |
try { |
- unit = parseCompilationUnit(source); |
+ dartEntry = internalParseDart(source); |
+ unit = dartEntry.anyParsedCompilationUnit; |
+ if (unit == null) { |
+ return null; |
+ } |
} on AnalysisException catch (exception) { |
return null; |
} |
} |
- return unit.accept(new ASTCloner()) as CompilationUnit; |
+ return new ResolvableCompilationUnit(dartEntry.modificationTime, (unit.accept(new ASTCloner()) as CompilationUnit)); |
} |
AnalysisContext extractContext(SourceContainer container) => extractContextInto(container, (AnalysisEngine.instance.createAnalysisContext() as InternalAnalysisContext)); |
InternalAnalysisContext extractContextInto(SourceContainer container, InternalAnalysisContext newContext) { |
@@ -2812,7 +3015,8 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
Source coreLibrarySource = libraryElement.context.sourceFactory.forUri(DartSdk.DART_CORE); |
LibraryElement coreElement = computeLibraryElement(coreLibrarySource); |
TypeProvider typeProvider = new TypeProviderImpl(coreElement); |
- CompilationUnit unitAST = computeResolvableCompilationUnit(unitSource); |
+ ResolvableCompilationUnit resolvableUnit = computeResolvableCompilationUnit(unitSource); |
+ CompilationUnit unitAST = resolvableUnit.compilationUnit; |
new DeclarationResolver().resolve(unitAST, find(libraryElement, unitSource)); |
RecordingErrorListener errorListener = new RecordingErrorListener(); |
TypeResolverVisitor typeResolverVisitor = new TypeResolverVisitor.con2(libraryElement, unitSource, typeProvider, errorListener); |
@@ -3809,7 +4013,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
} |
resultTime = htmlEntry.modificationTime; |
HtmlUnitBuilder builder = new HtmlUnitBuilder(this); |
- element = builder.buildHtmlElement2(source, unit); |
+ element = builder.buildHtmlElement2(source, resultTime, unit); |
resolutionErrors = builder.errorListener.getErrors2(source); |
} on AnalysisException catch (exception) { |
thrownException = exception; |
@@ -4124,7 +4328,7 @@ class AnalysisContextImpl implements InternalAnalysisContext { |
return null; |
} |
String uriContent = uriLiteral.stringValue.trim(); |
- if (uriContent == null) { |
+ if (uriContent == null || uriContent.isEmpty) { |
return null; |
} |
uriContent = Uri.encodeFull(uriContent); |
@@ -4515,21 +4719,25 @@ class RecursiveXmlVisitor_6 extends RecursiveXmlVisitor<Object> { |
if (javaStringEqualsIgnoreCase(attribute.name.lexeme, AnalysisContextImpl._ATTRIBUTE_SRC)) { |
scriptAttribute = attribute; |
} else if (javaStringEqualsIgnoreCase(attribute.name.lexeme, AnalysisContextImpl._ATTRIBUTE_TYPE)) { |
- if (javaStringEqualsIgnoreCase(attribute.text, AnalysisContextImpl._TYPE_DART)) { |
+ String text = attribute.text; |
+ if (text != null && javaStringEqualsIgnoreCase(text, AnalysisContextImpl._TYPE_DART)) { |
isDartScript = true; |
} |
} |
} |
if (isDartScript && scriptAttribute != null) { |
- try { |
- Uri uri = new Uri(path: scriptAttribute.text); |
- String fileName = uri.path; |
- Source librarySource = AnalysisContextImpl_this._sourceFactory.resolveUri(htmlSource, fileName); |
- if (librarySource.exists()) { |
- libraries.add(librarySource); |
+ String text = scriptAttribute.text; |
+ if (text != null) { |
+ try { |
+ Uri uri = new Uri(path: text); |
+ String fileName = uri.path; |
+ Source librarySource = AnalysisContextImpl_this._sourceFactory.resolveUri(htmlSource, fileName); |
+ if (librarySource != null && librarySource.exists()) { |
+ libraries.add(librarySource); |
+ } |
+ } catch (exception) { |
+ AnalysisEngine.instance.logger.logInformation2("Invalid URI ('${text}') in script tag in '${htmlSource.fullName}'", exception); |
} |
- } catch (exception) { |
- AnalysisEngine.instance.logger.logInformation2("Invalid URL ('${scriptAttribute.text}') in script tag in '${htmlSource.fullName}'", exception); |
} |
} |
} |
@@ -4606,7 +4814,7 @@ class AnalysisOptionsImpl implements AnalysisOptions { |
* A flag indicating whether analysis is to use strict mode. In strict mode, error reporting is |
* based exclusively on the static type information. |
*/ |
- bool _strictMode = false; |
+ bool _strictMode = true; |
/** |
* A flag indicating whether analysis is to generate hint results (e.g. type inference based |
@@ -4883,7 +5091,7 @@ class DelegatingAnalysisContextImpl extends AnalysisContextImpl { |
return super.computeLineInfo(source); |
} |
} |
- CompilationUnit computeResolvableCompilationUnit(Source source) { |
+ ResolvableCompilationUnit computeResolvableCompilationUnit(Source source) { |
if (source.isInSystemLibrary) { |
return _sdkAnalysisContext.computeResolvableCompilationUnit(source); |
} else { |
@@ -5196,7 +5404,7 @@ class InstrumentedAnalysisContextImpl implements InternalAnalysisContext { |
instrumentation.log(); |
} |
} |
- CompilationUnit computeResolvableCompilationUnit(Source source) => _basis.computeResolvableCompilationUnit(source); |
+ ResolvableCompilationUnit computeResolvableCompilationUnit(Source source) => _basis.computeResolvableCompilationUnit(source); |
AnalysisContext extractContext(SourceContainer container) { |
InstrumentationBuilder instrumentation = Instrumentation.builder2("Analysis-extractContext"); |
try { |
@@ -5599,7 +5807,7 @@ abstract class InternalAnalysisContext implements AnalysisContext { |
* @return the AST structure representing the content of the source |
* @throws AnalysisException if the analysis could not be performed |
*/ |
- CompilationUnit computeResolvableCompilationUnit(Source source); |
+ ResolvableCompilationUnit computeResolvableCompilationUnit(Source source); |
/** |
* Initialize the specified context by removing the specified sources from the receiver and adding |
@@ -5796,6 +6004,48 @@ class ResolutionEraser extends GeneralizingASTVisitor<Object> { |
} |
} |
/** |
+ * Instances of the class `ResolvableCompilationUnit` represent a compilation unit that is not |
+ * referenced by any other objects and for which we have modification stamp information. It is used |
+ * by the [LibraryResolver] to resolve a library. |
+ */ |
+class ResolvableCompilationUnit { |
+ |
+ /** |
+ * The modification time of the source from which the AST was created. |
+ */ |
+ int _modificationStamp = 0; |
+ |
+ /** |
+ * The AST that was created from the source. |
+ */ |
+ CompilationUnit _unit; |
+ |
+ /** |
+ * Initialize a newly created holder to hold the given values. |
+ * |
+ * @param modificationStamp the modification time of the source from which the AST was created |
+ * @param unit the AST that was created from the source |
+ */ |
+ ResolvableCompilationUnit(int modificationStamp, CompilationUnit unit) { |
+ this._modificationStamp = modificationStamp; |
+ this._unit = unit; |
+ } |
+ |
+ /** |
+ * Return the AST that was created from the source. |
+ * |
+ * @return the AST that was created from the source |
+ */ |
+ CompilationUnit get compilationUnit => _unit; |
+ |
+ /** |
+ * Return the modification time of the source from which the AST was created. |
+ * |
+ * @return the modification time of the source from which the AST was created |
+ */ |
+ int get modificationStamp => _modificationStamp; |
+} |
+/** |
* The interface `Logger` defines the behavior of objects that can be used to receive |
* information about errors within the analysis engine. Implementations usually write this |
* information to a file, but can also record the information for later use (such as during testing) |