OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library analyzer.src.context.context; | |
6 | |
7 import 'dart:async'; | |
8 import 'dart:collection'; | |
9 | |
10 import 'package:analyzer/instrumentation/instrumentation.dart'; | |
11 import 'package:analyzer/src/cancelable_future.dart'; | |
12 import 'package:analyzer/src/context/cache.dart'; | |
13 import 'package:analyzer/src/generated/ast.dart'; | |
14 import 'package:analyzer/src/generated/constant.dart'; | |
15 import 'package:analyzer/src/generated/element.dart'; | |
16 import 'package:analyzer/src/generated/engine.dart' | |
17 hide | |
18 AnalysisCache, | |
19 CachePartition, | |
20 SdkCachePartition, | |
21 UniversalCachePartition, | |
22 WorkManager; | |
23 import 'package:analyzer/src/generated/error.dart'; | |
24 import 'package:analyzer/src/generated/html.dart' as ht show HtmlUnit; | |
25 import 'package:analyzer/src/generated/incremental_resolver.dart'; | |
26 import 'package:analyzer/src/generated/java_core.dart'; | |
27 import 'package:analyzer/src/generated/java_engine.dart'; | |
28 import 'package:analyzer/src/generated/resolver.dart'; | |
29 import 'package:analyzer/src/generated/scanner.dart'; | |
30 import 'package:analyzer/src/generated/sdk.dart' show DartSdk; | |
31 import 'package:analyzer/src/generated/source.dart'; | |
32 import 'package:analyzer/src/generated/utilities_collection.dart'; | |
33 import 'package:analyzer/src/task/dart.dart'; | |
34 import 'package:analyzer/src/task/dart_work_manager.dart'; | |
35 import 'package:analyzer/src/task/driver.dart'; | |
36 import 'package:analyzer/src/task/html.dart'; | |
37 import 'package:analyzer/src/task/html_work_manager.dart'; | |
38 import 'package:analyzer/src/task/incremental_element_builder.dart'; | |
39 import 'package:analyzer/src/task/manager.dart'; | |
40 import 'package:analyzer/task/dart.dart'; | |
41 import 'package:analyzer/task/general.dart'; | |
42 import 'package:analyzer/task/html.dart'; | |
43 import 'package:analyzer/task/model.dart'; | |
44 import 'package:html/dom.dart' show Document; | |
45 | |
46 /** | |
47 * Type of callback functions used by PendingFuture. Functions of this type | |
48 * should perform a computation based on the data in [entry] and return it. If | |
49 * the computation can't be performed yet because more analysis is needed, | |
50 * `null` should be returned. | |
51 * | |
52 * The function may also throw an exception, in which case the corresponding | |
53 * future will be completed with failure. | |
54 * | |
55 * Because this function is called while the state of analysis is being updated, | |
56 * it should be free of side effects so that it doesn't cause reentrant changes | |
57 * to the analysis state. | |
58 */ | |
59 typedef T PendingFutureComputer<T>(CacheEntry entry); | |
60 | |
61 /** | |
62 * An [AnalysisContext] in which analysis can be performed. | |
63 */ | |
64 class AnalysisContextImpl implements InternalAnalysisContext { | |
65 /** | |
66 * The next context identifier. | |
67 */ | |
68 static int _NEXT_ID = 0; | |
69 | |
70 /** | |
71 * The unique identifier of this context. | |
72 */ | |
73 final int _id = _NEXT_ID++; | |
74 | |
75 /** | |
76 * A client-provided name used to identify this context, or `null` if the | |
77 * client has not provided a name. | |
78 */ | |
79 String name; | |
80 | |
81 /** | |
82 * The set of analysis options controlling the behavior of this context. | |
83 */ | |
84 AnalysisOptionsImpl _options = new AnalysisOptionsImpl(); | |
85 | |
86 /** | |
87 * A flag indicating whether this context is disposed. | |
88 */ | |
89 bool _disposed = false; | |
90 | |
91 /** | |
92 * A cache of content used to override the default content of a source. | |
93 */ | |
94 ContentCache _contentCache = new ContentCache(); | |
95 | |
96 /** | |
97 * The source factory used to create the sources that can be analyzed in this | |
98 * context. | |
99 */ | |
100 SourceFactory _sourceFactory; | |
101 | |
102 /** | |
103 * The set of declared variables used when computing constant values. | |
104 */ | |
105 DeclaredVariables _declaredVariables = new DeclaredVariables(); | |
106 | |
107 /** | |
108 * The partition that contains analysis results that are not shared with other | |
109 * contexts. | |
110 */ | |
111 CachePartition _privatePartition; | |
112 | |
113 /** | |
114 * The cache in which information about the results associated with targets | |
115 * are stored. | |
116 */ | |
117 AnalysisCache _cache; | |
118 | |
119 /** | |
120 * The task manager used to manage the tasks used to analyze code. | |
121 */ | |
122 TaskManager _taskManager; | |
123 | |
124 /** | |
125 * The [DartWorkManager] instance that performs Dart specific scheduling. | |
126 */ | |
127 DartWorkManager dartWorkManager; | |
128 | |
129 /** | |
130 * The work manager that performs HTML specific scheduling. | |
131 */ | |
132 HtmlWorkManager htmlWorkManager; | |
133 | |
134 /** | |
135 * The analysis driver used to perform analysis. | |
136 */ | |
137 AnalysisDriver driver; | |
138 | |
139 /** | |
140 * A list containing sources for which data should not be flushed. | |
141 */ | |
142 List<Source> _priorityOrder = <Source>[]; | |
143 | |
144 /** | |
145 * A map from all sources for which there are futures pending to a list of | |
146 * the corresponding PendingFuture objects. These sources will be analyzed | |
147 * in the same way as priority sources, except with higher priority. | |
148 */ | |
149 HashMap<AnalysisTarget, List<PendingFuture>> _pendingFutureTargets = | |
150 new HashMap<AnalysisTarget, List<PendingFuture>>(); | |
151 | |
152 /** | |
153 * A table mapping sources to the change notices that are waiting to be | |
154 * returned related to that source. | |
155 */ | |
156 HashMap<Source, ChangeNoticeImpl> _pendingNotices = | |
157 new HashMap<Source, ChangeNoticeImpl>(); | |
158 | |
159 /** | |
160 * The [TypeProvider] for this context, `null` if not yet created. | |
161 */ | |
162 TypeProvider _typeProvider; | |
163 | |
164 /** | |
165 * The controller for sending [SourcesChangedEvent]s. | |
166 */ | |
167 StreamController<SourcesChangedEvent> _onSourcesChangedController; | |
168 | |
169 /** | |
170 * The listeners that are to be notified when various analysis results are | |
171 * produced in this context. | |
172 */ | |
173 List<AnalysisListener> _listeners = new List<AnalysisListener>(); | |
174 | |
175 /** | |
176 * The most recently incrementally resolved source, or `null` when it was | |
177 * already validated, or the most recent change was not incrementally resolved
. | |
178 */ | |
179 Source incrementalResolutionValidation_lastUnitSource; | |
180 | |
181 /** | |
182 * The most recently incrementally resolved library source, or `null` when it | |
183 * was already validated, or the most recent change was not incrementally | |
184 * resolved. | |
185 */ | |
186 Source incrementalResolutionValidation_lastLibrarySource; | |
187 | |
188 /** | |
189 * The result of incremental resolution result of | |
190 * [incrementalResolutionValidation_lastSource]. | |
191 */ | |
192 CompilationUnit incrementalResolutionValidation_lastUnit; | |
193 | |
194 /** | |
195 * A factory to override how the [ResolverVisitor] is created. | |
196 */ | |
197 ResolverVisitorFactory resolverVisitorFactory; | |
198 | |
199 /** | |
200 * A factory to override how the [TypeResolverVisitor] is created. | |
201 */ | |
202 TypeResolverVisitorFactory typeResolverVisitorFactory; | |
203 | |
204 /** | |
205 * A factory to override how [LibraryResolver] is created. | |
206 */ | |
207 LibraryResolverFactory libraryResolverFactory; | |
208 | |
209 /** | |
210 * Initialize a newly created analysis context. | |
211 */ | |
212 AnalysisContextImpl() { | |
213 _privatePartition = new UniversalCachePartition(this); | |
214 _cache = createCacheFromSourceFactory(null); | |
215 _taskManager = AnalysisEngine.instance.taskManager; | |
216 // TODO(scheglov) Get WorkManager(Factory)(s) from plugins. | |
217 dartWorkManager = new DartWorkManager(this); | |
218 htmlWorkManager = new HtmlWorkManager(this); | |
219 driver = new AnalysisDriver( | |
220 _taskManager, <WorkManager>[dartWorkManager, htmlWorkManager], this); | |
221 _onSourcesChangedController = | |
222 new StreamController<SourcesChangedEvent>.broadcast(); | |
223 } | |
224 | |
225 @override | |
226 AnalysisCache get analysisCache => _cache; | |
227 | |
228 @override | |
229 AnalysisOptions get analysisOptions => _options; | |
230 | |
231 @override | |
232 void set analysisOptions(AnalysisOptions options) { | |
233 bool needsRecompute = this._options.analyzeFunctionBodiesPredicate != | |
234 options.analyzeFunctionBodiesPredicate || | |
235 this._options.generateImplicitErrors != | |
236 options.generateImplicitErrors || | |
237 this._options.generateSdkErrors != options.generateSdkErrors || | |
238 this._options.dart2jsHint != options.dart2jsHint || | |
239 (this._options.hint && !options.hint) || | |
240 (this._options.lint && !options.lint) || | |
241 this._options.preserveComments != options.preserveComments || | |
242 this._options.enableStrictCallChecks != options.enableStrictCallChecks; | |
243 int cacheSize = options.cacheSize; | |
244 if (this._options.cacheSize != cacheSize) { | |
245 this._options.cacheSize = cacheSize; | |
246 } | |
247 this._options.analyzeFunctionBodiesPredicate = | |
248 options.analyzeFunctionBodiesPredicate; | |
249 this._options.generateImplicitErrors = options.generateImplicitErrors; | |
250 this._options.generateSdkErrors = options.generateSdkErrors; | |
251 this._options.dart2jsHint = options.dart2jsHint; | |
252 this._options.enableStrictCallChecks = options.enableStrictCallChecks; | |
253 this._options.hint = options.hint; | |
254 this._options.incremental = options.incremental; | |
255 this._options.incrementalApi = options.incrementalApi; | |
256 this._options.incrementalValidation = options.incrementalValidation; | |
257 this._options.lint = options.lint; | |
258 this._options.preserveComments = options.preserveComments; | |
259 if (needsRecompute) { | |
260 dartWorkManager.onAnalysisOptionsChanged(); | |
261 htmlWorkManager.onAnalysisOptionsChanged(); | |
262 } | |
263 } | |
264 | |
265 @override | |
266 void set analysisPriorityOrder(List<Source> sources) { | |
267 if (sources == null || sources.isEmpty) { | |
268 _priorityOrder = Source.EMPTY_LIST; | |
269 } else { | |
270 while (sources.remove(null)) { | |
271 // Nothing else to do. | |
272 } | |
273 if (sources.isEmpty) { | |
274 _priorityOrder = Source.EMPTY_LIST; | |
275 } else { | |
276 _priorityOrder = sources; | |
277 } | |
278 } | |
279 dartWorkManager.applyPriorityTargets(_priorityOrder); | |
280 htmlWorkManager.applyPriorityTargets(_priorityOrder); | |
281 } | |
282 | |
283 @override | |
284 set contentCache(ContentCache value) { | |
285 _contentCache = value; | |
286 } | |
287 | |
288 @override | |
289 DeclaredVariables get declaredVariables => _declaredVariables; | |
290 | |
291 @override | |
292 List<AnalysisTarget> get explicitTargets { | |
293 List<AnalysisTarget> targets = <AnalysisTarget>[]; | |
294 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | |
295 while (iterator.moveNext()) { | |
296 if (iterator.value.explicitlyAdded) { | |
297 targets.add(iterator.key); | |
298 } | |
299 } | |
300 return targets; | |
301 } | |
302 | |
303 @override | |
304 List<Source> get htmlSources => _getSources(SourceKind.HTML); | |
305 | |
306 @override | |
307 bool get isDisposed => _disposed; | |
308 | |
309 @override | |
310 List<Source> get launchableClientLibrarySources { | |
311 List<Source> sources = <Source>[]; | |
312 for (Source source in _cache.sources) { | |
313 CacheEntry entry = _cache.get(source); | |
314 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && | |
315 !source.isInSystemLibrary && | |
316 isClientLibrary(source)) { | |
317 sources.add(source); | |
318 } | |
319 } | |
320 return sources; | |
321 } | |
322 | |
323 @override | |
324 List<Source> get launchableServerLibrarySources { | |
325 List<Source> sources = <Source>[]; | |
326 for (Source source in _cache.sources) { | |
327 CacheEntry entry = _cache.get(source); | |
328 if (source is Source && | |
329 entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY && | |
330 !source.isInSystemLibrary && | |
331 isServerLibrary(source)) { | |
332 sources.add(source); | |
333 } | |
334 } | |
335 return sources; | |
336 } | |
337 | |
338 @override | |
339 List<Source> get librarySources => _getSources(SourceKind.LIBRARY); | |
340 | |
341 @override | |
342 Stream<SourcesChangedEvent> get onSourcesChanged => | |
343 _onSourcesChangedController.stream; | |
344 | |
345 /** | |
346 * Make _pendingFutureSources available to unit tests. | |
347 */ | |
348 HashMap<AnalysisTarget, List<PendingFuture>> get pendingFutureSources_forTesti
ng => | |
349 _pendingFutureTargets; | |
350 | |
351 @override | |
352 List<Source> get prioritySources => _priorityOrder; | |
353 | |
354 @override | |
355 List<AnalysisTarget> get priorityTargets => prioritySources; | |
356 | |
357 @override | |
358 CachePartition get privateAnalysisCachePartition => _privatePartition; | |
359 | |
360 @override | |
361 SourceFactory get sourceFactory => _sourceFactory; | |
362 | |
363 @override | |
364 void set sourceFactory(SourceFactory factory) { | |
365 if (identical(_sourceFactory, factory)) { | |
366 return; | |
367 } else if (factory.context != null) { | |
368 throw new IllegalStateException( | |
369 "Source factories cannot be shared between contexts"); | |
370 } | |
371 if (_sourceFactory != null) { | |
372 _sourceFactory.context = null; | |
373 } | |
374 factory.context = this; | |
375 _sourceFactory = factory; | |
376 _cache = createCacheFromSourceFactory(factory); | |
377 dartWorkManager.onSourceFactoryChanged(); | |
378 htmlWorkManager.onSourceFactoryChanged(); | |
379 } | |
380 | |
381 @override | |
382 List<Source> get sources { | |
383 return _cache.sources.toList(); | |
384 } | |
385 | |
386 /** | |
387 * Return a list of the sources that would be processed by | |
388 * [performAnalysisTask]. This method duplicates, and must therefore be kept | |
389 * in sync with, [getNextAnalysisTask]. This method is intended to be used for | |
390 * testing purposes only. | |
391 */ | |
392 List<Source> get sourcesNeedingProcessing { | |
393 HashSet<Source> sources = new HashSet<Source>(); | |
394 bool hintsEnabled = _options.hint; | |
395 bool lintsEnabled = _options.lint; | |
396 | |
397 MapIterator<AnalysisTarget, CacheEntry> iterator = | |
398 _privatePartition.iterator(); | |
399 while (iterator.moveNext()) { | |
400 AnalysisTarget target = iterator.key; | |
401 if (target is Source) { | |
402 _getSourcesNeedingProcessing( | |
403 target, iterator.value, false, hintsEnabled, lintsEnabled, sources); | |
404 } | |
405 } | |
406 return new List<Source>.from(sources); | |
407 } | |
408 | |
409 @override | |
410 AnalysisContextStatistics get statistics { | |
411 AnalysisContextStatisticsImpl statistics = | |
412 new AnalysisContextStatisticsImpl(); | |
413 // TODO(brianwilkerson) Implement this. | |
414 // visitCacheItems(statistics._internalPutCacheItem); | |
415 // statistics.partitionData = _cache.partitionData; | |
416 return statistics; | |
417 } | |
418 | |
419 List<Source> get test_priorityOrder => _priorityOrder; | |
420 | |
421 @override | |
422 TypeProvider get typeProvider { | |
423 // Make sure a task didn't accidentally try to call back into the context | |
424 // to retrieve the type provider. | |
425 assert(!driver.isTaskRunning); | |
426 | |
427 if (_typeProvider != null) { | |
428 return _typeProvider; | |
429 } | |
430 Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE); | |
431 if (coreSource == null) { | |
432 throw new AnalysisException("Could not create a source for dart:core"); | |
433 } | |
434 LibraryElement coreElement = computeLibraryElement(coreSource); | |
435 if (coreElement == null) { | |
436 throw new AnalysisException("Could not create an element for dart:core"); | |
437 } | |
438 Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC); | |
439 if (asyncSource == null) { | |
440 throw new AnalysisException("Could not create a source for dart:async"); | |
441 } | |
442 LibraryElement asyncElement = computeLibraryElement(asyncSource); | |
443 if (asyncElement == null) { | |
444 throw new AnalysisException("Could not create an element for dart:async"); | |
445 } | |
446 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
447 return _typeProvider; | |
448 } | |
449 | |
450 /** | |
451 * Sets the [TypeProvider] for this context. | |
452 */ | |
453 void set typeProvider(TypeProvider typeProvider) { | |
454 _typeProvider = typeProvider; | |
455 } | |
456 | |
457 @override | |
458 void addListener(AnalysisListener listener) { | |
459 if (!_listeners.contains(listener)) { | |
460 _listeners.add(listener); | |
461 } | |
462 } | |
463 | |
464 @override | |
465 void applyAnalysisDelta(AnalysisDelta delta) { | |
466 ChangeSet changeSet = new ChangeSet(); | |
467 delta.analysisLevels.forEach((Source source, AnalysisLevel level) { | |
468 if (level == AnalysisLevel.NONE) { | |
469 changeSet.removedSource(source); | |
470 } else { | |
471 changeSet.addedSource(source); | |
472 } | |
473 }); | |
474 applyChanges(changeSet); | |
475 } | |
476 | |
477 @override | |
478 void applyChanges(ChangeSet changeSet) { | |
479 if (changeSet.isEmpty) { | |
480 return; | |
481 } | |
482 // | |
483 // First, compute the list of sources that have been removed. | |
484 // | |
485 List<Source> removedSources = | |
486 new List<Source>.from(changeSet.removedSources); | |
487 for (SourceContainer container in changeSet.removedContainers) { | |
488 _addSourcesInContainer(removedSources, container); | |
489 } | |
490 // | |
491 // Then determine which cached results are no longer valid. | |
492 // | |
493 for (Source source in changeSet.addedSources) { | |
494 _sourceAvailable(source); | |
495 } | |
496 for (Source source in changeSet.changedSources) { | |
497 if (_contentCache.getContents(source) != null) { | |
498 // This source is overridden in the content cache, so the change will | |
499 // have no effect. Just ignore it to avoid wasting time doing | |
500 // re-analysis. | |
501 continue; | |
502 } | |
503 _sourceChanged(source); | |
504 } | |
505 changeSet.changedContents.forEach((Source key, String value) { | |
506 _contentsChanged(key, value, false); | |
507 }); | |
508 changeSet.changedRanges | |
509 .forEach((Source source, ChangeSet_ContentChange change) { | |
510 _contentRangeChanged(source, change.contents, change.offset, | |
511 change.oldLength, change.newLength); | |
512 }); | |
513 for (Source source in changeSet.deletedSources) { | |
514 _sourceDeleted(source); | |
515 } | |
516 for (Source source in removedSources) { | |
517 _sourceRemoved(source); | |
518 } | |
519 dartWorkManager.applyChange( | |
520 changeSet.addedSources, changeSet.changedSources, removedSources); | |
521 htmlWorkManager.applyChange( | |
522 changeSet.addedSources, changeSet.changedSources, removedSources); | |
523 _onSourcesChangedController.add(new SourcesChangedEvent(changeSet)); | |
524 } | |
525 | |
526 @override | |
527 String computeDocumentationComment(Element element) { | |
528 if (element == null) { | |
529 return null; | |
530 } | |
531 Source source = element.source; | |
532 if (source == null) { | |
533 return null; | |
534 } | |
535 CompilationUnit unit = parseCompilationUnit(source); | |
536 if (unit == null) { | |
537 return null; | |
538 } | |
539 NodeLocator locator = new NodeLocator(element.nameOffset); | |
540 AstNode nameNode = locator.searchWithin(unit); | |
541 while (nameNode != null) { | |
542 if (nameNode is AnnotatedNode) { | |
543 Comment comment = nameNode.documentationComment; | |
544 if (comment == null) { | |
545 return null; | |
546 } | |
547 StringBuffer buffer = new StringBuffer(); | |
548 List<Token> tokens = comment.tokens; | |
549 for (int i = 0; i < tokens.length; i++) { | |
550 if (i > 0) { | |
551 buffer.write("\n"); | |
552 } | |
553 buffer.write(tokens[i].lexeme); | |
554 } | |
555 return buffer.toString(); | |
556 } | |
557 nameNode = nameNode.parent; | |
558 } | |
559 return null; | |
560 } | |
561 | |
562 @override | |
563 List<AnalysisError> computeErrors(Source source) { | |
564 String name = source.shortName; | |
565 if (AnalysisEngine.isDartFileName(name)) { | |
566 return computeResult(source, DART_ERRORS); | |
567 } else if (AnalysisEngine.isHtmlFileName(name)) { | |
568 return computeResult(source, HTML_ERRORS); | |
569 } | |
570 return AnalysisError.NO_ERRORS; | |
571 } | |
572 | |
573 @override | |
574 List<Source> computeExportedLibraries(Source source) => | |
575 computeResult(source, EXPORTED_LIBRARIES); | |
576 | |
577 @override | |
578 @deprecated | |
579 HtmlElement computeHtmlElement(Source source) { | |
580 // TODO(brianwilkerson) Remove this method after switching to the new task | |
581 // model. | |
582 throw new UnimplementedError('Not supported in the new task model'); | |
583 } | |
584 | |
585 @override | |
586 List<Source> computeImportedLibraries(Source source) => | |
587 computeResult(source, EXPLICITLY_IMPORTED_LIBRARIES); | |
588 | |
589 @override | |
590 SourceKind computeKindOf(Source source) { | |
591 String name = source.shortName; | |
592 if (AnalysisEngine.isDartFileName(name)) { | |
593 return computeResult(source, SOURCE_KIND); | |
594 } else if (AnalysisEngine.isHtmlFileName(name)) { | |
595 return SourceKind.HTML; | |
596 } | |
597 return SourceKind.UNKNOWN; | |
598 } | |
599 | |
600 @override | |
601 LibraryElement computeLibraryElement(Source source) { | |
602 //_computeResult(source, HtmlEntry.ELEMENT); | |
603 return computeResult(source, LIBRARY_ELEMENT); | |
604 } | |
605 | |
606 @override | |
607 LineInfo computeLineInfo(Source source) => computeResult(source, LINE_INFO); | |
608 | |
609 @override | |
610 @deprecated | |
611 CompilationUnit computeResolvableCompilationUnit(Source source) { | |
612 return null; | |
613 } | |
614 | |
615 @override | |
616 CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync( | |
617 Source unitSource, Source librarySource) { | |
618 if (!AnalysisEngine.isDartFileName(unitSource.shortName) || | |
619 !AnalysisEngine.isDartFileName(librarySource.shortName)) { | |
620 return new CancelableFuture.error(new AnalysisNotScheduledError()); | |
621 } | |
622 var unitTarget = new LibrarySpecificUnit(librarySource, unitSource); | |
623 return new _AnalysisFutureHelper<CompilationUnit>(this).computeAsync( | |
624 unitTarget, (CacheEntry entry) { | |
625 CacheState state = entry.getState(RESOLVED_UNIT); | |
626 if (state == CacheState.ERROR) { | |
627 throw entry.exception; | |
628 } else if (state == CacheState.INVALID) { | |
629 return null; | |
630 } | |
631 return entry.getValue(RESOLVED_UNIT); | |
632 }, () { | |
633 dartWorkManager.addPriorityResult(unitTarget, RESOLVED_UNIT); | |
634 }); | |
635 } | |
636 | |
637 Object /*V*/ computeResult( | |
638 AnalysisTarget target, ResultDescriptor /*<V>*/ descriptor) { | |
639 CacheEntry entry = getCacheEntry(target); | |
640 CacheState state = entry.getState(descriptor); | |
641 if (state == CacheState.FLUSHED || state == CacheState.INVALID) { | |
642 driver.computeResult(target, descriptor); | |
643 } | |
644 state = entry.getState(descriptor); | |
645 if (state == CacheState.ERROR) { | |
646 throw new AnalysisException( | |
647 'Cannot compute $descriptor for $target', entry.exception); | |
648 } | |
649 return entry.getValue(descriptor); | |
650 } | |
651 | |
652 /** | |
653 * Create an analysis cache based on the given source [factory]. | |
654 */ | |
655 AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { | |
656 if (factory == null) { | |
657 return new AnalysisCache(<CachePartition>[_privatePartition]); | |
658 } | |
659 DartSdk sdk = factory.dartSdk; | |
660 if (sdk == null) { | |
661 return new AnalysisCache(<CachePartition>[_privatePartition]); | |
662 } | |
663 return new AnalysisCache(<CachePartition>[ | |
664 AnalysisEngine.instance.partitionManager_new.forSdk(sdk), | |
665 _privatePartition | |
666 ]); | |
667 } | |
668 | |
669 @override | |
670 void dispose() { | |
671 _disposed = true; | |
672 for (List<PendingFuture> pendingFutures in _pendingFutureTargets.values) { | |
673 for (PendingFuture pendingFuture in pendingFutures) { | |
674 pendingFuture.forciblyComplete(); | |
675 } | |
676 } | |
677 _pendingFutureTargets.clear(); | |
678 _privatePartition.dispose(); | |
679 } | |
680 | |
681 @override | |
682 List<CompilationUnit> ensureResolvedDartUnits(Source unitSource) { | |
683 // Check every library. | |
684 List<CompilationUnit> units = <CompilationUnit>[]; | |
685 List<Source> containingLibraries = getLibrariesContaining(unitSource); | |
686 for (Source librarySource in containingLibraries) { | |
687 LibrarySpecificUnit target = | |
688 new LibrarySpecificUnit(librarySource, unitSource); | |
689 CompilationUnit unit = _cache.getValue(target, RESOLVED_UNIT); | |
690 if (unit == null) { | |
691 units = null; | |
692 break; | |
693 } | |
694 units.add(unit); | |
695 } | |
696 // If we have results, then we're done. | |
697 if (units != null) { | |
698 return units; | |
699 } | |
700 // Schedule recomputing RESOLVED_UNIT results. | |
701 for (Source librarySource in containingLibraries) { | |
702 LibrarySpecificUnit target = | |
703 new LibrarySpecificUnit(librarySource, unitSource); | |
704 if (_cache.getState(target, RESOLVED_UNIT) == CacheState.FLUSHED) { | |
705 dartWorkManager.addPriorityResult(target, RESOLVED_UNIT); | |
706 } | |
707 } | |
708 return null; | |
709 } | |
710 | |
711 @override | |
712 bool exists(Source source) { | |
713 if (source == null) { | |
714 return false; | |
715 } | |
716 if (_contentCache.getContents(source) != null) { | |
717 return true; | |
718 } | |
719 return source.exists(); | |
720 } | |
721 | |
722 @override | |
723 CacheEntry getCacheEntry(AnalysisTarget target) { | |
724 CacheEntry entry = _cache.get(target); | |
725 if (entry == null) { | |
726 entry = new CacheEntry(target); | |
727 if (target is Source) { | |
728 entry.modificationTime = getModificationStamp(target); | |
729 } | |
730 _cache.put(entry); | |
731 } | |
732 return entry; | |
733 } | |
734 | |
735 @override | |
736 CompilationUnitElement getCompilationUnitElement( | |
737 Source unitSource, Source librarySource) { | |
738 AnalysisTarget target = new LibrarySpecificUnit(librarySource, unitSource); | |
739 return _cache.getValue(target, COMPILATION_UNIT_ELEMENT); | |
740 } | |
741 | |
742 @override | |
743 TimestampedData<String> getContents(Source source) { | |
744 String contents = _contentCache.getContents(source); | |
745 if (contents != null) { | |
746 return new TimestampedData<String>( | |
747 _contentCache.getModificationStamp(source), contents); | |
748 } | |
749 return source.contents; | |
750 } | |
751 | |
752 @override | |
753 InternalAnalysisContext getContextFor(Source source) { | |
754 InternalAnalysisContext context = _cache.getContextFor(source); | |
755 return context == null ? this : context; | |
756 } | |
757 | |
758 @override | |
759 Element getElement(ElementLocation location) { | |
760 // TODO(brianwilkerson) This should not be a "get" method. | |
761 try { | |
762 List<String> components = location.components; | |
763 Source source = _computeSourceFromEncoding(components[0]); | |
764 String sourceName = source.shortName; | |
765 if (AnalysisEngine.isDartFileName(sourceName)) { | |
766 ElementImpl element = computeLibraryElement(source) as ElementImpl; | |
767 for (int i = 1; i < components.length; i++) { | |
768 if (element == null) { | |
769 return null; | |
770 } | |
771 element = element.getChild(components[i]); | |
772 } | |
773 return element; | |
774 } | |
775 } catch (exception) { | |
776 // If the location cannot be decoded for some reason then the underlying | |
777 // cause should have been logged already and we can fall though to return | |
778 // null. | |
779 } | |
780 return null; | |
781 } | |
782 | |
783 @override | |
784 AnalysisErrorInfo getErrors(Source source) { | |
785 String name = source.shortName; | |
786 if (AnalysisEngine.isDartFileName(name) || source is DartScript) { | |
787 return dartWorkManager.getErrors(source); | |
788 } else if (AnalysisEngine.isHtmlFileName(name)) { | |
789 return htmlWorkManager.getErrors(source); | |
790 } | |
791 return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null); | |
792 } | |
793 | |
794 @override | |
795 @deprecated | |
796 HtmlElement getHtmlElement(Source source) { | |
797 // TODO(brianwilkerson) Remove this method after switching to the new task | |
798 // model. | |
799 throw new UnimplementedError('Not supported in the new task model'); | |
800 } | |
801 | |
802 @override | |
803 List<Source> getHtmlFilesReferencing(Source source) { | |
804 if (!AnalysisEngine.isDartFileName(source.shortName)) { | |
805 return Source.EMPTY_LIST; | |
806 } | |
807 List<Source> htmlSources = <Source>[]; | |
808 List<Source> librarySources = getLibrariesContaining(source); | |
809 for (Source source in _cache.sources) { | |
810 if (AnalysisEngine.isHtmlFileName(source.shortName)) { | |
811 List<Source> referencedLibraries = | |
812 analysisCache.getValue(source, REFERENCED_LIBRARIES); | |
813 if (_containsAny(referencedLibraries, librarySources)) { | |
814 htmlSources.add(source); | |
815 } | |
816 } | |
817 } | |
818 if (htmlSources.isEmpty) { | |
819 return Source.EMPTY_LIST; | |
820 } | |
821 return htmlSources; | |
822 } | |
823 | |
824 @override | |
825 SourceKind getKindOf(Source source) { | |
826 String name = source.shortName; | |
827 if (AnalysisEngine.isDartFileName(name)) { | |
828 return _cache.getValue(source, SOURCE_KIND); | |
829 } else if (AnalysisEngine.isHtmlFileName(name)) { | |
830 return SourceKind.HTML; | |
831 } | |
832 return SourceKind.UNKNOWN; | |
833 } | |
834 | |
835 @override | |
836 List<Source> getLibrariesContaining(Source source) { | |
837 SourceKind kind = getKindOf(source); | |
838 if (kind == SourceKind.LIBRARY) { | |
839 return <Source>[source]; | |
840 } | |
841 return dartWorkManager.getLibrariesContainingPart(source); | |
842 } | |
843 | |
844 @override | |
845 List<Source> getLibrariesDependingOn(Source librarySource) { | |
846 List<Source> dependentLibraries = <Source>[]; | |
847 for (Source source in _cache.sources) { | |
848 CacheEntry entry = _cache.get(source); | |
849 if (entry.getValue(SOURCE_KIND) == SourceKind.LIBRARY) { | |
850 if (_contains(entry.getValue(EXPORTED_LIBRARIES), librarySource)) { | |
851 dependentLibraries.add(source); | |
852 } | |
853 if (_contains(entry.getValue(IMPORTED_LIBRARIES), librarySource)) { | |
854 dependentLibraries.add(source); | |
855 } | |
856 } | |
857 } | |
858 if (dependentLibraries.isEmpty) { | |
859 return Source.EMPTY_LIST; | |
860 } | |
861 return dependentLibraries; | |
862 } | |
863 | |
864 @override | |
865 List<Source> getLibrariesReferencedFromHtml(Source htmlSource) { | |
866 CacheEntry entry = _cache.get(htmlSource); | |
867 if (entry != null) { | |
868 return entry.getValue(REFERENCED_LIBRARIES); | |
869 } | |
870 return Source.EMPTY_LIST; | |
871 } | |
872 | |
873 @override | |
874 LibraryElement getLibraryElement(Source source) => | |
875 _cache.getValue(source, LIBRARY_ELEMENT); | |
876 | |
877 @override | |
878 LineInfo getLineInfo(Source source) => _cache.getValue(source, LINE_INFO); | |
879 | |
880 @override | |
881 int getModificationStamp(Source source) { | |
882 int stamp = _contentCache.getModificationStamp(source); | |
883 if (stamp != null) { | |
884 return stamp; | |
885 } | |
886 return source.modificationStamp; | |
887 } | |
888 | |
889 @override | |
890 ChangeNoticeImpl getNotice(Source source) { | |
891 ChangeNoticeImpl notice = _pendingNotices[source]; | |
892 if (notice == null) { | |
893 notice = new ChangeNoticeImpl(source); | |
894 _pendingNotices[source] = notice; | |
895 } | |
896 return notice; | |
897 } | |
898 | |
899 @override | |
900 Namespace getPublicNamespace(LibraryElement library) { | |
901 // TODO(brianwilkerson) Rename this to not start with 'get'. | |
902 // Note that this is not part of the API of the interface. | |
903 // TODO(brianwilkerson) The public namespace used to be cached, but no | |
904 // longer is. Konstantin adds: | |
905 // The only client of this method is NamespaceBuilder._createExportMapping()
, | |
906 // and it is not used with tasks - instead we compute export namespace once | |
907 // using BuildExportNamespaceTask and reuse in scopes. | |
908 NamespaceBuilder builder = new NamespaceBuilder(); | |
909 return builder.createPublicNamespaceForLibrary(library); | |
910 } | |
911 | |
912 @override | |
913 CompilationUnit getResolvedCompilationUnit( | |
914 Source unitSource, LibraryElement library) { | |
915 if (library == null || | |
916 !AnalysisEngine.isDartFileName(unitSource.shortName)) { | |
917 return null; | |
918 } | |
919 return getResolvedCompilationUnit2(unitSource, library.source); | |
920 } | |
921 | |
922 @override | |
923 CompilationUnit getResolvedCompilationUnit2( | |
924 Source unitSource, Source librarySource) { | |
925 if (!AnalysisEngine.isDartFileName(unitSource.shortName) || | |
926 !AnalysisEngine.isDartFileName(librarySource.shortName)) { | |
927 return null; | |
928 } | |
929 return _cache.getValue( | |
930 new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT); | |
931 } | |
932 | |
933 @override | |
934 @deprecated | |
935 ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) { | |
936 // TODO(brianwilkerson) Remove this method after switching to the new task | |
937 // model. | |
938 throw new UnimplementedError('Not supported in the new task model'); | |
939 } | |
940 | |
941 @override | |
942 List<Source> getSourcesWithFullName(String path) { | |
943 return analysisCache.getSourcesWithFullName(path); | |
944 } | |
945 | |
946 @override | |
947 bool handleContentsChanged( | |
948 Source source, String originalContents, String newContents, bool notify) { | |
949 CacheEntry entry = _cache.get(source); | |
950 if (entry == null) { | |
951 return false; | |
952 } | |
953 bool changed = newContents != originalContents; | |
954 if (newContents != null) { | |
955 if (changed) { | |
956 if (!analysisOptions.incremental || | |
957 !_tryPoorMansIncrementalResolution(source, newContents)) { | |
958 _sourceChanged(source); | |
959 } | |
960 entry.modificationTime = _contentCache.getModificationStamp(source); | |
961 entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST); | |
962 } else { | |
963 entry.modificationTime = _contentCache.getModificationStamp(source); | |
964 } | |
965 } else if (originalContents != null) { | |
966 // We are removing the overlay for the file, check if the file's | |
967 // contents is the same as it was in the overlay. | |
968 try { | |
969 TimestampedData<String> fileContents = getContents(source); | |
970 newContents = fileContents.data; | |
971 entry.modificationTime = fileContents.modificationTime; | |
972 if (newContents == originalContents) { | |
973 entry.setValue(CONTENT, newContents, TargetedResult.EMPTY_LIST); | |
974 changed = false; | |
975 } | |
976 } catch (e) {} | |
977 // If not the same content (e.g. the file is being closed without save), | |
978 // then force analysis. | |
979 if (changed) { | |
980 if (!analysisOptions.incremental || | |
981 !_tryPoorMansIncrementalResolution(source, newContents)) { | |
982 _sourceChanged(source); | |
983 } | |
984 } | |
985 } | |
986 if (notify && changed) { | |
987 _onSourcesChangedController | |
988 .add(new SourcesChangedEvent.changedContent(source, newContents)); | |
989 } | |
990 return changed; | |
991 } | |
992 | |
993 @override | |
994 void invalidateLibraryHints(Source librarySource) { | |
995 List<Source> sources = _cache.getValue(librarySource, UNITS); | |
996 if (sources != null) { | |
997 for (Source source in sources) { | |
998 getCacheEntry(source).setState(HINTS, CacheState.INVALID); | |
999 } | |
1000 } | |
1001 } | |
1002 | |
1003 @override | |
1004 bool isClientLibrary(Source librarySource) { | |
1005 CacheEntry entry = _cache.get(librarySource); | |
1006 return entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE); | |
1007 } | |
1008 | |
1009 @override | |
1010 bool isServerLibrary(Source librarySource) { | |
1011 CacheEntry entry = _cache.get(librarySource); | |
1012 return !entry.getValue(IS_CLIENT) && entry.getValue(IS_LAUNCHABLE); | |
1013 } | |
1014 | |
1015 @override | |
1016 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) { | |
1017 return driver.onResultComputed(descriptor); | |
1018 } | |
1019 | |
1020 @override | |
1021 CompilationUnit parseCompilationUnit(Source source) { | |
1022 if (!AnalysisEngine.isDartFileName(source.shortName)) { | |
1023 return null; | |
1024 } | |
1025 try { | |
1026 getContents(source); | |
1027 } catch (exception, stackTrace) { | |
1028 throw new AnalysisException('Could not get contents of $source', | |
1029 new CaughtException(exception, stackTrace)); | |
1030 } | |
1031 return computeResult(source, PARSED_UNIT); | |
1032 } | |
1033 | |
1034 @override | |
1035 Document parseHtmlDocument(Source source) { | |
1036 if (!AnalysisEngine.isHtmlFileName(source.shortName)) { | |
1037 return null; | |
1038 } | |
1039 return computeResult(source, HTML_DOCUMENT); | |
1040 } | |
1041 | |
1042 @override | |
1043 @deprecated // use parseHtmlDocument(source) | |
1044 ht.HtmlUnit parseHtmlUnit(Source source) { | |
1045 // TODO(brianwilkerson) Remove this method after switching to the new task | |
1046 // model. | |
1047 throw new UnimplementedError('Not supported in the new task model'); | |
1048 } | |
1049 | |
1050 @override | |
1051 AnalysisResult performAnalysisTask() { | |
1052 return PerformanceStatistics.performAnaysis.makeCurrentWhile(() { | |
1053 _evaluatePendingFutures(); | |
1054 bool done = !driver.performAnalysisTask(); | |
1055 List<ChangeNotice> notices = _getChangeNotices(done); | |
1056 if (notices != null) { | |
1057 int noticeCount = notices.length; | |
1058 for (int i = 0; i < noticeCount; i++) { | |
1059 ChangeNotice notice = notices[i]; | |
1060 _notifyErrors(notice.source, notice.errors, notice.lineInfo); | |
1061 } | |
1062 } | |
1063 return new AnalysisResult(notices, -1, '', -1); | |
1064 }); | |
1065 } | |
1066 | |
1067 @override | |
1068 void recordLibraryElements(Map<Source, LibraryElement> elementMap) { | |
1069 elementMap.forEach((Source librarySource, LibraryElement library) { | |
1070 // | |
1071 // Cache the element in the library's info. | |
1072 // | |
1073 CacheEntry entry = getCacheEntry(librarySource); | |
1074 setValue(ResultDescriptor result, value) { | |
1075 entry.setValue(result, value, TargetedResult.EMPTY_LIST); | |
1076 } | |
1077 setValue(BUILD_DIRECTIVES_ERRORS, AnalysisError.NO_ERRORS); | |
1078 setValue(BUILD_LIBRARY_ERRORS, AnalysisError.NO_ERRORS); | |
1079 // CLASS_ELEMENTS | |
1080 setValue(COMPILATION_UNIT_ELEMENT, library.definingCompilationUnit); | |
1081 // CONSTRUCTORS | |
1082 // CONSTRUCTORS_ERRORS | |
1083 entry.setState(CONTENT, CacheState.FLUSHED); | |
1084 setValue(EXPORTED_LIBRARIES, Source.EMPTY_LIST); | |
1085 // EXPORT_SOURCE_CLOSURE | |
1086 setValue(IMPORTED_LIBRARIES, Source.EMPTY_LIST); | |
1087 // IMPORT_SOURCE_CLOSURE | |
1088 setValue(INCLUDED_PARTS, Source.EMPTY_LIST); | |
1089 setValue(IS_CLIENT, true); | |
1090 setValue(IS_LAUNCHABLE, false); | |
1091 setValue(LIBRARY_ELEMENT, library); | |
1092 setValue(LIBRARY_ELEMENT1, library); | |
1093 setValue(LIBRARY_ELEMENT2, library); | |
1094 setValue(LIBRARY_ELEMENT3, library); | |
1095 setValue(LIBRARY_ELEMENT4, library); | |
1096 setValue(LIBRARY_ELEMENT5, library); | |
1097 setValue(LINE_INFO, new LineInfo(<int>[0])); | |
1098 setValue(PARSE_ERRORS, AnalysisError.NO_ERRORS); | |
1099 entry.setState(PARSED_UNIT, CacheState.FLUSHED); | |
1100 entry.setState(RESOLVE_TYPE_NAMES_ERRORS, CacheState.FLUSHED); | |
1101 setValue(SCAN_ERRORS, AnalysisError.NO_ERRORS); | |
1102 setValue(SOURCE_KIND, SourceKind.LIBRARY); | |
1103 entry.setState(TOKEN_STREAM, CacheState.FLUSHED); | |
1104 setValue(UNITS, <Source>[librarySource]); | |
1105 | |
1106 LibrarySpecificUnit unit = | |
1107 new LibrarySpecificUnit(librarySource, librarySource); | |
1108 entry = getCacheEntry(unit); | |
1109 setValue(HINTS, AnalysisError.NO_ERRORS); | |
1110 // dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS); | |
1111 entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED); | |
1112 entry.setState(RESOLVED_UNIT, CacheState.FLUSHED); | |
1113 entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED); | |
1114 entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED); | |
1115 entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED); | |
1116 entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED); | |
1117 entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED); | |
1118 // USED_IMPORTED_ELEMENTS | |
1119 // USED_LOCAL_ELEMENTS | |
1120 setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS); | |
1121 }); | |
1122 | |
1123 CacheEntry entry = getCacheEntry(AnalysisContextTarget.request); | |
1124 entry.setValue(TYPE_PROVIDER, typeProvider, TargetedResult.EMPTY_LIST); | |
1125 } | |
1126 | |
1127 @override | |
1128 void removeListener(AnalysisListener listener) { | |
1129 _listeners.remove(listener); | |
1130 } | |
1131 | |
1132 @override | |
1133 CompilationUnit resolveCompilationUnit( | |
1134 Source unitSource, LibraryElement library) { | |
1135 if (library == null) { | |
1136 return null; | |
1137 } | |
1138 return resolveCompilationUnit2(unitSource, library.source); | |
1139 } | |
1140 | |
1141 @override | |
1142 CompilationUnit resolveCompilationUnit2( | |
1143 Source unitSource, Source librarySource) { | |
1144 if (!AnalysisEngine.isDartFileName(unitSource.shortName) || | |
1145 !AnalysisEngine.isDartFileName(librarySource.shortName)) { | |
1146 return null; | |
1147 } | |
1148 return computeResult( | |
1149 new LibrarySpecificUnit(librarySource, unitSource), RESOLVED_UNIT); | |
1150 } | |
1151 | |
1152 @override | |
1153 @deprecated | |
1154 ht.HtmlUnit resolveHtmlUnit(Source htmlSource) { | |
1155 // TODO(brianwilkerson) Remove this method after switching to the new task | |
1156 // model. | |
1157 throw new UnimplementedError('Not supported in the new task model'); | |
1158 } | |
1159 | |
1160 @override | |
1161 void setChangedContents(Source source, String contents, int offset, | |
1162 int oldLength, int newLength) { | |
1163 if (_contentRangeChanged(source, contents, offset, oldLength, newLength)) { | |
1164 _onSourcesChangedController.add(new SourcesChangedEvent.changedRange( | |
1165 source, contents, offset, oldLength, newLength)); | |
1166 } | |
1167 } | |
1168 | |
1169 @override | |
1170 void setContents(Source source, String contents) { | |
1171 _contentsChanged(source, contents, true); | |
1172 } | |
1173 | |
1174 @override | |
1175 bool shouldErrorsBeAnalyzed(Source source, Object entry) { | |
1176 CacheEntry entry = analysisCache.get(source); | |
1177 if (source.isInSystemLibrary) { | |
1178 return _options.generateSdkErrors; | |
1179 } else if (!entry.explicitlyAdded) { | |
1180 return _options.generateImplicitErrors; | |
1181 } else { | |
1182 return true; | |
1183 } | |
1184 } | |
1185 | |
1186 @override | |
1187 void test_flushAstStructures(Source source) { | |
1188 CacheEntry entry = getCacheEntry(source); | |
1189 entry.setState(PARSED_UNIT, CacheState.FLUSHED); | |
1190 entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED); | |
1191 entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED); | |
1192 entry.setState(RESOLVED_UNIT3, CacheState.FLUSHED); | |
1193 entry.setState(RESOLVED_UNIT4, CacheState.FLUSHED); | |
1194 entry.setState(RESOLVED_UNIT5, CacheState.FLUSHED); | |
1195 entry.setState(RESOLVED_UNIT, CacheState.FLUSHED); | |
1196 } | |
1197 | |
1198 @override | |
1199 bool validateCacheConsistency() { | |
1200 int consistencyCheckStart = JavaSystem.nanoTime(); | |
1201 HashSet<Source> changedSources = new HashSet<Source>(); | |
1202 HashSet<Source> missingSources = new HashSet<Source>(); | |
1203 for (Source source in _cache.sources) { | |
1204 CacheEntry entry = _cache.get(source); | |
1205 int sourceTime = getModificationStamp(source); | |
1206 if (sourceTime != entry.modificationTime) { | |
1207 changedSources.add(source); | |
1208 } | |
1209 if (entry.exception != null) { | |
1210 if (!exists(source)) { | |
1211 missingSources.add(source); | |
1212 } | |
1213 } | |
1214 } | |
1215 for (Source source in changedSources) { | |
1216 _sourceChanged(source); | |
1217 } | |
1218 int removalCount = 0; | |
1219 for (Source source in missingSources) { | |
1220 if (getLibrariesContaining(source).isEmpty && | |
1221 getLibrariesDependingOn(source).isEmpty) { | |
1222 _cache.remove(source); | |
1223 removalCount++; | |
1224 } | |
1225 } | |
1226 int consistencyCheckEnd = JavaSystem.nanoTime(); | |
1227 if (changedSources.length > 0 || missingSources.length > 0) { | |
1228 StringBuffer buffer = new StringBuffer(); | |
1229 buffer.write("Consistency check took "); | |
1230 buffer.write((consistencyCheckEnd - consistencyCheckStart) / 1000000.0); | |
1231 buffer.writeln(" ms and found"); | |
1232 buffer.write(" "); | |
1233 buffer.write(changedSources.length); | |
1234 buffer.writeln(" inconsistent entries"); | |
1235 buffer.write(" "); | |
1236 buffer.write(missingSources.length); | |
1237 buffer.write(" missing sources ("); | |
1238 buffer.write(removalCount); | |
1239 buffer.writeln(" removed"); | |
1240 for (Source source in missingSources) { | |
1241 buffer.write(" "); | |
1242 buffer.writeln(source.fullName); | |
1243 } | |
1244 _logInformation(buffer.toString()); | |
1245 } | |
1246 return changedSources.length > 0; | |
1247 } | |
1248 | |
1249 @deprecated | |
1250 @override | |
1251 void visitCacheItems(void callback(Source source, SourceEntry dartEntry, | |
1252 DataDescriptor rowDesc, CacheState state)) { | |
1253 // TODO(brianwilkerson) Figure out where this is used and either remove it | |
1254 // or adjust the call sites to use CacheEntry's. | |
1255 // bool hintsEnabled = _options.hint; | |
1256 // bool lintsEnabled = _options.lint; | |
1257 // MapIterator<AnalysisTarget, cache.CacheEntry> iterator = _cache.iterator()
; | |
1258 // while (iterator.moveNext()) { | |
1259 // Source source = iterator.key; | |
1260 // cache.CacheEntry entry = iterator.value; | |
1261 // for (DataDescriptor descriptor in entry.descriptors) { | |
1262 // if (descriptor == DartEntry.SOURCE_KIND) { | |
1263 // // The source kind is always valid, so the state isn't interesting. | |
1264 // continue; | |
1265 // } else if (descriptor == DartEntry.CONTAINING_LIBRARIES) { | |
1266 // // The list of containing libraries is always valid, so the state | |
1267 // // isn't interesting. | |
1268 // continue; | |
1269 // } else if (descriptor == DartEntry.PUBLIC_NAMESPACE) { | |
1270 // // The public namespace isn't computed by performAnalysisTask() | |
1271 // // and therefore isn't interesting. | |
1272 // continue; | |
1273 // } else if (descriptor == HtmlEntry.HINTS) { | |
1274 // // We are not currently recording any hints related to HTML. | |
1275 // continue; | |
1276 // } | |
1277 // callback( | |
1278 // source, entry, descriptor, entry.getState(descriptor)); | |
1279 // } | |
1280 // if (entry is DartEntry) { | |
1281 // // get library-specific values | |
1282 // List<Source> librarySources = getLibrariesContaining(source); | |
1283 // for (Source librarySource in librarySources) { | |
1284 // for (DataDescriptor descriptor in entry.libraryDescriptors) { | |
1285 // if (descriptor == DartEntry.BUILT_ELEMENT || | |
1286 // descriptor == DartEntry.BUILT_UNIT) { | |
1287 // // These values are not currently being computed, so their state | |
1288 // // is not interesting. | |
1289 // continue; | |
1290 // } else if (!entry.explicitlyAdded && | |
1291 // !_generateImplicitErrors && | |
1292 // (descriptor == DartEntry.VERIFICATION_ERRORS || | |
1293 // descriptor == DartEntry.HINTS || | |
1294 // descriptor == DartEntry.LINTS)) { | |
1295 // continue; | |
1296 // } else if (source.isInSystemLibrary && | |
1297 // !_generateSdkErrors && | |
1298 // (descriptor == DartEntry.VERIFICATION_ERRORS || | |
1299 // descriptor == DartEntry.HINTS || | |
1300 // descriptor == DartEntry.LINTS)) { | |
1301 // continue; | |
1302 // } else if (!hintsEnabled && descriptor == DartEntry.HINTS) { | |
1303 // continue; | |
1304 // } else if (!lintsEnabled && descriptor == DartEntry.LINTS) { | |
1305 // continue; | |
1306 // } | |
1307 // callback(librarySource, entry, descriptor, | |
1308 // entry.getStateInLibrary(descriptor, librarySource)); | |
1309 // } | |
1310 // } | |
1311 // } | |
1312 // } | |
1313 } | |
1314 | |
1315 @override | |
1316 void visitContentCache(ContentCacheVisitor visitor) { | |
1317 _contentCache.accept(visitor); | |
1318 } | |
1319 | |
1320 /** | |
1321 * Add all of the sources contained in the given source [container] to the | |
1322 * given list of [sources]. | |
1323 */ | |
1324 void _addSourcesInContainer(List<Source> sources, SourceContainer container) { | |
1325 for (Source source in _cache.sources) { | |
1326 if (container.contains(source)) { | |
1327 sources.add(source); | |
1328 } | |
1329 } | |
1330 } | |
1331 | |
1332 /** | |
1333 * Remove the given [pendingFuture] from [_pendingFutureTargets], since the | |
1334 * client has indicated its computation is not needed anymore. | |
1335 */ | |
1336 void _cancelFuture(PendingFuture pendingFuture) { | |
1337 List<PendingFuture> pendingFutures = | |
1338 _pendingFutureTargets[pendingFuture.target]; | |
1339 if (pendingFutures != null) { | |
1340 pendingFutures.remove(pendingFuture); | |
1341 if (pendingFutures.isEmpty) { | |
1342 _pendingFutureTargets.remove(pendingFuture.target); | |
1343 } | |
1344 } | |
1345 } | |
1346 | |
1347 /** | |
1348 * Given the encoded form of a source ([encoding]), use the source factory to | |
1349 * reconstitute the original source. | |
1350 */ | |
1351 Source _computeSourceFromEncoding(String encoding) => | |
1352 _sourceFactory.fromEncoding(encoding); | |
1353 | |
1354 /** | |
1355 * Return `true` if the given list of [sources] contains the given | |
1356 * [targetSource]. | |
1357 */ | |
1358 bool _contains(List<Source> sources, Source targetSource) { | |
1359 for (Source source in sources) { | |
1360 if (source == targetSource) { | |
1361 return true; | |
1362 } | |
1363 } | |
1364 return false; | |
1365 } | |
1366 | |
1367 /** | |
1368 * Return `true` if the given list of [sources] contains any of the given | |
1369 * [targetSources]. | |
1370 */ | |
1371 bool _containsAny(List<Source> sources, List<Source> targetSources) { | |
1372 for (Source targetSource in targetSources) { | |
1373 if (_contains(sources, targetSource)) { | |
1374 return true; | |
1375 } | |
1376 } | |
1377 return false; | |
1378 } | |
1379 | |
1380 /** | |
1381 * Set the contents of the given [source] to the given [contents] and mark the | |
1382 * source as having changed. The additional [offset], [oldLength] and | |
1383 * [newLength] information is used by the context to determine what reanalysis | |
1384 * is necessary. The method [setChangedContents] triggers a source changed | |
1385 * event where as this method does not. | |
1386 */ | |
1387 bool _contentRangeChanged(Source source, String contents, int offset, | |
1388 int oldLength, int newLength) { | |
1389 bool changed = false; | |
1390 String originalContents = _contentCache.setContents(source, contents); | |
1391 if (contents != null) { | |
1392 if (contents != originalContents) { | |
1393 _sourceChanged(source); | |
1394 changed = true; | |
1395 CacheEntry entry = _cache.get(source); | |
1396 if (entry != null) { | |
1397 entry.modificationTime = _contentCache.getModificationStamp(source); | |
1398 entry.setValue(CONTENT, contents, TargetedResult.EMPTY_LIST); | |
1399 } | |
1400 } | |
1401 } else if (originalContents != null) { | |
1402 _sourceChanged(source); | |
1403 changed = true; | |
1404 } | |
1405 return changed; | |
1406 } | |
1407 | |
1408 /** | |
1409 * Set the contents of the given [source] to the given [contents] and mark the | |
1410 * source as having changed. This has the effect of overriding the default | |
1411 * contents of the source. If the contents are `null` the override is removed | |
1412 * so that the default contents will be returned. If [notify] is true, a | |
1413 * source changed event is triggered. | |
1414 */ | |
1415 void _contentsChanged(Source source, String contents, bool notify) { | |
1416 String originalContents = _contentCache.setContents(source, contents); | |
1417 handleContentsChanged(source, originalContents, contents, notify); | |
1418 } | |
1419 | |
1420 /** | |
1421 * Create a cache entry for the given [source]. The source was explicitly | |
1422 * added to this context if [explicitlyAdded] is `true`. Return the cache | |
1423 * entry that was created. | |
1424 */ | |
1425 CacheEntry _createCacheEntry(Source source, bool explicitlyAdded) { | |
1426 CacheEntry entry = new CacheEntry(source); | |
1427 entry.modificationTime = getModificationStamp(source); | |
1428 entry.explicitlyAdded = explicitlyAdded; | |
1429 _cache.put(entry); | |
1430 return entry; | |
1431 } | |
1432 | |
1433 /** | |
1434 * Return a list containing all of the cache entries for targets associated | |
1435 * with the given [source]. | |
1436 */ | |
1437 List<CacheEntry> _entriesFor(Source source) { | |
1438 List<CacheEntry> entries = <CacheEntry>[]; | |
1439 MapIterator<AnalysisTarget, CacheEntry> iterator = _cache.iterator(); | |
1440 while (iterator.moveNext()) { | |
1441 if (iterator.key.source == source) { | |
1442 entries.add(iterator.value); | |
1443 } | |
1444 } | |
1445 return entries; | |
1446 } | |
1447 | |
1448 void _evaluatePendingFutures() { | |
1449 for (AnalysisTarget target in _pendingFutureTargets.keys) { | |
1450 CacheEntry cacheEntry = _cache.get(target); | |
1451 List<PendingFuture> pendingFutures = _pendingFutureTargets[target]; | |
1452 for (int i = 0; i < pendingFutures.length;) { | |
1453 if (pendingFutures[i].evaluate(cacheEntry)) { | |
1454 pendingFutures.removeAt(i); | |
1455 } else { | |
1456 i++; | |
1457 } | |
1458 } | |
1459 } | |
1460 } | |
1461 | |
1462 /** | |
1463 * Return a list containing all of the change notices that are waiting to be | |
1464 * returned. If there are no notices, then return either `null` or an empty | |
1465 * list, depending on the value of [nullIfEmpty]. | |
1466 */ | |
1467 List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) { | |
1468 if (_pendingNotices.isEmpty) { | |
1469 if (nullIfEmpty) { | |
1470 return null; | |
1471 } | |
1472 return ChangeNoticeImpl.EMPTY_LIST; | |
1473 } | |
1474 List<ChangeNotice> notices = new List.from(_pendingNotices.values); | |
1475 _pendingNotices.clear(); | |
1476 return notices; | |
1477 } | |
1478 | |
1479 /** | |
1480 * Return a list containing all of the sources known to this context that have | |
1481 * the given [kind]. | |
1482 */ | |
1483 List<Source> _getSources(SourceKind kind) { | |
1484 List<Source> sources = <Source>[]; | |
1485 if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) { | |
1486 for (Source source in _cache.sources) { | |
1487 CacheEntry entry = _cache.get(source); | |
1488 if (entry.getValue(SOURCE_KIND) == kind) { | |
1489 sources.add(source); | |
1490 } | |
1491 } | |
1492 } else if (kind == SourceKind.HTML) { | |
1493 for (Source source in _cache.sources) { | |
1494 if (AnalysisEngine.isHtmlFileName(source.shortName)) { | |
1495 sources.add(source); | |
1496 } | |
1497 } | |
1498 } | |
1499 if (sources.isEmpty) { | |
1500 return Source.EMPTY_LIST; | |
1501 } | |
1502 return sources; | |
1503 } | |
1504 | |
1505 /** | |
1506 * Look at the given [source] to see whether a task needs to be performed | |
1507 * related to it. If so, add the source to the set of sources that need to be | |
1508 * processed. This method is intended to be used for testing purposes only. | |
1509 */ | |
1510 void _getSourcesNeedingProcessing(Source source, CacheEntry entry, | |
1511 bool isPriority, bool hintsEnabled, bool lintsEnabled, | |
1512 HashSet<Source> sources) { | |
1513 CacheState state = entry.getState(CONTENT); | |
1514 if (state == CacheState.INVALID || | |
1515 (isPriority && state == CacheState.FLUSHED)) { | |
1516 sources.add(source); | |
1517 return; | |
1518 } else if (state == CacheState.ERROR) { | |
1519 return; | |
1520 } | |
1521 state = entry.getState(SOURCE_KIND); | |
1522 if (state == CacheState.INVALID || | |
1523 (isPriority && state == CacheState.FLUSHED)) { | |
1524 sources.add(source); | |
1525 return; | |
1526 } else if (state == CacheState.ERROR) { | |
1527 return; | |
1528 } | |
1529 SourceKind kind = entry.getValue(SOURCE_KIND); | |
1530 if (kind == SourceKind.LIBRARY || kind == SourceKind.PART) { | |
1531 state = entry.getState(SCAN_ERRORS); | |
1532 if (state == CacheState.INVALID || | |
1533 (isPriority && state == CacheState.FLUSHED)) { | |
1534 sources.add(source); | |
1535 return; | |
1536 } else if (state == CacheState.ERROR) { | |
1537 return; | |
1538 } | |
1539 state = entry.getState(PARSE_ERRORS); | |
1540 if (state == CacheState.INVALID || | |
1541 (isPriority && state == CacheState.FLUSHED)) { | |
1542 sources.add(source); | |
1543 return; | |
1544 } else if (state == CacheState.ERROR) { | |
1545 return; | |
1546 } | |
1547 // if (isPriority) { | |
1548 // if (!entry.hasResolvableCompilationUnit) { | |
1549 // sources.add(source); | |
1550 // return; | |
1551 // } | |
1552 // } | |
1553 for (Source librarySource in getLibrariesContaining(source)) { | |
1554 CacheEntry libraryEntry = _cache.get(librarySource); | |
1555 state = libraryEntry.getState(LIBRARY_ELEMENT); | |
1556 if (state == CacheState.INVALID || | |
1557 (isPriority && state == CacheState.FLUSHED)) { | |
1558 sources.add(source); | |
1559 return; | |
1560 } else if (state == CacheState.ERROR) { | |
1561 return; | |
1562 } | |
1563 CacheEntry unitEntry = | |
1564 _cache.get(new LibrarySpecificUnit(librarySource, source)); | |
1565 state = unitEntry.getState(RESOLVED_UNIT); | |
1566 if (state == CacheState.INVALID || | |
1567 (isPriority && state == CacheState.FLUSHED)) { | |
1568 sources.add(source); | |
1569 return; | |
1570 } else if (state == CacheState.ERROR) { | |
1571 return; | |
1572 } | |
1573 if (shouldErrorsBeAnalyzed(source, unitEntry)) { | |
1574 state = unitEntry.getState(VERIFY_ERRORS); | |
1575 if (state == CacheState.INVALID || | |
1576 (isPriority && state == CacheState.FLUSHED)) { | |
1577 sources.add(source); | |
1578 return; | |
1579 } else if (state == CacheState.ERROR) { | |
1580 return; | |
1581 } | |
1582 if (hintsEnabled) { | |
1583 state = unitEntry.getState(HINTS); | |
1584 if (state == CacheState.INVALID || | |
1585 (isPriority && state == CacheState.FLUSHED)) { | |
1586 sources.add(source); | |
1587 return; | |
1588 } else if (state == CacheState.ERROR) { | |
1589 return; | |
1590 } | |
1591 } | |
1592 // if (lintsEnabled) { | |
1593 // state = unitEntry.getState(LINTS); | |
1594 // if (state == CacheState.INVALID || | |
1595 // (isPriority && state == CacheState.FLUSHED)) { | |
1596 // sources.add(source); | |
1597 // return; | |
1598 // } else if (state == CacheState.ERROR) { | |
1599 // return; | |
1600 // } | |
1601 // } | |
1602 } | |
1603 } | |
1604 // } else if (kind == SourceKind.HTML) { | |
1605 // CacheState parsedUnitState = entry.getState(HtmlEntry.PARSED_UNIT); | |
1606 // if (parsedUnitState == CacheState.INVALID || | |
1607 // (isPriority && parsedUnitState == CacheState.FLUSHED)) { | |
1608 // sources.add(source); | |
1609 // return; | |
1610 // } | |
1611 // CacheState resolvedUnitState = | |
1612 // entry.getState(HtmlEntry.RESOLVED_UNIT); | |
1613 // if (resolvedUnitState == CacheState.INVALID || | |
1614 // (isPriority && resolvedUnitState == CacheState.FLUSHED)) { | |
1615 // sources.add(source); | |
1616 // return; | |
1617 // } | |
1618 } | |
1619 } | |
1620 | |
1621 /** | |
1622 * Log the given debugging [message]. | |
1623 */ | |
1624 void _logInformation(String message) { | |
1625 AnalysisEngine.instance.logger.logInformation(message); | |
1626 } | |
1627 | |
1628 /** | |
1629 * Notify all of the analysis listeners that the errors associated with the | |
1630 * given [source] has been updated to the given [errors]. | |
1631 */ | |
1632 void _notifyErrors( | |
1633 Source source, List<AnalysisError> errors, LineInfo lineInfo) { | |
1634 int count = _listeners.length; | |
1635 for (int i = 0; i < count; i++) { | |
1636 _listeners[i].computedErrors(this, source, errors, lineInfo); | |
1637 } | |
1638 } | |
1639 | |
1640 /** | |
1641 * Remove the given [source] from the priority order if it is in the list. | |
1642 */ | |
1643 void _removeFromPriorityOrder(Source source) { | |
1644 int count = _priorityOrder.length; | |
1645 List<Source> newOrder = <Source>[]; | |
1646 for (int i = 0; i < count; i++) { | |
1647 if (_priorityOrder[i] != source) { | |
1648 newOrder.add(_priorityOrder[i]); | |
1649 } | |
1650 } | |
1651 if (newOrder.length < count) { | |
1652 analysisPriorityOrder = newOrder; | |
1653 } | |
1654 } | |
1655 | |
1656 /** | |
1657 * Create an entry for the newly added [source] and invalidate any sources | |
1658 * that referenced the source before it existed. | |
1659 */ | |
1660 void _sourceAvailable(Source source) { | |
1661 CacheEntry entry = _cache.get(source); | |
1662 if (entry == null) { | |
1663 _createCacheEntry(source, true); | |
1664 } else { | |
1665 entry.modificationTime = getModificationStamp(source); | |
1666 entry.setState(CONTENT, CacheState.INVALID); | |
1667 } | |
1668 } | |
1669 | |
1670 /** | |
1671 * Invalidate the [source] that was changed and any sources that referenced | |
1672 * the source before it existed. | |
1673 */ | |
1674 void _sourceChanged(Source source) { | |
1675 CacheEntry entry = _cache.get(source); | |
1676 // If the source is removed, we don't care about it. | |
1677 if (entry == null) { | |
1678 return; | |
1679 } | |
1680 // Check whether the content of the source is the same as it was the last | |
1681 // time. | |
1682 String sourceContent = entry.getValue(CONTENT); | |
1683 if (sourceContent != null) { | |
1684 entry.setState(CONTENT, CacheState.FLUSHED); | |
1685 try { | |
1686 TimestampedData<String> fileContents = getContents(source); | |
1687 if (fileContents.data == sourceContent) { | |
1688 int time = fileContents.modificationTime; | |
1689 for (CacheEntry entry in _entriesFor(source)) { | |
1690 entry.modificationTime = time; | |
1691 } | |
1692 return; | |
1693 } | |
1694 } catch (e) {} | |
1695 } | |
1696 // We need to invalidate the cache. | |
1697 { | |
1698 Object delta = null; | |
1699 if (AnalysisEngine.instance.limitInvalidationInTaskModel && | |
1700 AnalysisEngine.isDartFileName(source.fullName)) { | |
1701 // TODO(scheglov) Incorrect implementation in general. | |
1702 entry.setState(TOKEN_STREAM, CacheState.FLUSHED); | |
1703 entry.setState(PARSED_UNIT, CacheState.FLUSHED); | |
1704 List<Source> librarySources = getLibrariesContaining(source); | |
1705 if (librarySources.length == 1) { | |
1706 Source librarySource = librarySources[0]; | |
1707 CompilationUnit oldUnit = | |
1708 getResolvedCompilationUnit2(source, librarySource); | |
1709 if (oldUnit != null) { | |
1710 CompilationUnit newUnit = parseCompilationUnit(source); | |
1711 IncrementalCompilationUnitElementBuilder builder = | |
1712 new IncrementalCompilationUnitElementBuilder(oldUnit, newUnit); | |
1713 builder.build(); | |
1714 CompilationUnitElementDelta unitDelta = builder.unitDelta; | |
1715 if (!unitDelta.hasDirectiveChange) { | |
1716 DartDelta dartDelta = new DartDelta(source); | |
1717 dartDelta.hasDirectiveChange = unitDelta.hasDirectiveChange; | |
1718 unitDelta.addedDeclarations.forEach(dartDelta.elementAdded); | |
1719 unitDelta.removedDeclarations.forEach(dartDelta.elementRemoved); | |
1720 // print( | |
1721 // 'dartDelta: add=${dartDelta.addedNames} remove=${dartDelta.r
emovedNames}'); | |
1722 delta = dartDelta; | |
1723 entry.setState(CONTENT, CacheState.INVALID, delta: delta); | |
1724 return; | |
1725 } | |
1726 } | |
1727 } | |
1728 } | |
1729 entry.setState(CONTENT, CacheState.INVALID); | |
1730 } | |
1731 dartWorkManager.applyChange( | |
1732 Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST); | |
1733 htmlWorkManager.applyChange( | |
1734 Source.EMPTY_LIST, <Source>[source], Source.EMPTY_LIST); | |
1735 } | |
1736 | |
1737 /** | |
1738 * Record that the give [source] has been deleted. | |
1739 */ | |
1740 void _sourceDeleted(Source source) { | |
1741 // TODO(brianwilkerson) Implement this. | |
1742 // SourceEntry sourceEntry = _cache.get(source); | |
1743 // if (sourceEntry is HtmlEntry) { | |
1744 // HtmlEntry htmlEntry = sourceEntry; | |
1745 // htmlEntry.recordContentError(new CaughtException( | |
1746 // new AnalysisException("This source was marked as being deleted"), | |
1747 // null)); | |
1748 // } else if (sourceEntry is DartEntry) { | |
1749 // DartEntry dartEntry = sourceEntry; | |
1750 // HashSet<Source> libraries = new HashSet<Source>(); | |
1751 // for (Source librarySource in getLibrariesContaining(source)) { | |
1752 // libraries.add(librarySource); | |
1753 // for (Source dependentLibrary | |
1754 // in getLibrariesDependingOn(librarySource)) { | |
1755 // libraries.add(dependentLibrary); | |
1756 // } | |
1757 // } | |
1758 // for (Source librarySource in libraries) { | |
1759 // _invalidateLibraryResolution(librarySource); | |
1760 // } | |
1761 // dartEntry.recordContentError(new CaughtException( | |
1762 // new AnalysisException("This source was marked as being deleted"), | |
1763 // null)); | |
1764 // } | |
1765 _removeFromPriorityOrder(source); | |
1766 } | |
1767 | |
1768 /** | |
1769 * Record that the given [source] has been removed. | |
1770 */ | |
1771 void _sourceRemoved(Source source) { | |
1772 _cache.remove(source); | |
1773 _removeFromPriorityOrder(source); | |
1774 } | |
1775 | |
1776 /** | |
1777 * TODO(scheglov) A hackish, limited incremental resolution implementation. | |
1778 */ | |
1779 bool _tryPoorMansIncrementalResolution(Source unitSource, String newCode) { | |
1780 return PerformanceStatistics.incrementalAnalysis.makeCurrentWhile(() { | |
1781 incrementalResolutionValidation_lastUnitSource = null; | |
1782 incrementalResolutionValidation_lastLibrarySource = null; | |
1783 incrementalResolutionValidation_lastUnit = null; | |
1784 // prepare the entry | |
1785 CacheEntry sourceEntry = _cache.get(unitSource); | |
1786 if (sourceEntry == null) { | |
1787 return false; | |
1788 } | |
1789 // prepare the (only) library source | |
1790 List<Source> librarySources = getLibrariesContaining(unitSource); | |
1791 if (librarySources.length != 1) { | |
1792 return false; | |
1793 } | |
1794 Source librarySource = librarySources[0]; | |
1795 CacheEntry unitEntry = | |
1796 _cache.get(new LibrarySpecificUnit(librarySource, unitSource)); | |
1797 if (unitEntry == null) { | |
1798 return false; | |
1799 } | |
1800 // prepare the library element | |
1801 LibraryElement libraryElement = getLibraryElement(librarySource); | |
1802 if (libraryElement == null) { | |
1803 return false; | |
1804 } | |
1805 // prepare the existing unit | |
1806 CompilationUnit oldUnit = | |
1807 getResolvedCompilationUnit2(unitSource, librarySource); | |
1808 if (oldUnit == null) { | |
1809 return false; | |
1810 } | |
1811 // do resolution | |
1812 Stopwatch perfCounter = new Stopwatch()..start(); | |
1813 PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver( | |
1814 typeProvider, unitSource, null, sourceEntry, unitEntry, oldUnit, | |
1815 analysisOptions.incrementalApi, analysisOptions); | |
1816 bool success = resolver.resolve(newCode); | |
1817 AnalysisEngine.instance.instrumentationService.logPerformance( | |
1818 AnalysisPerformanceKind.INCREMENTAL, perfCounter, | |
1819 'success=$success,context_id=$_id,code_length=${newCode.length}'); | |
1820 if (!success) { | |
1821 return false; | |
1822 } | |
1823 // if validation, remember the result, but throw it away | |
1824 if (analysisOptions.incrementalValidation) { | |
1825 incrementalResolutionValidation_lastUnitSource = oldUnit.element.source; | |
1826 incrementalResolutionValidation_lastLibrarySource = | |
1827 oldUnit.element.library.source; | |
1828 incrementalResolutionValidation_lastUnit = oldUnit; | |
1829 return false; | |
1830 } | |
1831 // prepare notice | |
1832 { | |
1833 ChangeNoticeImpl notice = getNotice(unitSource); | |
1834 notice.resolvedDartUnit = oldUnit; | |
1835 AnalysisErrorInfo errorInfo = getErrors(unitSource); | |
1836 notice.setErrors(errorInfo.errors, errorInfo.lineInfo); | |
1837 } | |
1838 // schedule | |
1839 dartWorkManager.unitIncrementallyResolved(librarySource, unitSource); | |
1840 // OK | |
1841 return true; | |
1842 }); | |
1843 } | |
1844 } | |
1845 | |
1846 /** | |
1847 * An object that manages the partitions that can be shared between analysis | |
1848 * contexts. | |
1849 */ | |
1850 class PartitionManager { | |
1851 /** | |
1852 * A table mapping SDK's to the partitions used for those SDK's. | |
1853 */ | |
1854 HashMap<DartSdk, SdkCachePartition> _sdkPartitions = | |
1855 new HashMap<DartSdk, SdkCachePartition>(); | |
1856 | |
1857 /** | |
1858 * Clear any cached data being maintained by this manager. | |
1859 */ | |
1860 void clearCache() { | |
1861 _sdkPartitions.clear(); | |
1862 } | |
1863 | |
1864 /** | |
1865 * Return the partition being used for the given [sdk], creating the partition | |
1866 * if necessary. | |
1867 */ | |
1868 SdkCachePartition forSdk(DartSdk sdk) { | |
1869 // Call sdk.context now, because when it creates a new | |
1870 // InternalAnalysisContext instance, it calls forSdk() again, so creates an | |
1871 // SdkCachePartition instance. | |
1872 // So, if we initialize context after "partition == null", we end up | |
1873 // with two SdkCachePartition instances. | |
1874 InternalAnalysisContext sdkContext = sdk.context; | |
1875 // Check cache for an existing partition. | |
1876 SdkCachePartition partition = _sdkPartitions[sdk]; | |
1877 if (partition == null) { | |
1878 partition = new SdkCachePartition(sdkContext); | |
1879 _sdkPartitions[sdk] = partition; | |
1880 } | |
1881 return partition; | |
1882 } | |
1883 } | |
1884 | |
1885 /** | |
1886 * Representation of a pending computation which is based on the results of | |
1887 * analysis that may or may not have been completed. | |
1888 */ | |
1889 class PendingFuture<T> { | |
1890 /** | |
1891 * The context in which this computation runs. | |
1892 */ | |
1893 final AnalysisContextImpl _context; | |
1894 | |
1895 /** | |
1896 * The target used by this computation to compute its value. | |
1897 */ | |
1898 final AnalysisTarget target; | |
1899 | |
1900 /** | |
1901 * The function which implements the computation. | |
1902 */ | |
1903 final PendingFutureComputer<T> _computeValue; | |
1904 | |
1905 /** | |
1906 * The completer that should be completed once the computation has succeeded. | |
1907 */ | |
1908 CancelableCompleter<T> _completer; | |
1909 | |
1910 PendingFuture(this._context, this.target, this._computeValue) { | |
1911 _completer = new CancelableCompleter<T>(_onCancel); | |
1912 } | |
1913 | |
1914 /** | |
1915 * Retrieve the future which will be completed when this object is | |
1916 * successfully evaluated. | |
1917 */ | |
1918 CancelableFuture<T> get future => _completer.future; | |
1919 | |
1920 /** | |
1921 * Execute [_computeValue], passing it the given [entry], and complete | |
1922 * the pending future if it's appropriate to do so. If the pending future is | |
1923 * completed by this call, true is returned; otherwise false is returned. | |
1924 * | |
1925 * Once this function has returned true, it should not be called again. | |
1926 * | |
1927 * Other than completing the future, this method is free of side effects. | |
1928 * Note that any code the client has attached to the future will be executed | |
1929 * in a microtask, so there is no danger of side effects occurring due to | |
1930 * client callbacks. | |
1931 */ | |
1932 bool evaluate(CacheEntry entry) { | |
1933 assert(!_completer.isCompleted); | |
1934 try { | |
1935 T result = _computeValue(entry); | |
1936 if (result == null) { | |
1937 return false; | |
1938 } else { | |
1939 _completer.complete(result); | |
1940 return true; | |
1941 } | |
1942 } catch (exception, stackTrace) { | |
1943 _completer.completeError(exception, stackTrace); | |
1944 return true; | |
1945 } | |
1946 } | |
1947 | |
1948 /** | |
1949 * No further analysis updates are expected which affect this future, so | |
1950 * complete it with an AnalysisNotScheduledError in order to avoid | |
1951 * deadlocking the client. | |
1952 */ | |
1953 void forciblyComplete() { | |
1954 try { | |
1955 throw new AnalysisNotScheduledError(); | |
1956 } catch (exception, stackTrace) { | |
1957 _completer.completeError(exception, stackTrace); | |
1958 } | |
1959 } | |
1960 | |
1961 void _onCancel() { | |
1962 _context._cancelFuture(this); | |
1963 } | |
1964 } | |
1965 | |
1966 /** | |
1967 * An [AnalysisContext] that only contains sources for a Dart SDK. | |
1968 */ | |
1969 class SdkAnalysisContext extends AnalysisContextImpl { | |
1970 @override | |
1971 AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { | |
1972 if (factory == null) { | |
1973 return super.createCacheFromSourceFactory(factory); | |
1974 } | |
1975 DartSdk sdk = factory.dartSdk; | |
1976 if (sdk == null) { | |
1977 throw new IllegalArgumentException( | |
1978 "The source factory for an SDK analysis context must have a DartUriRes
olver"); | |
1979 } | |
1980 return new AnalysisCache(<CachePartition>[ | |
1981 AnalysisEngine.instance.partitionManager_new.forSdk(sdk) | |
1982 ]); | |
1983 } | |
1984 } | |
1985 | |
1986 /** | |
1987 * A helper class used to create futures for [AnalysisContextImpl]. | |
1988 * Using a helper class allows us to preserve the generic parameter T. | |
1989 */ | |
1990 class _AnalysisFutureHelper<T> { | |
1991 final AnalysisContextImpl _context; | |
1992 | |
1993 _AnalysisFutureHelper(this._context); | |
1994 | |
1995 /** | |
1996 * Return a future that will be completed with the result of calling | |
1997 * [computeValue]. If [computeValue] returns non-`null`, the future will be | |
1998 * completed immediately with the resulting value. If it returns `null`, then | |
1999 * [scheduleComputation] is invoked to schedule analysis that will produce | |
2000 * the required result, and [computeValue] will be re-executed in the future, | |
2001 * after the next time the cached information for [target] has changed. If | |
2002 * [computeValue] throws an exception, the future will fail with that | |
2003 * exception. | |
2004 * | |
2005 * If the [computeValue] still returns `null` after there is no further | |
2006 * analysis to be done for [target], then the future will be completed with | |
2007 * the error AnalysisNotScheduledError. | |
2008 * | |
2009 * Since [computeValue] will be called while the state of analysis is being | |
2010 * updated, it should be free of side effects so that it doesn't cause | |
2011 * reentrant changes to the analysis state. | |
2012 */ | |
2013 CancelableFuture<T> computeAsync(AnalysisTarget target, | |
2014 T computeValue(CacheEntry entry), void scheduleComputation()) { | |
2015 if (_context.isDisposed) { | |
2016 // No further analysis is expected, so return a future that completes | |
2017 // immediately with AnalysisNotScheduledError. | |
2018 return new CancelableFuture.error(new AnalysisNotScheduledError()); | |
2019 } | |
2020 CacheEntry entry = _context.getCacheEntry(target); | |
2021 PendingFuture pendingFuture = | |
2022 new PendingFuture<T>(_context, target, computeValue); | |
2023 if (!pendingFuture.evaluate(entry)) { | |
2024 _context._pendingFutureTargets | |
2025 .putIfAbsent(target, () => <PendingFuture>[]) | |
2026 .add(pendingFuture); | |
2027 scheduleComputation(); | |
2028 } | |
2029 return pendingFuture.future; | |
2030 } | |
2031 } | |
OLD | NEW |