Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(713)

Unified Diff: mojo/public/dart/third_party/analyzer/lib/src/context/context.dart

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mojo/public/dart/third_party/analyzer/lib/src/context/context.dart
diff --git a/mojo/public/dart/third_party/analyzer/lib/src/context/context.dart b/mojo/public/dart/third_party/analyzer/lib/src/context/context.dart
new file mode 100644
index 0000000000000000000000000000000000000000..f472ed15a6b59b95b12bfb9b4d2ab29b6bd487c1
--- /dev/null
+++ b/mojo/public/dart/third_party/analyzer/lib/src/context/context.dart
@@ -0,0 +1,2098 @@
+// Copyright (c) 2015, 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.
+
+library analyzer.src.context.context;
+
+import 'dart:async';
+import 'dart:collection';
+
+import 'package:analyzer/instrumentation/instrumentation.dart';
+import 'package:analyzer/plugin/task.dart';
+import 'package:analyzer/src/cancelable_future.dart';
+import 'package:analyzer/src/context/cache.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/constant.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/engine.dart'
+ hide
+ AnalysisCache,
+ CachePartition,
+ SdkCachePartition,
+ UniversalCachePartition,
+ WorkManager;
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/html.dart' as ht show HtmlUnit;
+import 'package:analyzer/src/generated/incremental_resolver.dart';
+import 'package:analyzer/src/generated/java_core.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart';
+import 'package:analyzer/src/task/dart.dart';
+import 'package:analyzer/src/task/dart_work_manager.dart';
+import 'package:analyzer/src/task/driver.dart';
+import 'package:analyzer/src/task/incremental_element_builder.dart';
+import 'package:analyzer/src/task/manager.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/general.dart';
+import 'package:analyzer/task/html.dart';
+import 'package:analyzer/task/model.dart';
+import 'package:html/dom.dart' show Document;
+
+/**
+ * Type of callback functions used by PendingFuture. Functions of this type
+ * should perform a computation based on the data in [entry] 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.
+ *
+ * Because 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<T>(CacheEntry entry);
+
+/**
+ * An [AnalysisContext] in which analysis can be performed.
+ */
+class AnalysisContextImpl implements InternalAnalysisContext {
+ /**
+ * 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 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();
+
+ /**
+ * The partition that contains analysis results that are not shared with other
+ * contexts.
+ */
+ CachePartition _privatePartition;
+
+ /**
+ * The cache in which information about the results associated with targets
+ * are stored.
+ */
+ AnalysisCache _cache;
+
+ /**
+ * The task manager used to manage the tasks used to analyze code.
+ */
+ TaskManager _taskManager;
+
+ /**
+ * A list of all [WorkManager]s used by this context.
+ */
+ final List<WorkManager> workManagers = <WorkManager>[];
+
+ /**
+ * The [DartWorkManager] instance that performs Dart specific scheduling.
+ */
+ DartWorkManager dartWorkManager;
+
+ /**
+ * The analysis driver used to perform analysis.
+ */
+ AnalysisDriver driver;
+
+ /**
+ * A list containing sources for which data should not be flushed.
+ */
+ List<Source> _priorityOrder = <Source>[];
+
+ /**
+ * 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.
+ */
+ HashMap<AnalysisTarget, List<PendingFuture>> _pendingFutureTargets =
+ new HashMap<AnalysisTarget, List<PendingFuture>>();
+
+ /**
+ * A table mapping sources to the change notices that are waiting to be
+ * returned related to that source.
+ */
+ HashMap<Source, ChangeNoticeImpl> _pendingNotices =
+ new HashMap<Source, ChangeNoticeImpl>();
+
+ /**
+ * The [TypeProvider] for this context, `null` if not yet created.
+ */
+ TypeProvider _typeProvider;
+
+ /**
+ * The controller for sending [SourcesChangedEvent]s.
+ */
+ StreamController<SourcesChangedEvent> _onSourcesChangedController;
+
+ /**
+ * A subscription for a stream of events indicating when files are (and are
+ * not) being implicitly analyzed.
+ */
+ StreamController<ImplicitAnalysisEvent> _implicitAnalysisEventsController;
+
+ /**
+ * The listeners that are to be notified when various analysis results are
+ * produced in this context.
+ */
+ List<AnalysisListener> _listeners = new List<AnalysisListener>();
+
+ /**
+ * 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() {
+ _privatePartition = new UniversalCachePartition(this);
+ _cache = createCacheFromSourceFactory(null);
+ _taskManager = AnalysisEngine.instance.taskManager;
+ for (WorkManagerFactory factory
+ in AnalysisEngine.instance.enginePlugin.workManagerFactories) {
+ WorkManager workManager = factory(this);
+ if (workManager != null) {
+ workManagers.add(workManager);
+ if (workManager is DartWorkManager) {
+ dartWorkManager = workManager;
+ }
+ }
+ }
+ driver = new AnalysisDriver(_taskManager, workManagers, this);
+ _onSourcesChangedController =
+ new StreamController<SourcesChangedEvent>.broadcast();
+ _implicitAnalysisEventsController =
+ new StreamController<ImplicitAnalysisEvent>.broadcast();
+ }
+
+ @override
+ AnalysisCache get analysisCache => _cache;
+
+ @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.lint && !options.lint) ||
+ this._options.preserveComments != options.preserveComments ||
+ this._options.enableStrictCallChecks !=
+ options.enableStrictCallChecks ||
+ this._options.enableSuperMixins != options.enableSuperMixins;
+ int cacheSize = options.cacheSize;
+ if (this._options.cacheSize != cacheSize) {
+ this._options.cacheSize = cacheSize;
+ }
+ this._options.analyzeFunctionBodiesPredicate =
+ options.analyzeFunctionBodiesPredicate;
+ this._options.generateImplicitErrors = options.generateImplicitErrors;
+ this._options.generateSdkErrors = options.generateSdkErrors;
+ this._options.dart2jsHint = options.dart2jsHint;
+ this._options.enableStrictCallChecks = options.enableStrictCallChecks;
+ this._options.enableSuperMixins = options.enableSuperMixins;
+ 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;
+ if (needsRecompute) {
+ for (WorkManager workManager in workManagers) {
+ workManager.onAnalysisOptionsChanged();
+ }
+ }
+ }
+
+ @override
+ void set analysisPriorityOrder(List<Source> sources) {
+ if (sources == null || sources.isEmpty) {
+ _priorityOrder = Source.EMPTY_LIST;
+ } else {
+ while (sources.remove(null)) {
+ // Nothing else to do.
+ }
+ if (sources.isEmpty) {
+ _priorityOrder = Source.EMPTY_LIST;
+ } else {
+ _priorityOrder = sources;
+ }
+ }
+ for (WorkManager workManager in workManagers) {
+ workManager.applyPriorityTargets(_priorityOrder);
+ }
+ }
+
+ @override
+ set contentCache(ContentCache value) {
+ _contentCache = value;
+ }
+
+ @override
+ DeclaredVariables get declaredVariables => _declaredVariables;
+
+ @override
+ List<AnalysisTarget> get explicitTargets {
+ List<AnalysisTarget> targets = <AnalysisTarget>[];
+ MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator();
+ while (iterator.moveNext()) {
+ if (iterator.value.explicitlyAdded) {
+ targets.add(iterator.key);
+ }
+ }
+ return targets;
+ }
+
+ @override
+ List<Source> get htmlSources => _getSources(SourceKind.HTML);
+
+ @override
+ Stream<ImplicitAnalysisEvent> get implicitAnalysisEvents =>
+ _implicitAnalysisEventsController.stream;
+
+ @override
+ bool get isDisposed => _disposed;
+
+ @override
+ List<Source> get launchableClientLibrarySources {
+ List<Source> sources = <Source>[];
+ for (Source source in _cache.sources) {
+ CacheEntry entry = _cache.get(source);
+ if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY &&
+ !source.isInSystemLibrary &&
+ isClientLibrary(source)) {
+ sources.add(source);
+ }
+ }
+ return sources;
+ }
+
+ @override
+ List<Source> get launchableServerLibrarySources {
+ List<Source> sources = <Source>[];
+ for (Source source in _cache.sources) {
+ CacheEntry entry = _cache.get(source);
+ if (source is Source &&
+ entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY &&
+ !source.isInSystemLibrary &&
+ isServerLibrary(source)) {
+ sources.add(source);
+ }
+ }
+ return sources;
+ }
+
+ @override
+ List<Source> get librarySources => _getSources(SourceKind.LIBRARY);
+
+ @override
+ Stream<SourcesChangedEvent> get onSourcesChanged =>
+ _onSourcesChangedController.stream;
+
+ /**
+ * Make _pendingFutureSources available to unit tests.
+ */
+ HashMap<AnalysisTarget,
+ List<PendingFuture>> get pendingFutureSources_forTesting =>
+ _pendingFutureTargets;
+
+ @override
+ List<Source> get prioritySources => _priorityOrder;
+
+ @override
+ List<AnalysisTarget> get priorityTargets => prioritySources;
+
+ @override
+ CachePartition get privateAnalysisCachePartition => _privatePartition;
+
+ @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;
+ _cache = createCacheFromSourceFactory(factory);
+ for (WorkManager workManager in workManagers) {
+ workManager.onSourceFactoryChanged();
+ }
+ }
+
+ @override
+ List<Source> get sources {
+ return _cache.sources.toList();
+ }
+
+ /**
+ * 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<Source> get sourcesNeedingProcessing {
+ HashSet<Source> sources = new HashSet<Source>();
+ bool hintsEnabled = _options.hint;
+ bool lintsEnabled = _options.lint;
+
+ MapIterator<AnalysisTarget, CacheEntry> iterator =
+ _privatePartition.iterator();
+ while (iterator.moveNext()) {
+ AnalysisTarget target = iterator.key;
+ if (target is Source) {
+ _getSourcesNeedingProcessing(
+ target, iterator.value, false, hintsEnabled, lintsEnabled, sources);
+ }
+ }
+ return new List<Source>.from(sources);
+ }
+
+ @override
+ AnalysisContextStatistics get statistics {
+ AnalysisContextStatisticsImpl statistics =
+ new AnalysisContextStatisticsImpl();
+ // TODO(brianwilkerson) Implement this.
+// visitCacheItems(statistics._internalPutCacheItem);
+// statistics.partitionData = _cache.partitionData;
+ return statistics;
+ }
+
+ List<Source> get test_priorityOrder => _priorityOrder;
+
+ @override
+ TypeProvider get typeProvider {
+ // Make sure a task didn't accidentally try to call back into the context
+ // to retrieve the type provider.
+ assert(!driver.isTaskRunning);
+
+ 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 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<Source> removedSources =
+ new List<Source>.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);
+ }
+ for (WorkManager workManager in workManagers) {
+ workManager.applyChange(
+ changeSet.addedSources, changeSet.changedSources, removedSources);
+ }
+ _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(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<Token> 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<AnalysisError> computeErrors(Source source) {
+ String name = source.shortName;
+ if (AnalysisEngine.isDartFileName(name)) {
+ return computeResult(source, DART_ERRORS);
+ } else if (AnalysisEngine.isHtmlFileName(name)) {
+ return computeResult(source, HTML_ERRORS);
+ }
+ return AnalysisError.NO_ERRORS;
+ }
+
+ @override
+ List<Source> computeExportedLibraries(Source source) =>
+ computeResult(source, EXPORTED_LIBRARIES);
+
+ @override
+ @deprecated
+ HtmlElement computeHtmlElement(Source source) {
+ // TODO(brianwilkerson) Remove this method after switching to the new task
+ // model.
+ throw new UnimplementedError('Not supported in the new task model');
+ }
+
+ @override
+ List<Source> computeImportedLibraries(Source source) =>
+ computeResult(source, EXPLICITLY_IMPORTED_LIBRARIES);
+
+ @override
+ SourceKind computeKindOf(Source source) {
+ String name = source.shortName;
+ if (AnalysisEngine.isDartFileName(name)) {
+ return computeResult(source, SOURCE_KIND);
+ } else if (AnalysisEngine.isHtmlFileName(name)) {
+ return SourceKind.HTML;
+ }
+ return SourceKind.UNKNOWN;
+ }
+
+ @override
+ LibraryElement computeLibraryElement(Source source) {
+ //_computeResult(source, HtmlEntry.ELEMENT);
+ return computeResult(source, LIBRARY_ELEMENT);
+ }
+
+ @override
+ LineInfo computeLineInfo(Source source) => computeResult(source, LINE_INFO);
+
+ @override
+ @deprecated
+ CompilationUnit computeResolvableCompilationUnit(Source source) {
+ return null;
+ }
+
+ @override
+ CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync(
+ Source unitSource, Source librarySource) {
+ if (!AnalysisEngine.isDartFileName(unitSource.shortName) ||
+ !AnalysisEngine.isDartFileName(librarySource.shortName)) {
+ return new CancelableFuture.error(new AnalysisNotScheduledError());
+ }
+ var unitTarget = new LibrarySpecificUnit(librarySource, unitSource);
+ return new _AnalysisFutureHelper<CompilationUnit>(this)
+ .computeAsync(unitTarget, (CacheEntry entry) {
+ CacheState state = entry.getState(RESOLVED_UNIT);
+ if (state == CacheState.ERROR) {
+ throw entry.exception;
+ } else if (state == CacheState.INVALID) {
+ return null;
+ }
+ return entry.getValue(RESOLVED_UNIT);
+ }, () {
+ dartWorkManager.addPriorityResult(unitTarget, RESOLVED_UNIT);
+ });
+ }
+
+ @override
+ Object /*V*/ computeResult(
+ AnalysisTarget target, ResultDescriptor /*<V>*/ descriptor) {
+ CacheEntry entry = getCacheEntry(target);
+ CacheState state = entry.getState(descriptor);
+ if (state == CacheState.FLUSHED || state == CacheState.INVALID) {
+ driver.computeResult(target, descriptor);
+ }
+ state = entry.getState(descriptor);
+ if (state == CacheState.ERROR) {
+ throw new AnalysisException(
+ 'Cannot compute $descriptor for $target', entry.exception);
+ }
+ return entry.getValue(descriptor);
+ }
+
+ /**
+ * Create an analysis cache based on the given source [factory].
+ */
+ AnalysisCache createCacheFromSourceFactory(SourceFactory factory) {
+ if (factory == null) {
+ return new AnalysisCache(<CachePartition>[_privatePartition]);
+ }
+ DartSdk sdk = factory.dartSdk;
+ if (sdk == null) {
+ return new AnalysisCache(<CachePartition>[_privatePartition]);
+ }
+ return new AnalysisCache(<CachePartition>[
+ AnalysisEngine.instance.partitionManager_new.forSdk(sdk),
+ _privatePartition
+ ]);
+ }
+
+ @override
+ void dispose() {
+ _disposed = true;
+ for (List<PendingFuture> pendingFutures in _pendingFutureTargets.values) {
+ for (PendingFuture pendingFuture in pendingFutures) {
+ pendingFuture.forciblyComplete();
+ }
+ }
+ _pendingFutureTargets.clear();
+ _privatePartition.dispose();
+ }
+
+ @override
+ List<CompilationUnit> ensureResolvedDartUnits(Source unitSource) {
+ // Check every library.
+ List<CompilationUnit> units = <CompilationUnit>[];
+ List<Source> containingLibraries = getLibrariesContaining(unitSource);
+ for (Source librarySource in containingLibraries) {
+ LibrarySpecificUnit target =
+ new LibrarySpecificUnit(librarySource, unitSource);
+ CompilationUnit unit = getResult(target, RESOLVED_UNIT);
+ if (unit == null) {
+ units = null;
+ break;
+ }
+ units.add(unit);
+ }
+ // If we have results, then we're done.
+ if (units != null) {
+ return units;
+ }
+ // Schedule recomputing RESOLVED_UNIT results.
+ for (Source librarySource in containingLibraries) {
+ LibrarySpecificUnit target =
+ new LibrarySpecificUnit(librarySource, unitSource);
+ if (_cache.getState(target, RESOLVED_UNIT) == CacheState.FLUSHED) {
+ dartWorkManager.addPriorityResult(target, RESOLVED_UNIT);
+ }
+ }
+ return null;
+ }
+
+ @override
+ bool exists(Source source) {
+ if (source == null) {
+ return false;
+ }
+ if (_contentCache.getContents(source) != null) {
+ return true;
+ }
+ return source.exists();
+ }
+
+ @override
+ CacheEntry getCacheEntry(AnalysisTarget target) {
+ CacheEntry entry = _cache.get(target);
+ if (entry == null) {
+ entry = new CacheEntry(target);
+ if (target is Source) {
+ entry.modificationTime = getModificationStamp(target);
+ }
+ _cache.put(entry);
+ if (target is Source) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(target, true));
+ }
+ }
+ return entry;
+ }
+
+ @override
+ CompilationUnitElement getCompilationUnitElement(
+ Source unitSource, Source librarySource) {
+ AnalysisTarget target = new LibrarySpecificUnit(librarySource, unitSource);
+ return getResult(target, COMPILATION_UNIT_ELEMENT);
+ }
+
+ @override
+ TimestampedData<String> getContents(Source source) {
+ String contents = _contentCache.getContents(source);
+ if (contents != null) {
+ return new TimestampedData<String>(
+ _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<String> 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;
+ }
+ } 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) {
+ List<AnalysisError> allErrors = <AnalysisError>[];
+ for (WorkManager workManager in workManagers) {
+ List<AnalysisError> errors = workManager.getErrors(source);
+ allErrors.addAll(errors);
+ }
+ LineInfo lineInfo = getLineInfo(source);
+ return new AnalysisErrorInfoImpl(allErrors, lineInfo);
+ }
+
+ @override
+ @deprecated
+ HtmlElement getHtmlElement(Source source) {
+ // TODO(brianwilkerson) Remove this method after switching to the new task
+ // model.
+ throw new UnimplementedError('Not supported in the new task model');
+ }
+
+ @override
+ List<Source> getHtmlFilesReferencing(Source source) {
+ if (!AnalysisEngine.isDartFileName(source.shortName)) {
+ return Source.EMPTY_LIST;
+ }
+ List<Source> htmlSources = <Source>[];
+ List<Source> librarySources = getLibrariesContaining(source);
+ for (Source source in _cache.sources) {
+ if (AnalysisEngine.isHtmlFileName(source.shortName)) {
+ List<Source> referencedLibraries =
+ getResult(source, REFERENCED_LIBRARIES);
+ if (_containsAny(referencedLibraries, librarySources)) {
+ htmlSources.add(source);
+ }
+ }
+ }
+ if (htmlSources.isEmpty) {
+ return Source.EMPTY_LIST;
+ }
+ return htmlSources;
+ }
+
+ @override
+ SourceKind getKindOf(Source source) {
+ String name = source.shortName;
+ if (AnalysisEngine.isDartFileName(name)) {
+ return getResult(source, SOURCE_KIND);
+ } else if (AnalysisEngine.isHtmlFileName(name)) {
+ return SourceKind.HTML;
+ }
+ return SourceKind.UNKNOWN;
+ }
+
+ @override
+ List<Source> getLibrariesContaining(Source source) {
+ SourceKind kind = getKindOf(source);
+ if (kind == SourceKind.LIBRARY) {
+ return <Source>[source];
+ }
+ return dartWorkManager.getLibrariesContainingPart(source);
+ }
+
+ @override
+ List<Source> getLibrariesDependingOn(Source librarySource) {
+ List<Source> dependentLibraries = <Source>[];
+ for (Source source in _cache.sources) {
+ CacheEntry entry = _cache.get(source);
+ if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) {
+ if (_contains(entry.getValue(EXPORTED_LIBRARIES), librarySource)) {
+ dependentLibraries.add(source);
+ }
+ if (_contains(entry.getValue(IMPORTED_LIBRARIES), librarySource)) {
+ dependentLibraries.add(source);
+ }
+ }
+ }
+ if (dependentLibraries.isEmpty) {
+ return Source.EMPTY_LIST;
+ }
+ return dependentLibraries;
+ }
+
+ @override
+ List<Source> getLibrariesReferencedFromHtml(Source htmlSource) {
+ CacheEntry entry = _cache.get(htmlSource);
+ if (entry != null) {
+ return entry.getValue(REFERENCED_LIBRARIES);
+ }
+ return Source.EMPTY_LIST;
+ }
+
+ @override
+ LibraryElement getLibraryElement(Source source) =>
+ getResult(source, LIBRARY_ELEMENT);
+
+ @override
+ LineInfo getLineInfo(Source source) => getResult(source, LINE_INFO);
+
+ @override
+ int getModificationStamp(Source source) {
+ int stamp = _contentCache.getModificationStamp(source);
+ if (stamp != null) {
+ return stamp;
+ }
+ return source.modificationStamp;
+ }
+
+ @override
+ ChangeNoticeImpl getNotice(Source source) {
+ ChangeNoticeImpl notice = _pendingNotices[source];
+ if (notice == null) {
+ notice = new ChangeNoticeImpl(source);
+ _pendingNotices[source] = notice;
+ }
+ return notice;
+ }
+
+ @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.
+ // TODO(brianwilkerson) The public namespace used to be cached, but no
+ // longer is. Konstantin adds:
+ // The only client of this method is NamespaceBuilder._createExportMapping(),
+ // and it is not used with tasks - instead we compute export namespace once
+ // using BuildExportNamespaceTask and reuse in scopes.
+ NamespaceBuilder builder = new NamespaceBuilder();
+ return builder.createPublicNamespaceForLibrary(library);
+ }
+
+ @override
+ CompilationUnit getResolvedCompilationUnit(
+ Source unitSource, LibraryElement library) {
+ if (library == null ||
+ !AnalysisEngine.isDartFileName(unitSource.shortName)) {
+ return null;
+ }
+ return getResolvedCompilationUnit2(unitSource, library.source);
+ }
+
+ @override
+ CompilationUnit getResolvedCompilationUnit2(
+ Source unitSource, Source librarySource) {
+ if (!AnalysisEngine.isDartFileName(unitSource.shortName) ||
+ !AnalysisEngine.isDartFileName(librarySource.shortName)) {
+ return null;
+ }
+ return getResult(
+ new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
+ }
+
+ @override
+ @deprecated
+ ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) {
+ // TODO(brianwilkerson) Remove this method after switching to the new task
+ // model.
+ throw new UnimplementedError('Not supported in the new task model');
+ }
+
+ @override
+ Object getResult(AnalysisTarget target, ResultDescriptor result) {
+ return _cache.getValue(target, result);
+ }
+
+ @override
+ List<Source> getSourcesWithFullName(String path) {
+ return analysisCache.getSourcesWithFullName(path);
+ }
+
+ @override
+ bool handleContentsChanged(
+ Source source, String originalContents, String newContents, bool notify) {
+ CacheEntry entry = _cache.get(source);
+ if (entry == null) {
+ return false;
+ }
+ bool changed = newContents != originalContents;
+ if (newContents != null) {
+ if (changed) {
+ if (!analysisOptions.incremental ||
+ !_tryPoorMansIncrementalResolution(source, newContents)) {
+ _sourceChanged(source);
+ }
+ entry.modificationTime = _contentCache.getModificationStamp(source);
+ entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST);
+ } else {
+ entry.modificationTime = _contentCache.getModificationStamp(source);
+ }
+ } else if (originalContents != null) {
+ // 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<String> fileContents = getContents(source);
+ newContents = fileContents.data;
+ entry.modificationTime = fileContents.modificationTime;
+ if (newContents == originalContents) {
+ entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST);
+ changed = false;
+ }
+ } catch (e) {}
+ // If not the same content (e.g. the file is being closed without save),
+ // then force analysis.
+ if (changed) {
+ if (!analysisOptions.incremental ||
+ !_tryPoorMansIncrementalResolution(source, newContents)) {
+ _sourceChanged(source);
+ }
+ }
+ }
+ if (notify && changed) {
+ _onSourcesChangedController
+ .add(new SourcesChangedEvent.changedContent(source, newContents));
+ }
+ return changed;
+ }
+
+ @override
+ void invalidateLibraryHints(Source librarySource) {
+ List<Source> sources = getResult(librarySource, UNITS);
+ if (sources != null) {
+ for (Source source in sources) {
+ getCacheEntry(source).setState(HINTS, CacheState.INVALID);
+ }
+ }
+ }
+
+ @override
+ bool isClientLibrary(Source librarySource) {
+ CacheEntry entry = _cache.get(librarySource);
+ return entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE);
+ }
+
+ @override
+ bool isServerLibrary(Source librarySource) {
+ CacheEntry entry = _cache.get(librarySource);
+ return !entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE);
+ }
+
+ @override
+ Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) {
+ return driver.onResultComputed(descriptor);
+ }
+
+ @override
+ CompilationUnit parseCompilationUnit(Source source) {
+ if (!AnalysisEngine.isDartFileName(source.shortName)) {
+ return null;
+ }
+ try {
+ getContents(source);
+ } catch (exception, stackTrace) {
+ throw new AnalysisException('Could not get contents of $source',
+ new CaughtException(exception, stackTrace));
+ }
+ return computeResult(source, PARSED_UNIT);
+ }
+
+ @override
+ Document parseHtmlDocument(Source source) {
+ if (!AnalysisEngine.isHtmlFileName(source.shortName)) {
+ return null;
+ }
+ return computeResult(source, HTML_DOCUMENT);
+ }
+
+ @override
+ @deprecated // use parseHtmlDocument(source)
+ ht.HtmlUnit parseHtmlUnit(Source source) {
+ // TODO(brianwilkerson) Remove this method after switching to the new task
+ // model.
+ throw new UnimplementedError('Not supported in the new task model');
+ }
+
+ @override
+ AnalysisResult performAnalysisTask() {
+ return PerformanceStatistics.performAnaysis.makeCurrentWhile(() {
+ _evaluatePendingFutures();
+ bool done = !driver.performAnalysisTask();
+ List<ChangeNotice> notices = _getChangeNotices(done);
+ if (notices != null) {
+ int noticeCount = notices.length;
+ for (int i = 0; i < noticeCount; i++) {
+ ChangeNotice notice = notices[i];
+ _notifyErrors(notice.source, notice.errors, notice.lineInfo);
+ }
+ }
+ return new AnalysisResult(notices, -1, '', -1);
+ });
+ }
+
+ @override
+ void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
+ elementMap.forEach((Source librarySource, LibraryElement library) {
+ //
+ // Cache the element in the library's info.
+ //
+ CacheEntry entry = getCacheEntry(librarySource);
+ setValue(ResultDescriptor result, value) {
+ entry.setValue(result, value, TargetedResult.EMPTY_LIST);
+ }
+ setValue(BUILD_DIRECTIVES_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(BUILD_LIBRARY_ERRORS, AnalysisError.NO_ERRORS);
+ // CLASS_ELEMENTS
+ setValue(COMPILATION_UNIT_ELEMENT, library.definingCompilationUnit);
+ // CONSTRUCTORS
+ // CONSTRUCTORS_ERRORS
+ entry.setState(CONTENT, CacheState.FLUSHED);
+ setValue(EXPORTED_LIBRARIES, Source.EMPTY_LIST);
+ // EXPORT_SOURCE_CLOSURE
+ setValue(IMPORTED_LIBRARIES, Source.EMPTY_LIST);
+ // IMPORT_SOURCE_CLOSURE
+ setValue(INCLUDED_PARTS, Source.EMPTY_LIST);
+ setValue(IS_CLIENT, true);
+ setValue(IS_LAUNCHABLE, false);
+ setValue(LIBRARY_ELEMENT, library);
+ setValue(LIBRARY_ELEMENT1, library);
+ setValue(LIBRARY_ELEMENT2, library);
+ setValue(LIBRARY_ELEMENT3, library);
+ setValue(LIBRARY_ELEMENT4, library);
+ setValue(LIBRARY_ELEMENT5, library);
+ setValue(LINE_INFO, new LineInfo(<int>[0]));
+ setValue(PARSE_ERRORS, AnalysisError.NO_ERRORS);
+ entry.setState(PARSED_UNIT, CacheState.FLUSHED);
+ entry.setState(RESOLVE_TYPE_NAMES_ERRORS, CacheState.FLUSHED);
+ setValue(SCAN_ERRORS, AnalysisError.NO_ERRORS);
+ setValue(SOURCE_KIND, SourceKind.LIBRARY);
+ entry.setState(TOKEN_STREAM, CacheState.FLUSHED);
+ setValue(UNITS, <Source>[librarySource]);
+
+ LibrarySpecificUnit unit =
+ new LibrarySpecificUnit(librarySource, librarySource);
+ entry = getCacheEntry(unit);
+ setValue(HINTS, AnalysisError.NO_ERRORS);
+ // dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS);
+ entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT6, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT7, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
+ // USED_IMPORTED_ELEMENTS
+ // USED_LOCAL_ELEMENTS
+ setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
+ });
+
+ CacheEntry entry = getCacheEntry(AnalysisContextTarget.request);
+ entry.setValue(TYPE_PROVIDER, typeProvider, TargetedResult.EMPTY_LIST);
+ }
+
+ @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) {
+ if (!AnalysisEngine.isDartFileName(unitSource.shortName) ||
+ !AnalysisEngine.isDartFileName(librarySource.shortName)) {
+ return null;
+ }
+ return computeResult(
+ new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT);
+ }
+
+ @override
+ @deprecated
+ ht.HtmlUnit resolveHtmlUnit(Source htmlSource) {
+ // TODO(brianwilkerson) Remove this method after switching to the new task
+ // model.
+ throw new UnimplementedError('Not supported in the new task model');
+ }
+
+ @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
+ bool shouldErrorsBeAnalyzed(Source source, Object entry) {
+ CacheEntry entry = analysisCache.get(source);
+ if (source.isInSystemLibrary) {
+ return _options.generateSdkErrors;
+ } else if (!entry.explicitlyAdded) {
+ return _options.generateImplicitErrors;
+ } else {
+ return true;
+ }
+ }
+
+ @override
+ void test_flushAstStructures(Source source) {
+ CacheEntry entry = getCacheEntry(source);
+ entry.setState(PARSED_UNIT, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT6, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
+ entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
+ }
+
+ @override
+ bool validateCacheConsistency() {
+ int consistencyCheckStart = JavaSystem.nanoTime();
+ HashSet<Source> changedSources = new HashSet<Source>();
+ HashSet<Source> missingSources = new HashSet<Source>();
+ for (Source source in _cache.sources) {
+ CacheEntry entry = _cache.get(source);
+ int sourceTime = getModificationStamp(source);
+ if (sourceTime != entry.modificationTime) {
+ changedSources.add(source);
+ }
+ if (entry.exception != null) {
+ if (!exists(source)) {
+ missingSources.add(source);
+ }
+ }
+ }
+ for (Source source in changedSources) {
+ _sourceChanged(source);
+ }
+ int removalCount = 0;
+ for (Source source in missingSources) {
+ if (getLibrariesContaining(source).isEmpty &&
+ getLibrariesDependingOn(source).isEmpty) {
+ _removeFromCache(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;
+ }
+
+ @deprecated
+ @override
+ void visitCacheItems(void callback(Source source, SourceEntry dartEntry,
+ DataDescriptor rowDesc, CacheState state)) {
+ // TODO(brianwilkerson) Figure out where this is used and either remove it
+ // or adjust the call sites to use CacheEntry's.
+// bool hintsEnabled = _options.hint;
+// bool lintsEnabled = _options.lint;
+// MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator();
+// while (iterator.moveNext()) {
+// Source source = iterator.key;
+// cache.CacheEntry entry = iterator.value;
+// for (DataDescriptor descriptor in entry.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, entry, descriptor, entry.getState(descriptor));
+// }
+// if (entry is DartEntry) {
+// // get library-specific values
+// List<Source> librarySources = getLibrariesContaining(source);
+// for (Source librarySource in librarySources) {
+// for (DataDescriptor descriptor in entry.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 (!entry.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, entry, descriptor,
+// entry.getStateInLibrary(descriptor, librarySource));
+// }
+// }
+// }
+// }
+ }
+
+ @override
+ void visitContentCache(ContentCacheVisitor visitor) {
+ _contentCache.accept(visitor);
+ }
+
+ /**
+ * Add all of the sources contained in the given source [container] to the
+ * given list of [sources].
+ */
+ void _addSourcesInContainer(List<Source> sources, SourceContainer container) {
+ for (Source source in _cache.sources) {
+ if (container.contains(source)) {
+ sources.add(source);
+ }
+ }
+ }
+
+ /**
+ * Remove the given [pendingFuture] from [_pendingFutureTargets], since the
+ * client has indicated its computation is not needed anymore.
+ */
+ void _cancelFuture(PendingFuture pendingFuture) {
+ List<PendingFuture> pendingFutures =
+ _pendingFutureTargets[pendingFuture.target];
+ if (pendingFutures != null) {
+ pendingFutures.remove(pendingFuture);
+ if (pendingFutures.isEmpty) {
+ _pendingFutureTargets.remove(pendingFuture.target);
+ }
+ }
+ }
+
+ /**
+ * 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<Source> 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<Source> sources, List<Source> 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) {
+ _sourceChanged(source);
+ changed = true;
+ CacheEntry entry = _cache.get(source);
+ if (entry != null) {
+ entry.modificationTime = _contentCache.getModificationStamp(source);
+ entry.setValue(CONTENT, contents, TargetedResult.EMPTY_LIST);
+ }
+ }
+ } else if (originalContents != null) {
+ _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 cache entry for the given [source]. The source was explicitly
+ * added to this context if [explicitlyAdded] is `true`. Return the cache
+ * entry that was created.
+ */
+ CacheEntry _createCacheEntry(Source source, bool explicitlyAdded) {
+ CacheEntry entry = new CacheEntry(source);
+ entry.modificationTime = getModificationStamp(source);
+ entry.explicitlyAdded = explicitlyAdded;
+ _cache.put(entry);
+ if (!explicitlyAdded) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(source, true));
+ }
+ return entry;
+ }
+
+ /**
+ * Return a list containing all of the cache entries for targets associated
+ * with the given [source].
+ */
+ List<CacheEntry> _entriesFor(Source source) {
+ List<CacheEntry> entries = <CacheEntry>[];
+ MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator();
+ while (iterator.moveNext()) {
+ if (iterator.key.source == source) {
+ entries.add(iterator.value);
+ }
+ }
+ return entries;
+ }
+
+ void _evaluatePendingFutures() {
+ for (AnalysisTarget target in _pendingFutureTargets.keys) {
+ CacheEntry cacheEntry = _cache.get(target);
+ List<PendingFuture> pendingFutures = _pendingFutureTargets[target];
+ for (int i = 0; i < pendingFutures.length;) {
+ if (pendingFutures[i].evaluate(cacheEntry)) {
+ pendingFutures.removeAt(i);
+ } else {
+ i++;
+ }
+ }
+ }
+ }
+
+ /**
+ * 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<ChangeNotice> _getChangeNotices(bool nullIfEmpty) {
+ if (_pendingNotices.isEmpty) {
+ if (nullIfEmpty) {
+ return null;
+ }
+ return ChangeNoticeImpl.EMPTY_LIST;
+ }
+ List<ChangeNotice> notices = new List.from(_pendingNotices.values);
+ _pendingNotices.clear();
+ return notices;
+ }
+
+ /**
+ * Return a list containing all of the sources known to this context that have
+ * the given [kind].
+ */
+ List<Source> _getSources(SourceKind kind) {
+ List<Source> sources = <Source>[];
+ if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) {
+ for (Source source in _cache.sources) {
+ CacheEntry entry = _cache.get(source);
+ if (entry.getValue(SOURCE_KIND) == kind) {
+ sources.add(source);
+ }
+ }
+ } else if (kind == SourceKind.HTML) {
+ for (Source source in _cache.sources) {
+ if (AnalysisEngine.isHtmlFileName(source.shortName)) {
+ sources.add(source);
+ }
+ }
+ }
+ if (sources.isEmpty) {
+ return Source.EMPTY_LIST;
+ }
+ 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 is intended to be used for testing purposes only.
+ */
+ void _getSourcesNeedingProcessing(
+ Source source,
+ CacheEntry entry,
+ bool isPriority,
+ bool hintsEnabled,
+ bool lintsEnabled,
+ HashSet<Source> sources) {
+ CacheState state = entry.getState(CONTENT);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ state = entry.getState(SOURCE_KIND);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ SourceKind kind = entry.getValue(SOURCE_KIND);
+ if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) {
+ state = entry.getState(SCAN_ERRORS);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ state = entry.getState(PARSE_ERRORS);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+// if (isPriority) {
+// if (!entry.hasResolvableCompilationUnit) {
+// sources.add(source);
+// return;
+// }
+// }
+ for (Source librarySource in getLibrariesContaining(source)) {
+ CacheEntry libraryEntry = _cache.get(librarySource);
+ state = libraryEntry.getState(LIBRARY_ELEMENT);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ CacheEntry unitEntry =
+ _cache.get(new LibrarySpecificUnit(librarySource, source));
+ state = unitEntry.getState(RESOLVED_UNIT);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ if (shouldErrorsBeAnalyzed(source, unitEntry)) {
+ state = unitEntry.getState(VERIFY_ERRORS);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ if (hintsEnabled) {
+ state = unitEntry.getState(HINTS);
+ if (state == CacheState.INVALID ||
+ (isPriority && state == CacheState.FLUSHED)) {
+ sources.add(source);
+ return;
+ } else if (state == CacheState.ERROR) {
+ return;
+ }
+ }
+// if (lintsEnabled) {
+// state = unitEntry.getState(LINTS);
+// if (state == CacheState.INVALID ||
+// (isPriority && state == CacheState.FLUSHED)) {
+// sources.add(source);
+// return;
+// } else if (state == CacheState.ERROR) {
+// return;
+// }
+// }
+ }
+ }
+// } else if (kind == SourceKind.HTML) {
+// CacheState parsedUnitState = entry.getState(HtmlEntry.PARSED_UNIT);
+// if (parsedUnitState == CacheState.INVALID ||
+// (isPriority && parsedUnitState == CacheState.FLUSHED)) {
+// sources.add(source);
+// return;
+// }
+// CacheState resolvedUnitState =
+// entry.getState(HtmlEntry.RESOLVED_UNIT);
+// if (resolvedUnitState == CacheState.INVALID ||
+// (isPriority && resolvedUnitState == CacheState.FLUSHED)) {
+// sources.add(source);
+// return;
+// }
+ }
+ }
+
+ /**
+ * Log the given debugging [message].
+ */
+ void _logInformation(String message) {
+ AnalysisEngine.instance.logger.logInformation(message);
+ }
+
+ /**
+ * 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<AnalysisError> errors, LineInfo lineInfo) {
+ int count = _listeners.length;
+ for (int i = 0; i < count; i++) {
+ _listeners[i].computedErrors(this, source, errors, lineInfo);
+ }
+ }
+
+ void _removeFromCache(Source source) {
+ CacheEntry entry = _cache.remove(source);
+ if (entry != null && !entry.explicitlyAdded) {
+ _implicitAnalysisEventsController
+ .add(new ImplicitAnalysisEvent(source, false));
+ }
+ }
+
+ /**
+ * Remove the given [source] from the priority order if it is in the list.
+ */
+ void _removeFromPriorityOrder(Source source) {
+ int count = _priorityOrder.length;
+ List<Source> newOrder = <Source>[];
+ for (int i = 0; i < count; i++) {
+ if (_priorityOrder[i] != source) {
+ newOrder.add(_priorityOrder[i]);
+ }
+ }
+ if (newOrder.length < count) {
+ analysisPriorityOrder = newOrder;
+ }
+ }
+
+ /**
+ * Create an entry for the newly added [source] and invalidate any sources
+ * that referenced the source before it existed.
+ */
+ void _sourceAvailable(Source source) {
+ // TODO(brianwilkerson) This method needs to check whether the source was
+ // previously being implicitly analyzed. If so, the cache entry needs to be
+ // update to reflect the new status and an event needs to be generated to
+ // inform clients that it is no longer being implicitly analyzed.
+ CacheEntry entry = _cache.get(source);
+ if (entry == null) {
+ _createCacheEntry(source, true);
+ } else {
+ entry.modificationTime = getModificationStamp(source);
+ entry.setState(CONTENT, CacheState.INVALID);
+ }
+ }
+
+ /**
+ * Invalidate the [source] that was changed and any sources that referenced
+ * the source before it existed.
+ */
+ void _sourceChanged(Source source) {
+ CacheEntry entry = _cache.get(source);
+ // If the source is removed, we don't care about it.
+ if (entry == null) {
+ return;
+ }
+ // Check whether the content of the source is the same as it was the last
+ // time.
+ String sourceContent = entry.getValue(CONTENT);
+ if (sourceContent != null) {
+ entry.setState(CONTENT, CacheState.FLUSHED);
+ try {
+ TimestampedData<String> fileContents = getContents(source);
+ if (fileContents.data == sourceContent) {
+ int time = fileContents.modificationTime;
+ for (CacheEntry entry in _entriesFor(source)) {
+ entry.modificationTime = time;
+ }
+ return;
+ }
+ } catch (e) {}
+ }
+ // We need to invalidate the cache.
+ {
+ Object delta = null;
+ if (AnalysisEngine.instance.limitInvalidationInTaskModel &&
+ AnalysisEngine.isDartFileName(source.fullName)) {
+ // TODO(scheglov) Incorrect implementation in general.
+ entry.setState(TOKEN_STREAM, CacheState.FLUSHED);
+ entry.setState(PARSED_UNIT, CacheState.FLUSHED);
+ List<Source> librarySources = getLibrariesContaining(source);
+ if (librarySources.length == 1) {
+ Source librarySource = librarySources[0];
+ CompilationUnit oldUnit =
+ getResolvedCompilationUnit2(source, librarySource);
+ if (oldUnit != null) {
+ CompilationUnit newUnit = parseCompilationUnit(source);
+ IncrementalCompilationUnitElementBuilder builder =
+ new IncrementalCompilationUnitElementBuilder(oldUnit, newUnit);
+ builder.build();
+ CompilationUnitElementDelta unitDelta = builder.unitDelta;
+ if (!unitDelta.hasDirectiveChange) {
+ DartDelta dartDelta = new DartDelta(source);
+ dartDelta.hasDirectiveChange = unitDelta.hasDirectiveChange;
+ unitDelta.addedDeclarations.forEach(dartDelta.elementAdded);
+ unitDelta.removedDeclarations.forEach(dartDelta.elementRemoved);
+// print(
+// 'dartDelta: add=${dartDelta.addedNames} remove=${dartDelta.removedNames}');
+ delta = dartDelta;
+ entry.setState(CONTENT, CacheState.INVALID, delta: delta);
+ return;
+ }
+ }
+ }
+ }
+ entry.setState(CONTENT, CacheState.INVALID);
+ }
+ for (WorkManager workManager in workManagers) {
+ workManager.applyChange(
+ Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST);
+ }
+ }
+
+ /**
+ * Record that the give [source] has been deleted.
+ */
+ void _sourceDeleted(Source source) {
+ // TODO(brianwilkerson) Implement or remove this.
+// 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<Source> libraries = new HashSet<Source>();
+// 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));
+// }
+ _removeFromPriorityOrder(source);
+ }
+
+ /**
+ * Record that the given [source] has been removed.
+ */
+ void _sourceRemoved(Source source) {
+ _removeFromCache(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
+ CacheEntry sourceEntry = _cache.get(unitSource);
+ if (sourceEntry == null) {
+ return false;
+ }
+ // prepare the (only) library source
+ List<Source> librarySources = getLibrariesContaining(unitSource);
+ if (librarySources.length != 1) {
+ return false;
+ }
+ Source librarySource = librarySources[0];
+ CacheEntry unitEntry =
+ _cache.get(new LibrarySpecificUnit(librarySource, unitSource));
+ if (unitEntry == null) {
+ return false;
+ }
+ // 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,
+ null,
+ sourceEntry,
+ unitEntry,
+ 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
+ {
+ ChangeNoticeImpl notice = getNotice(unitSource);
+ notice.resolvedDartUnit = oldUnit;
+ AnalysisErrorInfo errorInfo = getErrors(unitSource);
+ notice.setErrors(errorInfo.errors, errorInfo.lineInfo);
+ }
+ // schedule
+ dartWorkManager.unitIncrementallyResolved(librarySource, unitSource);
+ // OK
+ return true;
+ });
+ }
+}
+
+/**
+ * An object that manages the partitions that can be shared between analysis
+ * contexts.
+ */
+class PartitionManager {
+ /**
+ * A table mapping SDK's to the partitions used for those SDK's.
+ */
+ HashMap<DartSdk, SdkCachePartition> _sdkPartitions =
+ new HashMap<DartSdk, SdkCachePartition>();
+
+ /**
+ * Clear any cached data being maintained by this manager.
+ */
+ void clearCache() {
+ _sdkPartitions.clear();
+ }
+
+ /**
+ * Return the partition being used for the given [sdk], creating the partition
+ * if necessary.
+ */
+ SdkCachePartition forSdk(DartSdk sdk) {
+ // Call sdk.context now, because when it creates a new
+ // InternalAnalysisContext instance, it calls forSdk() again, so creates an
+ // SdkCachePartition instance.
+ // So, if we initialize context after "partition == null", we end up
+ // with two SdkCachePartition instances.
+ InternalAnalysisContext sdkContext = sdk.context;
+ // Check cache for an existing partition.
+ SdkCachePartition partition = _sdkPartitions[sdk];
+ if (partition == null) {
+ partition = new SdkCachePartition(sdkContext);
+ _sdkPartitions[sdk] = partition;
+ }
+ return partition;
+ }
+}
+
+/**
+ * Representation of a pending computation which is based on the results of
+ * analysis that may or may not have been completed.
+ */
+class PendingFuture<T> {
+ /**
+ * The context in which this computation runs.
+ */
+ final AnalysisContextImpl _context;
+
+ /**
+ * The target used by this computation to compute its value.
+ */
+ final AnalysisTarget target;
+
+ /**
+ * The function which implements the computation.
+ */
+ final PendingFutureComputer<T> _computeValue;
+
+ /**
+ * The completer that should be completed once the computation has succeeded.
+ */
+ CancelableCompleter<T> _completer;
+
+ PendingFuture(this._context, this.target, this._computeValue) {
+ _completer = new CancelableCompleter<T>(_onCancel);
+ }
+
+ /**
+ * Retrieve the future which will be completed when this object is
+ * successfully evaluated.
+ */
+ CancelableFuture<T> get future => _completer.future;
+
+ /**
+ * Execute [_computeValue], passing it the given [entry], and complete
+ * the pending future if it's appropriate to do so. If the pending future is
+ * completed by this call, true is returned; otherwise false is returned.
+ *
+ * Once this function has returned true, it should not be called again.
+ *
+ * Other than completing the future, this method is free of side effects.
+ * Note that any code the client has attached to the future will be executed
+ * in a microtask, so there is no danger of side effects occurring due to
+ * client callbacks.
+ */
+ bool evaluate(CacheEntry entry) {
+ assert(!_completer.isCompleted);
+ try {
+ T result = _computeValue(entry);
+ if (result == null) {
+ return false;
+ } else {
+ _completer.complete(result);
+ return true;
+ }
+ } catch (exception, stackTrace) {
+ _completer.completeError(exception, stackTrace);
+ return true;
+ }
+ }
+
+ /**
+ * No further analysis updates are expected which affect this future, so
+ * complete it with an AnalysisNotScheduledError in order to avoid
+ * deadlocking the client.
+ */
+ void forciblyComplete() {
+ try {
+ throw new AnalysisNotScheduledError();
+ } catch (exception, stackTrace) {
+ _completer.completeError(exception, stackTrace);
+ }
+ }
+
+ void _onCancel() {
+ _context._cancelFuture(this);
+ }
+}
+
+/**
+ * An [AnalysisContext] that only contains sources for a Dart SDK.
+ */
+class SdkAnalysisContext extends AnalysisContextImpl {
+ @override
+ AnalysisCache createCacheFromSourceFactory(SourceFactory factory) {
+ if (factory == null) {
+ return super.createCacheFromSourceFactory(factory);
+ }
+ DartSdk sdk = factory.dartSdk;
+ if (sdk == null) {
+ throw new IllegalArgumentException(
+ "The source factory for an SDK analysis context must have a DartUriResolver");
+ }
+ return new AnalysisCache(<CachePartition>[
+ AnalysisEngine.instance.partitionManager_new.forSdk(sdk)
+ ]);
+ }
+}
+
+/**
+ * A helper class used to create futures for [AnalysisContextImpl].
+ * Using a helper class allows us to preserve the generic parameter T.
+ */
+class _AnalysisFutureHelper<T> {
+ final AnalysisContextImpl _context;
+
+ _AnalysisFutureHelper(this._context);
+
+ /**
+ * Return a future that will be completed with the result of calling
+ * [computeValue]. If [computeValue] returns non-`null`, the future will be
+ * completed immediately with the resulting value. If it returns `null`, then
+ * [scheduleComputation] is invoked to schedule analysis that will produce
+ * the required result, and [computeValue] will be re-executed in the future,
+ * after the next time the cached information for [target] has changed. If
+ * [computeValue] throws an exception, the future will fail with that
+ * exception.
+ *
+ * If the [computeValue] still returns `null` after there is no further
+ * analysis to be done for [target], then the future will be completed with
+ * the error AnalysisNotScheduledError.
+ *
+ * Since [computeValue] will be 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.
+ */
+ CancelableFuture<T> computeAsync(AnalysisTarget target,
+ T computeValue(CacheEntry entry), void scheduleComputation()) {
+ if (_context.isDisposed) {
+ // No further analysis is expected, so return a future that completes
+ // immediately with AnalysisNotScheduledError.
+ return new CancelableFuture.error(new AnalysisNotScheduledError());
+ }
+ CacheEntry entry = _context.getCacheEntry(target);
+ PendingFuture pendingFuture =
+ new PendingFuture<T>(_context, target, computeValue);
+ if (!pendingFuture.evaluate(entry)) {
+ _context._pendingFutureTargets
+ .putIfAbsent(target, () => <PendingFuture>[])
+ .add(pendingFuture);
+ scheduleComputation();
+ }
+ return pendingFuture.future;
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698