Linter Demo Errors: 5Warnings: 185File: /home/fstrocco/Dart/dart/benchmark/analyzer/lib/src/generated/engine.dart // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. // This code was auto-generated, is not intended to be edited, and is subject to // significant change. Please see the README file for more information. library engine; import "dart:math" as math; import 'dart:async'; import 'dart:collection'; import 'package:analyzer/src/cancelable_future.dart'; import 'package:analyzer/src/generated/incremental_resolution_validator.dart'; import 'package:analyzer/src/plugin/engine_plugin.dart'; import 'package:analyzer/src/services/lint.dart'; import 'package:analyzer/src/task/task_dart.dart'; import '../../instrumentation/instrumentation.dart'; import 'ast.dart'; import 'constant.dart'; import 'element.dart'; import 'error.dart'; import 'error_verifier.dart'; import 'html.dart' as ht; import 'incremental_resolver.dart' show IncrementalResolver, PoorMansIncrementalResolver; import 'incremental_scanner.dart'; import 'java_core.dart'; import 'java_engine.dart'; import 'parser.dart' show Parser, IncrementalParser; import 'resolver.dart'; import 'scanner.dart'; import 'sdk.dart' show DartSdk; import 'source.dart'; import 'utilities_collection.dart'; import 'utilities_general.dart'; /** * Used by [AnalysisOptions] to allow function bodies to be analyzed in some * sources but not others. */ typedef bool AnalyzeFunctionBodiesPredicate(Source source); /** * Type of callback functions used by PendingFuture. Functions of this type * should perform a computation based on the data in [sourceEntry] and return * it. If the computation can't be performed yet because more analysis is * needed, null should be returned. * * The function may also throw an exception, in which case the corresponding * future will be completed with failure. * * Since this function is called while the state of analysis is being updated, * it should be free of side effects so that it doesn't cause reentrant * changes to the analysis state. */ typedef T PendingFutureComputer(SourceEntry sourceEntry); /** * An LRU cache of information related to analysis. */ class AnalysisCache { /** * A flag used to control whether trace information should be produced when * the content of the cache is modified. */ static bool _TRACE_CHANGES = false; /** * A list containing the partitions of which this cache is comprised. */ final List _partitions; /** * Initialize a newly created cache to have the given [_partitions]. The * partitions will be searched in the order in which they appear in the list, * so the most specific partition (usually an [SdkCachePartition]) should be * first and the most general (usually a [UniversalCachePartition]) last. */ AnalysisCache(this._partitions); /** * Return the number of entries in this cache that have an AST associated with * them. */ int get astSize => _partitions[_partitions.length - 1].astSize; /** * Return information about each of the partitions in this cache. */ List get partitionData { int count = _partitions.length; List data = new List(count); for (int i = 0; i < count; i++) { CachePartition partition = _partitions[i]; data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl( partition.astSize, partition.map.length); } return data; } /** * Record that the AST associated with the given [source] was just read from * the cache. */ void accessedAst(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { _partitions[i].accessedAst(source); return; } } } /** * Return the entry associated with the given [source]. */ SourceEntry get(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { return _partitions[i].get(source); } } // // We should never get to this point because the last partition should // always be a universal partition, except in the case of the SDK context, // in which case the source should always be part of the SDK. // return null; } /** * Return context that owns the given [source]. */ InternalAnalysisContext getContextFor(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { return _partitions[i].context; } } // // We should never get to this point because the last partition should // always be a universal partition, except in the case of the SDK context, // in which case the source should always be part of the SDK. // AnalysisEngine.instance.logger.logInformation( "Could not find context for ${source.fullName}", new CaughtException(new AnalysisException(), null)); return null; } /** * Return an iterator returning all of the map entries mapping sources to * cache entries. */ MapIterator iterator() { int count = _partitions.length; List> maps = new List(count); for (int i = 0; i < count; i++) { maps[i] = _partitions[i].map; } return new MultipleMapIterator(maps); } /** * Associate the given [entry] with the given [source]. */ void put(Source source, SourceEntry entry) { entry.fixExceptionState(); int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { if (_TRACE_CHANGES) { try { SourceEntry oldEntry = _partitions[i].get(source); if (oldEntry == null) { AnalysisEngine.instance.logger.logInformation( "Added a cache entry for '${source.fullName}'."); } else { AnalysisEngine.instance.logger.logInformation( "Modified the cache entry for ${source.fullName}'. Diff = ${entry.getDiff(oldEntry)}"); } } catch (exception) { // Ignored JavaSystem.currentTimeMillis(); } } _partitions[i].put(source, entry); return; } } } /** * Remove all information related to the given [source] from this cache. */ void remove(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { if (_TRACE_CHANGES) { try { AnalysisEngine.instance.logger.logInformation( "Removed the cache entry for ${source.fullName}'."); } catch (exception) { // Ignored JavaSystem.currentTimeMillis(); } } _partitions[i].remove(source); return; } } } /** * Record that the AST associated with the given [source] was just removed * from the cache. */ void removedAst(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { _partitions[i].removedAst(source); return; } } } /** * Return the number of sources that are mapped to cache entries. */ int size() { int size = 0; int count = _partitions.length; for (int i = 0; i < count; i++) { size += _partitions[i].size(); } return size; } /** * Record that the AST associated with the given [source] was just stored to * the cache. */ void storedAst(Source source) { int count = _partitions.length; for (int i = 0; i < count; i++) { if (_partitions[i].contains(source)) { _partitions[i].storedAst(source); return; } } } } /** * A context in which a single analysis can be performed and incrementally * maintained. The context includes such information as the version of the SDK * being analyzed against as well as the package-root used to resolve 'package:' * URI's. (Both of which are known indirectly through the [SourceFactory].) * * An analysis context also represents the state of the analysis, which includes * knowing which sources have been included in the analysis (either directly or * indirectly) and the results of the analysis. Sources must be added and * removed from the context using the method [applyChanges], which is also used * to notify the context when sources have been modified and, consequently, * previously known results might have been invalidated. * * There are two ways to access the results of the analysis. The most common is * to use one of the 'get' methods to access the results. The 'get' methods have * the advantage that they will always return quickly, but have the disadvantage * that if the results are not currently available they will return either * nothing or in some cases an incomplete result. The second way to access * results is by using one of the 'compute' methods. The 'compute' methods will * always attempt to compute the requested results but might block the caller * for a significant period of time. * * When results have been invalidated, have never been computed (as is the case * for newly added sources), or have been removed from the cache, they are * not automatically recreated. They will only be recreated if one of the * 'compute' methods is invoked. * * However, this is not always acceptable. Some clients need to keep the * analysis results up-to-date. For such clients there is a mechanism that * allows them to incrementally perform needed analysis and get notified of the * consequent changes to the analysis results. This mechanism is realized by the * method [performAnalysisTask]. * * Analysis engine allows for having more than one context. This can be used, * for example, to perform one analysis based on the state of files on disk and * a separate analysis based on the state of those files in open editors. It can * also be used to perform an analysis based on a proposed future state, such as * the state after a refactoring. */ abstract class AnalysisContext { /** * An empty list of contexts. */ static const List EMPTY_LIST = const []; /** * Return the set of analysis options controlling the behavior of this * context. Clients should not modify the returned set of options. The options * should only be set by invoking the method [setAnalysisOptions]. */ AnalysisOptions get analysisOptions; /** * Set the set of analysis options controlling the behavior of this context to * the given [options]. Clients can safely assume that all necessary analysis * results have been invalidated. */ void set analysisOptions(AnalysisOptions options); /** * Set the order in which sources will be analyzed by [performAnalysisTask] to * match the order of the sources in the given list of [sources]. If a source * that needs to be analyzed is not contained in the list, then it will be * treated as if it were at the end of the list. If the list is empty (or * `null`) then no sources will be given priority over other sources. * * Changes made to the list after this method returns will not be * reflected in the priority order. */ void set analysisPriorityOrder(List sources); /** * Return the set of declared variables used when computing constant values. */ DeclaredVariables get declaredVariables; /** * Return a list containing all of the sources known to this context that * represent HTML files. The contents of the list can be incomplete. */ List get htmlSources; /** * Returns `true` if this context was disposed using [dispose]. */ bool get isDisposed; /** * Return a list containing all of the sources known to this context that * represent the defining compilation unit of a library that can be run within * a browser. The sources that are returned represent libraries that have a * 'main' method and are either referenced by an HTML file or import, directly * or indirectly, a client-only library. The contents of the list can be * incomplete. */ List get launchableClientLibrarySources; /** * Return a list containing all of the sources known to this context that * represent the defining compilation unit of a library that can be run * outside of a browser. The contents of the list can be incomplete. */ List get launchableServerLibrarySources; /** * Return a list containing all of the sources known to this context that * represent the defining compilation unit of a library. The contents of the * list can be incomplete. */ List get librarySources; /** * Return a client-provided name used to identify this context, or `null` if * the client has not provided a name. */ String get name; /** * Set the client-provided name used to identify this context to the given * [name]. */ set name(String name); /** * The stream that is notified when sources have been added or removed, * or the source's content has changed. */ Stream get onSourcesChanged; /** * Return a list containing all of the sources known to this context whose * state is neither valid or flushed. These sources are not safe to update * during refactoring, because we might not know all the references in them. */ List get refactoringUnsafeSources; /** * Return the source factory used to create the sources that can be analyzed * in this context. */ SourceFactory get sourceFactory; /** * Set the source factory used to create the sources that can be analyzed in * this context to the given source [factory]. Clients can safely assume that * all analysis results have been invalidated. */ void set sourceFactory(SourceFactory factory); /** * Return a list containing all of the sources known to this context. */ List get sources; /** * Return a type provider for this context or throw [AnalysisException] if * either `dart:core` or `dart:async` cannot be resolved. */ TypeProvider get typeProvider; /** * Add the given [listener] to the list of objects that are to be notified * when various analysis results are produced in this context. */ void addListener(AnalysisListener listener); /** * Apply the given [delta] to change the level of analysis that will be * performed for the sources known to this context. */ void applyAnalysisDelta(AnalysisDelta delta); /** * Apply the changes specified by the given [changeSet] to this context. Any * analysis results that have been invalidated by these changes will be * removed. */ void applyChanges(ChangeSet changeSet); /** * Return the documentation comment for the given [element] as it appears in * the original source (complete with the beginning and ending delimiters) for * block documentation comments, or lines starting with `"///"` and separated * with `"\n"` characters for end-of-line documentation comments, or `null` if * the element does not have a documentation comment associated with it. This * can be a long-running operation if the information needed to access the * comment is not cached. * * Throws an [AnalysisException] if the documentation comment could not be * determined because the analysis could not be performed. * * Note: This method cannot be used in an async environment. */ String computeDocumentationComment(Element element); /** * Return a list containing all of the errors associated with the given * [source]. If the errors are not already known then the source will be * analyzed in order to determine the errors associated with it. * * Throws an [AnalysisException] if the errors could not be determined because * the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getErrors]. */ List computeErrors(Source source); /** * Return the element model corresponding to the HTML file defined by the * given [source]. If the element model does not yet exist it will be created. * The process of creating an element model for an HTML file can be * long-running, depending on the size of the file and the number of libraries * that are defined in it (via script tags) that also need to have a model * built for them. * * Throws AnalysisException if the element model could not be determined * because the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getHtmlElement]. */ HtmlElement computeHtmlElement(Source source); /** * Return the kind of the given [source], computing it's kind if it is not * already known. Return [SourceKind.UNKNOWN] if the source is not contained * in this context. * * Note: This method cannot be used in an async environment. * * See [getKindOf]. */ SourceKind computeKindOf(Source source); /** * Return the element model corresponding to the library defined by the given * [source]. If the element model does not yet exist it will be created. The * process of creating an element model for a library can long-running, * depending on the size of the library and the number of libraries that are * imported into it that also need to have a model built for them. * * Throws an [AnalysisException] if the element model could not be determined * because the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getLibraryElement]. */ LibraryElement computeLibraryElement(Source source); /** * Return the line information for the given [source], or `null` if the source * is not of a recognized kind (neither a Dart nor HTML file). If the line * information was not previously known it will be created. The line * information is used to map offsets from the beginning of the source to line * and column pairs. * * Throws an [AnalysisException] if the line information could not be * determined because the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getLineInfo]. */ LineInfo computeLineInfo(Source source); /** * Return a future which will be completed with the fully resolved AST for a * single compilation unit within the given library, once that AST is up to * date. * * If the resolved AST can't be computed for some reason, the future will be * completed with an error. One possible error is AnalysisNotScheduledError, * which means that the resolved AST can't be computed because the given * source file is not scheduled to be analyzed within the context of the * given library. */ CancelableFuture computeResolvedCompilationUnitAsync( Source source, Source librarySource); /** * Notifies the context that the client is going to stop using this context. */ void dispose(); /** * Return `true` if the given [source] exists. * * This method should be used rather than the method [Source.exists] because * contexts can have local overrides of the content of a source that the * source is not aware of and a source with local content is considered to * exist even if there is no file on disk. */ bool exists(Source source); /** * Return the element model corresponding to the compilation unit defined by * the given [unitSource] in the library defined by the given [librarySource], * or `null` if the element model does not currently exist or if the library * cannot be analyzed for some reason. */ CompilationUnitElement getCompilationUnitElement( Source unitSource, Source librarySource); /** * Return the contents and timestamp of the given [source]. * * This method should be used rather than the method [Source.getContents] * because contexts can have local overrides of the content of a source that * the source is not aware of. */ TimestampedData getContents(Source source); /** * Return the element referenced by the given [location], or `null` if the * element is not immediately available or if there is no element with the * given location. The latter condition can occur, for example, if the * location describes an element from a different context or if the element * has been removed from this context as a result of some change since it was * originally obtained. */ Element getElement(ElementLocation location); /** * Return an analysis error info containing the list of all of the errors and * the line info associated with the given [source]. The list of errors will * be empty if the source is not known to this context or if there are no * errors in the source. The errors contained in the list can be incomplete. * * See [computeErrors]. */ AnalysisErrorInfo getErrors(Source source); /** * Return the element model corresponding to the HTML file defined by the * given [source], or `null` if the source does not represent an HTML file, * the element representing the file has not yet been created, or the analysis * of the HTML file failed for some reason. * * See [computeHtmlElement]. */ HtmlElement getHtmlElement(Source source); /** * Return the sources for the HTML files that reference the compilation unit * with the given [source]. If the source does not represent a Dart source or * is not known to this context, the returned list will be empty. The contents * of the list can be incomplete. */ List getHtmlFilesReferencing(Source source); /** * Return the kind of the given [source], or `null` if the kind is not known * to this context. * * See [computeKindOf]. */ SourceKind getKindOf(Source source); /** * Return the sources for the defining compilation units of any libraries of * which the given [source] is a part. The list will normally contain a single * library because most Dart sources are only included in a single library, * but it is possible to have a part that is contained in multiple identically * named libraries. If the source represents the defining compilation unit of * a library, then the returned list will contain the given source as its only * element. If the source does not represent a Dart source or is not known to * this context, the returned list will be empty. The contents of the list can * be incomplete. */ List getLibrariesContaining(Source source); /** * Return the sources for the defining compilation units of any libraries that * depend on the library defined by the given [librarySource]. One library * depends on another if it either imports or exports that library. */ List getLibrariesDependingOn(Source librarySource); /** * Return the sources for the defining compilation units of any libraries that * are referenced from the HTML file defined by the given [htmlSource]. */ List getLibrariesReferencedFromHtml(Source htmlSource); /** * Return the element model corresponding to the library defined by the given * [source], or `null` if the element model does not currently exist or if the * library cannot be analyzed for some reason. */ LibraryElement getLibraryElement(Source source); /** * Return the line information for the given [source], or `null` if the line * information is not known. The line information is used to map offsets from * the beginning of the source to line and column pairs. * * See [computeLineInfo]. */ LineInfo getLineInfo(Source source); /** * Return the modification stamp for the [source], or a negative value if the * source does not exist. A modification stamp is a non-negative integer with * the property that if the contents of the source have not been modified * since the last time the modification stamp was accessed then the same value * will be returned, but if the contents of the source have been modified one * or more times (even if the net change is zero) the stamps will be different. * * This method should be used rather than the method * [Source.getModificationStamp] because contexts can have local overrides of * the content of a source that the source is not aware of. */ int getModificationStamp(Source source); /** * Return a fully resolved AST for the compilation unit defined by the given * [unitSource] within the given [library], or `null` if the resolved AST is * not already computed. * * See [resolveCompilationUnit]. */ CompilationUnit getResolvedCompilationUnit( Source unitSource, LibraryElement library); /** * Return a fully resolved AST for the compilation unit defined by the given * [unitSource] within the library defined by the given [librarySource], or * `null` if the resolved AST is not already computed. * * See [resolveCompilationUnit2]. */ CompilationUnit getResolvedCompilationUnit2( Source unitSource, Source librarySource); /** * Return the fully resolved HTML unit defined by the given [htmlSource], or * `null` if the resolved unit is not already computed. * * See [resolveHtmlUnit]. */ ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource); /** * Return a list of the sources being analyzed in this context whose full path * is equal to the given [path]. */ List getSourcesWithFullName(String path); /** * Return `true` if the given [librarySource] is known to be the defining * compilation unit of a library that can be run on a client (references * 'dart:html', either directly or indirectly). * * Note: In addition to the expected case of returning `false` if the * source is known to be a library that cannot be run on a client, this method * will also return `false` if the source is not known to be a library or if * we do not know whether it can be run on a client. */ bool isClientLibrary(Source librarySource); /** * Return `true` if the given [librarySource] is known to be the defining * compilation unit of a library that can be run on the server (does not * reference 'dart:html', either directly or indirectly). * * Note: In addition to the expected case of returning `false` if the * source is known to be a library that cannot be run on the server, this * method will also return `false` if the source is not known to be a library * or if we do not know whether it can be run on the server. */ bool isServerLibrary(Source librarySource); /** * Parse the content of the given [source] to produce an AST structure. The * resulting AST structure may or may not be resolved, and may have a slightly * different structure depending upon whether it is resolved. * * Throws an [AnalysisException] if the analysis could not be performed * * Note: This method cannot be used in an async environment. */ CompilationUnit parseCompilationUnit(Source source); /** * Parse a single HTML [source] to produce an AST structure. The resulting * HTML AST structure may or may not be resolved, and may have a slightly * different structure depending upon whether it is resolved. * * Throws an [AnalysisException] if the analysis could not be performed * * Note: This method cannot be used in an async environment. */ ht.HtmlUnit parseHtmlUnit(Source source); /** * Perform the next unit of work required to keep the analysis results * up-to-date and return information about the consequent changes to the * analysis results. This method can be long running. */ AnalysisResult performAnalysisTask(); /** * Remove the given [listener] from the list of objects that are to be * notified when various analysis results are produced in this context. */ void removeListener(AnalysisListener listener); /** * Return a fully resolved AST for the compilation unit defined by the given * [unitSource] within the given [library]. * * Throws an [AnalysisException] if the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getResolvedCompilationUnit]. */ CompilationUnit resolveCompilationUnit( Source unitSource, LibraryElement library); /** * Return a fully resolved AST for the compilation unit defined by the given * [unitSource] within the library defined by the given [librarySource]. * * Throws an [AnalysisException] if the analysis could not be performed. * * Note: This method cannot be used in an async environment. * * See [getResolvedCompilationUnit2]. */ CompilationUnit resolveCompilationUnit2( Source unitSource, Source librarySource); /** * Parse and resolve a single [htmlSource] within the given context to produce * a fully resolved AST. * * Throws an [AnalysisException] if the analysis could not be performed. * * Note: This method cannot be used in an async environment. */ ht.HtmlUnit resolveHtmlUnit(Source htmlSource); /** * Set the contents of the given [source] to the given [contents] and mark the * source as having changed. The additional [offset] and [length] information * is used by the context to determine what reanalysis is necessary. */ void setChangedContents( Source source, String contents, int offset, int oldLength, int newLength); /** * Set the contents of the given [source] to the given [contents] and mark the * source as having changed. This has the effect of overriding the default * contents of the source. If the contents are `null` the override is removed * so that the default contents will be returned. */ void setContents(Source source, String contents); } /** * An [AnalysisContext]. */ class AnalysisContextImpl implements InternalAnalysisContext { /** * The difference between the maximum cache size and the maximum priority * order size. The priority list must be capped so that it is less than the * cache size. Failure to do so can result in an infinite loop in * performAnalysisTask() because re-caching one AST structure can cause * another priority source's AST structure to be flushed. */ static int _PRIORITY_ORDER_SIZE_DELTA = 4; /** * A flag indicating whether trace output should be produced as analysis tasks * are performed. Used for debugging. */ static bool _TRACE_PERFORM_TASK = false; /** * The next context identifier. */ static int _NEXT_ID = 0; /** * The unique identifier of this context. */ final int _id = _NEXT_ID++; /** * A client-provided name used to identify this context, or `null` if the * client has not provided a name. */ String name; /** * The set of analysis options controlling the behavior of this context. */ AnalysisOptionsImpl _options = new AnalysisOptionsImpl(); /** * A flag indicating whether errors related to implicitly analyzed sources * should be generated and reported. */ bool _generateImplicitErrors = true; /** * A flag indicating whether errors related to sources in the SDK should be * generated and reported. */ bool _generateSdkErrors = true; /** * A flag indicating whether this context is disposed. */ bool _disposed = false; /** * A cache of content used to override the default content of a source. */ ContentCache _contentCache = new ContentCache(); /** * The source factory used to create the sources that can be analyzed in this * context. */ SourceFactory _sourceFactory; /** * The set of declared variables used when computing constant values. */ DeclaredVariables _declaredVariables = new DeclaredVariables(); /** * A source representing the core library. */ Source _coreLibrarySource; /** * A source representing the async library. */ Source _asyncLibrarySource; /** * The partition that contains analysis results that are not shared with other * contexts. */ CachePartition _privatePartition; /** * A table mapping the sources known to the context to the information known * about the source. */ AnalysisCache _cache; /** * A list containing sources for which data should not be flushed. */ List _priorityOrder = Source.EMPTY_ARRAY; /** * A map from all sources for which there are futures pending to a list of * the corresponding PendingFuture objects. These sources will be analyzed * in the same way as priority sources, except with higher priority. * * TODO(paulberry): since the size of this map is not constrained (as it is * for _priorityOrder), we run the risk of creating an analysis loop if * re-caching one AST structure causes the AST structure for another source * with pending futures to be flushed. However, this is unlikely to happen * in practice since sources are removed from this hash set as soon as their * futures have completed. */ HashMap> _pendingFutureSources = new HashMap>(); /** * A list containing sources whose AST structure is needed in order to resolve * the next library to be resolved. */ HashSet _neededForResolution = null; /** * A table mapping sources to the change notices that are waiting to be * returned related to that source. */ HashMap _pendingNotices = new HashMap(); /** * The object used to record the results of performing an analysis task. */ AnalysisContextImpl_AnalysisTaskResultRecorder _resultRecorder; /** * Cached information used in incremental analysis or `null` if none. */ IncrementalAnalysisCache _incrementalAnalysisCache; /** * The [TypeProvider] for this context, `null` if not yet created. */ TypeProvider _typeProvider; /** * The object used to manage the list of sources that need to be analyzed. */ WorkManager _workManager = new WorkManager(); /** * The [Stopwatch] of the current "perform tasks cycle". */ Stopwatch _performAnalysisTaskStopwatch; /** * The controller for sending [SourcesChangedEvent]s. */ StreamController _onSourcesChangedController; /** * The listeners that are to be notified when various analysis results are * produced in this context. */ List _listeners = new List(); /** * The most recently incrementally resolved source, or `null` when it was * already validated, or the most recent change was not incrementally resolved. */ Source incrementalResolutionValidation_lastUnitSource; /** * The most recently incrementally resolved library source, or `null` when it * was already validated, or the most recent change was not incrementally * resolved. */ Source incrementalResolutionValidation_lastLibrarySource; /** * The result of incremental resolution result of * [incrementalResolutionValidation_lastSource]. */ CompilationUnit incrementalResolutionValidation_lastUnit; /** * A factory to override how the [ResolverVisitor] is created. */ ResolverVisitorFactory resolverVisitorFactory; /** * A factory to override how the [TypeResolverVisitor] is created. */ TypeResolverVisitorFactory typeResolverVisitorFactory; /** * A factory to override how [LibraryResolver] is created. */ LibraryResolverFactory libraryResolverFactory; /** * Initialize a newly created analysis context. */ AnalysisContextImpl() { _resultRecorder = new AnalysisContextImpl_AnalysisTaskResultRecorder(this); _privatePartition = new UniversalCachePartition(this, AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, new AnalysisContextImpl_ContextRetentionPolicy(this)); _cache = createCacheFromSourceFactory(null); _onSourcesChangedController = new StreamController.broadcast(); } @override AnalysisOptions get analysisOptions => _options; @override void set analysisOptions(AnalysisOptions options) { bool needsRecompute = this._options.analyzeFunctionBodiesPredicate != options.analyzeFunctionBodiesPredicate || this._options.generateImplicitErrors != options.generateImplicitErrors || this._options.generateSdkErrors != options.generateSdkErrors || this._options.dart2jsHint != options.dart2jsHint || (this._options.hint && !options.hint) || this._options.preserveComments != options.preserveComments || this._options.enableNullAwareOperators != options.enableNullAwareOperators || this._options.enableStrictCallChecks != options.enableStrictCallChecks; int cacheSize = options.cacheSize; if (this._options.cacheSize != cacheSize) { this._options.cacheSize = cacheSize; //cache.setMaxCacheSize(cacheSize); _privatePartition.maxCacheSize = cacheSize; // // Cap the size of the priority list to being less than the cache size. // Failure to do so can result in an infinite loop in // performAnalysisTask() because re-caching one AST structure // can cause another priority source's AST structure to be flushed. // // TODO(brianwilkerson) Remove this constraint when the new task model is // implemented. // int maxPriorityOrderSize = cacheSize - _PRIORITY_ORDER_SIZE_DELTA; if (_priorityOrder.length > maxPriorityOrderSize) { _priorityOrder = _priorityOrder.sublist(0, maxPriorityOrderSize); } } this._options.analyzeFunctionBodiesPredicate = options.analyzeFunctionBodiesPredicate; this._options.generateImplicitErrors = options.generateImplicitErrors; this._options.generateSdkErrors = options.generateSdkErrors; this._options.dart2jsHint = options.dart2jsHint; this._options.enableNullAwareOperators = options.enableNullAwareOperators; this._options.enableStrictCallChecks = options.enableStrictCallChecks; this._options.hint = options.hint; this._options.incremental = options.incremental; this._options.incrementalApi = options.incrementalApi; this._options.incrementalValidation = options.incrementalValidation; this._options.lint = options.lint; this._options.preserveComments = options.preserveComments; _generateImplicitErrors = options.generateImplicitErrors; _generateSdkErrors = options.generateSdkErrors; if (needsRecompute) { _invalidateAllLocalResolutionInformation(false); } } @override void set analysisPriorityOrder(List sources) { if (sources == null || sources.isEmpty) { _priorityOrder = Source.EMPTY_ARRAY; } else { while (sources.remove(null)) { // Nothing else to do. } if (sources.isEmpty) { _priorityOrder = Source.EMPTY_ARRAY; } // // Cap the size of the priority list to being less than the cache size. // Failure to do so can result in an infinite loop in // performAnalysisTask() because re-caching one AST structure // can cause another priority source's AST structure to be flushed. // int count = math.min( sources.length, _options.cacheSize - _PRIORITY_ORDER_SIZE_DELTA); _priorityOrder = new List(count); for (int i = 0; i < count; i++) { _priorityOrder[i] = sources[i]; } // Ensure entries for every priority source. for (var source in _priorityOrder) { SourceEntry entry = _getReadableSourceEntry(source); if (entry == null) { _createSourceEntry(source, false); } } } } @override set contentCache(ContentCache value) { _contentCache = value; } @override DeclaredVariables get declaredVariables => _declaredVariables; @override List get htmlSources => _getSources(SourceKind.HTML); @override bool get isDisposed => _disposed; @override List get launchableClientLibrarySources { // TODO(brianwilkerson) This needs to filter out libraries that do not // reference dart:html, either directly or indirectly. List sources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { Source source = iterator.key; SourceEntry sourceEntry = iterator.value; if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) { // DartEntry dartEntry = (DartEntry) sourceEntry; // if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && dartEntry.getValue(DartEntry.IS_CLIENT)) { sources.add(source); // } } } return sources; } @override List get launchableServerLibrarySources { // TODO(brianwilkerson) This needs to filter out libraries that reference // dart:html, either directly or indirectly. List sources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { Source source = iterator.key; SourceEntry sourceEntry = iterator.value; if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) { // DartEntry dartEntry = (DartEntry) sourceEntry; // if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && !dartEntry.getValue(DartEntry.IS_CLIENT)) { sources.add(source); // } } } return sources; } @override List get librarySources => _getSources(SourceKind.LIBRARY); /** * Look through the cache for a task that needs to be performed. Return the * task that was found, or `null` if there is no more work to be done. */ AnalysisTask get nextAnalysisTask { bool hintsEnabled = _options.hint; bool lintsEnabled = _options.lint; bool hasBlockedTask = false; // // Look for incremental analysis // if (_incrementalAnalysisCache != null && _incrementalAnalysisCache.hasWork) { AnalysisTask task = new IncrementalAnalysisTask(this, _incrementalAnalysisCache); _incrementalAnalysisCache = null; return task; } // // Look for a source that needs to be analyzed because it has futures // pending. // if (_pendingFutureSources.isNotEmpty) { List sourcesToRemove = []; AnalysisTask task; for (Source source in _pendingFutureSources.keys) { SourceEntry sourceEntry = _cache.get(source); List pendingFutures = _pendingFutureSources[source]; for (int i = 0; i < pendingFutures.length;) { if (pendingFutures[i].evaluate(sourceEntry)) { pendingFutures.removeAt(i); } else { i++; } } if (pendingFutures.isEmpty) { sourcesToRemove.add(source); continue; } AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( source, sourceEntry, true, hintsEnabled, lintsEnabled); task = taskData.task; if (task != null) { break; } else if (taskData.isBlocked) { hasBlockedTask = true; } else { // There is no more work to do for this task, so forcibly complete // all its pending futures. for (PendingFuture pendingFuture in pendingFutures) { pendingFuture.forciblyComplete(); } sourcesToRemove.add(source); } } for (Source source in sourcesToRemove) { _pendingFutureSources.remove(source); } if (task != null) { return task; } } // // Look for a priority source that needs to be analyzed. // int priorityCount = _priorityOrder.length; for (int i = 0; i < priorityCount; i++) { Source source = _priorityOrder[i]; AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( source, _cache.get(source), true, hintsEnabled, lintsEnabled); AnalysisTask task = taskData.task; if (task != null) { return task; } else if (taskData.isBlocked) { hasBlockedTask = true; } } if (_neededForResolution != null) { List sourcesToRemove = new List(); for (Source source in _neededForResolution) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; if (!dartEntry.hasResolvableCompilationUnit) { if (dartEntry.getState(DartEntry.PARSED_UNIT) == CacheState.ERROR) { sourcesToRemove.add(source); } else { AnalysisContextImpl_TaskData taskData = _createParseDartTask(source, dartEntry); AnalysisTask task = taskData.task; if (task != null) { return task; } else if (taskData.isBlocked) { hasBlockedTask = true; } } } } } int count = sourcesToRemove.length; for (int i = 0; i < count; i++) { _neededForResolution.remove(sourcesToRemove[i]); } } // // Look for a non-priority source that needs to be analyzed. // List sourcesToRemove = new List(); WorkManager_WorkIterator sources = _workManager.iterator(); try { while (sources.hasNext) { Source source = sources.next(); AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( source, _cache.get(source), false, hintsEnabled, lintsEnabled); AnalysisTask task = taskData.task; if (task != null) { return task; } else if (taskData.isBlocked) { hasBlockedTask = true; } else { sourcesToRemove.add(source); } } } finally { int count = sourcesToRemove.length; for (int i = 0; i < count; i++) { _workManager.remove(sourcesToRemove[i]); } } if (hasBlockedTask) { // All of the analysis work is blocked waiting for an asynchronous task // to complete. return WaitForAsyncTask.instance; } return null; } @override Stream get onSourcesChanged => _onSourcesChangedController.stream; /** * Make _pendingFutureSources available to unit tests. */ HashMap> get pendingFutureSources_forTesting => _pendingFutureSources; @override List get prioritySources => _priorityOrder; @override List get refactoringUnsafeSources { List sources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { SourceEntry sourceEntry = iterator.value; if (sourceEntry is DartEntry) { Source source = iterator.key; if (!source.isInSystemLibrary && !sourceEntry.isRefactoringSafe) { sources.add(source); } } } return sources; } @override SourceFactory get sourceFactory => _sourceFactory; @override void set sourceFactory(SourceFactory factory) { if (identical(_sourceFactory, factory)) { return; } else if (factory.context != null) { throw new IllegalStateException( "Source factories cannot be shared between contexts"); } if (_sourceFactory != null) { _sourceFactory.context = null; } factory.context = this; _sourceFactory = factory; _coreLibrarySource = _sourceFactory.forUri(DartSdk.DART_CORE); _asyncLibrarySource = _sourceFactory.forUri(DartSdk.DART_ASYNC); _cache = createCacheFromSourceFactory(factory); _invalidateAllLocalResolutionInformation(true); } @override List get sources { List sources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { sources.add(iterator.key); } return sources; } /** * Return a list of the sources that would be processed by * [performAnalysisTask]. This method duplicates, and must therefore be kept * in sync with, [getNextAnalysisTask]. This method is intended to be used for * testing purposes only. */ List get sourcesNeedingProcessing { HashSet sources = new HashSet(); bool hintsEnabled = _options.hint; bool lintsEnabled = _options.lint; // // Look for priority sources that need to be analyzed. // for (Source source in _priorityOrder) { _getSourcesNeedingProcessing(source, _cache.get(source), true, hintsEnabled, lintsEnabled, sources); } // // Look for non-priority sources that need to be analyzed. // WorkManager_WorkIterator iterator = _workManager.iterator(); while (iterator.hasNext) { Source source = iterator.next(); _getSourcesNeedingProcessing(source, _cache.get(source), false, hintsEnabled, lintsEnabled, sources); } return new List.from(sources); } @override AnalysisContextStatistics get statistics { AnalysisContextStatisticsImpl statistics = new AnalysisContextStatisticsImpl(); visitCacheItems(statistics._internalPutCacheItem); statistics.partitionData = _cache.partitionData; return statistics; } IncrementalAnalysisCache get test_incrementalAnalysisCache { return _incrementalAnalysisCache; } set test_incrementalAnalysisCache(IncrementalAnalysisCache value) { _incrementalAnalysisCache = value; } List get test_priorityOrder => _priorityOrder; @override TypeProvider get typeProvider { if (_typeProvider != null) { return _typeProvider; } Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE); if (coreSource == null) { throw new AnalysisException("Could not create a source for dart:core"); } LibraryElement coreElement = computeLibraryElement(coreSource); if (coreElement == null) { throw new AnalysisException("Could not create an element for dart:core"); } Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC); if (asyncSource == null) { throw new AnalysisException("Could not create a source for dart:async"); } LibraryElement asyncElement = computeLibraryElement(asyncSource); if (asyncElement == null) { throw new AnalysisException("Could not create an element for dart:async"); } _typeProvider = new TypeProviderImpl(coreElement, asyncElement); return _typeProvider; } /** * Sets the [TypeProvider] for this context. */ void set typeProvider(TypeProvider typeProvider) { _typeProvider = typeProvider; } @override void addListener(AnalysisListener listener) { if (!_listeners.contains(listener)) { _listeners.add(listener); } } @override void addSourceInfo(Source source, SourceEntry info) { // This implementation assumes that the access to the cache does not need to // be synchronized because no other object can have access to this context // while this method is being invoked. _cache.put(source, info); } @override void applyAnalysisDelta(AnalysisDelta delta) { ChangeSet changeSet = new ChangeSet(); delta.analysisLevels.forEach((Source source, AnalysisLevel level) { if (level == AnalysisLevel.NONE) { changeSet.removedSource(source); } else { changeSet.addedSource(source); } }); applyChanges(changeSet); } @override void applyChanges(ChangeSet changeSet) { if (changeSet.isEmpty) { return; } // // First, compute the list of sources that have been removed. // List removedSources = new List.from(changeSet.removedSources); for (SourceContainer container in changeSet.removedContainers) { _addSourcesInContainer(removedSources, container); } // // Then determine which cached results are no longer valid. // for (Source source in changeSet.addedSources) { _sourceAvailable(source); } for (Source source in changeSet.changedSources) { if (_contentCache.getContents(source) != null) { // This source is overridden in the content cache, so the change will // have no effect. Just ignore it to avoid wasting time doing // re-analysis. continue; } _sourceChanged(source); } changeSet.changedContents.forEach((Source key, String value) { _contentsChanged(key, value, false); }); changeSet.changedRanges .forEach((Source source, ChangeSet_ContentChange change) { _contentRangeChanged(source, change.contents, change.offset, change.oldLength, change.newLength); }); for (Source source in changeSet.deletedSources) { _sourceDeleted(source); } for (Source source in removedSources) { _sourceRemoved(source); } _onSourcesChangedController.add(new SourcesChangedEvent(changeSet)); } @override String computeDocumentationComment(Element element) { if (element == null) { return null; } Source source = element.source; if (source == null) { return null; } CompilationUnit unit = parseCompilationUnit(source); if (unit == null) { return null; } NodeLocator locator = new NodeLocator.con1(element.nameOffset); AstNode nameNode = locator.searchWithin(unit); while (nameNode != null) { if (nameNode is AnnotatedNode) { Comment comment = nameNode.documentationComment; if (comment == null) { return null; } StringBuffer buffer = new StringBuffer(); List tokens = comment.tokens; for (int i = 0; i < tokens.length; i++) { if (i > 0) { buffer.write("\n"); } buffer.write(tokens[i].lexeme); } return buffer.toString(); } nameNode = nameNode.parent; } return null; } @override List computeErrors(Source source) { bool enableHints = _options.hint; bool enableLints = _options.lint; SourceEntry sourceEntry = _getReadableSourceEntry(source); if (sourceEntry is DartEntry) { List errors = new List(); try { DartEntry dartEntry = sourceEntry; ListUtilities.addAll( errors, _getDartScanData(source, dartEntry, DartEntry.SCAN_ERRORS)); dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartParseData(source, dartEntry, DartEntry.PARSE_ERRORS)); dartEntry = _getReadableDartEntry(source); if (dartEntry.getValue(DartEntry.SOURCE_KIND) == SourceKind.LIBRARY) { ListUtilities.addAll(errors, _getDartResolutionData( source, source, dartEntry, DartEntry.RESOLUTION_ERRORS)); dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartVerificationData( source, source, dartEntry, DartEntry.VERIFICATION_ERRORS)); if (enableHints) { dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartHintData(source, source, dartEntry, DartEntry.HINTS)); } if (enableLints) { dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartLintData(source, source, dartEntry, DartEntry.LINTS)); } } else { List libraries = getLibrariesContaining(source); for (Source librarySource in libraries) { ListUtilities.addAll(errors, _getDartResolutionData( source, librarySource, dartEntry, DartEntry.RESOLUTION_ERRORS)); dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartVerificationData(source, librarySource, dartEntry, DartEntry.VERIFICATION_ERRORS)); if (enableHints) { dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartHintData( source, librarySource, dartEntry, DartEntry.HINTS)); } if (enableLints) { dartEntry = _getReadableDartEntry(source); ListUtilities.addAll(errors, _getDartLintData( source, librarySource, dartEntry, DartEntry.LINTS)); } } } } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute errors", new CaughtException(exception, stackTrace)); } if (errors.isEmpty) { return AnalysisError.NO_ERRORS; } return errors; } else if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; try { return _getHtmlResolutionData2( source, htmlEntry, HtmlEntry.RESOLUTION_ERRORS); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute errors", new CaughtException(exception, stackTrace)); } } return AnalysisError.NO_ERRORS; } @override List computeExportedLibraries(Source source) => _getDartParseData2( source, DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_ARRAY); @override HtmlElement computeHtmlElement(Source source) => _getHtmlResolutionData(source, HtmlEntry.ELEMENT, null); @override List computeImportedLibraries(Source source) => _getDartParseData2( source, DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_ARRAY); @override SourceKind computeKindOf(Source source) { SourceEntry sourceEntry = _getReadableSourceEntry(source); if (sourceEntry == null) { return SourceKind.UNKNOWN; } else if (sourceEntry is DartEntry) { try { return _getDartParseData(source, sourceEntry, DartEntry.SOURCE_KIND); } on AnalysisException { return SourceKind.UNKNOWN; } } return sourceEntry.kind; } @override LibraryElement computeLibraryElement(Source source) => _getDartResolutionData2(source, source, DartEntry.ELEMENT, null); @override LineInfo computeLineInfo(Source source) { SourceEntry sourceEntry = _getReadableSourceEntry(source); try { if (sourceEntry is HtmlEntry) { return _getHtmlParseData(source, SourceEntry.LINE_INFO, null); } else if (sourceEntry is DartEntry) { return _getDartScanData2(source, SourceEntry.LINE_INFO, null); } } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute ${SourceEntry.LINE_INFO}", new CaughtException(exception, stackTrace)); } return null; } @override CompilationUnit computeResolvableCompilationUnit(Source source) { DartEntry dartEntry = _getReadableDartEntry(source); if (dartEntry == null) { throw new AnalysisException( "computeResolvableCompilationUnit for non-Dart: ${source.fullName}"); } dartEntry = _cacheDartParseData(source, dartEntry, DartEntry.PARSED_UNIT); CompilationUnit unit = dartEntry.resolvableCompilationUnit; if (unit == null) { throw new AnalysisException( "Internal error: computeResolvableCompilationUnit could not parse ${source.fullName}", new CaughtException(dartEntry.exception, null)); } return unit; } @override CancelableFuture computeResolvedCompilationUnitAsync( Source unitSource, Source librarySource) { return new _AnalysisFutureHelper(this).computeAsync( unitSource, (SourceEntry sourceEntry) { if (sourceEntry is DartEntry) { if (sourceEntry.getStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource) == CacheState.ERROR) { throw sourceEntry.exception; } return sourceEntry.getValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource); } throw new AnalysisNotScheduledError(); }); } /** * Create an analysis cache based on the given source [factory]. */ AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { if (factory == null) { return new AnalysisCache([_privatePartition]); } DartSdk sdk = factory.dartSdk; if (sdk == null) { return new AnalysisCache([_privatePartition]); } return new AnalysisCache([ AnalysisEngine.instance.partitionManager.forSdk(sdk), _privatePartition ]); } @override void dispose() { _disposed = true; for (List pendingFutures in _pendingFutureSources.values) { for (PendingFuture pendingFuture in pendingFutures) { pendingFuture.forciblyComplete(); } } _pendingFutureSources.clear(); } @override List ensureResolvedDartUnits(Source unitSource) { SourceEntry sourceEntry = _cache.get(unitSource); if (sourceEntry is! DartEntry) { return null; } DartEntry dartEntry = sourceEntry; // Check every library. List units = []; List containingLibraries = dartEntry.containingLibraries; for (Source librarySource in containingLibraries) { CompilationUnit unit = dartEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource); if (unit == null) { units = null; break; } units.add(unit); } // Invalidate the flushed RESOLVED_UNIT to force it eventually. if (units == null) { bool shouldBeScheduled = false; for (Source librarySource in containingLibraries) { if (dartEntry.getStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource) == CacheState.FLUSHED) { dartEntry.setStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource, CacheState.INVALID); shouldBeScheduled = true; } } if (shouldBeScheduled) { _workManager.add(unitSource, SourcePriority.UNKNOWN); } // We cannot provide resolved units right now, // but the future analysis will. return null; } // done return units; } @override bool exists(Source source) { if (source == null) { return false; } if (_contentCache.getContents(source) != null) { return true; } return source.exists(); } Element findElementById(int id) { _ElementByIdFinder finder = new _ElementByIdFinder(id); try { MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { SourceEntry sourceEntry = iterator.value; if (sourceEntry.kind == SourceKind.LIBRARY) { DartEntry dartEntry = sourceEntry; LibraryElement library = dartEntry.getValue(DartEntry.ELEMENT); if (library != null) { library.accept(finder); } } } } on _ElementByIdFinderException { return finder.result; } return null; } @override CompilationUnitElement getCompilationUnitElement( Source unitSource, Source librarySource) { LibraryElement libraryElement = getLibraryElement(librarySource); if (libraryElement != null) { // try defining unit CompilationUnitElement definingUnit = libraryElement.definingCompilationUnit; if (definingUnit.source == unitSource) { return definingUnit; } // try parts for (CompilationUnitElement partUnit in libraryElement.parts) { if (partUnit.source == unitSource) { return partUnit; } } } return null; } @override TimestampedData getContents(Source source) { String contents = _contentCache.getContents(source); if (contents != null) { return new TimestampedData( _contentCache.getModificationStamp(source), contents); } return source.contents; } @override InternalAnalysisContext getContextFor(Source source) { InternalAnalysisContext context = _cache.getContextFor(source); return context == null ? this : context; } @override Element getElement(ElementLocation location) { // TODO(brianwilkerson) This should not be a "get" method. try { List components = location.components; Source source = _computeSourceFromEncoding(components[0]); String sourceName = source.shortName; if (AnalysisEngine.isDartFileName(sourceName)) { ElementImpl element = computeLibraryElement(source) as ElementImpl; for (int i = 1; i < components.length; i++) { if (element == null) { return null; } element = element.getChild(components[i]); } return element; } if (AnalysisEngine.isHtmlFileName(sourceName)) { return computeHtmlElement(source); } } catch (exception) { // If the location cannot be decoded for some reason then the underlying // cause should have been logged already and we can fall though to return // null. } return null; } @override AnalysisErrorInfo getErrors(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; return new AnalysisErrorInfoImpl( dartEntry.allErrors, dartEntry.getValue(SourceEntry.LINE_INFO)); } else if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; return new AnalysisErrorInfoImpl( htmlEntry.allErrors, htmlEntry.getValue(SourceEntry.LINE_INFO)); } return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null); } @override HtmlElement getHtmlElement(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry is HtmlEntry) { return sourceEntry.getValue(HtmlEntry.ELEMENT); } return null; } @override List getHtmlFilesReferencing(Source source) { SourceKind sourceKind = getKindOf(source); if (sourceKind == null) { return Source.EMPTY_ARRAY; } List htmlSources = new List(); while (true) { if (sourceKind == SourceKind.PART) { List librarySources = getLibrariesContaining(source); MapIterator partIterator = _cache.iterator(); while (partIterator.moveNext()) { SourceEntry sourceEntry = partIterator.value; if (sourceEntry.kind == SourceKind.HTML) { List referencedLibraries = (sourceEntry as HtmlEntry) .getValue(HtmlEntry.REFERENCED_LIBRARIES); if (_containsAny(referencedLibraries, librarySources)) { htmlSources.add(partIterator.key); } } } } else { MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { SourceEntry sourceEntry = iterator.value; if (sourceEntry.kind == SourceKind.HTML) { List referencedLibraries = (sourceEntry as HtmlEntry) .getValue(HtmlEntry.REFERENCED_LIBRARIES); if (_contains(referencedLibraries, source)) { htmlSources.add(iterator.key); } } } } break; } if (htmlSources.isEmpty) { return Source.EMPTY_ARRAY; } return htmlSources; } @override SourceKind getKindOf(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry == null) { return SourceKind.UNKNOWN; } return sourceEntry.kind; } @override List getLibrariesContaining(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry is DartEntry) { return sourceEntry.containingLibraries; } return Source.EMPTY_ARRAY; } @override List getLibrariesDependingOn(Source librarySource) { List dependentLibraries = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { SourceEntry sourceEntry = iterator.value; if (sourceEntry.kind == SourceKind.LIBRARY) { if (_contains( (sourceEntry as DartEntry).getValue(DartEntry.EXPORTED_LIBRARIES), librarySource)) { dependentLibraries.add(iterator.key); } if (_contains( (sourceEntry as DartEntry).getValue(DartEntry.IMPORTED_LIBRARIES), librarySource)) { dependentLibraries.add(iterator.key); } } } if (dependentLibraries.isEmpty) { return Source.EMPTY_ARRAY; } return dependentLibraries; } @override List getLibrariesReferencedFromHtml(Source htmlSource) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource); if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; return htmlEntry.getValue(HtmlEntry.REFERENCED_LIBRARIES); } return Source.EMPTY_ARRAY; } @override LibraryElement getLibraryElement(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry is DartEntry) { return sourceEntry.getValue(DartEntry.ELEMENT); } return null; } @override LineInfo getLineInfo(Source source) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); if (sourceEntry != null) { return sourceEntry.getValue(SourceEntry.LINE_INFO); } return null; } @override int getModificationStamp(Source source) { int stamp = _contentCache.getModificationStamp(source); if (stamp != null) { return stamp; } return source.modificationStamp; } @override Namespace getPublicNamespace(LibraryElement library) { // TODO(brianwilkerson) Rename this to not start with 'get'. // Note that this is not part of the API of the interface. Source source = library.definingCompilationUnit.source; DartEntry dartEntry = _getReadableDartEntry(source); if (dartEntry == null) { return null; } Namespace namespace = null; if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) { namespace = dartEntry.getValue(DartEntry.PUBLIC_NAMESPACE); } if (namespace == null) { NamespaceBuilder builder = new NamespaceBuilder(); namespace = builder.createPublicNamespaceForLibrary(library); if (dartEntry == null) { AnalysisEngine.instance.logger.logError( "Could not compute the public namespace for ${library.source.fullName}", new CaughtException(new AnalysisException( "A Dart file became a non-Dart file: ${source.fullName}"), null)); return null; } if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) { dartEntry.setValue(DartEntry.PUBLIC_NAMESPACE, namespace); } } return namespace; } /** * Return the cache entry associated with the given [source], or `null` if * there is no entry associated with the source. */ SourceEntry getReadableSourceEntryOrNull(Source source) => _cache.get(source); @override CompilationUnit getResolvedCompilationUnit( Source unitSource, LibraryElement library) { if (library == null) { return null; } return getResolvedCompilationUnit2(unitSource, library.source); } @override CompilationUnit getResolvedCompilationUnit2( Source unitSource, Source librarySource) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(unitSource); if (sourceEntry is DartEntry) { return sourceEntry.getValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource); } return null; } @override ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) { SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource); if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT); } return null; } @override List getSourcesWithFullName(String path) { List sources = []; MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { if (iterator.key.fullName == path) { sources.add(iterator.key); } } return sources; } @override bool handleContentsChanged( Source source, String originalContents, String newContents, bool notify) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry == null) { return false; } bool changed = newContents != originalContents; if (newContents != null) { if (newContents != originalContents) { _incrementalAnalysisCache = IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); if (!analysisOptions.incremental || !_tryPoorMansIncrementalResolution(source, newContents)) { _sourceChanged(source); } sourceEntry.modificationTime = _contentCache.getModificationStamp(source); sourceEntry.setValue(SourceEntry.CONTENT, newContents); } else { sourceEntry.modificationTime = _contentCache.getModificationStamp(source); } } else if (originalContents != null) { _incrementalAnalysisCache = IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); changed = newContents != originalContents; // We are removing the overlay for the file, check if the file's // contents is the same as it was in the overlay. try { TimestampedData fileContents = getContents(source); String fileContentsData = fileContents.data; if (fileContentsData == originalContents) { sourceEntry.setValue(SourceEntry.CONTENT, fileContentsData); sourceEntry.modificationTime = fileContents.modificationTime; changed = false; } } catch (e) {} // If not the same content (e.g. the file is being closed without save), // then force analysis. if (changed) { _sourceChanged(source); } } if (notify && changed) { _onSourcesChangedController .add(new SourcesChangedEvent.changedContent(source, newContents)); } return changed; } /** * Invalidates hints in the given [librarySource] and included parts. */ void invalidateLibraryHints(Source librarySource) { SourceEntry sourceEntry = _cache.get(librarySource); if (sourceEntry is! DartEntry) { return; } DartEntry dartEntry = sourceEntry; // Prepare sources to invalidate hints in. List sources = [librarySource]; sources.addAll(dartEntry.getValue(DartEntry.INCLUDED_PARTS)); // Invalidate hints. for (Source source in sources) { DartEntry dartEntry = _cache.get(source); if (dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource) == CacheState.VALID) { dartEntry.setStateInLibrary( DartEntry.HINTS, librarySource, CacheState.INVALID); } } } @override bool isClientLibrary(Source librarySource) { SourceEntry sourceEntry = _getReadableSourceEntry(librarySource); if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; return dartEntry.getValue(DartEntry.IS_CLIENT) && dartEntry.getValue(DartEntry.IS_LAUNCHABLE); } return false; } @override bool isServerLibrary(Source librarySource) { SourceEntry sourceEntry = _getReadableSourceEntry(librarySource); if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; return !dartEntry.getValue(DartEntry.IS_CLIENT) && dartEntry.getValue(DartEntry.IS_LAUNCHABLE); } return false; } @override CompilationUnit parseCompilationUnit(Source source) => _getDartParseData2(source, DartEntry.PARSED_UNIT, null); @override ht.HtmlUnit parseHtmlUnit(Source source) => _getHtmlParseData(source, HtmlEntry.PARSED_UNIT, null); @override AnalysisResult performAnalysisTask() { if (_TRACE_PERFORM_TASK) { print("----------------------------------------"); } return PerformanceStatistics.performAnaysis.makeCurrentWhile(() { int getStart = JavaSystem.currentTimeMillis(); AnalysisTask task = PerformanceStatistics.nextTask .makeCurrentWhile(() => nextAnalysisTask); int getEnd = JavaSystem.currentTimeMillis(); if (task == null && _validateCacheConsistency()) { task = nextAnalysisTask; } if (task == null) { _validateLastIncrementalResolutionResult(); if (_performAnalysisTaskStopwatch != null) { AnalysisEngine.instance.instrumentationService.logPerformance( AnalysisPerformanceKind.FULL, _performAnalysisTaskStopwatch, 'context_id=$_id'); _performAnalysisTaskStopwatch = null; } return new AnalysisResult( _getChangeNotices(true), getEnd - getStart, null, -1); } if (_performAnalysisTaskStopwatch == null) { _performAnalysisTaskStopwatch = new Stopwatch()..start(); } String taskDescription = task.toString(); _notifyAboutToPerformTask(taskDescription); if (_TRACE_PERFORM_TASK) { print(taskDescription); } int performStart = JavaSystem.currentTimeMillis(); try { task.perform(_resultRecorder); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not perform analysis task: $taskDescription", new CaughtException(exception, stackTrace)); } on AnalysisException catch (exception, stackTrace) { if (exception.cause is! JavaIOException) { AnalysisEngine.instance.logger.logError( "Internal error while performing the task: $task", new CaughtException(exception, stackTrace)); } } int performEnd = JavaSystem.currentTimeMillis(); List notices = _getChangeNotices(false); int noticeCount = notices.length; for (int i = 0; i < noticeCount; i++) { ChangeNotice notice = notices[i]; Source source = notice.source; // TODO(brianwilkerson) Figure out whether the compilation unit is // always resolved, or whether we need to decide whether to invoke the // "parsed" or "resolved" method. This might be better done when // recording task results in order to reduce the chance of errors. // if (notice.getCompilationUnit() != null) { // notifyResolvedDart(source, notice.getCompilationUnit()); // } else if (notice.getHtmlUnit() != null) { // notifyResolvedHtml(source, notice.getHtmlUnit()); // } _notifyErrors(source, notice.errors, notice.lineInfo); } return new AnalysisResult(notices, getEnd - getStart, task.runtimeType.toString(), performEnd - performStart); }); } @override void recordLibraryElements(Map elementMap) { Source htmlSource = _sourceFactory.forUri(DartSdk.DART_HTML); elementMap.forEach((Source librarySource, LibraryElement library) { // // Cache the element in the library's info. // DartEntry dartEntry = _getReadableDartEntry(librarySource); if (dartEntry != null) { _recordElementData(dartEntry, library, library.source, htmlSource); dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); dartEntry.setValue(SourceEntry.LINE_INFO, new LineInfo([0])); // DartEntry.ELEMENT - set in recordElementData dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_ARRAY); dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_ARRAY); dartEntry.setValue(DartEntry.INCLUDED_PARTS, Source.EMPTY_ARRAY); // DartEntry.IS_CLIENT - set in recordElementData // DartEntry.IS_LAUNCHABLE - set in recordElementData dartEntry.setValue(DartEntry.PARSE_ERRORS, AnalysisError.NO_ERRORS); dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); dartEntry.setState(DartEntry.PUBLIC_NAMESPACE, CacheState.FLUSHED); dartEntry.setValue(DartEntry.SCAN_ERRORS, AnalysisError.NO_ERRORS); dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED); dartEntry.setValueInLibrary(DartEntry.RESOLUTION_ERRORS, librarySource, AnalysisError.NO_ERRORS); dartEntry.setStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource, CacheState.FLUSHED); dartEntry.setValueInLibrary(DartEntry.VERIFICATION_ERRORS, librarySource, AnalysisError.NO_ERRORS); dartEntry.setValueInLibrary( DartEntry.HINTS, librarySource, AnalysisError.NO_ERRORS); dartEntry.setValueInLibrary( DartEntry.LINTS, librarySource, AnalysisError.NO_ERRORS); } }); } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry recordResolveDartLibraryCycleTaskResults( ResolveDartLibraryCycleTask task) { LibraryResolver2 resolver = task.libraryResolver; CaughtException thrownException = task.exception; Source unitSource = task.unitSource; DartEntry unitEntry = _getReadableDartEntry(unitSource); if (resolver != null) { // // The resolver should only be null if an exception was thrown before (or // while) it was being created. // List resolvedLibraries = resolver.resolvedLibraries; if (resolvedLibraries == null) { // // The resolved libraries should only be null if an exception was thrown // during resolution. // if (thrownException == null) { var message = "In recordResolveDartLibraryCycleTaskResults, " "resolvedLibraries was null and there was no thrown exception"; unitEntry.recordResolutionError( new CaughtException(new AnalysisException(message), null)); } else { unitEntry.recordResolutionError(thrownException); } _cache.remove(unitSource); if (thrownException != null) { throw new AnalysisException('', thrownException); } return unitEntry; } Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML); RecordingErrorListener errorListener = resolver.errorListener; for (ResolvableLibrary library in resolvedLibraries) { Source librarySource = library.librarySource; for (Source source in library.compilationUnitSources) { CompilationUnit unit = library.getAST(source); List errors = errorListener.getErrorsForSource(source); LineInfo lineInfo = getLineInfo(source); DartEntry dartEntry = _cache.get(source); if (thrownException == null) { dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); dartEntry.setValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource, unit); dartEntry.setValueInLibrary( DartEntry.RESOLUTION_ERRORS, librarySource, errors); if (source == librarySource) { _recordElementData( dartEntry, library.libraryElement, librarySource, htmlSource); } _cache.storedAst(source); } else { dartEntry.recordResolutionErrorInLibrary( librarySource, thrownException); } if (source != librarySource) { _workManager.add(source, SourcePriority.PRIORITY_PART); } ChangeNoticeImpl notice = _getNotice(source); notice.resolvedDartUnit = unit; notice.setErrors(dartEntry.allErrors, lineInfo); } } } if (thrownException != null) { throw new AnalysisException('', thrownException); } return unitEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry recordResolveDartLibraryTaskResults(ResolveDartLibraryTask task) { LibraryResolver resolver = task.libraryResolver; CaughtException thrownException = task.exception; Source unitSource = task.unitSource; DartEntry unitEntry = _getReadableDartEntry(unitSource); if (resolver != null) { // // The resolver should only be null if an exception was thrown before (or // while) it was being created. // Set resolvedLibraries = resolver.resolvedLibraries; if (resolvedLibraries == null) { // // The resolved libraries should only be null if an exception was thrown // during resolution. // if (thrownException == null) { String message = "In recordResolveDartLibraryTaskResults, " "resolvedLibraries was null and there was no thrown exception"; unitEntry.recordResolutionError( new CaughtException(new AnalysisException(message), null)); } else { unitEntry.recordResolutionError(thrownException); } _cache.remove(unitSource); if (thrownException != null) { throw new AnalysisException('', thrownException); } return unitEntry; } Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML); RecordingErrorListener errorListener = resolver.errorListener; for (Library library in resolvedLibraries) { Source librarySource = library.librarySource; for (Source source in library.compilationUnitSources) { CompilationUnit unit = library.getAST(source); List errors = errorListener.getErrorsForSource(source); LineInfo lineInfo = getLineInfo(source); DartEntry dartEntry = _cache.get(source); if (thrownException == null) { dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo); dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); dartEntry.setValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource, unit); dartEntry.setValueInLibrary( DartEntry.RESOLUTION_ERRORS, librarySource, errors); if (source == librarySource) { _recordElementData( dartEntry, library.libraryElement, librarySource, htmlSource); } _cache.storedAst(source); } else { dartEntry.recordResolutionErrorInLibrary( librarySource, thrownException); _cache.remove(source); } if (source != librarySource) { _workManager.add(source, SourcePriority.PRIORITY_PART); } ChangeNoticeImpl notice = _getNotice(source); notice.resolvedDartUnit = unit; notice.setErrors(dartEntry.allErrors, lineInfo); } } } if (thrownException != null) { throw new AnalysisException('', thrownException); } return unitEntry; } @override void removeListener(AnalysisListener listener) { _listeners.remove(listener); } @override CompilationUnit resolveCompilationUnit( Source unitSource, LibraryElement library) { if (library == null) { return null; } return resolveCompilationUnit2(unitSource, library.source); } @override CompilationUnit resolveCompilationUnit2( Source unitSource, Source librarySource) => _getDartResolutionData2( unitSource, librarySource, DartEntry.RESOLVED_UNIT, null); @override ht.HtmlUnit resolveHtmlUnit(Source htmlSource) { computeHtmlElement(htmlSource); return parseHtmlUnit(htmlSource); } @override void setChangedContents(Source source, String contents, int offset, int oldLength, int newLength) { if (_contentRangeChanged(source, contents, offset, oldLength, newLength)) { _onSourcesChangedController.add(new SourcesChangedEvent.changedRange( source, contents, offset, oldLength, newLength)); } } @override void setContents(Source source, String contents) { _contentsChanged(source, contents, true); } @override void visitCacheItems(void callback(Source source, SourceEntry dartEntry, DataDescriptor rowDesc, CacheState state)) { bool hintsEnabled = _options.hint; bool lintsEnabled = _options.lint; MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { Source source = iterator.key; SourceEntry sourceEntry = iterator.value; for (DataDescriptor descriptor in sourceEntry.descriptors) { if (descriptor == DartEntry.SOURCE_KIND) { // The source kind is always valid, so the state isn't interesting. continue; } else if (descriptor == DartEntry.CONTAINING_LIBRARIES) { // The list of containing libraries is always valid, so the state // isn't interesting. continue; } else if (descriptor == DartEntry.PUBLIC_NAMESPACE) { // The public namespace isn't computed by performAnalysisTask() // and therefore isn't interesting. continue; } else if (descriptor == HtmlEntry.HINTS) { // We are not currently recording any hints related to HTML. continue; } callback( source, sourceEntry, descriptor, sourceEntry.getState(descriptor)); } if (sourceEntry is DartEntry) { // get library-specific values List librarySources = getLibrariesContaining(source); for (Source librarySource in librarySources) { for (DataDescriptor descriptor in sourceEntry.libraryDescriptors) { if (descriptor == DartEntry.BUILT_ELEMENT || descriptor == DartEntry.BUILT_UNIT) { // These values are not currently being computed, so their state // is not interesting. continue; } else if (!sourceEntry.explicitlyAdded && !_generateImplicitErrors && (descriptor == DartEntry.VERIFICATION_ERRORS || descriptor == DartEntry.HINTS || descriptor == DartEntry.LINTS)) { continue; } else if (source.isInSystemLibrary && !_generateSdkErrors && (descriptor == DartEntry.VERIFICATION_ERRORS || descriptor == DartEntry.HINTS || descriptor == DartEntry.LINTS)) { continue; } else if (!hintsEnabled && descriptor == DartEntry.HINTS) { continue; } else if (!lintsEnabled && descriptor == DartEntry.LINTS) { continue; } callback(librarySource, sourceEntry, descriptor, sourceEntry.getStateInLibrary(descriptor, librarySource)); } } } } } /** * Visit all entries of the content cache. */ void visitContentCache(ContentCacheVisitor visitor) { _contentCache.accept(visitor); } /** * Record that we have accessed the AST structure associated with the given * [source]. At the moment, there is no differentiation between the parsed and * resolved forms of the AST. */ void _accessedAst(Source source) { _cache.accessedAst(source); } /** * Add all of the sources contained in the given source [container] to the * given list of [sources]. */ void _addSourcesInContainer(List sources, SourceContainer container) { MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { Source source = iterator.key; if (container.contains(source)) { sources.add(source); } } } /** * Given the [unitSource] of a Dart file and the [librarySource] of the * library that contains it, return a cache entry in which the state of the * data represented by the given [descriptor] is either [CacheState.VALID] or * [CacheState.ERROR]. This method assumes that the data can be produced by * generating hints for the library if the data is not already cached. The * [dartEntry] is the cache entry associated with the Dart file. * * Throws an [AnalysisException] if data could not be returned because the * source could not be parsed. */ DartEntry _cacheDartHintData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. // Unless the modification date of the source continues to change, // this loop will eventually terminate. // DartEntry libraryEntry = _getReadableDartEntry(librarySource); libraryEntry = _cacheDartResolutionData( librarySource, librarySource, libraryEntry, DartEntry.ELEMENT); LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); CompilationUnitElement definingUnit = libraryElement.definingCompilationUnit; List parts = libraryElement.parts; List> units = new List(parts.length + 1); units[0] = _getResolvedUnit(definingUnit, librarySource); if (units[0] == null) { Source source = definingUnit.source; units[0] = new TimestampedData( getModificationStamp(source), resolveCompilationUnit(source, libraryElement)); } for (int i = 0; i < parts.length; i++) { units[i + 1] = _getResolvedUnit(parts[i], librarySource); if (units[i + 1] == null) { Source source = parts[i].source; units[i + 1] = new TimestampedData( getModificationStamp(source), resolveCompilationUnit(source, libraryElement)); } } dartEntry = new GenerateDartHintsTask( this, units, getLibraryElement(librarySource)) .perform(_resultRecorder) as DartEntry; state = dartEntry.getStateInLibrary(descriptor, librarySource); } return dartEntry; } /** * Given a source for a Dart file and the library that contains it, return a * cache entry in which the state of the data represented by the given * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method * assumes that the data can be produced by generating lints for the library * if the data is not already cached. * * Note: This method cannot be used in an async environment. */ DartEntry _cacheDartLintData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. // Unless the modification date of the source continues to change, // this loop will eventually terminate. // DartEntry libraryEntry = _getReadableDartEntry(librarySource); libraryEntry = _cacheDartResolutionData( librarySource, librarySource, libraryEntry, DartEntry.ELEMENT); LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); CompilationUnitElement definingUnit = libraryElement.definingCompilationUnit; List parts = libraryElement.parts; List> units = new List(parts.length + 1); units[0] = _getResolvedUnit(definingUnit, librarySource); if (units[0] == null) { Source source = definingUnit.source; units[0] = new TimestampedData( getModificationStamp(source), resolveCompilationUnit(source, libraryElement)); } for (int i = 0; i < parts.length; i++) { units[i + 1] = _getResolvedUnit(parts[i], librarySource); if (units[i + 1] == null) { Source source = parts[i].source; units[i + 1] = new TimestampedData( getModificationStamp(source), resolveCompilationUnit(source, libraryElement)); } } //TODO(pquitslund): revisit if we need all units or whether one will do dartEntry = new GenerateDartLintsTask( this, units, getLibraryElement(librarySource)) .perform(_resultRecorder) as DartEntry; state = dartEntry.getStateInLibrary(descriptor, librarySource); } return dartEntry; } /** * Given a source for a Dart file, return a cache entry in which the state of * the data represented by the given descriptor is either [CacheState.VALID] * or [CacheState.ERROR]. This method assumes that the data can be produced by * parsing the source if it is not already cached. * * Note: This method cannot be used in an async environment. */ DartEntry _cacheDartParseData( Source source, DartEntry dartEntry, DataDescriptor descriptor) { if (identical(descriptor, DartEntry.PARSED_UNIT)) { if (dartEntry.hasResolvableCompilationUnit) { return dartEntry; } } // // Check to see whether we already have the information being requested. // CacheState state = dartEntry.getState(descriptor); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // dartEntry = _cacheDartScanData(source, dartEntry, DartEntry.TOKEN_STREAM); dartEntry = new ParseDartTask(this, source, dartEntry.getValue(DartEntry.TOKEN_STREAM), dartEntry.getValue(SourceEntry.LINE_INFO)) .perform(_resultRecorder) as DartEntry; state = dartEntry.getState(descriptor); } return dartEntry; } /** * Given a source for a Dart file and the library that contains it, return a * cache entry in which the state of the data represented by the given * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method * assumes that the data can be produced by resolving the source in the * context of the library if it is not already cached. * * Note: This method cannot be used in an async environment. */ DartEntry _cacheDartResolutionData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = (identical(descriptor, DartEntry.ELEMENT)) ? dartEntry.getState(descriptor) : dartEntry.getStateInLibrary(descriptor, librarySource); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // // TODO(brianwilkerson) As an optimization, if we already have the // element model for the library we can use ResolveDartUnitTask to produce // the resolved AST structure much faster. dartEntry = new ResolveDartLibraryTask(this, unitSource, librarySource) .perform(_resultRecorder) as DartEntry; state = (identical(descriptor, DartEntry.ELEMENT)) ? dartEntry.getState(descriptor) : dartEntry.getStateInLibrary(descriptor, librarySource); } return dartEntry; } /** * Given a source for a Dart file, return a cache entry in which the state of * the data represented by the given descriptor is either [CacheState.VALID] * or [CacheState.ERROR]. This method assumes that the data can be produced by * scanning the source if it is not already cached. * * Note: This method cannot be used in an async environment. */ DartEntry _cacheDartScanData( Source source, DartEntry dartEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = dartEntry.getState(descriptor); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // try { if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { dartEntry = new GetContentTask(this, source) .perform(_resultRecorder) as DartEntry; } dartEntry = new ScanDartTask( this, source, dartEntry.getValue(SourceEntry.CONTENT)) .perform(_resultRecorder) as DartEntry; } on AnalysisException catch (exception) { throw exception; } catch (exception, stackTrace) { throw new AnalysisException( "Exception", new CaughtException(exception, stackTrace)); } state = dartEntry.getState(descriptor); } return dartEntry; } /** * Given a source for a Dart file and the library that contains it, return a * cache entry in which the state of the data represented by the given * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method * assumes that the data can be produced by verifying the source in the given * library if the data is not already cached. * * Note: This method cannot be used in an async environment. */ DartEntry _cacheDartVerificationData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // LibraryElement library = computeLibraryElement(librarySource); CompilationUnit unit = resolveCompilationUnit(unitSource, library); if (unit == null) { throw new AnalysisException( "Could not resolve compilation unit ${unitSource.fullName} in ${librarySource.fullName}"); } dartEntry = new GenerateDartErrorsTask(this, unitSource, unit, library) .perform(_resultRecorder) as DartEntry; state = dartEntry.getStateInLibrary(descriptor, librarySource); } return dartEntry; } /** * Given a source for an HTML file, return a cache entry in which all of the * data represented by the state of the given descriptors is either * [CacheState.VALID] or [CacheState.ERROR]. This method assumes that the data * can be produced by parsing the source if it is not already cached. * * Note: This method cannot be used in an async environment. */ HtmlEntry _cacheHtmlParseData( Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { if (identical(descriptor, HtmlEntry.PARSED_UNIT)) { ht.HtmlUnit unit = htmlEntry.anyParsedUnit; if (unit != null) { return htmlEntry; } } // // Check to see whether we already have the information being requested. // CacheState state = htmlEntry.getState(descriptor); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // try { if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { htmlEntry = new GetContentTask(this, source) .perform(_resultRecorder) as HtmlEntry; } htmlEntry = new ParseHtmlTask( this, source, htmlEntry.getValue(SourceEntry.CONTENT)) .perform(_resultRecorder) as HtmlEntry; } on AnalysisException catch (exception) { throw exception; } catch (exception, stackTrace) { throw new AnalysisException( "Exception", new CaughtException(exception, stackTrace)); } state = htmlEntry.getState(descriptor); } return htmlEntry; } /** * Given a source for an HTML file, return a cache entry in which the state of * the data represented by the given descriptor is either [CacheState.VALID] * or [CacheState.ERROR]. This method assumes that the data can be produced by * resolving the source if it is not already cached. * * Note: This method cannot be used in an async environment. */ HtmlEntry _cacheHtmlResolutionData( Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { // // Check to see whether we already have the information being requested. // CacheState state = htmlEntry.getState(descriptor); while (state != CacheState.ERROR && state != CacheState.VALID) { // // If not, compute the information. Unless the modification date of the // source continues to change, this loop will eventually terminate. // htmlEntry = _cacheHtmlParseData(source, htmlEntry, HtmlEntry.PARSED_UNIT); htmlEntry = new ResolveHtmlTask(this, source, htmlEntry.modificationTime, htmlEntry.getValue(HtmlEntry.PARSED_UNIT)) .perform(_resultRecorder) as HtmlEntry; state = htmlEntry.getState(descriptor); } return htmlEntry; } /** * Remove the given [pendingFuture] from [_pendingFutureSources], since the * client has indicated its computation is not needed anymore. */ void _cancelFuture(PendingFuture pendingFuture) { List pendingFutures = _pendingFutureSources[pendingFuture.source]; if (pendingFutures != null) { pendingFutures.remove(pendingFuture); if (pendingFutures.isEmpty) { _pendingFutureSources.remove(pendingFuture.source); } } } /** * Compute the transitive closure of all libraries that depend on the given * [library] by adding such libraries to the given collection of * [librariesToInvalidate]. */ void _computeAllLibrariesDependingOn( Source library, HashSet librariesToInvalidate) { if (librariesToInvalidate.add(library)) { for (Source dependentLibrary in getLibrariesDependingOn(library)) { _computeAllLibrariesDependingOn( dependentLibrary, librariesToInvalidate); } } } /** * Return the priority that should be used when the source associated with * the given [dartEntry] is added to the work manager. */ SourcePriority _computePriority(DartEntry dartEntry) { SourceKind kind = dartEntry.kind; if (kind == SourceKind.LIBRARY) { return SourcePriority.LIBRARY; } else if (kind == SourceKind.PART) { return SourcePriority.NORMAL_PART; } return SourcePriority.UNKNOWN; } /** * Given the encoded form of a source ([encoding]), use the source factory to * reconstitute the original source. */ Source _computeSourceFromEncoding(String encoding) => _sourceFactory.fromEncoding(encoding); /** * Return `true` if the given list of [sources] contains the given * [targetSource]. */ bool _contains(List sources, Source targetSource) { for (Source source in sources) { if (source == targetSource) { return true; } } return false; } /** * Return `true` if the given list of [sources] contains any of the given * [targetSources]. */ bool _containsAny(List sources, List targetSources) { for (Source targetSource in targetSources) { if (_contains(sources, targetSource)) { return true; } } return false; } /** * Set the contents of the given [source] to the given [contents] and mark the * source as having changed. The additional [offset], [oldLength] and * [newLength] information is used by the context to determine what reanalysis * is necessary. The method [setChangedContents] triggers a source changed * event where as this method does not. */ bool _contentRangeChanged(Source source, String contents, int offset, int oldLength, int newLength) { bool changed = false; String originalContents = _contentCache.setContents(source, contents); if (contents != null) { if (contents != originalContents) { if (_options.incremental) { _incrementalAnalysisCache = IncrementalAnalysisCache.update( _incrementalAnalysisCache, source, originalContents, contents, offset, oldLength, newLength, _getReadableSourceEntry(source)); } _sourceChanged(source); changed = true; SourceEntry sourceEntry = _cache.get(source); if (sourceEntry != null) { sourceEntry.modificationTime = _contentCache.getModificationStamp(source); sourceEntry.setValue(SourceEntry.CONTENT, contents); } } } else if (originalContents != null) { _incrementalAnalysisCache = IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); _sourceChanged(source); changed = true; } return changed; } /** * Set the contents of the given [source] to the given [contents] and mark the * source as having changed. This has the effect of overriding the default * contents of the source. If the contents are `null` the override is removed * so that the default contents will be returned. If [notify] is true, a * source changed event is triggered. */ void _contentsChanged(Source source, String contents, bool notify) { String originalContents = _contentCache.setContents(source, contents); handleContentsChanged(source, originalContents, contents, notify); } /** * Create a [GenerateDartErrorsTask] for the given [unitSource], marking the * verification errors as being in-process. The compilation unit and the * library can be the same if the compilation unit is the defining compilation * unit of the library. */ AnalysisContextImpl_TaskData _createGenerateDartErrorsTask(Source unitSource, DartEntry unitEntry, Source librarySource, DartEntry libraryEntry) { if (unitEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) != CacheState.VALID || libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { return _createResolveDartLibraryTask(librarySource, libraryEntry); } CompilationUnit unit = unitEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource); if (unit == null) { CaughtException exception = new CaughtException(new AnalysisException( "Entry has VALID state for RESOLVED_UNIT but null value for ${unitSource.fullName} in ${librarySource.fullName}"), null); AnalysisEngine.instance.logger.logInformation( exception.toString(), exception); unitEntry.recordResolutionError(exception); return new AnalysisContextImpl_TaskData(null, false); } LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); return new AnalysisContextImpl_TaskData( new GenerateDartErrorsTask(this, unitSource, unit, libraryElement), false); } /** * Create a [GenerateDartHintsTask] for the given [source], marking the hints * as being in-process. */ AnalysisContextImpl_TaskData _createGenerateDartHintsTask(Source source, DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) { if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { return _createResolveDartLibraryTask(librarySource, libraryEntry); } LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); CompilationUnitElement definingUnit = libraryElement.definingCompilationUnit; List parts = libraryElement.parts; List> units = new List(parts.length + 1); units[0] = _getResolvedUnit(definingUnit, librarySource); if (units[0] == null) { // TODO(brianwilkerson) We should return a ResolveDartUnitTask // (unless there are multiple ASTs that need to be resolved). return _createResolveDartLibraryTask(librarySource, libraryEntry); } for (int i = 0; i < parts.length; i++) { units[i + 1] = _getResolvedUnit(parts[i], librarySource); if (units[i + 1] == null) { // TODO(brianwilkerson) We should return a ResolveDartUnitTask // (unless there are multiple ASTs that need to be resolved). return _createResolveDartLibraryTask(librarySource, libraryEntry); } } return new AnalysisContextImpl_TaskData( new GenerateDartHintsTask(this, units, libraryElement), false); } /** * Create a [GenerateDartLintsTask] for the given [source], marking the lints * as being in-process. */ AnalysisContextImpl_TaskData _createGenerateDartLintsTask(Source source, DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) { if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { return _createResolveDartLibraryTask(librarySource, libraryEntry); } LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); CompilationUnitElement definingUnit = libraryElement.definingCompilationUnit; List parts = libraryElement.parts; List> units = new List(parts.length + 1); units[0] = _getResolvedUnit(definingUnit, librarySource); if (units[0] == null) { // TODO(brianwilkerson) We should return a ResolveDartUnitTask // (unless there are multiple ASTs that need to be resolved). return _createResolveDartLibraryTask(librarySource, libraryEntry); } for (int i = 0; i < parts.length; i++) { units[i + 1] = _getResolvedUnit(parts[i], librarySource); if (units[i + 1] == null) { // TODO(brianwilkerson) We should return a ResolveDartUnitTask // (unless there are multiple ASTs that need to be resolved). return _createResolveDartLibraryTask(librarySource, libraryEntry); } } //TODO(pquitslund): revisit if we need all units or whether one will do return new AnalysisContextImpl_TaskData( new GenerateDartLintsTask(this, units, libraryElement), false); } /** * Create a [GetContentTask] for the given [source], marking the content as * being in-process. */ AnalysisContextImpl_TaskData _createGetContentTask( Source source, SourceEntry sourceEntry) { return new AnalysisContextImpl_TaskData( new GetContentTask(this, source), false); } /** * Create a [ParseDartTask] for the given [source]. */ AnalysisContextImpl_TaskData _createParseDartTask( Source source, DartEntry dartEntry) { if (dartEntry.getState(DartEntry.TOKEN_STREAM) != CacheState.VALID || dartEntry.getState(SourceEntry.LINE_INFO) != CacheState.VALID) { return _createScanDartTask(source, dartEntry); } Token tokenStream = dartEntry.getValue(DartEntry.TOKEN_STREAM); dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED); return new AnalysisContextImpl_TaskData(new ParseDartTask(this, source, tokenStream, dartEntry.getValue(SourceEntry.LINE_INFO)), false); } /** * Create a [ParseHtmlTask] for the given [source]. */ AnalysisContextImpl_TaskData _createParseHtmlTask( Source source, HtmlEntry htmlEntry) { if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { return _createGetContentTask(source, htmlEntry); } String content = htmlEntry.getValue(SourceEntry.CONTENT); htmlEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); return new AnalysisContextImpl_TaskData( new ParseHtmlTask(this, source, content), false); } /** * Create a [ResolveDartLibraryTask] for the given [source], marking ? as * being in-process. */ AnalysisContextImpl_TaskData _createResolveDartLibraryTask( Source source, DartEntry dartEntry) { try { AnalysisContextImpl_CycleBuilder builder = new AnalysisContextImpl_CycleBuilder(this); PerformanceStatistics.cycles.makeCurrentWhile(() { builder.computeCycleContaining(source); }); AnalysisContextImpl_TaskData taskData = builder.taskData; if (taskData != null) { return taskData; } return new AnalysisContextImpl_TaskData(new ResolveDartLibraryCycleTask( this, source, source, builder.librariesInCycle), false); } on AnalysisException catch (exception, stackTrace) { dartEntry .recordResolutionError(new CaughtException(exception, stackTrace)); AnalysisEngine.instance.logger.logError( "Internal error trying to create a ResolveDartLibraryTask", new CaughtException(exception, stackTrace)); } return new AnalysisContextImpl_TaskData(null, false); } /** * Create a [ResolveHtmlTask] for the given [source], marking the resolved * unit as being in-process. */ AnalysisContextImpl_TaskData _createResolveHtmlTask( Source source, HtmlEntry htmlEntry) { if (htmlEntry.getState(HtmlEntry.PARSED_UNIT) != CacheState.VALID) { return _createParseHtmlTask(source, htmlEntry); } return new AnalysisContextImpl_TaskData(new ResolveHtmlTask(this, source, htmlEntry.modificationTime, htmlEntry.getValue(HtmlEntry.PARSED_UNIT)), false); } /** * Create a [ScanDartTask] for the given [source], marking the scan errors as * being in-process. */ AnalysisContextImpl_TaskData _createScanDartTask( Source source, DartEntry dartEntry) { if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { return _createGetContentTask(source, dartEntry); } String content = dartEntry.getValue(SourceEntry.CONTENT); dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); return new AnalysisContextImpl_TaskData( new ScanDartTask(this, source, content), false); } /** * Create a source entry for the given [source]. Return the source entry that * was created, or `null` if the source should not be tracked by this context. */ SourceEntry _createSourceEntry(Source source, bool explicitlyAdded) { String name = source.shortName; if (AnalysisEngine.isHtmlFileName(name)) { HtmlEntry htmlEntry = new HtmlEntry(); htmlEntry.modificationTime = getModificationStamp(source); htmlEntry.explicitlyAdded = explicitlyAdded; _cache.put(source, htmlEntry); return htmlEntry; } else { DartEntry dartEntry = new DartEntry(); dartEntry.modificationTime = getModificationStamp(source); dartEntry.explicitlyAdded = explicitlyAdded; _cache.put(source, dartEntry); return dartEntry; } } /** * Return a list containing all of the change notices that are waiting to be * returned. If there are no notices, then return either `null` or an empty * list, depending on the value of [nullIfEmpty]. */ List _getChangeNotices(bool nullIfEmpty) { if (_pendingNotices.isEmpty) { if (nullIfEmpty) { return null; } return ChangeNoticeImpl.EMPTY_ARRAY; } List notices = new List.from(_pendingNotices.values); _pendingNotices.clear(); return notices; } /** * Given a source for a Dart file and the library that contains it, return the * data represented by the given descriptor that is associated with that * source. This method assumes that the data can be produced by generating * hints for the library if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getDartHintData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartHintData(unitSource, librarySource, dartEntry, descriptor); if (identical(descriptor, DartEntry.ELEMENT)) { return dartEntry.getValue(descriptor); } return dartEntry.getValueInLibrary(descriptor, librarySource); } /** * Given a source for a Dart file and the library that contains it, return the * data represented by the given descriptor that is associated with that * source. This method assumes that the data can be produced by generating * lints for the library if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getDartLintData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartLintData(unitSource, librarySource, dartEntry, descriptor); if (identical(descriptor, DartEntry.ELEMENT)) { return dartEntry.getValue(descriptor); } return dartEntry.getValueInLibrary(descriptor, librarySource); } /** * Given a source for a Dart file, return the data represented by the given * descriptor that is associated with that source. This method assumes that * the data can be produced by parsing the source if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be parsed. * * Note: This method cannot be used in an async environment. */ Object _getDartParseData( Source source, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartParseData(source, dartEntry, descriptor); if (identical(descriptor, DartEntry.PARSED_UNIT)) { _accessedAst(source); return dartEntry.anyParsedCompilationUnit; } return dartEntry.getValue(descriptor); } /** * Given a source for a Dart file, return the data represented by the given * descriptor that is associated with that source, or the given default value * if the source is not a Dart file. This method assumes that the data can be * produced by parsing the source if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be parsed. * * Note: This method cannot be used in an async environment. */ Object _getDartParseData2( Source source, DataDescriptor descriptor, Object defaultValue) { DartEntry dartEntry = _getReadableDartEntry(source); if (dartEntry == null) { return defaultValue; } try { return _getDartParseData(source, dartEntry, descriptor); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute $descriptor", new CaughtException(exception, stackTrace)); return defaultValue; } } /** * Given a source for a Dart file and the library that contains it, return the * data represented by the given descriptor that is associated with that * source. This method assumes that the data can be produced by resolving the * source in the context of the library if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getDartResolutionData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartResolutionData( unitSource, librarySource, dartEntry, descriptor); if (identical(descriptor, DartEntry.ELEMENT)) { return dartEntry.getValue(descriptor); } else if (identical(descriptor, DartEntry.RESOLVED_UNIT)) { _accessedAst(unitSource); } return dartEntry.getValueInLibrary(descriptor, librarySource); } /** * Given a source for a Dart file and the library that contains it, return the * data represented by the given descriptor that is associated with that * source, or the given default value if the source is not a Dart file. This * method assumes that the data can be produced by resolving the source in the * context of the library if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getDartResolutionData2(Source unitSource, Source librarySource, DataDescriptor descriptor, Object defaultValue) { DartEntry dartEntry = _getReadableDartEntry(unitSource); if (dartEntry == null) { return defaultValue; } try { return _getDartResolutionData( unitSource, librarySource, dartEntry, descriptor); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute $descriptor", new CaughtException(exception, stackTrace)); return defaultValue; } } /** * Given a source for a Dart file, return the data represented by the given * descriptor that is associated with that source. This method assumes that * the data can be produced by scanning the source if it is not already * cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be scanned. * * Note: This method cannot be used in an async environment. */ Object _getDartScanData( Source source, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartScanData(source, dartEntry, descriptor); return dartEntry.getValue(descriptor); } /** * Given a source for a Dart file, return the data represented by the given * descriptor that is associated with that source, or the given default value * if the source is not a Dart file. This method assumes that the data can be * produced by scanning the source if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be scanned. * * Note: This method cannot be used in an async environment. */ Object _getDartScanData2( Source source, DataDescriptor descriptor, Object defaultValue) { DartEntry dartEntry = _getReadableDartEntry(source); if (dartEntry == null) { return defaultValue; } try { return _getDartScanData(source, dartEntry, descriptor); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute $descriptor", new CaughtException(exception, stackTrace)); return defaultValue; } } /** * Given a source for a Dart file and the library that contains it, return the * data represented by the given descriptor that is associated with that * source. This method assumes that the data can be produced by verifying the * source within the given library if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getDartVerificationData(Source unitSource, Source librarySource, DartEntry dartEntry, DataDescriptor descriptor) { dartEntry = _cacheDartVerificationData( unitSource, librarySource, dartEntry, descriptor); return dartEntry.getValueInLibrary(descriptor, librarySource); } /** * Given a source for an HTML file, return the data represented by the given * descriptor that is associated with that source, or the given default value * if the source is not an HTML file. This method assumes that the data can be * produced by parsing the source if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be parsed. * * Note: This method cannot be used in an async environment. */ Object _getHtmlParseData( Source source, DataDescriptor descriptor, Object defaultValue) { HtmlEntry htmlEntry = _getReadableHtmlEntry(source); if (htmlEntry == null) { return defaultValue; } htmlEntry = _cacheHtmlParseData(source, htmlEntry, descriptor); if (identical(descriptor, HtmlEntry.PARSED_UNIT)) { _accessedAst(source); return htmlEntry.anyParsedUnit; } return htmlEntry.getValue(descriptor); } /** * Given a source for an HTML file, return the data represented by the given * descriptor that is associated with that source, or the given default value * if the source is not an HTML file. This method assumes that the data can be * produced by resolving the source if it is not already cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getHtmlResolutionData( Source source, DataDescriptor descriptor, Object defaultValue) { HtmlEntry htmlEntry = _getReadableHtmlEntry(source); if (htmlEntry == null) { return defaultValue; } try { return _getHtmlResolutionData2(source, htmlEntry, descriptor); } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { AnalysisEngine.instance.logger.logInformation( "Could not compute $descriptor", new CaughtException(exception, stackTrace)); return defaultValue; } } /** * Given a source for an HTML file, return the data represented by the given * descriptor that is associated with that source. This method assumes that * the data can be produced by resolving the source if it is not already * cached. * * Throws an [AnalysisException] if data could not be returned because the * source could not be resolved. * * Note: This method cannot be used in an async environment. */ Object _getHtmlResolutionData2( Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { htmlEntry = _cacheHtmlResolutionData(source, htmlEntry, descriptor); if (identical(descriptor, HtmlEntry.RESOLVED_UNIT)) { _accessedAst(source); } return htmlEntry.getValue(descriptor); } /** * Look at the given [source] to see whether a task needs to be performed * related to it. Return the task that should be performed, or `null` if there * is no more work to be done for the source. */ AnalysisContextImpl_TaskData _getNextAnalysisTaskForSource(Source source, SourceEntry sourceEntry, bool isPriority, bool hintsEnabled, bool lintsEnabled) { // Refuse to generate tasks for html based files that are above 1500 KB if (_isTooBigHtmlSourceEntry(source, sourceEntry)) { // TODO (jwren) we still need to report an error of some kind back to the // client. return new AnalysisContextImpl_TaskData(null, false); } if (sourceEntry == null) { return new AnalysisContextImpl_TaskData(null, false); } CacheState contentState = sourceEntry.getState(SourceEntry.CONTENT); if (contentState == CacheState.INVALID) { return _createGetContentTask(source, sourceEntry); } else if (contentState == CacheState.IN_PROCESS) { // We are already in the process of getting the content. // There's nothing else we can do with this source until that's complete. return new AnalysisContextImpl_TaskData(null, true); } else if (contentState == CacheState.ERROR) { // We have done all of the analysis we can for this source because we // cannot get its content. return new AnalysisContextImpl_TaskData(null, false); } if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS); if (scanErrorsState == CacheState.INVALID || (isPriority && scanErrorsState == CacheState.FLUSHED)) { return _createScanDartTask(source, dartEntry); } CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS); if (parseErrorsState == CacheState.INVALID || (isPriority && parseErrorsState == CacheState.FLUSHED)) { return _createParseDartTask(source, dartEntry); } if (isPriority && parseErrorsState != CacheState.ERROR) { if (!dartEntry.hasResolvableCompilationUnit) { return _createParseDartTask(source, dartEntry); } } SourceKind kind = dartEntry.getValue(DartEntry.SOURCE_KIND); if (kind == SourceKind.UNKNOWN) { return _createParseDartTask(source, dartEntry); } else if (kind == SourceKind.LIBRARY) { CacheState elementState = dartEntry.getState(DartEntry.ELEMENT); if (elementState == CacheState.INVALID) { return _createResolveDartLibraryTask(source, dartEntry); } } List librariesContaining = dartEntry.containingLibraries; for (Source librarySource in librariesContaining) { SourceEntry librarySourceEntry = _cache.get(librarySource); if (librarySourceEntry is DartEntry) { DartEntry libraryEntry = librarySourceEntry; CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT); if (elementState == CacheState.INVALID || (isPriority && elementState == CacheState.FLUSHED)) { // return createResolveDartLibraryTask(librarySource, (DartEntry) libraryEntry); return new AnalysisContextImpl_TaskData( new ResolveDartLibraryTask(this, source, librarySource), false); } CacheState resolvedUnitState = dartEntry.getStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource); if (resolvedUnitState == CacheState.INVALID || (isPriority && resolvedUnitState == CacheState.FLUSHED)) { // // The commented out lines below are an optimization that doesn't // quite work yet. The problem is that if the source was not // resolved because it wasn't part of any library, then there won't // be any elements in the element model that we can use to resolve // it. // // LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); // if (libraryElement != null) { // return new ResolveDartUnitTask(this, source, libraryElement); // } // Possibly replace with: // return createResolveDartLibraryTask(librarySource, (DartEntry) libraryEntry); return new AnalysisContextImpl_TaskData( new ResolveDartLibraryTask(this, source, librarySource), false); } if (_shouldErrorsBeAnalyzed(source, dartEntry)) { CacheState verificationErrorsState = dartEntry.getStateInLibrary( DartEntry.VERIFICATION_ERRORS, librarySource); if (verificationErrorsState == CacheState.INVALID || (isPriority && verificationErrorsState == CacheState.FLUSHED)) { return _createGenerateDartErrorsTask( source, dartEntry, librarySource, libraryEntry); } if (hintsEnabled) { CacheState hintsState = dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource); if (hintsState == CacheState.INVALID || (isPriority && hintsState == CacheState.FLUSHED)) { return _createGenerateDartHintsTask( source, dartEntry, librarySource, libraryEntry); } } if (lintsEnabled) { CacheState lintsState = dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource); if (lintsState == CacheState.INVALID || (isPriority && lintsState == CacheState.FLUSHED)) { return _createGenerateDartLintsTask( source, dartEntry, librarySource, libraryEntry); } } } } } } else if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; CacheState parseErrorsState = htmlEntry.getState(HtmlEntry.PARSE_ERRORS); if (parseErrorsState == CacheState.INVALID || (isPriority && parseErrorsState == CacheState.FLUSHED)) { return _createParseHtmlTask(source, htmlEntry); } if (isPriority && parseErrorsState != CacheState.ERROR) { ht.HtmlUnit parsedUnit = htmlEntry.anyParsedUnit; if (parsedUnit == null) { return _createParseHtmlTask(source, htmlEntry); } } CacheState resolvedUnitState = htmlEntry.getState(HtmlEntry.RESOLVED_UNIT); if (resolvedUnitState == CacheState.INVALID || (isPriority && resolvedUnitState == CacheState.FLUSHED)) { return _createResolveHtmlTask(source, htmlEntry); } } return new AnalysisContextImpl_TaskData(null, false); } /** * Return a change notice for the given [source], creating one if one does not * already exist. */ ChangeNoticeImpl _getNotice(Source source) { ChangeNoticeImpl notice = _pendingNotices[source]; if (notice == null) { notice = new ChangeNoticeImpl(source); _pendingNotices[source] = notice; } return notice; } /** * Return the cache entry associated with the given [source], or `null` if the * source is not a Dart file. * * @param source the source for which a cache entry is being sought * @return the source cache entry associated with the given source */ DartEntry _getReadableDartEntry(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry == null) { sourceEntry = _createSourceEntry(source, false); } if (sourceEntry is DartEntry) { return sourceEntry; } return null; } /** * Return the cache entry associated with the given [source], or `null` if the * source is not an HTML file. */ HtmlEntry _getReadableHtmlEntry(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry == null) { sourceEntry = _createSourceEntry(source, false); } if (sourceEntry is HtmlEntry) { return sourceEntry; } return null; } /** * Return the cache entry associated with the given [source], creating it if * necessary. */ SourceEntry _getReadableSourceEntry(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry == null) { sourceEntry = _createSourceEntry(source, false); } return sourceEntry; } /** * Return a resolved compilation unit corresponding to the given [element] in * the library defined by the given [librarySource], or `null` if the * information is not cached. */ TimestampedData _getResolvedUnit( CompilationUnitElement element, Source librarySource) { SourceEntry sourceEntry = _cache.get(element.source); if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; if (dartEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) == CacheState.VALID) { return new TimestampedData(dartEntry.modificationTime, dartEntry.getValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource)); } } return null; } /** * Return a list containing all of the sources known to this context that have * the given [kind]. */ List _getSources(SourceKind kind) { List sources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { if (iterator.value.kind == kind) { sources.add(iterator.key); } } return sources; } /** * Look at the given [source] to see whether a task needs to be performed * related to it. If so, add the source to the set of sources that need to be * processed. This method duplicates, and must therefore be kept in sync with, * [_getNextAnalysisTaskForSource]. This method is intended to be used for * testing purposes only. */ void _getSourcesNeedingProcessing(Source source, SourceEntry sourceEntry, bool isPriority, bool hintsEnabled, bool lintsEnabled, HashSet sources) { if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS); if (scanErrorsState == CacheState.INVALID || (isPriority && scanErrorsState == CacheState.FLUSHED)) { sources.add(source); return; } CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS); if (parseErrorsState == CacheState.INVALID || (isPriority && parseErrorsState == CacheState.FLUSHED)) { sources.add(source); return; } if (isPriority) { if (!dartEntry.hasResolvableCompilationUnit) { sources.add(source); return; } } for (Source librarySource in getLibrariesContaining(source)) { SourceEntry libraryEntry = _cache.get(librarySource); if (libraryEntry is DartEntry) { CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT); if (elementState == CacheState.INVALID || (isPriority && elementState == CacheState.FLUSHED)) { sources.add(source); return; } CacheState resolvedUnitState = dartEntry.getStateInLibrary( DartEntry.RESOLVED_UNIT, librarySource); if (resolvedUnitState == CacheState.INVALID || (isPriority && resolvedUnitState == CacheState.FLUSHED)) { LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); if (libraryElement != null) { sources.add(source); return; } } if (_shouldErrorsBeAnalyzed(source, dartEntry)) { CacheState verificationErrorsState = dartEntry.getStateInLibrary( DartEntry.VERIFICATION_ERRORS, librarySource); if (verificationErrorsState == CacheState.INVALID || (isPriority && verificationErrorsState == CacheState.FLUSHED)) { LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); if (libraryElement != null) { sources.add(source); return; } } if (hintsEnabled) { CacheState hintsState = dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource); if (hintsState == CacheState.INVALID || (isPriority && hintsState == CacheState.FLUSHED)) { LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); if (libraryElement != null) { sources.add(source); return; } } } if (lintsEnabled) { CacheState lintsState = dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource); if (lintsState == CacheState.INVALID || (isPriority && lintsState == CacheState.FLUSHED)) { LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); if (libraryElement != null) { sources.add(source); return; } } } } } } } else if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; CacheState parsedUnitState = htmlEntry.getState(HtmlEntry.PARSED_UNIT); if (parsedUnitState == CacheState.INVALID || (isPriority && parsedUnitState == CacheState.FLUSHED)) { sources.add(source); return; } CacheState resolvedUnitState = htmlEntry.getState(HtmlEntry.RESOLVED_UNIT); if (resolvedUnitState == CacheState.INVALID || (isPriority && resolvedUnitState == CacheState.FLUSHED)) { sources.add(source); return; } } } /** * Invalidate all of the resolution results computed by this context. The flag * [invalidateUris] should be `true` if the cached results of converting URIs * to source files should also be invalidated. */ void _invalidateAllLocalResolutionInformation(bool invalidateUris) { HashMap> oldPartMap = new HashMap>(); MapIterator iterator = _privatePartition.iterator(); while (iterator.moveNext()) { Source source = iterator.key; SourceEntry sourceEntry = iterator.value; if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; htmlEntry.invalidateAllResolutionInformation(invalidateUris); iterator.value = htmlEntry; _workManager.add(source, SourcePriority.HTML); } else if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; oldPartMap[source] = dartEntry.getValue(DartEntry.INCLUDED_PARTS); dartEntry.invalidateAllResolutionInformation(invalidateUris); iterator.value = dartEntry; _workManager.add(source, _computePriority(dartEntry)); } } _removeFromPartsUsingMap(oldPartMap); } /** * In response to a change to at least one of the compilation units in the * library defined by the given [librarySource], invalidate any results that * are dependent on the result of resolving that library. * * Note: Any cache entries that were accessed before this method was * invoked must be re-accessed after this method returns. */ void _invalidateLibraryResolution(Source librarySource) { // TODO(brianwilkerson) This could be optimized. There's no need to flush // all of these entries 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. DartEntry libraryEntry = _getReadableDartEntry(librarySource); if (libraryEntry != null) { List includedParts = libraryEntry.getValue(DartEntry.INCLUDED_PARTS); libraryEntry.invalidateAllResolutionInformation(false); _workManager.add(librarySource, SourcePriority.LIBRARY); for (Source partSource in includedParts) { SourceEntry partEntry = _cache.get(partSource); if (partEntry is DartEntry) { partEntry.invalidateAllResolutionInformation(false); } } } } /** * Return `true` if the given [library] is, or depends on, 'dart:html'. The * [visitedLibraries] is a collection of the libraries that have been visited, * used to prevent infinite recursion. */ bool _isClient(LibraryElement library, Source htmlSource, HashSet visitedLibraries) { if (visitedLibraries.contains(library)) { return false; } if (library.source == htmlSource) { return true; } visitedLibraries.add(library); for (LibraryElement imported in library.importedLibraries) { if (_isClient(imported, htmlSource, visitedLibraries)) { return true; } } for (LibraryElement exported in library.exportedLibraries) { if (_isClient(exported, htmlSource, visitedLibraries)) { return true; } } return false; } bool _isTooBigHtmlSourceEntry(Source source, SourceEntry sourceEntry) => false; /** * Log the given debugging [message]. */ void _logInformation(String message) { AnalysisEngine.instance.logger.logInformation(message); } /** * Notify all of the analysis listeners that a task is about to be performed. */ void _notifyAboutToPerformTask(String taskDescription) { int count = _listeners.length; for (int i = 0; i < count; i++) { _listeners[i].aboutToPerformTask(this, taskDescription); } } /** * Notify all of the analysis listeners that the errors associated with the * given [source] has been updated to the given [errors]. */ void _notifyErrors( Source source, List errors, LineInfo lineInfo) { int count = _listeners.length; for (int i = 0; i < count; i++) { _listeners[i].computedErrors(this, source, errors, lineInfo); } } /** * Given that the given [source] (with the corresponding [sourceEntry]) has * been invalidated, invalidate all of the libraries that depend on it. */ void _propagateInvalidation(Source source, SourceEntry sourceEntry) { if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; htmlEntry.modificationTime = getModificationStamp(source); htmlEntry.invalidateAllInformation(); _cache.removedAst(source); _workManager.add(source, SourcePriority.HTML); } else if (sourceEntry is DartEntry) { List containingLibraries = getLibrariesContaining(source); List dependentLibraries = getLibrariesDependingOn(source); HashSet librariesToInvalidate = new HashSet(); for (Source containingLibrary in containingLibraries) { _computeAllLibrariesDependingOn( containingLibrary, librariesToInvalidate); } for (Source dependentLibrary in dependentLibraries) { _computeAllLibrariesDependingOn( dependentLibrary, librariesToInvalidate); } for (Source library in librariesToInvalidate) { _invalidateLibraryResolution(library); } DartEntry dartEntry = _cache.get(source); _removeFromParts(source, dartEntry); dartEntry.modificationTime = getModificationStamp(source); dartEntry.invalidateAllInformation(); _cache.removedAst(source); _workManager.add(source, SourcePriority.UNKNOWN); } // reset unit in the notification, it is out of date now ChangeNoticeImpl notice = _pendingNotices[source]; if (notice != null) { notice.resolvedDartUnit = null; notice.resolvedHtmlUnit = null; } } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordBuildUnitElementTask(BuildUnitElementTask task) { Source source = task.source; Source library = task.library; DartEntry dartEntry = _cache.get(source); CaughtException thrownException = task.exception; if (thrownException != null) { dartEntry.recordBuildElementErrorInLibrary(library, thrownException); throw new AnalysisException('', thrownException); } dartEntry.setValueInLibrary(DartEntry.BUILT_UNIT, library, task.unit); dartEntry.setValueInLibrary( DartEntry.BUILT_ELEMENT, library, task.unitElement); ChangeNoticeImpl notice = _getNotice(source); LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); notice.setErrors(dartEntry.allErrors, lineInfo); return dartEntry; } // /** // * Notify all of the analysis listeners that the given source is no longer included in the set of // * sources that are being analyzed. // * // * @param source the source that is no longer being analyzed // */ // void _notifyExcludedSource(Source source) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].excludedSource(this, source); // } // } // /** // * Notify all of the analysis listeners that the given source is now included in the set of // * sources that are being analyzed. // * // * @param source the source that is now being analyzed // */ // void _notifyIncludedSource(Source source) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].includedSource(this, source); // } // } // /** // * Notify all of the analysis listeners that the given Dart source was parsed. // * // * @param source the source that was parsed // * @param unit the result of parsing the source // */ // void _notifyParsedDart(Source source, CompilationUnit unit) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].parsedDart(this, source, unit); // } // } // /** // * Notify all of the analysis listeners that the given HTML source was parsed. // * // * @param source the source that was parsed // * @param unit the result of parsing the source // */ // void _notifyParsedHtml(Source source, ht.HtmlUnit unit) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].parsedHtml(this, source, unit); // } // } // /** // * Notify all of the analysis listeners that the given Dart source was resolved. // * // * @param source the source that was resolved // * @param unit the result of resolving the source // */ // void _notifyResolvedDart(Source source, CompilationUnit unit) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].resolvedDart(this, source, unit); // } // } // /** // * Notify all of the analysis listeners that the given HTML source was resolved. // * // * @param source the source that was resolved // * @param unit the result of resolving the source // */ // void _notifyResolvedHtml(Source source, ht.HtmlUnit unit) { // int count = _listeners.length; // for (int i = 0; i < count; i++) { // _listeners[i].resolvedHtml(this, source, unit); // } // } /** * Given a [dartEntry] and a [library] element, record the library element and * other information gleaned from the element in the cache entry. */ void _recordElementData(DartEntry dartEntry, LibraryElement library, Source librarySource, Source htmlSource) { dartEntry.setValue(DartEntry.ELEMENT, library); dartEntry.setValue(DartEntry.IS_LAUNCHABLE, library.entryPoint != null); dartEntry.setValue(DartEntry.IS_CLIENT, _isClient(library, htmlSource, new HashSet())); } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordGenerateDartErrorsTask(GenerateDartErrorsTask task) { Source source = task.source; DartEntry dartEntry = _cache.get(source); Source librarySource = task.libraryElement.source; CaughtException thrownException = task.exception; if (thrownException != null) { dartEntry.recordVerificationErrorInLibrary( librarySource, thrownException); throw new AnalysisException('', thrownException); } dartEntry.setValueInLibrary( DartEntry.VERIFICATION_ERRORS, librarySource, task.errors); ChangeNoticeImpl notice = _getNotice(source); LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); notice.setErrors(dartEntry.allErrors, lineInfo); return dartEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordGenerateDartHintsTask(GenerateDartHintsTask task) { Source librarySource = task.libraryElement.source; CaughtException thrownException = task.exception; DartEntry libraryEntry = null; HashMap> hintMap = task.hintMap; if (hintMap == null) { // We don't have any information about which sources to mark as invalid // other than the library source. DartEntry libraryEntry = _cache.get(librarySource); if (thrownException == null) { String message = "GenerateDartHintsTask returned a null hint map " "without throwing an exception: ${librarySource.fullName}"; thrownException = new CaughtException(new AnalysisException(message), null); } libraryEntry.recordHintErrorInLibrary(librarySource, thrownException); throw new AnalysisException('', thrownException); } hintMap.forEach((Source unitSource, List hints) { DartEntry dartEntry = _cache.get(unitSource); if (unitSource == librarySource) { libraryEntry = dartEntry; } if (thrownException == null) { dartEntry.setValueInLibrary(DartEntry.HINTS, librarySource, hints); ChangeNoticeImpl notice = _getNotice(unitSource); LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); notice.setErrors(dartEntry.allErrors, lineInfo); } else { dartEntry.recordHintErrorInLibrary(librarySource, thrownException); } }); if (thrownException != null) { throw new AnalysisException('', thrownException); } return libraryEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordGenerateDartLintsTask(GenerateDartLintsTask task) { Source librarySource = task.libraryElement.source; CaughtException thrownException = task.exception; DartEntry libraryEntry = null; HashMap> lintMap = task.lintMap; if (lintMap == null) { // We don't have any information about which sources to mark as invalid // other than the library source. DartEntry libraryEntry = _cache.get(librarySource); if (thrownException == null) { String message = "GenerateDartLintsTask returned a null lint map " "without throwing an exception: ${librarySource.fullName}"; thrownException = new CaughtException(new AnalysisException(message), null); } libraryEntry.recordLintErrorInLibrary(librarySource, thrownException); throw new AnalysisException('', thrownException); } lintMap.forEach((Source unitSource, List lints) { DartEntry dartEntry = _cache.get(unitSource); if (unitSource == librarySource) { libraryEntry = dartEntry; } if (thrownException == null) { dartEntry.setValueInLibrary(DartEntry.LINTS, librarySource, lints); ChangeNoticeImpl notice = _getNotice(unitSource); LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); notice.setErrors(dartEntry.allErrors, lineInfo); } else { dartEntry.recordLintErrorInLibrary(librarySource, thrownException); } }); if (thrownException != null) { throw new AnalysisException('', thrownException); } return libraryEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ SourceEntry _recordGetContentsTask(GetContentTask task) { if (!task.isComplete) { return null; } Source source = task.source; SourceEntry sourceEntry = _cache.get(source); CaughtException thrownException = task.exception; if (thrownException != null) { sourceEntry.recordContentError(thrownException); { sourceEntry.setValue(SourceEntry.CONTENT_ERRORS, task.errors); ChangeNoticeImpl notice = _getNotice(source); notice.setErrors(sourceEntry.allErrors, null); } _workManager.remove(source); throw new AnalysisException('', thrownException); } sourceEntry.modificationTime = task.modificationTime; sourceEntry.setValue(SourceEntry.CONTENT, task.content); return sourceEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordIncrementalAnalysisTaskResults( IncrementalAnalysisTask task) { CompilationUnit unit = task.compilationUnit; if (unit != null) { ChangeNoticeImpl notice = _getNotice(task.source); notice.resolvedDartUnit = unit; _incrementalAnalysisCache = IncrementalAnalysisCache.cacheResult(task.cache, unit); } return null; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordParseDartTaskResults(ParseDartTask task) { Source source = task.source; DartEntry dartEntry = _cache.get(source); _removeFromParts(source, dartEntry); CaughtException thrownException = task.exception; if (thrownException != null) { _removeFromParts(source, dartEntry); dartEntry.recordParseError(thrownException); _cache.removedAst(source); throw new AnalysisException('', thrownException); } if (task.hasNonPartOfDirective) { dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); dartEntry.containingLibrary = source; _workManager.add(source, SourcePriority.LIBRARY); } else if (task.hasPartOfDirective) { dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART); dartEntry.removeContainingLibrary(source); _workManager.add(source, SourcePriority.NORMAL_PART); } else { // The file contains no directives. List containingLibraries = dartEntry.containingLibraries; if (containingLibraries.length > 1 || (containingLibraries.length == 1 && containingLibraries[0] != source)) { dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART); dartEntry.removeContainingLibrary(source); _workManager.add(source, SourcePriority.NORMAL_PART); } else { dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); dartEntry.containingLibrary = source; _workManager.add(source, SourcePriority.LIBRARY); } } List newParts = task.includedSources; for (int i = 0; i < newParts.length; i++) { Source partSource = newParts[i]; DartEntry partEntry = _getReadableDartEntry(partSource); if (partEntry != null && !identical(partEntry, dartEntry)) { // TODO(brianwilkerson) Change the kind of the "part" if it was marked // as a library and it has no directives. partEntry.addContainingLibrary(source); } } dartEntry.setValue(DartEntry.PARSED_UNIT, task.compilationUnit); dartEntry.setValue(DartEntry.PARSE_ERRORS, task.errors); dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, task.exportedSources); dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, task.importedSources); dartEntry.setValue(DartEntry.INCLUDED_PARTS, newParts); _cache.storedAst(source); ChangeNoticeImpl notice = _getNotice(source); if (notice.resolvedDartUnit == null) { notice.parsedDartUnit = task.compilationUnit; } notice.setErrors(dartEntry.allErrors, task.lineInfo); // Verify that the incrementally parsed and resolved unit in the incremental // cache is structurally equivalent to the fully parsed unit _incrementalAnalysisCache = IncrementalAnalysisCache.verifyStructure( _incrementalAnalysisCache, source, task.compilationUnit); return dartEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ HtmlEntry _recordParseHtmlTaskResults(ParseHtmlTask task) { Source source = task.source; HtmlEntry htmlEntry = _cache.get(source); CaughtException thrownException = task.exception; if (thrownException != null) { htmlEntry.recordParseError(thrownException); _cache.removedAst(source); throw new AnalysisException('', thrownException); } LineInfo lineInfo = task.lineInfo; htmlEntry.setValue(SourceEntry.LINE_INFO, lineInfo); htmlEntry.setValue(HtmlEntry.PARSED_UNIT, task.htmlUnit); htmlEntry.setValue(HtmlEntry.PARSE_ERRORS, task.errors); htmlEntry.setValue( HtmlEntry.REFERENCED_LIBRARIES, task.referencedLibraries); _cache.storedAst(source); ChangeNoticeImpl notice = _getNotice(source); notice.setErrors(htmlEntry.allErrors, lineInfo); return htmlEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordResolveDartUnitTaskResults(ResolveDartUnitTask task) { Source unitSource = task.source; DartEntry dartEntry = _cache.get(unitSource); Source librarySource = task.librarySource; CaughtException thrownException = task.exception; if (thrownException != null) { dartEntry.recordResolutionErrorInLibrary(librarySource, thrownException); _cache.removedAst(unitSource); throw new AnalysisException('', thrownException); } dartEntry.setValueInLibrary( DartEntry.RESOLVED_UNIT, librarySource, task.resolvedUnit); _cache.storedAst(unitSource); return dartEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ HtmlEntry _recordResolveHtmlTaskResults(ResolveHtmlTask task) { Source source = task.source; HtmlEntry htmlEntry = _cache.get(source); CaughtException thrownException = task.exception; if (thrownException != null) { htmlEntry.recordResolutionError(thrownException); _cache.removedAst(source); throw new AnalysisException('', thrownException); } htmlEntry.setState(HtmlEntry.PARSED_UNIT, CacheState.FLUSHED); htmlEntry.setValue(HtmlEntry.RESOLVED_UNIT, task.resolvedUnit); htmlEntry.setValue(HtmlEntry.ELEMENT, task.element); htmlEntry.setValue(HtmlEntry.RESOLUTION_ERRORS, task.resolutionErrors); _cache.storedAst(source); ChangeNoticeImpl notice = _getNotice(source); notice.resolvedHtmlUnit = task.resolvedUnit; LineInfo lineInfo = htmlEntry.getValue(SourceEntry.LINE_INFO); notice.setErrors(htmlEntry.allErrors, lineInfo); return htmlEntry; } /** * Record the results produced by performing a [task] and return the cache * entry associated with the results. */ DartEntry _recordScanDartTaskResults(ScanDartTask task) { Source source = task.source; DartEntry dartEntry = _cache.get(source); CaughtException thrownException = task.exception; if (thrownException != null) { _removeFromParts(source, dartEntry); dartEntry.recordScanError(thrownException); _cache.removedAst(source); throw new AnalysisException('', thrownException); } LineInfo lineInfo = task.lineInfo; dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo); dartEntry.setValue(DartEntry.TOKEN_STREAM, task.tokenStream); dartEntry.setValue(DartEntry.SCAN_ERRORS, task.errors); _cache.storedAst(source); ChangeNoticeImpl notice = _getNotice(source); notice.setErrors(dartEntry.allErrors, lineInfo); return dartEntry; } /** * Remove the given [librarySource] from the list of containing libraries for * all of the parts referenced by the given [dartEntry]. */ void _removeFromParts(Source librarySource, DartEntry dartEntry) { List oldParts = dartEntry.getValue(DartEntry.INCLUDED_PARTS); for (int i = 0; i < oldParts.length; i++) { Source partSource = oldParts[i]; DartEntry partEntry = _getReadableDartEntry(partSource); if (partEntry != null && !identical(partEntry, dartEntry)) { partEntry.removeContainingLibrary(librarySource); if (partEntry.containingLibraries.length == 0 && !exists(partSource)) { _cache.remove(partSource); } } } } /** * Remove the given libraries that are keys in the given map from the list of * containing libraries for each of the parts in the corresponding value. */ void _removeFromPartsUsingMap(HashMap> oldPartMap) { oldPartMap.forEach((Source librarySource, List oldParts) { for (int i = 0; i < oldParts.length; i++) { Source partSource = oldParts[i]; if (partSource != librarySource) { DartEntry partEntry = _getReadableDartEntry(partSource); if (partEntry != null) { partEntry.removeContainingLibrary(librarySource); if (partEntry.containingLibraries.length == 0 && !exists(partSource)) { _cache.remove(partSource); } } } } }); } /** * Remove the given [source] from the priority order if it is in the list. */ void _removeFromPriorityOrder(Source source) { int count = _priorityOrder.length; List newOrder = new List(); for (int i = 0; i < count; i++) { if (_priorityOrder[i] != source) { newOrder.add(_priorityOrder[i]); } } if (newOrder.length < count) { analysisPriorityOrder = newOrder; } } /** * Return `true` if errors should be produced for the given [source]. The * [dartEntry] associated with the source is passed in for efficiency. */ bool _shouldErrorsBeAnalyzed(Source source, DartEntry dartEntry) { if (source.isInSystemLibrary) { return _generateSdkErrors; } else if (!dartEntry.explicitlyAdded) { return _generateImplicitErrors; } else { return true; } } /** * Create an entry for the newly added [source] and invalidate any sources * that referenced the source before it existed. */ void _sourceAvailable(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry == null) { sourceEntry = _createSourceEntry(source, true); } else { _propagateInvalidation(source, sourceEntry); sourceEntry = _cache.get(source); } if (sourceEntry is HtmlEntry) { _workManager.add(source, SourcePriority.HTML); } else if (sourceEntry is DartEntry) { _workManager.add(source, _computePriority(sourceEntry)); } } /** * Invalidate the [source] that was changed and any sources that referenced * the source before it existed. */ void _sourceChanged(Source source) { SourceEntry sourceEntry = _cache.get(source); // If the source is removed, we don't care about it. if (sourceEntry == null) { return; } // Check if the content of the source is the same as it was the last time. String sourceContent = sourceEntry.getValue(SourceEntry.CONTENT); if (sourceContent != null) { sourceEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); try { TimestampedData fileContents = getContents(source); if (fileContents.data == sourceContent) { return; } } catch (e) {} } // We have to invalidate the cache. _propagateInvalidation(source, sourceEntry); } /** * Record that the give [source] has been deleted. */ void _sourceDeleted(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry is HtmlEntry) { HtmlEntry htmlEntry = sourceEntry; htmlEntry.recordContentError(new CaughtException( new AnalysisException("This source was marked as being deleted"), null)); } else if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; HashSet libraries = new HashSet(); for (Source librarySource in getLibrariesContaining(source)) { libraries.add(librarySource); for (Source dependentLibrary in getLibrariesDependingOn(librarySource)) { libraries.add(dependentLibrary); } } for (Source librarySource in libraries) { _invalidateLibraryResolution(librarySource); } dartEntry.recordContentError(new CaughtException( new AnalysisException("This source was marked as being deleted"), null)); } _workManager.remove(source); _removeFromPriorityOrder(source); } /** * Record that the given [source] has been removed. */ void _sourceRemoved(Source source) { SourceEntry sourceEntry = _cache.get(source); if (sourceEntry is HtmlEntry) {} else if (sourceEntry is DartEntry) { HashSet libraries = new HashSet(); for (Source librarySource in getLibrariesContaining(source)) { libraries.add(librarySource); for (Source dependentLibrary in getLibrariesDependingOn(librarySource)) { libraries.add(dependentLibrary); } } for (Source librarySource in libraries) { _invalidateLibraryResolution(librarySource); } } _cache.remove(source); _workManager.remove(source); _removeFromPriorityOrder(source); } /** * TODO(scheglov) A hackish, limited incremental resolution implementation. */ bool _tryPoorMansIncrementalResolution(Source unitSource, String newCode) { return PerformanceStatistics.incrementalAnalysis.makeCurrentWhile(() { incrementalResolutionValidation_lastUnitSource = null; incrementalResolutionValidation_lastLibrarySource = null; incrementalResolutionValidation_lastUnit = null; // prepare the entry DartEntry dartEntry = _cache.get(unitSource); if (dartEntry == null) { return false; } // prepare the (only) library source List librarySources = getLibrariesContaining(unitSource); if (librarySources.length != 1) { return false; } Source librarySource = librarySources[0]; // prepare the library element LibraryElement libraryElement = getLibraryElement(librarySource); if (libraryElement == null) { return false; } // prepare the existing unit CompilationUnit oldUnit = getResolvedCompilationUnit2(unitSource, librarySource); if (oldUnit == null) { return false; } // do resolution Stopwatch perfCounter = new Stopwatch()..start(); PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver( typeProvider, unitSource, dartEntry, oldUnit, analysisOptions.incrementalApi, analysisOptions); bool success = resolver.resolve(newCode); AnalysisEngine.instance.instrumentationService.logPerformance( AnalysisPerformanceKind.INCREMENTAL, perfCounter, 'success=$success,context_id=$_id,code_length=${newCode.length}'); if (!success) { return false; } // if validation, remember the result, but throw it away if (analysisOptions.incrementalValidation) { incrementalResolutionValidation_lastUnitSource = oldUnit.element.source; incrementalResolutionValidation_lastLibrarySource = oldUnit.element.library.source; incrementalResolutionValidation_lastUnit = oldUnit; return false; } // prepare notice { LineInfo lineInfo = getLineInfo(unitSource); ChangeNoticeImpl notice = _getNotice(unitSource); notice.resolvedDartUnit = oldUnit; notice.setErrors(dartEntry.allErrors, lineInfo); } // OK return true; }); } /** * 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. Return `true` if at least one entry was invalid. */ bool _validateCacheConsistency() { int consistencyCheckStart = JavaSystem.nanoTime(); List changedSources = new List(); List missingSources = new List(); MapIterator iterator = _cache.iterator(); while (iterator.moveNext()) { Source source = iterator.key; SourceEntry sourceEntry = iterator.value; int sourceTime = getModificationStamp(source); if (sourceTime != sourceEntry.modificationTime) { changedSources.add(source); } if (sourceEntry.exception != null) { if (!exists(source)) { missingSources.add(source); } } } int count = changedSources.length; for (int i = 0; i < count; i++) { _sourceChanged(changedSources[i]); } int removalCount = 0; for (Source source in missingSources) { if (getLibrariesContaining(source).isEmpty && getLibrariesDependingOn(source).isEmpty) { _cache.remove(source); removalCount++; } } int consistencyCheckEnd = JavaSystem.nanoTime(); if (changedSources.length > 0 || missingSources.length > 0) { StringBuffer buffer = new StringBuffer(); buffer.write("Consistency check took "); buffer.write((consistencyCheckEnd - consistencyCheckStart) / 1000000.0); buffer.writeln(" ms and found"); buffer.write(" "); buffer.write(changedSources.length); buffer.writeln(" inconsistent entries"); buffer.write(" "); buffer.write(missingSources.length); buffer.write(" missing sources ("); buffer.write(removalCount); buffer.writeln(" removed"); for (Source source in missingSources) { buffer.write(" "); buffer.writeln(source.fullName); } _logInformation(buffer.toString()); } return changedSources.length > 0; } void _validateLastIncrementalResolutionResult() { if (incrementalResolutionValidation_lastUnitSource == null || incrementalResolutionValidation_lastLibrarySource == null || incrementalResolutionValidation_lastUnit == null) { return; } CompilationUnit fullUnit = getResolvedCompilationUnit2( incrementalResolutionValidation_lastUnitSource, incrementalResolutionValidation_lastLibrarySource); if (fullUnit != null) { try { assertSameResolution( incrementalResolutionValidation_lastUnit, fullUnit); } on IncrementalResolutionMismatch catch (mismatch, stack) { String failure = mismatch.message; String message = 'Incremental resolution mismatch:\n$failure\nat\n$stack'; AnalysisEngine.instance.logger.logError(message); } } incrementalResolutionValidation_lastUnitSource = null; incrementalResolutionValidation_lastLibrarySource = null; incrementalResolutionValidation_lastUnit = null; } } /** * An object used by an analysis context to record the results of a task. */ class AnalysisContextImpl_AnalysisTaskResultRecorder implements AnalysisTaskVisitor { final AnalysisContextImpl AnalysisContextImpl_this; AnalysisContextImpl_AnalysisTaskResultRecorder(this.AnalysisContextImpl_this); @override DartEntry visitBuildUnitElementTask(BuildUnitElementTask task) => AnalysisContextImpl_this._recordBuildUnitElementTask(task); @override DartEntry visitGenerateDartErrorsTask(GenerateDartErrorsTask task) => AnalysisContextImpl_this._recordGenerateDartErrorsTask(task); @override DartEntry visitGenerateDartHintsTask(GenerateDartHintsTask task) => AnalysisContextImpl_this._recordGenerateDartHintsTask(task); @override DartEntry visitGenerateDartLintsTask(GenerateDartLintsTask task) => AnalysisContextImpl_this._recordGenerateDartLintsTask(task); @override SourceEntry visitGetContentTask(GetContentTask task) => AnalysisContextImpl_this._recordGetContentsTask(task); @override DartEntry visitIncrementalAnalysisTask(IncrementalAnalysisTask task) => AnalysisContextImpl_this._recordIncrementalAnalysisTaskResults(task); @override DartEntry visitParseDartTask(ParseDartTask task) => AnalysisContextImpl_this._recordParseDartTaskResults(task); @override HtmlEntry visitParseHtmlTask(ParseHtmlTask task) => AnalysisContextImpl_this._recordParseHtmlTaskResults(task); @override DartEntry visitResolveDartLibraryCycleTask( ResolveDartLibraryCycleTask task) => AnalysisContextImpl_this.recordResolveDartLibraryCycleTaskResults(task); @override DartEntry visitResolveDartLibraryTask(ResolveDartLibraryTask task) => AnalysisContextImpl_this.recordResolveDartLibraryTaskResults(task); @override DartEntry visitResolveDartUnitTask(ResolveDartUnitTask task) => AnalysisContextImpl_this._recordResolveDartUnitTaskResults(task); @override HtmlEntry visitResolveHtmlTask(ResolveHtmlTask task) => AnalysisContextImpl_this._recordResolveHtmlTaskResults(task); @override DartEntry visitScanDartTask(ScanDartTask task) => AnalysisContextImpl_this._recordScanDartTaskResults(task); } class AnalysisContextImpl_ContextRetentionPolicy implements CacheRetentionPolicy { final AnalysisContextImpl AnalysisContextImpl_this; AnalysisContextImpl_ContextRetentionPolicy(this.AnalysisContextImpl_this); @override RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) { int priorityCount = AnalysisContextImpl_this._priorityOrder.length; for (int i = 0; i < priorityCount; i++) { if (source == AnalysisContextImpl_this._priorityOrder[i]) { return RetentionPriority.HIGH; } } if (AnalysisContextImpl_this._neededForResolution != null && AnalysisContextImpl_this._neededForResolution.contains(source)) { return RetentionPriority.HIGH; } if (sourceEntry is DartEntry) { DartEntry dartEntry = sourceEntry; if (_astIsNeeded(dartEntry)) { return RetentionPriority.MEDIUM; } } return RetentionPriority.LOW; } bool _astIsNeeded(DartEntry dartEntry) => dartEntry.hasInvalidData(DartEntry.HINTS) || dartEntry.hasInvalidData(DartEntry.LINTS) || dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) || dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS); } /** * An object used to construct a list of the libraries that must be resolved * together in order to resolve any one of the libraries. */ class AnalysisContextImpl_CycleBuilder { final AnalysisContextImpl AnalysisContextImpl_this; /** * A table mapping the sources of the defining compilation units of libraries * to the representation of the library that has the information needed to * resolve the library. */ HashMap _libraryMap = new HashMap(); /** * The dependency graph used to compute the libraries in the cycle. */ DirectedGraph _dependencyGraph; /** * A list containing the libraries that are ready to be resolved. */ List _librariesInCycle; /** * The analysis task that needs to be performed before the cycle of libraries * can be resolved, or `null` if the libraries are ready to be resolved. */ AnalysisContextImpl_TaskData _taskData; /** * Initialize a newly created cycle builder. */ AnalysisContextImpl_CycleBuilder(this.AnalysisContextImpl_this) : super(); /** * Return a list containing the libraries that are ready to be resolved * (assuming that [getTaskData] returns `null`). */ List get librariesInCycle => _librariesInCycle; /** * Return a representation of an analysis task that needs to be performed * before the cycle of libraries can be resolved, or `null` if the libraries * are ready to be resolved. */ AnalysisContextImpl_TaskData get taskData => _taskData; /** * Compute a list of the libraries that need to be resolved together in orde * to resolve the given [librarySource]. */ void computeCycleContaining(Source librarySource) { // // Create the object representing the library being resolved. // ResolvableLibrary targetLibrary = _createLibrary(librarySource); // // Compute the set of libraries that need to be resolved together. // _dependencyGraph = new DirectedGraph(); _computeLibraryDependencies(targetLibrary); if (_taskData != null) { return; } _librariesInCycle = _dependencyGraph.findCycleContaining(targetLibrary); // // Ensure that all of the data needed to resolve them has been computed. // _ensureImportsAndExports(); if (_taskData != null) { // At least one imported library needs to be resolved before the target // library. AnalysisTask task = _taskData.task; if (task is ResolveDartLibraryTask) { AnalysisContextImpl_this._workManager.addFirst( task.librarySource, SourcePriority.LIBRARY); } return; } _computePartsInCycle(librarySource); if (_taskData != null) { // At least one part needs to be parsed. return; } // All of the AST's necessary to perform a resolution of the library cycle // have been gathered, so it is no longer necessary to retain them in the // cache. AnalysisContextImpl_this._neededForResolution = null; } bool _addDependency(ResolvableLibrary dependant, Source dependency, List dependencyList) { if (dependant.librarySource == dependency) { // Don't add a dependency of a library on itself; there's no point. return true; } ResolvableLibrary importedLibrary = _libraryMap[dependency]; if (importedLibrary == null) { importedLibrary = _createLibraryOrNull(dependency); if (importedLibrary != null) { _computeLibraryDependencies(importedLibrary); if (_taskData != null) { return false; } } } if (importedLibrary != null) { if (dependencyList != null) { dependencyList.add(importedLibrary); } _dependencyGraph.addEdge(dependant, importedLibrary); } return true; } /** * Recursively traverse the libraries reachable from the given [library], * creating instances of the class [Library] to represent them, and record the * references in the library objects. * * Throws an [AnalysisException] if some portion of the library graph could * not be traversed. */ void _computeLibraryDependencies(ResolvableLibrary library) { Source librarySource = library.librarySource; DartEntry dartEntry = AnalysisContextImpl_this._getReadableDartEntry(librarySource); List importedSources = _getSources(librarySource, dartEntry, DartEntry.IMPORTED_LIBRARIES); if (_taskData != null) { return; } List exportedSources = _getSources(librarySource, dartEntry, DartEntry.EXPORTED_LIBRARIES); if (_taskData != null) { return; } _computeLibraryDependenciesFromDirectives( library, importedSources, exportedSources); } /** * Recursively traverse the libraries reachable from the given [library], * creating instances of the class [Library] to represent them, and record the * references in the library objects. The [importedSources] is a list * containing the sources that are imported into the given library. The * [exportedSources] is a list containing the sources that are exported from * the given library. */ void _computeLibraryDependenciesFromDirectives(ResolvableLibrary library, List importedSources, List exportedSources) { int importCount = importedSources.length; List importedLibraries = new List(); bool explicitlyImportsCore = false; bool importsAsync = false; for (int i = 0; i < importCount; i++) { Source importedSource = importedSources[i]; if (importedSource == AnalysisContextImpl_this._coreLibrarySource) { explicitlyImportsCore = true; } else if (importedSource == AnalysisContextImpl_this._asyncLibrarySource) { importsAsync = true; } if (!_addDependency(library, importedSource, importedLibraries)) { return; } } library.explicitlyImportsCore = explicitlyImportsCore; if (!explicitlyImportsCore) { if (!_addDependency(library, AnalysisContextImpl_this._coreLibrarySource, importedLibraries)) { return; } } if (!importsAsync) { // Add a dependency on async to ensure that the Future element will be // built before we generate errors and warnings for async methods. Also // include it in importedLibraries, so that it will be picked up by // LibraryResolver2._buildLibraryMap(). // TODO(paulberry): this is a bit of a hack, since the async library // isn't actually being imported. Also, it's not clear whether it should // be necessary: in theory, dart:core already (indirectly) imports // dart:async, so if core has been built, async should have been built // too. However, removing this code causes unit test failures. if (!_addDependency(library, AnalysisContextImpl_this._asyncLibrarySource, importedLibraries)) { return; } } library.importedLibraries = importedLibraries; int exportCount = exportedSources.length; if (exportCount > 0) { List exportedLibraries = new List(); for (int i = 0; i < exportCount; i++) { Source exportedSource = exportedSources[i]; if (!_addDependency(library, exportedSource, exportedLibraries)) { return; } } library.exportedLibraries = exportedLibraries; } } /** * Gather the resolvable AST structures for each of the compilation units in * each of the libraries in the cycle. This is done in two phases: first we * ensure that we have cached an AST structure for each compilation unit, then * we gather them. We split the work this way because getting the AST * structures can change the state of the cache in such a way that we would * have more work to do if any compilation unit didn't have a resolvable AST * structure. */ void _computePartsInCycle(Source librarySource) { int count = _librariesInCycle.length; List libraryData = new List(); for (int i = 0; i < count; i++) { ResolvableLibrary library = _librariesInCycle[i]; libraryData.add(new CycleBuilder_LibraryPair( library, _ensurePartsInLibrary(library))); } AnalysisContextImpl_this._neededForResolution = _gatherSources(libraryData); if (AnalysisContextImpl._TRACE_PERFORM_TASK) { print( " preserve resolution data for ${AnalysisContextImpl_this._neededForResolution.length} sources while resolving ${librarySource.fullName}"); } if (_taskData != null) { return; } for (int i = 0; i < count; i++) { _computePartsInLibrary(libraryData[i]); } } /** * Gather the resolvable compilation units for each of the compilation units * in the library represented by the [libraryPair]. */ void _computePartsInLibrary(CycleBuilder_LibraryPair libraryPair) { ResolvableLibrary library = libraryPair.library; List entryPairs = libraryPair.entryPairs; int count = entryPairs.length; List units = new List(count); for (int i = 0; i < count; i++) { CycleBuilder_SourceEntryPair entryPair = entryPairs[i]; Source source = entryPair.source; DartEntry dartEntry = entryPair.entry; units[i] = new ResolvableCompilationUnit( source, dartEntry.resolvableCompilationUnit); } library.resolvableCompilationUnits = units; } /** * Create an object to represent the information about the library defined by * the compilation unit with the given [librarySource]. */ ResolvableLibrary _createLibrary(Source librarySource) { ResolvableLibrary library = new ResolvableLibrary(librarySource); SourceEntry sourceEntry = AnalysisContextImpl_this._cache.get(librarySource); if (sourceEntry is DartEntry) { LibraryElementImpl libraryElement = sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl; if (libraryElement != null) { library.libraryElement = libraryElement; } } _libraryMap[librarySource] = library; return library; } /** * Create an object to represent the information about the library defined by * the compilation unit with the given [librarySource]. */ ResolvableLibrary _createLibraryOrNull(Source librarySource) { ResolvableLibrary library = new ResolvableLibrary(librarySource); SourceEntry sourceEntry = AnalysisContextImpl_this._cache.get(librarySource); if (sourceEntry is DartEntry) { LibraryElementImpl libraryElement = sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl; if (libraryElement != null) { library.libraryElement = libraryElement; } } _libraryMap[librarySource] = library; return library; } /** * Ensure that the given [library] has an element model built for it. If * another task needs to be executed first in order to build the element * model, that task is placed in [taskData]. */ void _ensureElementModel(ResolvableLibrary library) { Source librarySource = library.librarySource; DartEntry libraryEntry = AnalysisContextImpl_this._getReadableDartEntry(librarySource); if (libraryEntry != null && libraryEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) { AnalysisContextImpl_this._workManager.addFirst( librarySource, SourcePriority.LIBRARY); if (_taskData == null) { _taskData = AnalysisContextImpl_this._createResolveDartLibraryTask( librarySource, libraryEntry); } } } /** * Ensure that all of the libraries that are exported by the given [library] * (but are not themselves in the cycle) have element models built for them. * If another task needs to be executed first in order to build the element * model, that task is placed in [taskData]. */ void _ensureExports( ResolvableLibrary library, HashSet visitedLibraries) { List dependencies = library.exports; int dependencyCount = dependencies.length; for (int i = 0; i < dependencyCount; i++) { ResolvableLibrary dependency = dependencies[i]; if (!_librariesInCycle.contains(dependency) && visitedLibraries.add(dependency.librarySource)) { if (dependency.libraryElement == null) { _ensureElementModel(dependency); } else { _ensureExports(dependency, visitedLibraries); } if (_taskData != null) { return; } } } } /** * Ensure that all of the libraries that are exported by the given [library] * (but are not themselves in the cycle) have element models built for them. * If another task needs to be executed first in order to build the element * model, that task is placed in [taskData]. */ void _ensureImports(ResolvableLibrary library) { List dependencies = library.imports; int dependencyCount = dependencies.length; for (int i = 0; i < dependencyCount; i++) { ResolvableLibrary dependency = dependencies[i]; if (!_librariesInCycle.contains(dependency) && dependency.libraryElement == null) { _ensureElementModel(dependency); if (_taskData != null) { return; } } } } /** * Ensure that all of the libraries that are either imported or exported by * libraries in the cycle (but are not themselves in the cycle) have element * models built for them. */ void _ensureImportsAndExports() { HashSet visitedLibraries = new HashSet(); int libraryCount = _librariesInCycle.length; for (int i = 0; i < libraryCount; i++) { ResolvableLibrary library = _librariesInCycle[i]; _ensureImports(library); if (_taskData != null) { return; } _ensureExports(library, visitedLibraries); if (_taskData != null) { return; } } } /** * Ensure that there is a resolvable compilation unit available for all of the * compilation units in the given [library]. */ List _ensurePartsInLibrary( ResolvableLibrary library) { List pairs = new List(); Source librarySource = library.librarySource; DartEntry libraryEntry = AnalysisContextImpl_this._getReadableDartEntry(librarySource); if (libraryEntry == null) { throw new AnalysisException( "Cannot find entry for ${librarySource.fullName}"); } else if (libraryEntry.getState(DartEntry.PARSED_UNIT) == CacheState.ERROR) { String message = "Cannot compute parsed unit for ${librarySource.fullName}"; CaughtException exception = libraryEntry.exception; if (exception == null) { throw new AnalysisException(message); } throw new AnalysisException( message, new CaughtException(exception, null)); } _ensureResolvableCompilationUnit(librarySource, libraryEntry); pairs.add(new CycleBuilder_SourceEntryPair(librarySource, libraryEntry)); List partSources = _getSources(librarySource, libraryEntry, DartEntry.INCLUDED_PARTS); int count = partSources.length; for (int i = 0; i < count; i++) { Source partSource = partSources[i]; DartEntry partEntry = AnalysisContextImpl_this._getReadableDartEntry(partSource); if (partEntry != null && partEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) { _ensureResolvableCompilationUnit(partSource, partEntry); pairs.add(new CycleBuilder_SourceEntryPair(partSource, partEntry)); } } return pairs; } /** * Ensure that there is a resolvable compilation unit available for the given * [source]. */ void _ensureResolvableCompilationUnit(Source source, DartEntry dartEntry) { // The entry will be null if the source represents a non-Dart file. if (dartEntry != null && !dartEntry.hasResolvableCompilationUnit) { if (_taskData == null) { _taskData = AnalysisContextImpl_this._createParseDartTask(source, dartEntry); } } } HashSet _gatherSources(List libraryData) { int libraryCount = libraryData.length; HashSet sources = new HashSet(); for (int i = 0; i < libraryCount; i++) { List entryPairs = libraryData[i].entryPairs; int entryCount = entryPairs.length; for (int j = 0; j < entryCount; j++) { sources.add(entryPairs[j].source); } } return sources; } /** * Return the sources described by the given [descriptor]. */ List _getSources(Source source, DartEntry dartEntry, DataDescriptor> descriptor) { if (dartEntry == null) { return Source.EMPTY_ARRAY; } CacheState exportState = dartEntry.getState(descriptor); if (exportState == CacheState.ERROR) { return Source.EMPTY_ARRAY; } else if (exportState != CacheState.VALID) { if (_taskData == null) { _taskData = AnalysisContextImpl_this._createParseDartTask(source, dartEntry); } return Source.EMPTY_ARRAY; } return dartEntry.getValue(descriptor); } } /** * Information about the next task to be performed. Each data has an implicit * associated source: the source that might need to be analyzed. There are * essentially three states that can be represented: * * * If [getTask] returns a non-`null` value, then that is the task that should * be executed to further analyze the associated source. * * Otherwise, if [isBlocked] returns `true`, then there is no work that can be * done, but analysis for the associated source is not complete. * * Otherwise, [getDependentSource] should return a source that needs to be * analyzed before the analysis of the associated source can be completed. */ class AnalysisContextImpl_TaskData { /** * The task that is to be performed. */ final AnalysisTask task; /** * A flag indicating whether the associated source is blocked waiting for its * contents to be loaded. */ final bool _blocked; /** * Initialize a newly created data holder. */ AnalysisContextImpl_TaskData(this.task, this._blocked); /** * Return `true` if the associated source is blocked waiting for its contents * to be loaded. */ bool get isBlocked => _blocked; @override String toString() { if (task == null) { return "blocked: $_blocked"; } return task.toString(); } } /** * Statistics and information about a single [AnalysisContext]. */ abstract class AnalysisContextStatistics { /** * Return the statistics for each kind of cached data. */ List get cacheRows; /** * Return the exceptions that caused some entries to have a state of * [CacheState.ERROR]. */ List get exceptions; /** * Return information about each of the partitions in the cache. */ List get partitionData; /** * Return a list containing all of the sources in the cache. */ List get sources; } /** * Information about single piece of data in the cache. */ abstract class AnalysisContextStatistics_CacheRow { /** * List of possible states which can be queried. */ static const List STATES = const [ CacheState.ERROR, CacheState.FLUSHED, CacheState.IN_PROCESS, CacheState.INVALID, CacheState.VALID ]; /** * Return the number of entries whose state is [CacheState.ERROR]. */ int get errorCount; /** * Return the number of entries whose state is [CacheState.FLUSHED]. */ int get flushedCount; /** * Return the number of entries whose state is [CacheState.IN_PROCESS]. */ int get inProcessCount; /** * Return the number of entries whose state is [CacheState.INVALID]. */ int get invalidCount; /** * Return the name of the data represented by this object. */ String get name; /** * Return the number of entries whose state is [CacheState.VALID]. */ int get validCount; /** * Return the number of entries whose state is [state]. */ int getCount(CacheState state); } /** * Information about a single partition in the cache. */ abstract class AnalysisContextStatistics_PartitionData { /** * Return the number of entries in the partition that have an AST structure in * one state or another. */ int get astCount; /** * Return the total number of entries in the partition. */ int get totalCount; } /** * Implementation of the [AnalysisContextStatistics]. */ class AnalysisContextStatisticsImpl implements AnalysisContextStatistics { Map _dataMap = new HashMap(); List