OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, 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 // This code was auto-generated, is not intended to be edited, and is subject to | |
6 // significant change. Please see the README file for more information. | |
7 | |
8 library engine; | |
9 | |
10 import 'dart:async'; | |
11 import 'dart:collection'; | |
12 import 'dart:math' as math; | |
13 | |
14 import 'package:analyzer/src/cancelable_future.dart'; | |
15 import 'package:analyzer/src/context/cache.dart' as cache; | |
16 import 'package:analyzer/src/context/context.dart' as newContext; | |
17 import 'package:analyzer/src/generated/incremental_resolution_validator.dart'; | |
18 import 'package:analyzer/src/plugin/command_line_plugin.dart'; | |
19 import 'package:analyzer/src/plugin/engine_plugin.dart'; | |
20 import 'package:analyzer/src/plugin/options_plugin.dart'; | |
21 import 'package:analyzer/src/services/lint.dart'; | |
22 import 'package:analyzer/src/task/manager.dart'; | |
23 import 'package:analyzer/task/dart.dart'; | |
24 import 'package:analyzer/task/model.dart'; | |
25 import 'package:html/dom.dart' show Document; | |
26 import 'package:plugin/manager.dart'; | |
27 import 'package:plugin/plugin.dart'; | |
28 | |
29 import '../../instrumentation/instrumentation.dart'; | |
30 import 'ast.dart'; | |
31 import 'constant.dart'; | |
32 import 'element.dart'; | |
33 import 'error.dart'; | |
34 import 'error_verifier.dart'; | |
35 import 'html.dart' as ht; | |
36 import 'incremental_resolver.dart' | |
37 show IncrementalResolver, PoorMansIncrementalResolver; | |
38 import 'incremental_scanner.dart'; | |
39 import 'java_core.dart'; | |
40 import 'java_engine.dart'; | |
41 import 'parser.dart' show Parser, IncrementalParser; | |
42 import 'resolver.dart'; | |
43 import 'scanner.dart'; | |
44 import 'sdk.dart' show DartSdk; | |
45 import 'source.dart'; | |
46 import 'utilities_collection.dart'; | |
47 import 'utilities_general.dart'; | |
48 | |
49 /** | |
50 * Used by [AnalysisOptions] to allow function bodies to be analyzed in some | |
51 * sources but not others. | |
52 */ | |
53 typedef bool AnalyzeFunctionBodiesPredicate(Source source); | |
54 | |
55 /** | |
56 * Type of callback functions used by PendingFuture. Functions of this type | |
57 * should perform a computation based on the data in [sourceEntry] and return | |
58 * it. If the computation can't be performed yet because more analysis is | |
59 * needed, null should be returned. | |
60 * | |
61 * The function may also throw an exception, in which case the corresponding | |
62 * future will be completed with failure. | |
63 * | |
64 * Since this function is called while the state of analysis is being updated, | |
65 * it should be free of side effects so that it doesn't cause reentrant | |
66 * changes to the analysis state. | |
67 */ | |
68 typedef T PendingFutureComputer<T>(SourceEntry sourceEntry); | |
69 | |
70 /** | |
71 * An LRU cache of information related to analysis. | |
72 */ | |
73 class AnalysisCache { | |
74 /** | |
75 * A flag used to control whether trace information should be produced when | |
76 * the content of the cache is modified. | |
77 */ | |
78 static bool _TRACE_CHANGES = false; | |
79 | |
80 /** | |
81 * A list containing the partitions of which this cache is comprised. | |
82 */ | |
83 final List<CachePartition> _partitions; | |
84 | |
85 /** | |
86 * Initialize a newly created cache to have the given [_partitions]. The | |
87 * partitions will be searched in the order in which they appear in the list, | |
88 * so the most specific partition (usually an [SdkCachePartition]) should be | |
89 * first and the most general (usually a [UniversalCachePartition]) last. | |
90 */ | |
91 AnalysisCache(this._partitions); | |
92 | |
93 /** | |
94 * Return the number of entries in this cache that have an AST associated with | |
95 * them. | |
96 */ | |
97 int get astSize => _partitions[_partitions.length - 1].astSize; | |
98 | |
99 /** | |
100 * Return information about each of the partitions in this cache. | |
101 */ | |
102 List<AnalysisContextStatistics_PartitionData> get partitionData { | |
103 int count = _partitions.length; | |
104 List<AnalysisContextStatistics_PartitionData> data = | |
105 new List<AnalysisContextStatistics_PartitionData>(count); | |
106 for (int i = 0; i < count; i++) { | |
107 CachePartition partition = _partitions[i]; | |
108 data[i] = new AnalysisContextStatisticsImpl_PartitionDataImpl( | |
109 partition.astSize, partition.map.length); | |
110 } | |
111 return data; | |
112 } | |
113 | |
114 /** | |
115 * Record that the AST associated with the given [source] was just read from | |
116 * the cache. | |
117 */ | |
118 void accessedAst(Source source) { | |
119 int count = _partitions.length; | |
120 for (int i = 0; i < count; i++) { | |
121 if (_partitions[i].contains(source)) { | |
122 _partitions[i].accessedAst(source); | |
123 return; | |
124 } | |
125 } | |
126 } | |
127 | |
128 /** | |
129 * Return the entry associated with the given [source]. | |
130 */ | |
131 SourceEntry get(Source source) { | |
132 int count = _partitions.length; | |
133 for (int i = 0; i < count; i++) { | |
134 if (_partitions[i].contains(source)) { | |
135 return _partitions[i].get(source); | |
136 } | |
137 } | |
138 // | |
139 // We should never get to this point because the last partition should | |
140 // always be a universal partition, except in the case of the SDK context, | |
141 // in which case the source should always be part of the SDK. | |
142 // | |
143 return null; | |
144 } | |
145 | |
146 /** | |
147 * Return context that owns the given [source]. | |
148 */ | |
149 InternalAnalysisContext getContextFor(Source source) { | |
150 int count = _partitions.length; | |
151 for (int i = 0; i < count; i++) { | |
152 if (_partitions[i].contains(source)) { | |
153 return _partitions[i].context; | |
154 } | |
155 } | |
156 // | |
157 // We should never get to this point because the last partition should | |
158 // always be a universal partition, except in the case of the SDK context, | |
159 // in which case the source should always be part of the SDK. | |
160 // | |
161 AnalysisEngine.instance.logger.logInformation( | |
162 "Could not find context for ${source.fullName}", | |
163 new CaughtException(new AnalysisException(), null)); | |
164 return null; | |
165 } | |
166 | |
167 /** | |
168 * Return an iterator returning all of the map entries mapping sources to | |
169 * cache entries. | |
170 */ | |
171 MapIterator<Source, SourceEntry> iterator() { | |
172 int count = _partitions.length; | |
173 List<Map<Source, SourceEntry>> maps = | |
174 new List<Map<Source, SourceEntry>>(count); | |
175 for (int i = 0; i < count; i++) { | |
176 maps[i] = _partitions[i].map; | |
177 } | |
178 return new MultipleMapIterator<Source, SourceEntry>(maps); | |
179 } | |
180 | |
181 /** | |
182 * Associate the given [entry] with the given [source]. | |
183 */ | |
184 void put(Source source, SourceEntry entry) { | |
185 entry.fixExceptionState(); | |
186 int count = _partitions.length; | |
187 for (int i = 0; i < count; i++) { | |
188 if (_partitions[i].contains(source)) { | |
189 if (_TRACE_CHANGES) { | |
190 try { | |
191 SourceEntry oldEntry = _partitions[i].get(source); | |
192 if (oldEntry == null) { | |
193 AnalysisEngine.instance.logger.logInformation( | |
194 "Added a cache entry for '${source.fullName}'."); | |
195 } else { | |
196 AnalysisEngine.instance.logger.logInformation( | |
197 "Modified the cache entry for ${source.fullName}'. Diff = ${en
try.getDiff(oldEntry)}"); | |
198 } | |
199 } catch (exception) { | |
200 // Ignored | |
201 JavaSystem.currentTimeMillis(); | |
202 } | |
203 } | |
204 _partitions[i].put(source, entry); | |
205 return; | |
206 } | |
207 } | |
208 } | |
209 | |
210 /** | |
211 * Remove all information related to the given [source] from this cache. | |
212 */ | |
213 void remove(Source source) { | |
214 int count = _partitions.length; | |
215 for (int i = 0; i < count; i++) { | |
216 if (_partitions[i].contains(source)) { | |
217 if (_TRACE_CHANGES) { | |
218 try { | |
219 AnalysisEngine.instance.logger.logInformation( | |
220 "Removed the cache entry for ${source.fullName}'."); | |
221 } catch (exception) { | |
222 // Ignored | |
223 JavaSystem.currentTimeMillis(); | |
224 } | |
225 } | |
226 _partitions[i].remove(source); | |
227 return; | |
228 } | |
229 } | |
230 } | |
231 | |
232 /** | |
233 * Record that the AST associated with the given [source] was just removed | |
234 * from the cache. | |
235 */ | |
236 void removedAst(Source source) { | |
237 int count = _partitions.length; | |
238 for (int i = 0; i < count; i++) { | |
239 if (_partitions[i].contains(source)) { | |
240 _partitions[i].removedAst(source); | |
241 return; | |
242 } | |
243 } | |
244 } | |
245 | |
246 /** | |
247 * Return the number of sources that are mapped to cache entries. | |
248 */ | |
249 int size() { | |
250 int size = 0; | |
251 int count = _partitions.length; | |
252 for (int i = 0; i < count; i++) { | |
253 size += _partitions[i].size(); | |
254 } | |
255 return size; | |
256 } | |
257 | |
258 /** | |
259 * Record that the AST associated with the given [source] was just stored to | |
260 * the cache. | |
261 */ | |
262 void storedAst(Source source) { | |
263 int count = _partitions.length; | |
264 for (int i = 0; i < count; i++) { | |
265 if (_partitions[i].contains(source)) { | |
266 _partitions[i].storedAst(source); | |
267 return; | |
268 } | |
269 } | |
270 } | |
271 } | |
272 | |
273 /** | |
274 * A context in which a single analysis can be performed and incrementally | |
275 * maintained. The context includes such information as the version of the SDK | |
276 * being analyzed against as well as the package-root used to resolve 'package:' | |
277 * URI's. (Both of which are known indirectly through the [SourceFactory].) | |
278 * | |
279 * An analysis context also represents the state of the analysis, which includes | |
280 * knowing which sources have been included in the analysis (either directly or | |
281 * indirectly) and the results of the analysis. Sources must be added and | |
282 * removed from the context using the method [applyChanges], which is also used | |
283 * to notify the context when sources have been modified and, consequently, | |
284 * previously known results might have been invalidated. | |
285 * | |
286 * There are two ways to access the results of the analysis. The most common is | |
287 * to use one of the 'get' methods to access the results. The 'get' methods have | |
288 * the advantage that they will always return quickly, but have the disadvantage | |
289 * that if the results are not currently available they will return either | |
290 * nothing or in some cases an incomplete result. The second way to access | |
291 * results is by using one of the 'compute' methods. The 'compute' methods will | |
292 * always attempt to compute the requested results but might block the caller | |
293 * for a significant period of time. | |
294 * | |
295 * When results have been invalidated, have never been computed (as is the case | |
296 * for newly added sources), or have been removed from the cache, they are | |
297 * <b>not</b> automatically recreated. They will only be recreated if one of the | |
298 * 'compute' methods is invoked. | |
299 * | |
300 * However, this is not always acceptable. Some clients need to keep the | |
301 * analysis results up-to-date. For such clients there is a mechanism that | |
302 * allows them to incrementally perform needed analysis and get notified of the | |
303 * consequent changes to the analysis results. This mechanism is realized by the | |
304 * method [performAnalysisTask]. | |
305 * | |
306 * Analysis engine allows for having more than one context. This can be used, | |
307 * for example, to perform one analysis based on the state of files on disk and | |
308 * a separate analysis based on the state of those files in open editors. It can | |
309 * also be used to perform an analysis based on a proposed future state, such as | |
310 * the state after a refactoring. | |
311 */ | |
312 abstract class AnalysisContext { | |
313 /** | |
314 * An empty list of contexts. | |
315 */ | |
316 static const List<AnalysisContext> EMPTY_LIST = const <AnalysisContext>[]; | |
317 | |
318 /** | |
319 * Return the set of analysis options controlling the behavior of this | |
320 * context. Clients should not modify the returned set of options. The options | |
321 * should only be set by invoking the method [setAnalysisOptions]. | |
322 */ | |
323 AnalysisOptions get analysisOptions; | |
324 | |
325 /** | |
326 * Set the set of analysis options controlling the behavior of this context to | |
327 * the given [options]. Clients can safely assume that all necessary analysis | |
328 * results have been invalidated. | |
329 */ | |
330 void set analysisOptions(AnalysisOptions options); | |
331 | |
332 /** | |
333 * Set the order in which sources will be analyzed by [performAnalysisTask] to | |
334 * match the order of the sources in the given list of [sources]. If a source | |
335 * that needs to be analyzed is not contained in the list, then it will be | |
336 * treated as if it were at the end of the list. If the list is empty (or | |
337 * `null`) then no sources will be given priority over other sources. | |
338 * | |
339 * Changes made to the list after this method returns will <b>not</b> be | |
340 * reflected in the priority order. | |
341 */ | |
342 void set analysisPriorityOrder(List<Source> sources); | |
343 | |
344 /** | |
345 * Return the set of declared variables used when computing constant values. | |
346 */ | |
347 DeclaredVariables get declaredVariables; | |
348 | |
349 /** | |
350 * Return a list containing all of the sources known to this context that | |
351 * represent HTML files. The contents of the list can be incomplete. | |
352 */ | |
353 List<Source> get htmlSources; | |
354 | |
355 /** | |
356 * Returns `true` if this context was disposed using [dispose]. | |
357 */ | |
358 bool get isDisposed; | |
359 | |
360 /** | |
361 * Return a list containing all of the sources known to this context that | |
362 * represent the defining compilation unit of a library that can be run within | |
363 * a browser. The sources that are returned represent libraries that have a | |
364 * 'main' method and are either referenced by an HTML file or import, directly | |
365 * or indirectly, a client-only library. The contents of the list can be | |
366 * incomplete. | |
367 */ | |
368 List<Source> get launchableClientLibrarySources; | |
369 | |
370 /** | |
371 * Return a list containing all of the sources known to this context that | |
372 * represent the defining compilation unit of a library that can be run | |
373 * outside of a browser. The contents of the list can be incomplete. | |
374 */ | |
375 List<Source> get launchableServerLibrarySources; | |
376 | |
377 /** | |
378 * Return a list containing all of the sources known to this context that | |
379 * represent the defining compilation unit of a library. The contents of the | |
380 * list can be incomplete. | |
381 */ | |
382 List<Source> get librarySources; | |
383 | |
384 /** | |
385 * Return a client-provided name used to identify this context, or `null` if | |
386 * the client has not provided a name. | |
387 */ | |
388 String get name; | |
389 | |
390 /** | |
391 * Set the client-provided name used to identify this context to the given | |
392 * [name]. | |
393 */ | |
394 set name(String name); | |
395 | |
396 /** | |
397 * The stream that is notified when sources have been added or removed, | |
398 * or the source's content has changed. | |
399 */ | |
400 Stream<SourcesChangedEvent> get onSourcesChanged; | |
401 | |
402 /** | |
403 * Return the source factory used to create the sources that can be analyzed | |
404 * in this context. | |
405 */ | |
406 SourceFactory get sourceFactory; | |
407 | |
408 /** | |
409 * Set the source factory used to create the sources that can be analyzed in | |
410 * this context to the given source [factory]. Clients can safely assume that | |
411 * all analysis results have been invalidated. | |
412 */ | |
413 void set sourceFactory(SourceFactory factory); | |
414 | |
415 /** | |
416 * Return a list containing all of the sources known to this context. | |
417 */ | |
418 List<Source> get sources; | |
419 | |
420 /** | |
421 * Return a type provider for this context or throw [AnalysisException] if | |
422 * either `dart:core` or `dart:async` cannot be resolved. | |
423 */ | |
424 TypeProvider get typeProvider; | |
425 | |
426 /** | |
427 * Add the given [listener] to the list of objects that are to be notified | |
428 * when various analysis results are produced in this context. | |
429 */ | |
430 void addListener(AnalysisListener listener); | |
431 | |
432 /** | |
433 * Apply the given [delta] to change the level of analysis that will be | |
434 * performed for the sources known to this context. | |
435 */ | |
436 void applyAnalysisDelta(AnalysisDelta delta); | |
437 | |
438 /** | |
439 * Apply the changes specified by the given [changeSet] to this context. Any | |
440 * analysis results that have been invalidated by these changes will be | |
441 * removed. | |
442 */ | |
443 void applyChanges(ChangeSet changeSet); | |
444 | |
445 /** | |
446 * Return the documentation comment for the given [element] as it appears in | |
447 * the original source (complete with the beginning and ending delimiters) for | |
448 * block documentation comments, or lines starting with `"///"` and separated | |
449 * with `"\n"` characters for end-of-line documentation comments, or `null` if | |
450 * the element does not have a documentation comment associated with it. This | |
451 * can be a long-running operation if the information needed to access the | |
452 * comment is not cached. | |
453 * | |
454 * Throws an [AnalysisException] if the documentation comment could not be | |
455 * determined because the analysis could not be performed. | |
456 * | |
457 * <b>Note:</b> This method cannot be used in an async environment. | |
458 */ | |
459 String computeDocumentationComment(Element element); | |
460 | |
461 /** | |
462 * Return a list containing all of the errors associated with the given | |
463 * [source]. If the errors are not already known then the source will be | |
464 * analyzed in order to determine the errors associated with it. | |
465 * | |
466 * Throws an [AnalysisException] if the errors could not be determined because | |
467 * the analysis could not be performed. | |
468 * | |
469 * <b>Note:</b> This method cannot be used in an async environment. | |
470 * | |
471 * See [getErrors]. | |
472 */ | |
473 List<AnalysisError> computeErrors(Source source); | |
474 | |
475 /** | |
476 * Return the element model corresponding to the HTML file defined by the | |
477 * given [source]. If the element model does not yet exist it will be created. | |
478 * The process of creating an element model for an HTML file can be | |
479 * long-running, depending on the size of the file and the number of libraries | |
480 * that are defined in it (via script tags) that also need to have a model | |
481 * built for them. | |
482 * | |
483 * Throws AnalysisException if the element model could not be determined | |
484 * because the analysis could not be performed. | |
485 * | |
486 * <b>Note:</b> This method cannot be used in an async environment. | |
487 * | |
488 * See [getHtmlElement]. | |
489 */ | |
490 @deprecated | |
491 HtmlElement computeHtmlElement(Source source); | |
492 | |
493 /** | |
494 * Return the kind of the given [source], computing it's kind if it is not | |
495 * already known. Return [SourceKind.UNKNOWN] if the source is not contained | |
496 * in this context. | |
497 * | |
498 * <b>Note:</b> This method cannot be used in an async environment. | |
499 * | |
500 * See [getKindOf]. | |
501 */ | |
502 SourceKind computeKindOf(Source source); | |
503 | |
504 /** | |
505 * Return the element model corresponding to the library defined by the given | |
506 * [source]. If the element model does not yet exist it will be created. The | |
507 * process of creating an element model for a library can long-running, | |
508 * depending on the size of the library and the number of libraries that are | |
509 * imported into it that also need to have a model built for them. | |
510 * | |
511 * Throws an [AnalysisException] if the element model could not be determined | |
512 * because the analysis could not be performed. | |
513 * | |
514 * <b>Note:</b> This method cannot be used in an async environment. | |
515 * | |
516 * See [getLibraryElement]. | |
517 */ | |
518 LibraryElement computeLibraryElement(Source source); | |
519 | |
520 /** | |
521 * Return the line information for the given [source], or `null` if the source | |
522 * is not of a recognized kind (neither a Dart nor HTML file). If the line | |
523 * information was not previously known it will be created. The line | |
524 * information is used to map offsets from the beginning of the source to line | |
525 * and column pairs. | |
526 * | |
527 * Throws an [AnalysisException] if the line information could not be | |
528 * determined because the analysis could not be performed. | |
529 * | |
530 * <b>Note:</b> This method cannot be used in an async environment. | |
531 * | |
532 * See [getLineInfo]. | |
533 */ | |
534 LineInfo computeLineInfo(Source source); | |
535 | |
536 /** | |
537 * Return a future which will be completed with the fully resolved AST for a | |
538 * single compilation unit within the given library, once that AST is up to | |
539 * date. | |
540 * | |
541 * If the resolved AST can't be computed for some reason, the future will be | |
542 * completed with an error. One possible error is AnalysisNotScheduledError, | |
543 * which means that the resolved AST can't be computed because the given | |
544 * source file is not scheduled to be analyzed within the context of the | |
545 * given library. | |
546 */ | |
547 CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync( | |
548 Source source, Source librarySource); | |
549 | |
550 /** | |
551 * Notifies the context that the client is going to stop using this context. | |
552 */ | |
553 void dispose(); | |
554 | |
555 /** | |
556 * Return `true` if the given [source] exists. | |
557 * | |
558 * This method should be used rather than the method [Source.exists] because | |
559 * contexts can have local overrides of the content of a source that the | |
560 * source is not aware of and a source with local content is considered to | |
561 * exist even if there is no file on disk. | |
562 */ | |
563 bool exists(Source source); | |
564 | |
565 /** | |
566 * Return the element model corresponding to the compilation unit defined by | |
567 * the given [unitSource] in the library defined by the given [librarySource], | |
568 * or `null` if the element model does not currently exist or if the library | |
569 * cannot be analyzed for some reason. | |
570 */ | |
571 CompilationUnitElement getCompilationUnitElement( | |
572 Source unitSource, Source librarySource); | |
573 | |
574 /** | |
575 * Return the contents and timestamp of the given [source]. | |
576 * | |
577 * This method should be used rather than the method [Source.getContents] | |
578 * because contexts can have local overrides of the content of a source that | |
579 * the source is not aware of. | |
580 */ | |
581 TimestampedData<String> getContents(Source source); | |
582 | |
583 /** | |
584 * Return the element referenced by the given [location], or `null` if the | |
585 * element is not immediately available or if there is no element with the | |
586 * given location. The latter condition can occur, for example, if the | |
587 * location describes an element from a different context or if the element | |
588 * has been removed from this context as a result of some change since it was | |
589 * originally obtained. | |
590 */ | |
591 Element getElement(ElementLocation location); | |
592 | |
593 /** | |
594 * Return an analysis error info containing the list of all of the errors and | |
595 * the line info associated with the given [source]. The list of errors will | |
596 * be empty if the source is not known to this context or if there are no | |
597 * errors in the source. The errors contained in the list can be incomplete. | |
598 * | |
599 * See [computeErrors]. | |
600 */ | |
601 AnalysisErrorInfo getErrors(Source source); | |
602 | |
603 /** | |
604 * Return the element model corresponding to the HTML file defined by the | |
605 * given [source], or `null` if the source does not represent an HTML file, | |
606 * the element representing the file has not yet been created, or the analysis | |
607 * of the HTML file failed for some reason. | |
608 * | |
609 * See [computeHtmlElement]. | |
610 */ | |
611 @deprecated | |
612 HtmlElement getHtmlElement(Source source); | |
613 | |
614 /** | |
615 * Return the sources for the HTML files that reference the compilation unit | |
616 * with the given [source]. If the source does not represent a Dart source or | |
617 * is not known to this context, the returned list will be empty. The contents | |
618 * of the list can be incomplete. | |
619 */ | |
620 List<Source> getHtmlFilesReferencing(Source source); | |
621 | |
622 /** | |
623 * Return the kind of the given [source], or `null` if the kind is not known | |
624 * to this context. | |
625 * | |
626 * See [computeKindOf]. | |
627 */ | |
628 SourceKind getKindOf(Source source); | |
629 | |
630 /** | |
631 * Return the sources for the defining compilation units of any libraries of | |
632 * which the given [source] is a part. The list will normally contain a single | |
633 * library because most Dart sources are only included in a single library, | |
634 * but it is possible to have a part that is contained in multiple identically | |
635 * named libraries. If the source represents the defining compilation unit of | |
636 * a library, then the returned list will contain the given source as its only | |
637 * element. If the source does not represent a Dart source or is not known to | |
638 * this context, the returned list will be empty. The contents of the list can | |
639 * be incomplete. | |
640 */ | |
641 List<Source> getLibrariesContaining(Source source); | |
642 | |
643 /** | |
644 * Return the sources for the defining compilation units of any libraries that | |
645 * depend on the library defined by the given [librarySource]. One library | |
646 * depends on another if it either imports or exports that library. | |
647 */ | |
648 List<Source> getLibrariesDependingOn(Source librarySource); | |
649 | |
650 /** | |
651 * Return the sources for the defining compilation units of any libraries that | |
652 * are referenced from the HTML file defined by the given [htmlSource]. | |
653 */ | |
654 List<Source> getLibrariesReferencedFromHtml(Source htmlSource); | |
655 | |
656 /** | |
657 * Return the element model corresponding to the library defined by the given | |
658 * [source], or `null` if the element model does not currently exist or if the | |
659 * library cannot be analyzed for some reason. | |
660 */ | |
661 LibraryElement getLibraryElement(Source source); | |
662 | |
663 /** | |
664 * Return the line information for the given [source], or `null` if the line | |
665 * information is not known. The line information is used to map offsets from | |
666 * the beginning of the source to line and column pairs. | |
667 * | |
668 * See [computeLineInfo]. | |
669 */ | |
670 LineInfo getLineInfo(Source source); | |
671 | |
672 /** | |
673 * Return the modification stamp for the [source], or a negative value if the | |
674 * source does not exist. A modification stamp is a non-negative integer with | |
675 * the property that if the contents of the source have not been modified | |
676 * since the last time the modification stamp was accessed then the same value | |
677 * will be returned, but if the contents of the source have been modified one | |
678 * or more times (even if the net change is zero) the stamps will be different
. | |
679 * | |
680 * This method should be used rather than the method | |
681 * [Source.getModificationStamp] because contexts can have local overrides of | |
682 * the content of a source that the source is not aware of. | |
683 */ | |
684 int getModificationStamp(Source source); | |
685 | |
686 /** | |
687 * Return a fully resolved AST for the compilation unit defined by the given | |
688 * [unitSource] within the given [library], or `null` if the resolved AST is | |
689 * not already computed. | |
690 * | |
691 * See [resolveCompilationUnit]. | |
692 */ | |
693 CompilationUnit getResolvedCompilationUnit( | |
694 Source unitSource, LibraryElement library); | |
695 | |
696 /** | |
697 * Return a fully resolved AST for the compilation unit defined by the given | |
698 * [unitSource] within the library defined by the given [librarySource], or | |
699 * `null` if the resolved AST is not already computed. | |
700 * | |
701 * See [resolveCompilationUnit2]. | |
702 */ | |
703 CompilationUnit getResolvedCompilationUnit2( | |
704 Source unitSource, Source librarySource); | |
705 | |
706 /** | |
707 * Return the fully resolved HTML unit defined by the given [htmlSource], or | |
708 * `null` if the resolved unit is not already computed. | |
709 * | |
710 * See [resolveHtmlUnit]. | |
711 */ | |
712 @deprecated | |
713 ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource); | |
714 | |
715 /** | |
716 * Return a list of the sources being analyzed in this context whose full path | |
717 * is equal to the given [path]. | |
718 */ | |
719 List<Source> getSourcesWithFullName(String path); | |
720 | |
721 /** | |
722 * Invalidates hints in the given [librarySource] and included parts. | |
723 */ | |
724 void invalidateLibraryHints(Source librarySource); | |
725 | |
726 /** | |
727 * Return `true` if the given [librarySource] is known to be the defining | |
728 * compilation unit of a library that can be run on a client (references | |
729 * 'dart:html', either directly or indirectly). | |
730 * | |
731 * <b>Note:</b> In addition to the expected case of returning `false` if the | |
732 * source is known to be a library that cannot be run on a client, this method | |
733 * will also return `false` if the source is not known to be a library or if | |
734 * we do not know whether it can be run on a client. | |
735 */ | |
736 bool isClientLibrary(Source librarySource); | |
737 | |
738 /** | |
739 * Return `true` if the given [librarySource] is known to be the defining | |
740 * compilation unit of a library that can be run on the server (does not | |
741 * reference 'dart:html', either directly or indirectly). | |
742 * | |
743 * <b>Note:</b> In addition to the expected case of returning `false` if the | |
744 * source is known to be a library that cannot be run on the server, this | |
745 * method will also return `false` if the source is not known to be a library | |
746 * or if we do not know whether it can be run on the server. | |
747 */ | |
748 bool isServerLibrary(Source librarySource); | |
749 | |
750 /** | |
751 * Return the stream that is notified when a new value for the given | |
752 * [descriptor] is computed. | |
753 */ | |
754 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor); | |
755 | |
756 /** | |
757 * Parse the content of the given [source] to produce an AST structure. The | |
758 * resulting AST structure may or may not be resolved, and may have a slightly | |
759 * different structure depending upon whether it is resolved. | |
760 * | |
761 * Throws an [AnalysisException] if the analysis could not be performed | |
762 * | |
763 * <b>Note:</b> This method cannot be used in an async environment. | |
764 */ | |
765 CompilationUnit parseCompilationUnit(Source source); | |
766 | |
767 /** | |
768 * Parse a single HTML [source] to produce a document model. | |
769 * | |
770 * Throws an [AnalysisException] if the analysis could not be performed | |
771 * | |
772 * <b>Note:</b> This method cannot be used in an async environment. | |
773 */ | |
774 Document parseHtmlDocument(Source source); | |
775 | |
776 /** | |
777 * Parse a single HTML [source] to produce an AST structure. The resulting | |
778 * HTML AST structure may or may not be resolved, and may have a slightly | |
779 * different structure depending upon whether it is resolved. | |
780 * | |
781 * Throws an [AnalysisException] if the analysis could not be performed | |
782 * | |
783 * <b>Note:</b> This method cannot be used in an async environment. | |
784 */ | |
785 @deprecated // use parseHtmlDocument(source) | |
786 ht.HtmlUnit parseHtmlUnit(Source source); | |
787 | |
788 /** | |
789 * Perform the next unit of work required to keep the analysis results | |
790 * up-to-date and return information about the consequent changes to the | |
791 * analysis results. This method can be long running. | |
792 * | |
793 * The implementation that uses the task model notifies subscribers of | |
794 * [onResultComputed] about computed results. | |
795 * | |
796 * The following results are computed for Dart sources. | |
797 * | |
798 * 1. For explicit and implicit sources: | |
799 * [PARSED_UNIT] | |
800 * [RESOLVED_UNIT] | |
801 * | |
802 * 2. For explicit sources: | |
803 * [DART_ERRORS]. | |
804 * | |
805 * 3. For explicit and implicit library sources: | |
806 * [LIBRARY_ELEMENT]. | |
807 */ | |
808 AnalysisResult performAnalysisTask(); | |
809 | |
810 /** | |
811 * Remove the given [listener] from the list of objects that are to be | |
812 * notified when various analysis results are produced in this context. | |
813 */ | |
814 void removeListener(AnalysisListener listener); | |
815 | |
816 /** | |
817 * Return a fully resolved AST for the compilation unit defined by the given | |
818 * [unitSource] within the given [library]. | |
819 * | |
820 * Throws an [AnalysisException] if the analysis could not be performed. | |
821 * | |
822 * <b>Note:</b> This method cannot be used in an async environment. | |
823 * | |
824 * See [getResolvedCompilationUnit]. | |
825 */ | |
826 CompilationUnit resolveCompilationUnit( | |
827 Source unitSource, LibraryElement library); | |
828 | |
829 /** | |
830 * Return a fully resolved AST for the compilation unit defined by the given | |
831 * [unitSource] within the library defined by the given [librarySource]. | |
832 * | |
833 * Throws an [AnalysisException] if the analysis could not be performed. | |
834 * | |
835 * <b>Note:</b> This method cannot be used in an async environment. | |
836 * | |
837 * See [getResolvedCompilationUnit2]. | |
838 */ | |
839 CompilationUnit resolveCompilationUnit2( | |
840 Source unitSource, Source librarySource); | |
841 | |
842 /** | |
843 * Parse and resolve a single [htmlSource] within the given context to produce | |
844 * a fully resolved AST. | |
845 * | |
846 * Throws an [AnalysisException] if the analysis could not be performed. | |
847 * | |
848 * <b>Note:</b> This method cannot be used in an async environment. | |
849 */ | |
850 @deprecated | |
851 ht.HtmlUnit resolveHtmlUnit(Source htmlSource); | |
852 | |
853 /** | |
854 * Set the contents of the given [source] to the given [contents] and mark the | |
855 * source as having changed. The additional [offset] and [length] information | |
856 * is used by the context to determine what reanalysis is necessary. | |
857 */ | |
858 void setChangedContents( | |
859 Source source, String contents, int offset, int oldLength, int newLength); | |
860 | |
861 /** | |
862 * Set the contents of the given [source] to the given [contents] and mark the | |
863 * source as having changed. This has the effect of overriding the default | |
864 * contents of the source. If the contents are `null` the override is removed | |
865 * so that the default contents will be returned. | |
866 */ | |
867 void setContents(Source source, String contents); | |
868 | |
869 /** | |
870 * Check the cache for any invalid entries (entries whose modification time | |
871 * does not match the modification time of the source associated with the | |
872 * entry). Invalid entries will be marked as invalid so that the source will | |
873 * be re-analyzed. Return `true` if at least one entry was invalid. | |
874 */ | |
875 bool validateCacheConsistency(); | |
876 } | |
877 | |
878 /** | |
879 * An [AnalysisContext]. | |
880 */ | |
881 class AnalysisContextImpl implements InternalAnalysisContext { | |
882 /** | |
883 * The difference between the maximum cache size and the maximum priority | |
884 * order size. The priority list must be capped so that it is less than the | |
885 * cache size. Failure to do so can result in an infinite loop in | |
886 * performAnalysisTask() because re-caching one AST structure can cause | |
887 * another priority source's AST structure to be flushed. | |
888 */ | |
889 static int _PRIORITY_ORDER_SIZE_DELTA = 4; | |
890 | |
891 /** | |
892 * A flag indicating whether trace output should be produced as analysis tasks | |
893 * are performed. Used for debugging. | |
894 */ | |
895 static bool _TRACE_PERFORM_TASK = false; | |
896 | |
897 /** | |
898 * The next context identifier. | |
899 */ | |
900 static int _NEXT_ID = 0; | |
901 | |
902 /** | |
903 * The unique identifier of this context. | |
904 */ | |
905 final int _id = _NEXT_ID++; | |
906 | |
907 /** | |
908 * A client-provided name used to identify this context, or `null` if the | |
909 * client has not provided a name. | |
910 */ | |
911 String name; | |
912 | |
913 /** | |
914 * The set of analysis options controlling the behavior of this context. | |
915 */ | |
916 AnalysisOptionsImpl _options = new AnalysisOptionsImpl(); | |
917 | |
918 /** | |
919 * A flag indicating whether errors related to implicitly analyzed sources | |
920 * should be generated and reported. | |
921 */ | |
922 bool _generateImplicitErrors = true; | |
923 | |
924 /** | |
925 * A flag indicating whether errors related to sources in the SDK should be | |
926 * generated and reported. | |
927 */ | |
928 bool _generateSdkErrors = true; | |
929 | |
930 /** | |
931 * A flag indicating whether this context is disposed. | |
932 */ | |
933 bool _disposed = false; | |
934 | |
935 /** | |
936 * A cache of content used to override the default content of a source. | |
937 */ | |
938 ContentCache _contentCache = new ContentCache(); | |
939 | |
940 /** | |
941 * The source factory used to create the sources that can be analyzed in this | |
942 * context. | |
943 */ | |
944 SourceFactory _sourceFactory; | |
945 | |
946 /** | |
947 * The set of declared variables used when computing constant values. | |
948 */ | |
949 DeclaredVariables _declaredVariables = new DeclaredVariables(); | |
950 | |
951 /** | |
952 * A source representing the core library. | |
953 */ | |
954 Source _coreLibrarySource; | |
955 | |
956 /** | |
957 * A source representing the async library. | |
958 */ | |
959 Source _asyncLibrarySource; | |
960 | |
961 /** | |
962 * The partition that contains analysis results that are not shared with other | |
963 * contexts. | |
964 */ | |
965 CachePartition _privatePartition; | |
966 | |
967 /** | |
968 * A table mapping the sources known to the context to the information known | |
969 * about the source. | |
970 */ | |
971 AnalysisCache _cache; | |
972 | |
973 /** | |
974 * A list containing sources for which data should not be flushed. | |
975 */ | |
976 List<Source> _priorityOrder = Source.EMPTY_LIST; | |
977 | |
978 /** | |
979 * A map from all sources for which there are futures pending to a list of | |
980 * the corresponding PendingFuture objects. These sources will be analyzed | |
981 * in the same way as priority sources, except with higher priority. | |
982 * | |
983 * TODO(paulberry): since the size of this map is not constrained (as it is | |
984 * for _priorityOrder), we run the risk of creating an analysis loop if | |
985 * re-caching one AST structure causes the AST structure for another source | |
986 * with pending futures to be flushed. However, this is unlikely to happen | |
987 * in practice since sources are removed from this hash set as soon as their | |
988 * futures have completed. | |
989 */ | |
990 HashMap<Source, List<PendingFuture>> _pendingFutureSources = | |
991 new HashMap<Source, List<PendingFuture>>(); | |
992 | |
993 /** | |
994 * A list containing sources whose AST structure is needed in order to resolve | |
995 * the next library to be resolved. | |
996 */ | |
997 HashSet<Source> _neededForResolution = null; | |
998 | |
999 /** | |
1000 * A table mapping sources to the change notices that are waiting to be | |
1001 * returned related to that source. | |
1002 */ | |
1003 HashMap<Source, ChangeNoticeImpl> _pendingNotices = | |
1004 new HashMap<Source, ChangeNoticeImpl>(); | |
1005 | |
1006 /** | |
1007 * The object used to record the results of performing an analysis task. | |
1008 */ | |
1009 AnalysisContextImpl_AnalysisTaskResultRecorder _resultRecorder; | |
1010 | |
1011 /** | |
1012 * Cached information used in incremental analysis or `null` if none. | |
1013 */ | |
1014 IncrementalAnalysisCache _incrementalAnalysisCache; | |
1015 | |
1016 /** | |
1017 * The [TypeProvider] for this context, `null` if not yet created. | |
1018 */ | |
1019 TypeProvider _typeProvider; | |
1020 | |
1021 /** | |
1022 * The object used to manage the list of sources that need to be analyzed. | |
1023 */ | |
1024 WorkManager _workManager = new WorkManager(); | |
1025 | |
1026 /** | |
1027 * The [Stopwatch] of the current "perform tasks cycle". | |
1028 */ | |
1029 Stopwatch _performAnalysisTaskStopwatch; | |
1030 | |
1031 /** | |
1032 * The controller for sending [SourcesChangedEvent]s. | |
1033 */ | |
1034 StreamController<SourcesChangedEvent> _onSourcesChangedController; | |
1035 | |
1036 /** | |
1037 * The listeners that are to be notified when various analysis results are | |
1038 * produced in this context. | |
1039 */ | |
1040 List<AnalysisListener> _listeners = new List<AnalysisListener>(); | |
1041 | |
1042 /** | |
1043 * The most recently incrementally resolved source, or `null` when it was | |
1044 * already validated, or the most recent change was not incrementally resolved
. | |
1045 */ | |
1046 Source incrementalResolutionValidation_lastUnitSource; | |
1047 | |
1048 /** | |
1049 * The most recently incrementally resolved library source, or `null` when it | |
1050 * was already validated, or the most recent change was not incrementally | |
1051 * resolved. | |
1052 */ | |
1053 Source incrementalResolutionValidation_lastLibrarySource; | |
1054 | |
1055 /** | |
1056 * The result of incremental resolution result of | |
1057 * [incrementalResolutionValidation_lastSource]. | |
1058 */ | |
1059 CompilationUnit incrementalResolutionValidation_lastUnit; | |
1060 | |
1061 /** | |
1062 * A factory to override how the [ResolverVisitor] is created. | |
1063 */ | |
1064 ResolverVisitorFactory resolverVisitorFactory; | |
1065 | |
1066 /** | |
1067 * A factory to override how the [TypeResolverVisitor] is created. | |
1068 */ | |
1069 TypeResolverVisitorFactory typeResolverVisitorFactory; | |
1070 | |
1071 /** | |
1072 * A factory to override how [LibraryResolver] is created. | |
1073 */ | |
1074 LibraryResolverFactory libraryResolverFactory; | |
1075 | |
1076 /** | |
1077 * Initialize a newly created analysis context. | |
1078 */ | |
1079 AnalysisContextImpl() { | |
1080 _resultRecorder = new AnalysisContextImpl_AnalysisTaskResultRecorder(this); | |
1081 _privatePartition = new UniversalCachePartition(this, | |
1082 AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, | |
1083 new AnalysisContextImpl_ContextRetentionPolicy(this)); | |
1084 _cache = createCacheFromSourceFactory(null); | |
1085 _onSourcesChangedController = | |
1086 new StreamController<SourcesChangedEvent>.broadcast(); | |
1087 } | |
1088 | |
1089 @override | |
1090 AnalysisCache get analysisCache => _cache; | |
1091 | |
1092 @override | |
1093 AnalysisOptions get analysisOptions => _options; | |
1094 | |
1095 @override | |
1096 void set analysisOptions(AnalysisOptions options) { | |
1097 bool needsRecompute = this._options.analyzeFunctionBodiesPredicate != | |
1098 options.analyzeFunctionBodiesPredicate || | |
1099 this._options.generateImplicitErrors != | |
1100 options.generateImplicitErrors || | |
1101 this._options.generateSdkErrors != options.generateSdkErrors || | |
1102 this._options.dart2jsHint != options.dart2jsHint || | |
1103 (this._options.hint && !options.hint) || | |
1104 this._options.preserveComments != options.preserveComments || | |
1105 this._options.enableStrictCallChecks != options.enableStrictCallChecks; | |
1106 int cacheSize = options.cacheSize; | |
1107 if (this._options.cacheSize != cacheSize) { | |
1108 this._options.cacheSize = cacheSize; | |
1109 //cache.setMaxCacheSize(cacheSize); | |
1110 _privatePartition.maxCacheSize = cacheSize; | |
1111 // | |
1112 // Cap the size of the priority list to being less than the cache size. | |
1113 // Failure to do so can result in an infinite loop in | |
1114 // performAnalysisTask() because re-caching one AST structure | |
1115 // can cause another priority source's AST structure to be flushed. | |
1116 // | |
1117 // TODO(brianwilkerson) Remove this constraint when the new task model is | |
1118 // implemented. | |
1119 // | |
1120 int maxPriorityOrderSize = cacheSize - _PRIORITY_ORDER_SIZE_DELTA; | |
1121 if (_priorityOrder.length > maxPriorityOrderSize) { | |
1122 _priorityOrder = _priorityOrder.sublist(0, maxPriorityOrderSize); | |
1123 } | |
1124 } | |
1125 this._options.analyzeFunctionBodiesPredicate = | |
1126 options.analyzeFunctionBodiesPredicate; | |
1127 this._options.generateImplicitErrors = options.generateImplicitErrors; | |
1128 this._options.generateSdkErrors = options.generateSdkErrors; | |
1129 this._options.dart2jsHint = options.dart2jsHint; | |
1130 this._options.enableStrictCallChecks = options.enableStrictCallChecks; | |
1131 this._options.hint = options.hint; | |
1132 this._options.incremental = options.incremental; | |
1133 this._options.incrementalApi = options.incrementalApi; | |
1134 this._options.incrementalValidation = options.incrementalValidation; | |
1135 this._options.lint = options.lint; | |
1136 this._options.preserveComments = options.preserveComments; | |
1137 _generateImplicitErrors = options.generateImplicitErrors; | |
1138 _generateSdkErrors = options.generateSdkErrors; | |
1139 if (needsRecompute) { | |
1140 _invalidateAllLocalResolutionInformation(false); | |
1141 } | |
1142 } | |
1143 | |
1144 @override | |
1145 void set analysisPriorityOrder(List<Source> sources) { | |
1146 if (sources == null || sources.isEmpty) { | |
1147 _priorityOrder = Source.EMPTY_LIST; | |
1148 } else { | |
1149 while (sources.remove(null)) { | |
1150 // Nothing else to do. | |
1151 } | |
1152 if (sources.isEmpty) { | |
1153 _priorityOrder = Source.EMPTY_LIST; | |
1154 } | |
1155 // | |
1156 // Cap the size of the priority list to being less than the cache size. | |
1157 // Failure to do so can result in an infinite loop in | |
1158 // performAnalysisTask() because re-caching one AST structure | |
1159 // can cause another priority source's AST structure to be flushed. | |
1160 // | |
1161 int count = math.min( | |
1162 sources.length, _options.cacheSize - _PRIORITY_ORDER_SIZE_DELTA); | |
1163 _priorityOrder = new List<Source>(count); | |
1164 for (int i = 0; i < count; i++) { | |
1165 _priorityOrder[i] = sources[i]; | |
1166 } | |
1167 // Ensure entries for every priority source. | |
1168 for (var source in _priorityOrder) { | |
1169 SourceEntry entry = _getReadableSourceEntry(source); | |
1170 if (entry == null) { | |
1171 _createSourceEntry(source, false); | |
1172 } | |
1173 } | |
1174 } | |
1175 } | |
1176 | |
1177 @override | |
1178 set contentCache(ContentCache value) { | |
1179 _contentCache = value; | |
1180 } | |
1181 | |
1182 @override | |
1183 DeclaredVariables get declaredVariables => _declaredVariables; | |
1184 | |
1185 @override | |
1186 List<AnalysisTarget> get explicitTargets { | |
1187 List<AnalysisTarget> targets = <AnalysisTarget>[]; | |
1188 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
1189 while (iterator.moveNext()) { | |
1190 if (iterator.value.explicitlyAdded) { | |
1191 targets.add(iterator.key); | |
1192 } | |
1193 } | |
1194 return targets; | |
1195 } | |
1196 | |
1197 @override | |
1198 List<Source> get htmlSources => _getSources(SourceKind.HTML); | |
1199 | |
1200 @override | |
1201 bool get isDisposed => _disposed; | |
1202 | |
1203 @override | |
1204 List<Source> get launchableClientLibrarySources { | |
1205 // TODO(brianwilkerson) This needs to filter out libraries that do not | |
1206 // reference dart:html, either directly or indirectly. | |
1207 List<Source> sources = new List<Source>(); | |
1208 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
1209 while (iterator.moveNext()) { | |
1210 Source source = iterator.key; | |
1211 SourceEntry sourceEntry = iterator.value; | |
1212 if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) { | |
1213 // DartEntry dartEntry = (DartEntry) sourceEntry; | |
1214 // if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && dartEntry.getValu
e(DartEntry.IS_CLIENT)) { | |
1215 sources.add(source); | |
1216 // } | |
1217 } | |
1218 } | |
1219 return sources; | |
1220 } | |
1221 | |
1222 @override | |
1223 List<Source> get launchableServerLibrarySources { | |
1224 // TODO(brianwilkerson) This needs to filter out libraries that reference | |
1225 // dart:html, either directly or indirectly. | |
1226 List<Source> sources = new List<Source>(); | |
1227 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
1228 while (iterator.moveNext()) { | |
1229 Source source = iterator.key; | |
1230 SourceEntry sourceEntry = iterator.value; | |
1231 if (sourceEntry.kind == SourceKind.LIBRARY && !source.isInSystemLibrary) { | |
1232 // DartEntry dartEntry = (DartEntry) sourceEntry; | |
1233 // if (dartEntry.getValue(DartEntry.IS_LAUNCHABLE) && !dartEntry.getVal
ue(DartEntry.IS_CLIENT)) { | |
1234 sources.add(source); | |
1235 // } | |
1236 } | |
1237 } | |
1238 return sources; | |
1239 } | |
1240 | |
1241 @override | |
1242 List<Source> get librarySources => _getSources(SourceKind.LIBRARY); | |
1243 | |
1244 /** | |
1245 * Look through the cache for a task that needs to be performed. Return the | |
1246 * task that was found, or `null` if there is no more work to be done. | |
1247 */ | |
1248 AnalysisTask get nextAnalysisTask { | |
1249 bool hintsEnabled = _options.hint; | |
1250 bool lintsEnabled = _options.lint; | |
1251 bool hasBlockedTask = false; | |
1252 // | |
1253 // Look for incremental analysis | |
1254 // | |
1255 if (_incrementalAnalysisCache != null && | |
1256 _incrementalAnalysisCache.hasWork) { | |
1257 AnalysisTask task = | |
1258 new IncrementalAnalysisTask(this, _incrementalAnalysisCache); | |
1259 _incrementalAnalysisCache = null; | |
1260 return task; | |
1261 } | |
1262 // | |
1263 // Look for a source that needs to be analyzed because it has futures | |
1264 // pending. | |
1265 // | |
1266 if (_pendingFutureSources.isNotEmpty) { | |
1267 List<Source> sourcesToRemove = <Source>[]; | |
1268 AnalysisTask task; | |
1269 for (Source source in _pendingFutureSources.keys) { | |
1270 SourceEntry sourceEntry = _cache.get(source); | |
1271 List<PendingFuture> pendingFutures = _pendingFutureSources[source]; | |
1272 for (int i = 0; i < pendingFutures.length;) { | |
1273 if (pendingFutures[i].evaluate(sourceEntry)) { | |
1274 pendingFutures.removeAt(i); | |
1275 } else { | |
1276 i++; | |
1277 } | |
1278 } | |
1279 if (pendingFutures.isEmpty) { | |
1280 sourcesToRemove.add(source); | |
1281 continue; | |
1282 } | |
1283 AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( | |
1284 source, sourceEntry, true, hintsEnabled, lintsEnabled); | |
1285 task = taskData.task; | |
1286 if (task != null) { | |
1287 break; | |
1288 } else if (taskData.isBlocked) { | |
1289 hasBlockedTask = true; | |
1290 } else { | |
1291 // There is no more work to do for this task, so forcibly complete | |
1292 // all its pending futures. | |
1293 for (PendingFuture pendingFuture in pendingFutures) { | |
1294 pendingFuture.forciblyComplete(); | |
1295 } | |
1296 sourcesToRemove.add(source); | |
1297 } | |
1298 } | |
1299 for (Source source in sourcesToRemove) { | |
1300 _pendingFutureSources.remove(source); | |
1301 } | |
1302 if (task != null) { | |
1303 return task; | |
1304 } | |
1305 } | |
1306 // | |
1307 // Look for a priority source that needs to be analyzed. | |
1308 // | |
1309 int priorityCount = _priorityOrder.length; | |
1310 for (int i = 0; i < priorityCount; i++) { | |
1311 Source source = _priorityOrder[i]; | |
1312 AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( | |
1313 source, _cache.get(source), true, hintsEnabled, lintsEnabled); | |
1314 AnalysisTask task = taskData.task; | |
1315 if (task != null) { | |
1316 return task; | |
1317 } else if (taskData.isBlocked) { | |
1318 hasBlockedTask = true; | |
1319 } | |
1320 } | |
1321 if (_neededForResolution != null) { | |
1322 List<Source> sourcesToRemove = new List<Source>(); | |
1323 for (Source source in _neededForResolution) { | |
1324 SourceEntry sourceEntry = _cache.get(source); | |
1325 if (sourceEntry is DartEntry) { | |
1326 DartEntry dartEntry = sourceEntry; | |
1327 if (!dartEntry.hasResolvableCompilationUnit) { | |
1328 if (dartEntry.getState(DartEntry.PARSED_UNIT) == CacheState.ERROR) { | |
1329 sourcesToRemove.add(source); | |
1330 } else { | |
1331 AnalysisContextImpl_TaskData taskData = | |
1332 _createParseDartTask(source, dartEntry); | |
1333 AnalysisTask task = taskData.task; | |
1334 if (task != null) { | |
1335 return task; | |
1336 } else if (taskData.isBlocked) { | |
1337 hasBlockedTask = true; | |
1338 } | |
1339 } | |
1340 } | |
1341 } | |
1342 } | |
1343 int count = sourcesToRemove.length; | |
1344 for (int i = 0; i < count; i++) { | |
1345 _neededForResolution.remove(sourcesToRemove[i]); | |
1346 } | |
1347 } | |
1348 // | |
1349 // Look for a non-priority source that needs to be analyzed. | |
1350 // | |
1351 List<Source> sourcesToRemove = new List<Source>(); | |
1352 WorkManager_WorkIterator sources = _workManager.iterator(); | |
1353 try { | |
1354 while (sources.hasNext) { | |
1355 Source source = sources.next(); | |
1356 AnalysisContextImpl_TaskData taskData = _getNextAnalysisTaskForSource( | |
1357 source, _cache.get(source), false, hintsEnabled, lintsEnabled); | |
1358 AnalysisTask task = taskData.task; | |
1359 if (task != null) { | |
1360 return task; | |
1361 } else if (taskData.isBlocked) { | |
1362 hasBlockedTask = true; | |
1363 } else { | |
1364 sourcesToRemove.add(source); | |
1365 } | |
1366 } | |
1367 } finally { | |
1368 int count = sourcesToRemove.length; | |
1369 for (int i = 0; i < count; i++) { | |
1370 _workManager.remove(sourcesToRemove[i]); | |
1371 } | |
1372 } | |
1373 if (hasBlockedTask) { | |
1374 // All of the analysis work is blocked waiting for an asynchronous task | |
1375 // to complete. | |
1376 return WaitForAsyncTask.instance; | |
1377 } | |
1378 return null; | |
1379 } | |
1380 | |
1381 @override | |
1382 Stream<SourcesChangedEvent> get onSourcesChanged => | |
1383 _onSourcesChangedController.stream; | |
1384 | |
1385 /** | |
1386 * Make _pendingFutureSources available to unit tests. | |
1387 */ | |
1388 HashMap<Source, List<PendingFuture>> get pendingFutureSources_forTesting => | |
1389 _pendingFutureSources; | |
1390 | |
1391 @override | |
1392 List<Source> get prioritySources => _priorityOrder; | |
1393 | |
1394 @override | |
1395 List<AnalysisTarget> get priorityTargets => prioritySources; | |
1396 | |
1397 @override | |
1398 CachePartition get privateAnalysisCachePartition => _privatePartition; | |
1399 | |
1400 @override | |
1401 SourceFactory get sourceFactory => _sourceFactory; | |
1402 | |
1403 @override | |
1404 void set sourceFactory(SourceFactory factory) { | |
1405 if (identical(_sourceFactory, factory)) { | |
1406 return; | |
1407 } else if (factory.context != null) { | |
1408 throw new IllegalStateException( | |
1409 "Source factories cannot be shared between contexts"); | |
1410 } | |
1411 if (_sourceFactory != null) { | |
1412 _sourceFactory.context = null; | |
1413 } | |
1414 factory.context = this; | |
1415 _sourceFactory = factory; | |
1416 _coreLibrarySource = _sourceFactory.forUri(DartSdk.DART_CORE); | |
1417 _asyncLibrarySource = _sourceFactory.forUri(DartSdk.DART_ASYNC); | |
1418 _cache = createCacheFromSourceFactory(factory); | |
1419 _invalidateAllLocalResolutionInformation(true); | |
1420 } | |
1421 | |
1422 @override | |
1423 List<Source> get sources { | |
1424 List<Source> sources = new List<Source>(); | |
1425 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
1426 while (iterator.moveNext()) { | |
1427 sources.add(iterator.key); | |
1428 } | |
1429 return sources; | |
1430 } | |
1431 | |
1432 /** | |
1433 * Return a list of the sources that would be processed by | |
1434 * [performAnalysisTask]. This method duplicates, and must therefore be kept | |
1435 * in sync with, [getNextAnalysisTask]. This method is intended to be used for | |
1436 * testing purposes only. | |
1437 */ | |
1438 List<Source> get sourcesNeedingProcessing { | |
1439 HashSet<Source> sources = new HashSet<Source>(); | |
1440 bool hintsEnabled = _options.hint; | |
1441 bool lintsEnabled = _options.lint; | |
1442 | |
1443 // | |
1444 // Look for priority sources that need to be analyzed. | |
1445 // | |
1446 for (Source source in _priorityOrder) { | |
1447 _getSourcesNeedingProcessing(source, _cache.get(source), true, | |
1448 hintsEnabled, lintsEnabled, sources); | |
1449 } | |
1450 // | |
1451 // Look for non-priority sources that need to be analyzed. | |
1452 // | |
1453 WorkManager_WorkIterator iterator = _workManager.iterator(); | |
1454 while (iterator.hasNext) { | |
1455 Source source = iterator.next(); | |
1456 _getSourcesNeedingProcessing(source, _cache.get(source), false, | |
1457 hintsEnabled, lintsEnabled, sources); | |
1458 } | |
1459 return new List<Source>.from(sources); | |
1460 } | |
1461 | |
1462 @override | |
1463 AnalysisContextStatistics get statistics { | |
1464 AnalysisContextStatisticsImpl statistics = | |
1465 new AnalysisContextStatisticsImpl(); | |
1466 visitCacheItems(statistics._internalPutCacheItem); | |
1467 statistics.partitionData = _cache.partitionData; | |
1468 return statistics; | |
1469 } | |
1470 | |
1471 IncrementalAnalysisCache get test_incrementalAnalysisCache { | |
1472 return _incrementalAnalysisCache; | |
1473 } | |
1474 | |
1475 set test_incrementalAnalysisCache(IncrementalAnalysisCache value) { | |
1476 _incrementalAnalysisCache = value; | |
1477 } | |
1478 | |
1479 List<Source> get test_priorityOrder => _priorityOrder; | |
1480 | |
1481 @override | |
1482 TypeProvider get typeProvider { | |
1483 if (_typeProvider != null) { | |
1484 return _typeProvider; | |
1485 } | |
1486 Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE); | |
1487 if (coreSource == null) { | |
1488 throw new AnalysisException("Could not create a source for dart:core"); | |
1489 } | |
1490 LibraryElement coreElement = computeLibraryElement(coreSource); | |
1491 if (coreElement == null) { | |
1492 throw new AnalysisException("Could not create an element for dart:core"); | |
1493 } | |
1494 Source asyncSource = sourceFactory.forUri(DartSdk.DART_ASYNC); | |
1495 if (asyncSource == null) { | |
1496 throw new AnalysisException("Could not create a source for dart:async"); | |
1497 } | |
1498 LibraryElement asyncElement = computeLibraryElement(asyncSource); | |
1499 if (asyncElement == null) { | |
1500 throw new AnalysisException("Could not create an element for dart:async"); | |
1501 } | |
1502 _typeProvider = new TypeProviderImpl(coreElement, asyncElement); | |
1503 return _typeProvider; | |
1504 } | |
1505 | |
1506 /** | |
1507 * Sets the [TypeProvider] for this context. | |
1508 */ | |
1509 void set typeProvider(TypeProvider typeProvider) { | |
1510 _typeProvider = typeProvider; | |
1511 } | |
1512 | |
1513 @override | |
1514 void addListener(AnalysisListener listener) { | |
1515 if (!_listeners.contains(listener)) { | |
1516 _listeners.add(listener); | |
1517 } | |
1518 } | |
1519 | |
1520 @override | |
1521 void applyAnalysisDelta(AnalysisDelta delta) { | |
1522 ChangeSet changeSet = new ChangeSet(); | |
1523 delta.analysisLevels.forEach((Source source, AnalysisLevel level) { | |
1524 if (level == AnalysisLevel.NONE) { | |
1525 changeSet.removedSource(source); | |
1526 } else { | |
1527 changeSet.addedSource(source); | |
1528 } | |
1529 }); | |
1530 applyChanges(changeSet); | |
1531 } | |
1532 | |
1533 @override | |
1534 void applyChanges(ChangeSet changeSet) { | |
1535 if (changeSet.isEmpty) { | |
1536 return; | |
1537 } | |
1538 // | |
1539 // First, compute the list of sources that have been removed. | |
1540 // | |
1541 List<Source> removedSources = | |
1542 new List<Source>.from(changeSet.removedSources); | |
1543 for (SourceContainer container in changeSet.removedContainers) { | |
1544 _addSourcesInContainer(removedSources, container); | |
1545 } | |
1546 // | |
1547 // Then determine which cached results are no longer valid. | |
1548 // | |
1549 for (Source source in changeSet.addedSources) { | |
1550 _sourceAvailable(source); | |
1551 } | |
1552 for (Source source in changeSet.changedSources) { | |
1553 if (_contentCache.getContents(source) != null) { | |
1554 // This source is overridden in the content cache, so the change will | |
1555 // have no effect. Just ignore it to avoid wasting time doing | |
1556 // re-analysis. | |
1557 continue; | |
1558 } | |
1559 _sourceChanged(source); | |
1560 } | |
1561 changeSet.changedContents.forEach((Source key, String value) { | |
1562 _contentsChanged(key, value, false); | |
1563 }); | |
1564 changeSet.changedRanges | |
1565 .forEach((Source source, ChangeSet_ContentChange change) { | |
1566 _contentRangeChanged(source, change.contents, change.offset, | |
1567 change.oldLength, change.newLength); | |
1568 }); | |
1569 for (Source source in changeSet.deletedSources) { | |
1570 _sourceDeleted(source); | |
1571 } | |
1572 for (Source source in removedSources) { | |
1573 _sourceRemoved(source); | |
1574 } | |
1575 _onSourcesChangedController.add(new SourcesChangedEvent(changeSet)); | |
1576 } | |
1577 | |
1578 @override | |
1579 String computeDocumentationComment(Element element) { | |
1580 if (element == null) { | |
1581 return null; | |
1582 } | |
1583 Source source = element.source; | |
1584 if (source == null) { | |
1585 return null; | |
1586 } | |
1587 CompilationUnit unit = parseCompilationUnit(source); | |
1588 if (unit == null) { | |
1589 return null; | |
1590 } | |
1591 NodeLocator locator = new NodeLocator(element.nameOffset); | |
1592 AstNode nameNode = locator.searchWithin(unit); | |
1593 while (nameNode != null) { | |
1594 if (nameNode is AnnotatedNode) { | |
1595 Comment comment = nameNode.documentationComment; | |
1596 if (comment == null) { | |
1597 return null; | |
1598 } | |
1599 StringBuffer buffer = new StringBuffer(); | |
1600 List<Token> tokens = comment.tokens; | |
1601 for (int i = 0; i < tokens.length; i++) { | |
1602 if (i > 0) { | |
1603 buffer.write("\n"); | |
1604 } | |
1605 buffer.write(tokens[i].lexeme); | |
1606 } | |
1607 return buffer.toString(); | |
1608 } | |
1609 nameNode = nameNode.parent; | |
1610 } | |
1611 return null; | |
1612 } | |
1613 | |
1614 @override | |
1615 List<AnalysisError> computeErrors(Source source) { | |
1616 bool enableHints = _options.hint; | |
1617 bool enableLints = _options.lint; | |
1618 | |
1619 SourceEntry sourceEntry = _getReadableSourceEntry(source); | |
1620 if (sourceEntry is DartEntry) { | |
1621 List<AnalysisError> errors = new List<AnalysisError>(); | |
1622 try { | |
1623 DartEntry dartEntry = sourceEntry; | |
1624 ListUtilities.addAll( | |
1625 errors, _getDartScanData(source, dartEntry, DartEntry.SCAN_ERRORS)); | |
1626 dartEntry = _getReadableDartEntry(source); | |
1627 ListUtilities.addAll(errors, | |
1628 _getDartParseData(source, dartEntry, DartEntry.PARSE_ERRORS)); | |
1629 dartEntry = _getReadableDartEntry(source); | |
1630 if (dartEntry.getValue(DartEntry.SOURCE_KIND) == SourceKind.LIBRARY) { | |
1631 ListUtilities.addAll(errors, _getDartResolutionData( | |
1632 source, source, dartEntry, DartEntry.RESOLUTION_ERRORS)); | |
1633 dartEntry = _getReadableDartEntry(source); | |
1634 ListUtilities.addAll(errors, _getDartVerificationData( | |
1635 source, source, dartEntry, DartEntry.VERIFICATION_ERRORS)); | |
1636 if (enableHints) { | |
1637 dartEntry = _getReadableDartEntry(source); | |
1638 ListUtilities.addAll(errors, | |
1639 _getDartHintData(source, source, dartEntry, DartEntry.HINTS)); | |
1640 } | |
1641 if (enableLints) { | |
1642 dartEntry = _getReadableDartEntry(source); | |
1643 ListUtilities.addAll(errors, | |
1644 _getDartLintData(source, source, dartEntry, DartEntry.LINTS)); | |
1645 } | |
1646 } else { | |
1647 List<Source> libraries = getLibrariesContaining(source); | |
1648 for (Source librarySource in libraries) { | |
1649 ListUtilities.addAll(errors, _getDartResolutionData( | |
1650 source, librarySource, dartEntry, DartEntry.RESOLUTION_ERRORS)); | |
1651 dartEntry = _getReadableDartEntry(source); | |
1652 ListUtilities.addAll(errors, _getDartVerificationData(source, | |
1653 librarySource, dartEntry, DartEntry.VERIFICATION_ERRORS)); | |
1654 if (enableHints) { | |
1655 dartEntry = _getReadableDartEntry(source); | |
1656 ListUtilities.addAll(errors, _getDartHintData( | |
1657 source, librarySource, dartEntry, DartEntry.HINTS)); | |
1658 } | |
1659 if (enableLints) { | |
1660 dartEntry = _getReadableDartEntry(source); | |
1661 ListUtilities.addAll(errors, _getDartLintData( | |
1662 source, librarySource, dartEntry, DartEntry.LINTS)); | |
1663 } | |
1664 } | |
1665 } | |
1666 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
1667 AnalysisEngine.instance.logger.logInformation( | |
1668 "Could not compute errors", | |
1669 new CaughtException(exception, stackTrace)); | |
1670 } | |
1671 if (errors.isEmpty) { | |
1672 return AnalysisError.NO_ERRORS; | |
1673 } | |
1674 return errors; | |
1675 } else if (sourceEntry is HtmlEntry) { | |
1676 HtmlEntry htmlEntry = sourceEntry; | |
1677 try { | |
1678 return _getHtmlResolutionData2( | |
1679 source, htmlEntry, HtmlEntry.RESOLUTION_ERRORS); | |
1680 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
1681 AnalysisEngine.instance.logger.logInformation( | |
1682 "Could not compute errors", | |
1683 new CaughtException(exception, stackTrace)); | |
1684 } | |
1685 } | |
1686 return AnalysisError.NO_ERRORS; | |
1687 } | |
1688 | |
1689 @override | |
1690 List<Source> computeExportedLibraries(Source source) => _getDartParseData2( | |
1691 source, DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_LIST); | |
1692 | |
1693 @override | |
1694 @deprecated | |
1695 HtmlElement computeHtmlElement(Source source) => | |
1696 _getHtmlResolutionData(source, HtmlEntry.ELEMENT, null); | |
1697 | |
1698 @override | |
1699 List<Source> computeImportedLibraries(Source source) => _getDartParseData2( | |
1700 source, DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_LIST); | |
1701 | |
1702 @override | |
1703 SourceKind computeKindOf(Source source) { | |
1704 SourceEntry sourceEntry = _getReadableSourceEntry(source); | |
1705 if (sourceEntry == null) { | |
1706 return SourceKind.UNKNOWN; | |
1707 } else if (sourceEntry is DartEntry) { | |
1708 try { | |
1709 return _getDartParseData(source, sourceEntry, DartEntry.SOURCE_KIND); | |
1710 } on AnalysisException { | |
1711 return SourceKind.UNKNOWN; | |
1712 } | |
1713 } | |
1714 return sourceEntry.kind; | |
1715 } | |
1716 | |
1717 @override | |
1718 LibraryElement computeLibraryElement(Source source) => | |
1719 _getDartResolutionData2(source, source, DartEntry.ELEMENT, null); | |
1720 | |
1721 @override | |
1722 LineInfo computeLineInfo(Source source) { | |
1723 SourceEntry sourceEntry = _getReadableSourceEntry(source); | |
1724 try { | |
1725 if (sourceEntry is HtmlEntry) { | |
1726 return _getHtmlParseData(source, SourceEntry.LINE_INFO, null); | |
1727 } else if (sourceEntry is DartEntry) { | |
1728 return _getDartScanData2(source, SourceEntry.LINE_INFO, null); | |
1729 } | |
1730 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
1731 AnalysisEngine.instance.logger.logInformation( | |
1732 "Could not compute ${SourceEntry.LINE_INFO}", | |
1733 new CaughtException(exception, stackTrace)); | |
1734 } | |
1735 return null; | |
1736 } | |
1737 | |
1738 @override | |
1739 CompilationUnit computeResolvableCompilationUnit(Source source) { | |
1740 DartEntry dartEntry = _getReadableDartEntry(source); | |
1741 if (dartEntry == null) { | |
1742 throw new AnalysisException( | |
1743 "computeResolvableCompilationUnit for non-Dart: ${source.fullName}"); | |
1744 } | |
1745 dartEntry = _cacheDartParseData(source, dartEntry, DartEntry.PARSED_UNIT); | |
1746 CompilationUnit unit = dartEntry.resolvableCompilationUnit; | |
1747 if (unit == null) { | |
1748 throw new AnalysisException( | |
1749 "Internal error: computeResolvableCompilationUnit could not parse ${so
urce.fullName}", | |
1750 new CaughtException(dartEntry.exception, null)); | |
1751 } | |
1752 return unit; | |
1753 } | |
1754 | |
1755 @override | |
1756 CancelableFuture<CompilationUnit> computeResolvedCompilationUnitAsync( | |
1757 Source unitSource, Source librarySource) { | |
1758 return new _AnalysisFutureHelper<CompilationUnit>(this).computeAsync( | |
1759 unitSource, (SourceEntry sourceEntry) { | |
1760 if (sourceEntry is DartEntry) { | |
1761 if (sourceEntry.getStateInLibrary( | |
1762 DartEntry.RESOLVED_UNIT, librarySource) == | |
1763 CacheState.ERROR) { | |
1764 throw sourceEntry.exception; | |
1765 } | |
1766 return sourceEntry.getValueInLibrary( | |
1767 DartEntry.RESOLVED_UNIT, librarySource); | |
1768 } | |
1769 throw new AnalysisNotScheduledError(); | |
1770 }); | |
1771 } | |
1772 | |
1773 /** | |
1774 * Create an analysis cache based on the given source [factory]. | |
1775 */ | |
1776 AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { | |
1777 if (factory == null) { | |
1778 return new AnalysisCache(<CachePartition>[_privatePartition]); | |
1779 } | |
1780 DartSdk sdk = factory.dartSdk; | |
1781 if (sdk == null) { | |
1782 return new AnalysisCache(<CachePartition>[_privatePartition]); | |
1783 } | |
1784 return new AnalysisCache(<CachePartition>[ | |
1785 AnalysisEngine.instance.partitionManager.forSdk(sdk), | |
1786 _privatePartition | |
1787 ]); | |
1788 } | |
1789 | |
1790 @override | |
1791 void dispose() { | |
1792 _disposed = true; | |
1793 for (List<PendingFuture> pendingFutures in _pendingFutureSources.values) { | |
1794 for (PendingFuture pendingFuture in pendingFutures) { | |
1795 pendingFuture.forciblyComplete(); | |
1796 } | |
1797 } | |
1798 _pendingFutureSources.clear(); | |
1799 } | |
1800 | |
1801 @override | |
1802 List<CompilationUnit> ensureResolvedDartUnits(Source unitSource) { | |
1803 SourceEntry sourceEntry = _cache.get(unitSource); | |
1804 if (sourceEntry is! DartEntry) { | |
1805 return null; | |
1806 } | |
1807 DartEntry dartEntry = sourceEntry; | |
1808 // Check every library. | |
1809 List<CompilationUnit> units = <CompilationUnit>[]; | |
1810 List<Source> containingLibraries = dartEntry.containingLibraries; | |
1811 for (Source librarySource in containingLibraries) { | |
1812 CompilationUnit unit = | |
1813 dartEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource); | |
1814 if (unit == null) { | |
1815 units = null; | |
1816 break; | |
1817 } | |
1818 units.add(unit); | |
1819 } | |
1820 // Invalidate the flushed RESOLVED_UNIT to force it eventually. | |
1821 if (units == null) { | |
1822 bool shouldBeScheduled = false; | |
1823 for (Source librarySource in containingLibraries) { | |
1824 if (dartEntry.getStateInLibrary( | |
1825 DartEntry.RESOLVED_UNIT, librarySource) == | |
1826 CacheState.FLUSHED) { | |
1827 dartEntry.setStateInLibrary( | |
1828 DartEntry.RESOLVED_UNIT, librarySource, CacheState.INVALID); | |
1829 shouldBeScheduled = true; | |
1830 } | |
1831 } | |
1832 if (shouldBeScheduled) { | |
1833 _workManager.add(unitSource, SourcePriority.UNKNOWN); | |
1834 } | |
1835 // We cannot provide resolved units right now, | |
1836 // but the future analysis will. | |
1837 return null; | |
1838 } | |
1839 // done | |
1840 return units; | |
1841 } | |
1842 | |
1843 @override | |
1844 bool exists(Source source) { | |
1845 if (source == null) { | |
1846 return false; | |
1847 } | |
1848 if (_contentCache.getContents(source) != null) { | |
1849 return true; | |
1850 } | |
1851 return source.exists(); | |
1852 } | |
1853 | |
1854 @override | |
1855 cache.CacheEntry getCacheEntry(AnalysisTarget target) { | |
1856 return null; | |
1857 } | |
1858 | |
1859 @override | |
1860 CompilationUnitElement getCompilationUnitElement( | |
1861 Source unitSource, Source librarySource) { | |
1862 LibraryElement libraryElement = getLibraryElement(librarySource); | |
1863 if (libraryElement != null) { | |
1864 // try defining unit | |
1865 CompilationUnitElement definingUnit = | |
1866 libraryElement.definingCompilationUnit; | |
1867 if (definingUnit.source == unitSource) { | |
1868 return definingUnit; | |
1869 } | |
1870 // try parts | |
1871 for (CompilationUnitElement partUnit in libraryElement.parts) { | |
1872 if (partUnit.source == unitSource) { | |
1873 return partUnit; | |
1874 } | |
1875 } | |
1876 } | |
1877 return null; | |
1878 } | |
1879 | |
1880 @override | |
1881 TimestampedData<String> getContents(Source source) { | |
1882 String contents = _contentCache.getContents(source); | |
1883 if (contents != null) { | |
1884 return new TimestampedData<String>( | |
1885 _contentCache.getModificationStamp(source), contents); | |
1886 } | |
1887 return source.contents; | |
1888 } | |
1889 | |
1890 @override | |
1891 InternalAnalysisContext getContextFor(Source source) { | |
1892 InternalAnalysisContext context = _cache.getContextFor(source); | |
1893 return context == null ? this : context; | |
1894 } | |
1895 | |
1896 @override | |
1897 Element getElement(ElementLocation location) { | |
1898 // TODO(brianwilkerson) This should not be a "get" method. | |
1899 try { | |
1900 List<String> components = location.components; | |
1901 Source source = _computeSourceFromEncoding(components[0]); | |
1902 String sourceName = source.shortName; | |
1903 if (AnalysisEngine.isDartFileName(sourceName)) { | |
1904 ElementImpl element = computeLibraryElement(source) as ElementImpl; | |
1905 for (int i = 1; i < components.length; i++) { | |
1906 if (element == null) { | |
1907 return null; | |
1908 } | |
1909 element = element.getChild(components[i]); | |
1910 } | |
1911 return element; | |
1912 } | |
1913 if (AnalysisEngine.isHtmlFileName(sourceName)) { | |
1914 return computeHtmlElement(source); | |
1915 } | |
1916 } catch (exception) { | |
1917 // If the location cannot be decoded for some reason then the underlying | |
1918 // cause should have been logged already and we can fall though to return | |
1919 // null. | |
1920 } | |
1921 return null; | |
1922 } | |
1923 | |
1924 @override | |
1925 AnalysisErrorInfo getErrors(Source source) { | |
1926 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
1927 if (sourceEntry is DartEntry) { | |
1928 DartEntry dartEntry = sourceEntry; | |
1929 return new AnalysisErrorInfoImpl( | |
1930 dartEntry.allErrors, dartEntry.getValue(SourceEntry.LINE_INFO)); | |
1931 } else if (sourceEntry is HtmlEntry) { | |
1932 HtmlEntry htmlEntry = sourceEntry; | |
1933 return new AnalysisErrorInfoImpl( | |
1934 htmlEntry.allErrors, htmlEntry.getValue(SourceEntry.LINE_INFO)); | |
1935 } | |
1936 return new AnalysisErrorInfoImpl(AnalysisError.NO_ERRORS, null); | |
1937 } | |
1938 | |
1939 @override | |
1940 @deprecated | |
1941 HtmlElement getHtmlElement(Source source) { | |
1942 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
1943 if (sourceEntry is HtmlEntry) { | |
1944 return sourceEntry.getValue(HtmlEntry.ELEMENT); | |
1945 } | |
1946 return null; | |
1947 } | |
1948 | |
1949 @override | |
1950 List<Source> getHtmlFilesReferencing(Source source) { | |
1951 SourceKind sourceKind = getKindOf(source); | |
1952 if (sourceKind == null) { | |
1953 return Source.EMPTY_LIST; | |
1954 } | |
1955 List<Source> htmlSources = new List<Source>(); | |
1956 while (true) { | |
1957 if (sourceKind == SourceKind.PART) { | |
1958 List<Source> librarySources = getLibrariesContaining(source); | |
1959 MapIterator<Source, SourceEntry> partIterator = _cache.iterator(); | |
1960 while (partIterator.moveNext()) { | |
1961 SourceEntry sourceEntry = partIterator.value; | |
1962 if (sourceEntry.kind == SourceKind.HTML) { | |
1963 List<Source> referencedLibraries = (sourceEntry as HtmlEntry) | |
1964 .getValue(HtmlEntry.REFERENCED_LIBRARIES); | |
1965 if (_containsAny(referencedLibraries, librarySources)) { | |
1966 htmlSources.add(partIterator.key); | |
1967 } | |
1968 } | |
1969 } | |
1970 } else { | |
1971 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
1972 while (iterator.moveNext()) { | |
1973 SourceEntry sourceEntry = iterator.value; | |
1974 if (sourceEntry.kind == SourceKind.HTML) { | |
1975 List<Source> referencedLibraries = (sourceEntry as HtmlEntry) | |
1976 .getValue(HtmlEntry.REFERENCED_LIBRARIES); | |
1977 if (_contains(referencedLibraries, source)) { | |
1978 htmlSources.add(iterator.key); | |
1979 } | |
1980 } | |
1981 } | |
1982 } | |
1983 break; | |
1984 } | |
1985 if (htmlSources.isEmpty) { | |
1986 return Source.EMPTY_LIST; | |
1987 } | |
1988 return htmlSources; | |
1989 } | |
1990 | |
1991 @override | |
1992 SourceKind getKindOf(Source source) { | |
1993 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
1994 if (sourceEntry == null) { | |
1995 return SourceKind.UNKNOWN; | |
1996 } | |
1997 return sourceEntry.kind; | |
1998 } | |
1999 | |
2000 @override | |
2001 List<Source> getLibrariesContaining(Source source) { | |
2002 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
2003 if (sourceEntry is DartEntry) { | |
2004 return sourceEntry.containingLibraries; | |
2005 } | |
2006 return Source.EMPTY_LIST; | |
2007 } | |
2008 | |
2009 @override | |
2010 List<Source> getLibrariesDependingOn(Source librarySource) { | |
2011 List<Source> dependentLibraries = new List<Source>(); | |
2012 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
2013 while (iterator.moveNext()) { | |
2014 SourceEntry sourceEntry = iterator.value; | |
2015 if (sourceEntry.kind == SourceKind.LIBRARY) { | |
2016 if (_contains( | |
2017 (sourceEntry as DartEntry).getValue(DartEntry.EXPORTED_LIBRARIES), | |
2018 librarySource)) { | |
2019 dependentLibraries.add(iterator.key); | |
2020 } | |
2021 if (_contains( | |
2022 (sourceEntry as DartEntry).getValue(DartEntry.IMPORTED_LIBRARIES), | |
2023 librarySource)) { | |
2024 dependentLibraries.add(iterator.key); | |
2025 } | |
2026 } | |
2027 } | |
2028 if (dependentLibraries.isEmpty) { | |
2029 return Source.EMPTY_LIST; | |
2030 } | |
2031 return dependentLibraries; | |
2032 } | |
2033 | |
2034 @override | |
2035 List<Source> getLibrariesReferencedFromHtml(Source htmlSource) { | |
2036 SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource); | |
2037 if (sourceEntry is HtmlEntry) { | |
2038 HtmlEntry htmlEntry = sourceEntry; | |
2039 return htmlEntry.getValue(HtmlEntry.REFERENCED_LIBRARIES); | |
2040 } | |
2041 return Source.EMPTY_LIST; | |
2042 } | |
2043 | |
2044 @override | |
2045 LibraryElement getLibraryElement(Source source) { | |
2046 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
2047 if (sourceEntry is DartEntry) { | |
2048 return sourceEntry.getValue(DartEntry.ELEMENT); | |
2049 } | |
2050 return null; | |
2051 } | |
2052 | |
2053 @override | |
2054 LineInfo getLineInfo(Source source) { | |
2055 SourceEntry sourceEntry = getReadableSourceEntryOrNull(source); | |
2056 if (sourceEntry != null) { | |
2057 return sourceEntry.getValue(SourceEntry.LINE_INFO); | |
2058 } | |
2059 return null; | |
2060 } | |
2061 | |
2062 @override | |
2063 int getModificationStamp(Source source) { | |
2064 int stamp = _contentCache.getModificationStamp(source); | |
2065 if (stamp != null) { | |
2066 return stamp; | |
2067 } | |
2068 return source.modificationStamp; | |
2069 } | |
2070 | |
2071 @override | |
2072 ChangeNoticeImpl getNotice(Source source) { | |
2073 ChangeNoticeImpl notice = _pendingNotices[source]; | |
2074 if (notice == null) { | |
2075 notice = new ChangeNoticeImpl(source); | |
2076 _pendingNotices[source] = notice; | |
2077 } | |
2078 return notice; | |
2079 } | |
2080 | |
2081 @override | |
2082 Namespace getPublicNamespace(LibraryElement library) { | |
2083 // TODO(brianwilkerson) Rename this to not start with 'get'. | |
2084 // Note that this is not part of the API of the interface. | |
2085 Source source = library.definingCompilationUnit.source; | |
2086 DartEntry dartEntry = _getReadableDartEntry(source); | |
2087 if (dartEntry == null) { | |
2088 return null; | |
2089 } | |
2090 Namespace namespace = null; | |
2091 if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) { | |
2092 namespace = dartEntry.getValue(DartEntry.PUBLIC_NAMESPACE); | |
2093 } | |
2094 if (namespace == null) { | |
2095 NamespaceBuilder builder = new NamespaceBuilder(); | |
2096 namespace = builder.createPublicNamespaceForLibrary(library); | |
2097 if (dartEntry == null) { | |
2098 AnalysisEngine.instance.logger.logError( | |
2099 "Could not compute the public namespace for ${library.source.fullNam
e}", | |
2100 new CaughtException(new AnalysisException( | |
2101 "A Dart file became a non-Dart file: ${source.fullName}"), | |
2102 null)); | |
2103 return null; | |
2104 } | |
2105 if (identical(dartEntry.getValue(DartEntry.ELEMENT), library)) { | |
2106 dartEntry.setValue(DartEntry.PUBLIC_NAMESPACE, namespace); | |
2107 } | |
2108 } | |
2109 return namespace; | |
2110 } | |
2111 | |
2112 /** | |
2113 * Return the cache entry associated with the given [source], or `null` if | |
2114 * there is no entry associated with the source. | |
2115 */ | |
2116 SourceEntry getReadableSourceEntryOrNull(Source source) => _cache.get(source); | |
2117 | |
2118 @override | |
2119 CompilationUnit getResolvedCompilationUnit( | |
2120 Source unitSource, LibraryElement library) { | |
2121 if (library == null) { | |
2122 return null; | |
2123 } | |
2124 return getResolvedCompilationUnit2(unitSource, library.source); | |
2125 } | |
2126 | |
2127 @override | |
2128 CompilationUnit getResolvedCompilationUnit2( | |
2129 Source unitSource, Source librarySource) { | |
2130 SourceEntry sourceEntry = getReadableSourceEntryOrNull(unitSource); | |
2131 if (sourceEntry is DartEntry) { | |
2132 return sourceEntry.getValueInLibrary( | |
2133 DartEntry.RESOLVED_UNIT, librarySource); | |
2134 } | |
2135 return null; | |
2136 } | |
2137 | |
2138 @override | |
2139 @deprecated | |
2140 ht.HtmlUnit getResolvedHtmlUnit(Source htmlSource) { | |
2141 SourceEntry sourceEntry = getReadableSourceEntryOrNull(htmlSource); | |
2142 if (sourceEntry is HtmlEntry) { | |
2143 HtmlEntry htmlEntry = sourceEntry; | |
2144 return htmlEntry.getValue(HtmlEntry.RESOLVED_UNIT); | |
2145 } | |
2146 return null; | |
2147 } | |
2148 | |
2149 @override | |
2150 List<Source> getSourcesWithFullName(String path) { | |
2151 List<Source> sources = <Source>[]; | |
2152 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
2153 while (iterator.moveNext()) { | |
2154 if (iterator.key.fullName == path) { | |
2155 sources.add(iterator.key); | |
2156 } | |
2157 } | |
2158 return sources; | |
2159 } | |
2160 | |
2161 @override | |
2162 bool handleContentsChanged( | |
2163 Source source, String originalContents, String newContents, bool notify) { | |
2164 SourceEntry sourceEntry = _cache.get(source); | |
2165 if (sourceEntry == null) { | |
2166 return false; | |
2167 } | |
2168 bool changed = newContents != originalContents; | |
2169 if (newContents != null) { | |
2170 if (changed) { | |
2171 _incrementalAnalysisCache = | |
2172 IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); | |
2173 if (!analysisOptions.incremental || | |
2174 !_tryPoorMansIncrementalResolution(source, newContents)) { | |
2175 _sourceChanged(source); | |
2176 } | |
2177 sourceEntry.modificationTime = | |
2178 _contentCache.getModificationStamp(source); | |
2179 sourceEntry.setValue(SourceEntry.CONTENT, newContents); | |
2180 } else { | |
2181 sourceEntry.modificationTime = | |
2182 _contentCache.getModificationStamp(source); | |
2183 } | |
2184 } else if (originalContents != null) { | |
2185 _incrementalAnalysisCache = | |
2186 IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); | |
2187 // We are removing the overlay for the file, check if the file's | |
2188 // contents is the same as it was in the overlay. | |
2189 try { | |
2190 TimestampedData<String> fileContents = getContents(source); | |
2191 newContents = fileContents.data; | |
2192 sourceEntry.modificationTime = fileContents.modificationTime; | |
2193 if (newContents == originalContents) { | |
2194 sourceEntry.setValue(SourceEntry.CONTENT, newContents); | |
2195 changed = false; | |
2196 } | |
2197 } catch (e) {} | |
2198 // If not the same content (e.g. the file is being closed without save), | |
2199 // then force analysis. | |
2200 if (changed) { | |
2201 if (!analysisOptions.incremental || | |
2202 !_tryPoorMansIncrementalResolution(source, newContents)) { | |
2203 _sourceChanged(source); | |
2204 } | |
2205 } | |
2206 } | |
2207 if (notify && changed) { | |
2208 _onSourcesChangedController | |
2209 .add(new SourcesChangedEvent.changedContent(source, newContents)); | |
2210 } | |
2211 return changed; | |
2212 } | |
2213 | |
2214 @override | |
2215 void invalidateLibraryHints(Source librarySource) { | |
2216 SourceEntry sourceEntry = _cache.get(librarySource); | |
2217 if (sourceEntry is! DartEntry) { | |
2218 return; | |
2219 } | |
2220 DartEntry dartEntry = sourceEntry; | |
2221 // Prepare sources to invalidate hints in. | |
2222 List<Source> sources = <Source>[librarySource]; | |
2223 sources.addAll(dartEntry.getValue(DartEntry.INCLUDED_PARTS)); | |
2224 // Invalidate hints and lints. | |
2225 for (Source source in sources) { | |
2226 DartEntry dartEntry = _cache.get(source); | |
2227 if (dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource) == | |
2228 CacheState.VALID) { | |
2229 dartEntry.setStateInLibrary( | |
2230 DartEntry.HINTS, librarySource, CacheState.INVALID); | |
2231 } | |
2232 if (dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource) == | |
2233 CacheState.VALID) { | |
2234 dartEntry.setStateInLibrary( | |
2235 DartEntry.LINTS, librarySource, CacheState.INVALID); | |
2236 } | |
2237 } | |
2238 } | |
2239 | |
2240 @override | |
2241 bool isClientLibrary(Source librarySource) { | |
2242 SourceEntry sourceEntry = _getReadableSourceEntry(librarySource); | |
2243 if (sourceEntry is DartEntry) { | |
2244 DartEntry dartEntry = sourceEntry; | |
2245 return dartEntry.getValue(DartEntry.IS_CLIENT) && | |
2246 dartEntry.getValue(DartEntry.IS_LAUNCHABLE); | |
2247 } | |
2248 return false; | |
2249 } | |
2250 | |
2251 @override | |
2252 bool isServerLibrary(Source librarySource) { | |
2253 SourceEntry sourceEntry = _getReadableSourceEntry(librarySource); | |
2254 if (sourceEntry is DartEntry) { | |
2255 DartEntry dartEntry = sourceEntry; | |
2256 return !dartEntry.getValue(DartEntry.IS_CLIENT) && | |
2257 dartEntry.getValue(DartEntry.IS_LAUNCHABLE); | |
2258 } | |
2259 return false; | |
2260 } | |
2261 | |
2262 @override | |
2263 Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor) { | |
2264 throw new NotImplementedException('In not task-based AnalysisContext.'); | |
2265 } | |
2266 | |
2267 @override | |
2268 CompilationUnit parseCompilationUnit(Source source) => | |
2269 _getDartParseData2(source, DartEntry.PARSED_UNIT, null); | |
2270 | |
2271 @override | |
2272 Document parseHtmlDocument(Source source) { | |
2273 return null; | |
2274 } | |
2275 | |
2276 @override | |
2277 @deprecated | |
2278 ht.HtmlUnit parseHtmlUnit(Source source) => | |
2279 _getHtmlParseData(source, HtmlEntry.PARSED_UNIT, null); | |
2280 | |
2281 @override | |
2282 AnalysisResult performAnalysisTask() { | |
2283 if (_TRACE_PERFORM_TASK) { | |
2284 print("----------------------------------------"); | |
2285 } | |
2286 return PerformanceStatistics.performAnaysis.makeCurrentWhile(() { | |
2287 int getStart = JavaSystem.currentTimeMillis(); | |
2288 AnalysisTask task = PerformanceStatistics.nextTask | |
2289 .makeCurrentWhile(() => nextAnalysisTask); | |
2290 int getEnd = JavaSystem.currentTimeMillis(); | |
2291 if (task == null) { | |
2292 _validateLastIncrementalResolutionResult(); | |
2293 if (_performAnalysisTaskStopwatch != null) { | |
2294 AnalysisEngine.instance.instrumentationService.logPerformance( | |
2295 AnalysisPerformanceKind.FULL, _performAnalysisTaskStopwatch, | |
2296 'context_id=$_id'); | |
2297 _performAnalysisTaskStopwatch = null; | |
2298 } | |
2299 return new AnalysisResult( | |
2300 _getChangeNotices(true), getEnd - getStart, null, -1); | |
2301 } | |
2302 if (_performAnalysisTaskStopwatch == null) { | |
2303 _performAnalysisTaskStopwatch = new Stopwatch()..start(); | |
2304 } | |
2305 String taskDescription = task.toString(); | |
2306 _notifyAboutToPerformTask(taskDescription); | |
2307 if (_TRACE_PERFORM_TASK) { | |
2308 print(taskDescription); | |
2309 } | |
2310 int performStart = JavaSystem.currentTimeMillis(); | |
2311 try { | |
2312 task.perform(_resultRecorder); | |
2313 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
2314 AnalysisEngine.instance.logger.logInformation( | |
2315 "Could not perform analysis task: $taskDescription", | |
2316 new CaughtException(exception, stackTrace)); | |
2317 } on AnalysisException catch (exception, stackTrace) { | |
2318 if (exception.cause is! JavaIOException) { | |
2319 AnalysisEngine.instance.logger.logError( | |
2320 "Internal error while performing the task: $task", | |
2321 new CaughtException(exception, stackTrace)); | |
2322 } | |
2323 } | |
2324 int performEnd = JavaSystem.currentTimeMillis(); | |
2325 List<ChangeNotice> notices = _getChangeNotices(false); | |
2326 int noticeCount = notices.length; | |
2327 for (int i = 0; i < noticeCount; i++) { | |
2328 ChangeNotice notice = notices[i]; | |
2329 Source source = notice.source; | |
2330 // TODO(brianwilkerson) Figure out whether the compilation unit is | |
2331 // always resolved, or whether we need to decide whether to invoke the | |
2332 // "parsed" or "resolved" method. This might be better done when | |
2333 // recording task results in order to reduce the chance of errors. | |
2334 // if (notice.getCompilationUnit() != null) { | |
2335 // notifyResolvedDart(source, notice.getCompilationUnit()); | |
2336 // } else if (notice.getHtmlUnit() != null) { | |
2337 // notifyResolvedHtml(source, notice.getHtmlUnit()); | |
2338 // } | |
2339 _notifyErrors(source, notice.errors, notice.lineInfo); | |
2340 } | |
2341 return new AnalysisResult(notices, getEnd - getStart, | |
2342 task.runtimeType.toString(), performEnd - performStart); | |
2343 }); | |
2344 } | |
2345 | |
2346 @override | |
2347 void recordLibraryElements(Map<Source, LibraryElement> elementMap) { | |
2348 Source htmlSource = _sourceFactory.forUri(DartSdk.DART_HTML); | |
2349 elementMap.forEach((Source librarySource, LibraryElement library) { | |
2350 // | |
2351 // Cache the element in the library's info. | |
2352 // | |
2353 DartEntry dartEntry = _getReadableDartEntry(librarySource); | |
2354 if (dartEntry != null) { | |
2355 _recordElementData(dartEntry, library, library.source, htmlSource); | |
2356 dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); | |
2357 dartEntry.setValue(SourceEntry.LINE_INFO, new LineInfo(<int>[0])); | |
2358 // DartEntry.ELEMENT - set in recordElementData | |
2359 dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, Source.EMPTY_LIST); | |
2360 dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, Source.EMPTY_LIST); | |
2361 dartEntry.setValue(DartEntry.INCLUDED_PARTS, Source.EMPTY_LIST); | |
2362 // DartEntry.IS_CLIENT - set in recordElementData | |
2363 // DartEntry.IS_LAUNCHABLE - set in recordElementData | |
2364 dartEntry.setValue(DartEntry.PARSE_ERRORS, AnalysisError.NO_ERRORS); | |
2365 dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); | |
2366 dartEntry.setState(DartEntry.PUBLIC_NAMESPACE, CacheState.FLUSHED); | |
2367 dartEntry.setValue(DartEntry.SCAN_ERRORS, AnalysisError.NO_ERRORS); | |
2368 dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); | |
2369 dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED); | |
2370 dartEntry.setValueInLibrary(DartEntry.RESOLUTION_ERRORS, librarySource, | |
2371 AnalysisError.NO_ERRORS); | |
2372 dartEntry.setStateInLibrary( | |
2373 DartEntry.RESOLVED_UNIT, librarySource, CacheState.FLUSHED); | |
2374 dartEntry.setValueInLibrary(DartEntry.VERIFICATION_ERRORS, | |
2375 librarySource, AnalysisError.NO_ERRORS); | |
2376 dartEntry.setValueInLibrary( | |
2377 DartEntry.HINTS, librarySource, AnalysisError.NO_ERRORS); | |
2378 dartEntry.setValueInLibrary( | |
2379 DartEntry.LINTS, librarySource, AnalysisError.NO_ERRORS); | |
2380 } | |
2381 }); | |
2382 } | |
2383 | |
2384 /** | |
2385 * Record the results produced by performing a [task] and return the cache | |
2386 * entry associated with the results. | |
2387 */ | |
2388 DartEntry recordResolveDartLibraryCycleTaskResults( | |
2389 ResolveDartLibraryCycleTask task) { | |
2390 LibraryResolver2 resolver = task.libraryResolver; | |
2391 CaughtException thrownException = task.exception; | |
2392 Source unitSource = task.unitSource; | |
2393 DartEntry unitEntry = _getReadableDartEntry(unitSource); | |
2394 if (resolver != null) { | |
2395 // | |
2396 // The resolver should only be null if an exception was thrown before (or | |
2397 // while) it was being created. | |
2398 // | |
2399 List<ResolvableLibrary> resolvedLibraries = resolver.resolvedLibraries; | |
2400 if (resolvedLibraries == null) { | |
2401 // | |
2402 // The resolved libraries should only be null if an exception was thrown | |
2403 // during resolution. | |
2404 // | |
2405 if (thrownException == null) { | |
2406 var message = "In recordResolveDartLibraryCycleTaskResults, " | |
2407 "resolvedLibraries was null and there was no thrown exception"; | |
2408 unitEntry.recordResolutionError( | |
2409 new CaughtException(new AnalysisException(message), null)); | |
2410 } else { | |
2411 unitEntry.recordResolutionError(thrownException); | |
2412 } | |
2413 _cache.remove(unitSource); | |
2414 if (thrownException != null) { | |
2415 throw new AnalysisException('<rethrow>', thrownException); | |
2416 } | |
2417 return unitEntry; | |
2418 } | |
2419 Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML); | |
2420 RecordingErrorListener errorListener = resolver.errorListener; | |
2421 for (ResolvableLibrary library in resolvedLibraries) { | |
2422 Source librarySource = library.librarySource; | |
2423 for (Source source in library.compilationUnitSources) { | |
2424 CompilationUnit unit = library.getAST(source); | |
2425 List<AnalysisError> errors = errorListener.getErrorsForSource(source); | |
2426 LineInfo lineInfo = getLineInfo(source); | |
2427 DartEntry dartEntry = _cache.get(source); | |
2428 if (thrownException == null) { | |
2429 dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); | |
2430 dartEntry.setValueInLibrary( | |
2431 DartEntry.RESOLVED_UNIT, librarySource, unit); | |
2432 dartEntry.setValueInLibrary( | |
2433 DartEntry.RESOLUTION_ERRORS, librarySource, errors); | |
2434 if (source == librarySource) { | |
2435 _recordElementData( | |
2436 dartEntry, library.libraryElement, librarySource, htmlSource); | |
2437 } | |
2438 _cache.storedAst(source); | |
2439 } else { | |
2440 dartEntry.recordResolutionErrorInLibrary( | |
2441 librarySource, thrownException); | |
2442 } | |
2443 if (source != librarySource) { | |
2444 _workManager.add(source, SourcePriority.PRIORITY_PART); | |
2445 } | |
2446 ChangeNoticeImpl notice = getNotice(source); | |
2447 notice.resolvedDartUnit = unit; | |
2448 notice.setErrors(dartEntry.allErrors, lineInfo); | |
2449 } | |
2450 } | |
2451 } | |
2452 if (thrownException != null) { | |
2453 throw new AnalysisException('<rethrow>', thrownException); | |
2454 } | |
2455 return unitEntry; | |
2456 } | |
2457 | |
2458 /** | |
2459 * Record the results produced by performing a [task] and return the cache | |
2460 * entry associated with the results. | |
2461 */ | |
2462 DartEntry recordResolveDartLibraryTaskResults(ResolveDartLibraryTask task) { | |
2463 LibraryResolver resolver = task.libraryResolver; | |
2464 CaughtException thrownException = task.exception; | |
2465 Source unitSource = task.unitSource; | |
2466 DartEntry unitEntry = _getReadableDartEntry(unitSource); | |
2467 if (resolver != null) { | |
2468 // | |
2469 // The resolver should only be null if an exception was thrown before (or | |
2470 // while) it was being created. | |
2471 // | |
2472 Set<Library> resolvedLibraries = resolver.resolvedLibraries; | |
2473 if (resolvedLibraries == null) { | |
2474 // | |
2475 // The resolved libraries should only be null if an exception was thrown | |
2476 // during resolution. | |
2477 // | |
2478 if (thrownException == null) { | |
2479 String message = "In recordResolveDartLibraryTaskResults, " | |
2480 "resolvedLibraries was null and there was no thrown exception"; | |
2481 unitEntry.recordResolutionError( | |
2482 new CaughtException(new AnalysisException(message), null)); | |
2483 } else { | |
2484 unitEntry.recordResolutionError(thrownException); | |
2485 } | |
2486 _cache.remove(unitSource); | |
2487 if (thrownException != null) { | |
2488 throw new AnalysisException('<rethrow>', thrownException); | |
2489 } | |
2490 return unitEntry; | |
2491 } | |
2492 Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML); | |
2493 RecordingErrorListener errorListener = resolver.errorListener; | |
2494 for (Library library in resolvedLibraries) { | |
2495 Source librarySource = library.librarySource; | |
2496 for (Source source in library.compilationUnitSources) { | |
2497 CompilationUnit unit = library.getAST(source); | |
2498 List<AnalysisError> errors = errorListener.getErrorsForSource(source); | |
2499 LineInfo lineInfo = getLineInfo(source); | |
2500 DartEntry dartEntry = _cache.get(source); | |
2501 if (thrownException == null) { | |
2502 dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo); | |
2503 dartEntry.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED); | |
2504 dartEntry.setValueInLibrary( | |
2505 DartEntry.RESOLVED_UNIT, librarySource, unit); | |
2506 dartEntry.setValueInLibrary( | |
2507 DartEntry.RESOLUTION_ERRORS, librarySource, errors); | |
2508 if (source == librarySource) { | |
2509 _recordElementData( | |
2510 dartEntry, library.libraryElement, librarySource, htmlSource); | |
2511 } | |
2512 _cache.storedAst(source); | |
2513 } else { | |
2514 dartEntry.recordResolutionErrorInLibrary( | |
2515 librarySource, thrownException); | |
2516 _cache.remove(source); | |
2517 } | |
2518 if (source != librarySource) { | |
2519 _workManager.add(source, SourcePriority.PRIORITY_PART); | |
2520 } | |
2521 ChangeNoticeImpl notice = getNotice(source); | |
2522 notice.resolvedDartUnit = unit; | |
2523 notice.setErrors(dartEntry.allErrors, lineInfo); | |
2524 } | |
2525 } | |
2526 } | |
2527 if (thrownException != null) { | |
2528 throw new AnalysisException('<rethrow>', thrownException); | |
2529 } | |
2530 return unitEntry; | |
2531 } | |
2532 | |
2533 @override | |
2534 void removeListener(AnalysisListener listener) { | |
2535 _listeners.remove(listener); | |
2536 } | |
2537 | |
2538 @override | |
2539 CompilationUnit resolveCompilationUnit( | |
2540 Source unitSource, LibraryElement library) { | |
2541 if (library == null) { | |
2542 return null; | |
2543 } | |
2544 return resolveCompilationUnit2(unitSource, library.source); | |
2545 } | |
2546 | |
2547 @override | |
2548 CompilationUnit resolveCompilationUnit2( | |
2549 Source unitSource, Source librarySource) => _getDartResolutionData2( | |
2550 unitSource, librarySource, DartEntry.RESOLVED_UNIT, null); | |
2551 | |
2552 @override | |
2553 @deprecated | |
2554 ht.HtmlUnit resolveHtmlUnit(Source htmlSource) { | |
2555 computeHtmlElement(htmlSource); | |
2556 return parseHtmlUnit(htmlSource); | |
2557 } | |
2558 | |
2559 @override | |
2560 void setChangedContents(Source source, String contents, int offset, | |
2561 int oldLength, int newLength) { | |
2562 if (_contentRangeChanged(source, contents, offset, oldLength, newLength)) { | |
2563 _onSourcesChangedController.add(new SourcesChangedEvent.changedRange( | |
2564 source, contents, offset, oldLength, newLength)); | |
2565 } | |
2566 } | |
2567 | |
2568 @override | |
2569 void setContents(Source source, String contents) { | |
2570 _contentsChanged(source, contents, true); | |
2571 } | |
2572 | |
2573 @override | |
2574 bool shouldErrorsBeAnalyzed(Source source, Object entry) { | |
2575 DartEntry dartEntry = entry; | |
2576 if (source.isInSystemLibrary) { | |
2577 return _generateSdkErrors; | |
2578 } else if (!dartEntry.explicitlyAdded) { | |
2579 return _generateImplicitErrors; | |
2580 } else { | |
2581 return true; | |
2582 } | |
2583 } | |
2584 | |
2585 @override | |
2586 void test_flushAstStructures(Source source) { | |
2587 DartEntry dartEntry = getReadableSourceEntryOrNull(source); | |
2588 dartEntry.flushAstStructures(); | |
2589 } | |
2590 | |
2591 @override | |
2592 bool validateCacheConsistency() { | |
2593 int consistencyCheckStart = JavaSystem.nanoTime(); | |
2594 List<Source> changedSources = new List<Source>(); | |
2595 List<Source> missingSources = new List<Source>(); | |
2596 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
2597 while (iterator.moveNext()) { | |
2598 Source source = iterator.key; | |
2599 SourceEntry sourceEntry = iterator.value; | |
2600 int sourceTime = getModificationStamp(source); | |
2601 if (sourceTime != sourceEntry.modificationTime) { | |
2602 changedSources.add(source); | |
2603 } | |
2604 if (sourceEntry.exception != null) { | |
2605 if (!exists(source)) { | |
2606 missingSources.add(source); | |
2607 } | |
2608 } | |
2609 } | |
2610 int count = changedSources.length; | |
2611 for (int i = 0; i < count; i++) { | |
2612 _sourceChanged(changedSources[i]); | |
2613 } | |
2614 int removalCount = 0; | |
2615 for (Source source in missingSources) { | |
2616 if (getLibrariesContaining(source).isEmpty && | |
2617 getLibrariesDependingOn(source).isEmpty) { | |
2618 _cache.remove(source); | |
2619 removalCount++; | |
2620 } | |
2621 } | |
2622 int consistencyCheckEnd = JavaSystem.nanoTime(); | |
2623 if (changedSources.length > 0 || missingSources.length > 0) { | |
2624 StringBuffer buffer = new StringBuffer(); | |
2625 buffer.write("Consistency check took "); | |
2626 buffer.write((consistencyCheckEnd - consistencyCheckStart) / 1000000.0); | |
2627 buffer.writeln(" ms and found"); | |
2628 buffer.write(" "); | |
2629 buffer.write(changedSources.length); | |
2630 buffer.writeln(" inconsistent entries"); | |
2631 buffer.write(" "); | |
2632 buffer.write(missingSources.length); | |
2633 buffer.write(" missing sources ("); | |
2634 buffer.write(removalCount); | |
2635 buffer.writeln(" removed"); | |
2636 for (Source source in missingSources) { | |
2637 buffer.write(" "); | |
2638 buffer.writeln(source.fullName); | |
2639 } | |
2640 _logInformation(buffer.toString()); | |
2641 } | |
2642 return changedSources.length > 0; | |
2643 } | |
2644 | |
2645 @deprecated | |
2646 @override | |
2647 void visitCacheItems(void callback(Source source, SourceEntry dartEntry, | |
2648 DataDescriptor rowDesc, CacheState state)) { | |
2649 bool hintsEnabled = _options.hint; | |
2650 bool lintsEnabled = _options.lint; | |
2651 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
2652 while (iterator.moveNext()) { | |
2653 Source source = iterator.key; | |
2654 SourceEntry sourceEntry = iterator.value; | |
2655 for (DataDescriptor descriptor in sourceEntry.descriptors) { | |
2656 if (descriptor == DartEntry.SOURCE_KIND) { | |
2657 // The source kind is always valid, so the state isn't interesting. | |
2658 continue; | |
2659 } else if (descriptor == DartEntry.CONTAINING_LIBRARIES) { | |
2660 // The list of containing libraries is always valid, so the state | |
2661 // isn't interesting. | |
2662 continue; | |
2663 } else if (descriptor == DartEntry.PUBLIC_NAMESPACE) { | |
2664 // The public namespace isn't computed by performAnalysisTask() | |
2665 // and therefore isn't interesting. | |
2666 continue; | |
2667 } else if (descriptor == HtmlEntry.HINTS) { | |
2668 // We are not currently recording any hints related to HTML. | |
2669 continue; | |
2670 } | |
2671 callback( | |
2672 source, sourceEntry, descriptor, sourceEntry.getState(descriptor)); | |
2673 } | |
2674 if (sourceEntry is DartEntry) { | |
2675 // get library-specific values | |
2676 List<Source> librarySources = getLibrariesContaining(source); | |
2677 for (Source librarySource in librarySources) { | |
2678 for (DataDescriptor descriptor in sourceEntry.libraryDescriptors) { | |
2679 if (descriptor == DartEntry.BUILT_ELEMENT || | |
2680 descriptor == DartEntry.BUILT_UNIT) { | |
2681 // These values are not currently being computed, so their state | |
2682 // is not interesting. | |
2683 continue; | |
2684 } else if (!sourceEntry.explicitlyAdded && | |
2685 !_generateImplicitErrors && | |
2686 (descriptor == DartEntry.VERIFICATION_ERRORS || | |
2687 descriptor == DartEntry.HINTS || | |
2688 descriptor == DartEntry.LINTS)) { | |
2689 continue; | |
2690 } else if (source.isInSystemLibrary && | |
2691 !_generateSdkErrors && | |
2692 (descriptor == DartEntry.VERIFICATION_ERRORS || | |
2693 descriptor == DartEntry.HINTS || | |
2694 descriptor == DartEntry.LINTS)) { | |
2695 continue; | |
2696 } else if (!hintsEnabled && descriptor == DartEntry.HINTS) { | |
2697 continue; | |
2698 } else if (!lintsEnabled && descriptor == DartEntry.LINTS) { | |
2699 continue; | |
2700 } | |
2701 callback(librarySource, sourceEntry, descriptor, | |
2702 sourceEntry.getStateInLibrary(descriptor, librarySource)); | |
2703 } | |
2704 } | |
2705 } | |
2706 } | |
2707 } | |
2708 | |
2709 @override | |
2710 void visitContentCache(ContentCacheVisitor visitor) { | |
2711 _contentCache.accept(visitor); | |
2712 } | |
2713 | |
2714 /** | |
2715 * Record that we have accessed the AST structure associated with the given | |
2716 * [source]. At the moment, there is no differentiation between the parsed and | |
2717 * resolved forms of the AST. | |
2718 */ | |
2719 void _accessedAst(Source source) { | |
2720 _cache.accessedAst(source); | |
2721 } | |
2722 | |
2723 /** | |
2724 * Add all of the sources contained in the given source [container] to the | |
2725 * given list of [sources]. | |
2726 */ | |
2727 void _addSourcesInContainer(List<Source> sources, SourceContainer container) { | |
2728 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
2729 while (iterator.moveNext()) { | |
2730 Source source = iterator.key; | |
2731 if (container.contains(source)) { | |
2732 sources.add(source); | |
2733 } | |
2734 } | |
2735 } | |
2736 | |
2737 /** | |
2738 * Given the [unitSource] of a Dart file and the [librarySource] of the | |
2739 * library that contains it, return a cache entry in which the state of the | |
2740 * data represented by the given [descriptor] is either [CacheState.VALID] or | |
2741 * [CacheState.ERROR]. This method assumes that the data can be produced by | |
2742 * generating hints for the library if the data is not already cached. The | |
2743 * [dartEntry] is the cache entry associated with the Dart file. | |
2744 * | |
2745 * Throws an [AnalysisException] if data could not be returned because the | |
2746 * source could not be parsed. | |
2747 */ | |
2748 DartEntry _cacheDartHintData(Source unitSource, Source librarySource, | |
2749 DartEntry dartEntry, DataDescriptor descriptor) { | |
2750 // | |
2751 // Check to see whether we already have the information being requested. | |
2752 // | |
2753 CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2754 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2755 // | |
2756 // If not, compute the information. | |
2757 // Unless the modification date of the source continues to change, | |
2758 // this loop will eventually terminate. | |
2759 // | |
2760 DartEntry libraryEntry = _getReadableDartEntry(librarySource); | |
2761 libraryEntry = _cacheDartResolutionData( | |
2762 librarySource, librarySource, libraryEntry, DartEntry.ELEMENT); | |
2763 LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); | |
2764 CompilationUnitElement definingUnit = | |
2765 libraryElement.definingCompilationUnit; | |
2766 List<CompilationUnitElement> parts = libraryElement.parts; | |
2767 List<TimestampedData<CompilationUnit>> units = | |
2768 new List<TimestampedData<CompilationUnit>>(parts.length + 1); | |
2769 units[0] = _getResolvedUnit(definingUnit, librarySource); | |
2770 if (units[0] == null) { | |
2771 Source source = definingUnit.source; | |
2772 units[0] = new TimestampedData<CompilationUnit>( | |
2773 getModificationStamp(source), | |
2774 resolveCompilationUnit(source, libraryElement)); | |
2775 } | |
2776 for (int i = 0; i < parts.length; i++) { | |
2777 units[i + 1] = _getResolvedUnit(parts[i], librarySource); | |
2778 if (units[i + 1] == null) { | |
2779 Source source = parts[i].source; | |
2780 units[i + 1] = new TimestampedData<CompilationUnit>( | |
2781 getModificationStamp(source), | |
2782 resolveCompilationUnit(source, libraryElement)); | |
2783 } | |
2784 } | |
2785 dartEntry = new GenerateDartHintsTask( | |
2786 this, units, getLibraryElement(librarySource)) | |
2787 .perform(_resultRecorder) as DartEntry; | |
2788 state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2789 } | |
2790 return dartEntry; | |
2791 } | |
2792 | |
2793 /** | |
2794 * Given a source for a Dart file and the library that contains it, return a | |
2795 * cache entry in which the state of the data represented by the given | |
2796 * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method | |
2797 * assumes that the data can be produced by generating lints for the library | |
2798 * if the data is not already cached. | |
2799 * | |
2800 * <b>Note:</b> This method cannot be used in an async environment. | |
2801 */ | |
2802 DartEntry _cacheDartLintData(Source unitSource, Source librarySource, | |
2803 DartEntry dartEntry, DataDescriptor descriptor) { | |
2804 // | |
2805 // Check to see whether we already have the information being requested. | |
2806 // | |
2807 CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2808 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2809 // | |
2810 // If not, compute the information. | |
2811 // Unless the modification date of the source continues to change, | |
2812 // this loop will eventually terminate. | |
2813 // | |
2814 DartEntry libraryEntry = _getReadableDartEntry(librarySource); | |
2815 libraryEntry = _cacheDartResolutionData( | |
2816 librarySource, librarySource, libraryEntry, DartEntry.ELEMENT); | |
2817 LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); | |
2818 CompilationUnitElement definingUnit = | |
2819 libraryElement.definingCompilationUnit; | |
2820 List<CompilationUnitElement> parts = libraryElement.parts; | |
2821 List<TimestampedData<CompilationUnit>> units = | |
2822 new List<TimestampedData<CompilationUnit>>(parts.length + 1); | |
2823 units[0] = _getResolvedUnit(definingUnit, librarySource); | |
2824 if (units[0] == null) { | |
2825 Source source = definingUnit.source; | |
2826 units[0] = new TimestampedData<CompilationUnit>( | |
2827 getModificationStamp(source), | |
2828 resolveCompilationUnit(source, libraryElement)); | |
2829 } | |
2830 for (int i = 0; i < parts.length; i++) { | |
2831 units[i + 1] = _getResolvedUnit(parts[i], librarySource); | |
2832 if (units[i + 1] == null) { | |
2833 Source source = parts[i].source; | |
2834 units[i + 1] = new TimestampedData<CompilationUnit>( | |
2835 getModificationStamp(source), | |
2836 resolveCompilationUnit(source, libraryElement)); | |
2837 } | |
2838 } | |
2839 //TODO(pquitslund): revisit if we need all units or whether one will do | |
2840 dartEntry = new GenerateDartLintsTask( | |
2841 this, units, getLibraryElement(librarySource)) | |
2842 .perform(_resultRecorder) as DartEntry; | |
2843 state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2844 } | |
2845 return dartEntry; | |
2846 } | |
2847 | |
2848 /** | |
2849 * Given a source for a Dart file, return a cache entry in which the state of | |
2850 * the data represented by the given descriptor is either [CacheState.VALID] | |
2851 * or [CacheState.ERROR]. This method assumes that the data can be produced by | |
2852 * parsing the source if it is not already cached. | |
2853 * | |
2854 * <b>Note:</b> This method cannot be used in an async environment. | |
2855 */ | |
2856 DartEntry _cacheDartParseData( | |
2857 Source source, DartEntry dartEntry, DataDescriptor descriptor) { | |
2858 if (identical(descriptor, DartEntry.PARSED_UNIT)) { | |
2859 if (dartEntry.hasResolvableCompilationUnit) { | |
2860 return dartEntry; | |
2861 } | |
2862 } | |
2863 // | |
2864 // Check to see whether we already have the information being requested. | |
2865 // | |
2866 CacheState state = dartEntry.getState(descriptor); | |
2867 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2868 // | |
2869 // If not, compute the information. Unless the modification date of the | |
2870 // source continues to change, this loop will eventually terminate. | |
2871 // | |
2872 dartEntry = _cacheDartScanData(source, dartEntry, DartEntry.TOKEN_STREAM); | |
2873 dartEntry = new ParseDartTask(this, source, | |
2874 dartEntry.getValue(DartEntry.TOKEN_STREAM), | |
2875 dartEntry.getValue(SourceEntry.LINE_INFO)) | |
2876 .perform(_resultRecorder) as DartEntry; | |
2877 state = dartEntry.getState(descriptor); | |
2878 } | |
2879 return dartEntry; | |
2880 } | |
2881 | |
2882 /** | |
2883 * Given a source for a Dart file and the library that contains it, return a | |
2884 * cache entry in which the state of the data represented by the given | |
2885 * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method | |
2886 * assumes that the data can be produced by resolving the source in the | |
2887 * context of the library if it is not already cached. | |
2888 * | |
2889 * <b>Note:</b> This method cannot be used in an async environment. | |
2890 */ | |
2891 DartEntry _cacheDartResolutionData(Source unitSource, Source librarySource, | |
2892 DartEntry dartEntry, DataDescriptor descriptor) { | |
2893 // | |
2894 // Check to see whether we already have the information being requested. | |
2895 // | |
2896 CacheState state = (identical(descriptor, DartEntry.ELEMENT)) | |
2897 ? dartEntry.getState(descriptor) | |
2898 : dartEntry.getStateInLibrary(descriptor, librarySource); | |
2899 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2900 // | |
2901 // If not, compute the information. Unless the modification date of the | |
2902 // source continues to change, this loop will eventually terminate. | |
2903 // | |
2904 // TODO(brianwilkerson) As an optimization, if we already have the | |
2905 // element model for the library we can use ResolveDartUnitTask to produce | |
2906 // the resolved AST structure much faster. | |
2907 dartEntry = new ResolveDartLibraryTask(this, unitSource, librarySource) | |
2908 .perform(_resultRecorder) as DartEntry; | |
2909 state = (identical(descriptor, DartEntry.ELEMENT)) | |
2910 ? dartEntry.getState(descriptor) | |
2911 : dartEntry.getStateInLibrary(descriptor, librarySource); | |
2912 } | |
2913 return dartEntry; | |
2914 } | |
2915 | |
2916 /** | |
2917 * Given a source for a Dart file, return a cache entry in which the state of | |
2918 * the data represented by the given descriptor is either [CacheState.VALID] | |
2919 * or [CacheState.ERROR]. This method assumes that the data can be produced by | |
2920 * scanning the source if it is not already cached. | |
2921 * | |
2922 * <b>Note:</b> This method cannot be used in an async environment. | |
2923 */ | |
2924 DartEntry _cacheDartScanData( | |
2925 Source source, DartEntry dartEntry, DataDescriptor descriptor) { | |
2926 // | |
2927 // Check to see whether we already have the information being requested. | |
2928 // | |
2929 CacheState state = dartEntry.getState(descriptor); | |
2930 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2931 // | |
2932 // If not, compute the information. Unless the modification date of the | |
2933 // source continues to change, this loop will eventually terminate. | |
2934 // | |
2935 try { | |
2936 if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { | |
2937 dartEntry = new GetContentTask(this, source) | |
2938 .perform(_resultRecorder) as DartEntry; | |
2939 } | |
2940 dartEntry = new ScanDartTask( | |
2941 this, source, dartEntry.getValue(SourceEntry.CONTENT)) | |
2942 .perform(_resultRecorder) as DartEntry; | |
2943 } on AnalysisException catch (exception) { | |
2944 throw exception; | |
2945 } catch (exception, stackTrace) { | |
2946 throw new AnalysisException( | |
2947 "Exception", new CaughtException(exception, stackTrace)); | |
2948 } | |
2949 state = dartEntry.getState(descriptor); | |
2950 } | |
2951 return dartEntry; | |
2952 } | |
2953 | |
2954 /** | |
2955 * Given a source for a Dart file and the library that contains it, return a | |
2956 * cache entry in which the state of the data represented by the given | |
2957 * descriptor is either [CacheState.VALID] or [CacheState.ERROR]. This method | |
2958 * assumes that the data can be produced by verifying the source in the given | |
2959 * library if the data is not already cached. | |
2960 * | |
2961 * <b>Note:</b> This method cannot be used in an async environment. | |
2962 */ | |
2963 DartEntry _cacheDartVerificationData(Source unitSource, Source librarySource, | |
2964 DartEntry dartEntry, DataDescriptor descriptor) { | |
2965 // | |
2966 // Check to see whether we already have the information being requested. | |
2967 // | |
2968 CacheState state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2969 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
2970 // | |
2971 // If not, compute the information. Unless the modification date of the | |
2972 // source continues to change, this loop will eventually terminate. | |
2973 // | |
2974 LibraryElement library = computeLibraryElement(librarySource); | |
2975 CompilationUnit unit = resolveCompilationUnit(unitSource, library); | |
2976 if (unit == null) { | |
2977 throw new AnalysisException( | |
2978 "Could not resolve compilation unit ${unitSource.fullName} in ${libr
arySource.fullName}"); | |
2979 } | |
2980 dartEntry = new GenerateDartErrorsTask(this, unitSource, unit, library) | |
2981 .perform(_resultRecorder) as DartEntry; | |
2982 state = dartEntry.getStateInLibrary(descriptor, librarySource); | |
2983 } | |
2984 return dartEntry; | |
2985 } | |
2986 | |
2987 /** | |
2988 * Given a source for an HTML file, return a cache entry in which all of the | |
2989 * data represented by the state of the given descriptors is either | |
2990 * [CacheState.VALID] or [CacheState.ERROR]. This method assumes that the data | |
2991 * can be produced by parsing the source if it is not already cached. | |
2992 * | |
2993 * <b>Note:</b> This method cannot be used in an async environment. | |
2994 */ | |
2995 HtmlEntry _cacheHtmlParseData( | |
2996 Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { | |
2997 if (identical(descriptor, HtmlEntry.PARSED_UNIT)) { | |
2998 ht.HtmlUnit unit = htmlEntry.anyParsedUnit; | |
2999 if (unit != null) { | |
3000 return htmlEntry; | |
3001 } | |
3002 } | |
3003 // | |
3004 // Check to see whether we already have the information being requested. | |
3005 // | |
3006 CacheState state = htmlEntry.getState(descriptor); | |
3007 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
3008 // | |
3009 // If not, compute the information. Unless the modification date of the | |
3010 // source continues to change, this loop will eventually terminate. | |
3011 // | |
3012 try { | |
3013 if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { | |
3014 htmlEntry = new GetContentTask(this, source) | |
3015 .perform(_resultRecorder) as HtmlEntry; | |
3016 } | |
3017 htmlEntry = new ParseHtmlTask( | |
3018 this, source, htmlEntry.getValue(SourceEntry.CONTENT)) | |
3019 .perform(_resultRecorder) as HtmlEntry; | |
3020 } on AnalysisException catch (exception) { | |
3021 throw exception; | |
3022 } catch (exception, stackTrace) { | |
3023 throw new AnalysisException( | |
3024 "Exception", new CaughtException(exception, stackTrace)); | |
3025 } | |
3026 state = htmlEntry.getState(descriptor); | |
3027 } | |
3028 return htmlEntry; | |
3029 } | |
3030 | |
3031 /** | |
3032 * Given a source for an HTML file, return a cache entry in which the state of | |
3033 * the data represented by the given descriptor is either [CacheState.VALID] | |
3034 * or [CacheState.ERROR]. This method assumes that the data can be produced by | |
3035 * resolving the source if it is not already cached. | |
3036 * | |
3037 * <b>Note:</b> This method cannot be used in an async environment. | |
3038 */ | |
3039 HtmlEntry _cacheHtmlResolutionData( | |
3040 Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { | |
3041 // | |
3042 // Check to see whether we already have the information being requested. | |
3043 // | |
3044 CacheState state = htmlEntry.getState(descriptor); | |
3045 while (state != CacheState.ERROR && state != CacheState.VALID) { | |
3046 // | |
3047 // If not, compute the information. Unless the modification date of the | |
3048 // source continues to change, this loop will eventually terminate. | |
3049 // | |
3050 htmlEntry = _cacheHtmlParseData(source, htmlEntry, HtmlEntry.PARSED_UNIT); | |
3051 htmlEntry = new ResolveHtmlTask(this, source, htmlEntry.modificationTime, | |
3052 htmlEntry.getValue(HtmlEntry.PARSED_UNIT)) | |
3053 .perform(_resultRecorder) as HtmlEntry; | |
3054 state = htmlEntry.getState(descriptor); | |
3055 } | |
3056 return htmlEntry; | |
3057 } | |
3058 | |
3059 /** | |
3060 * Remove the given [pendingFuture] from [_pendingFutureSources], since the | |
3061 * client has indicated its computation is not needed anymore. | |
3062 */ | |
3063 void _cancelFuture(PendingFuture pendingFuture) { | |
3064 List<PendingFuture> pendingFutures = | |
3065 _pendingFutureSources[pendingFuture.source]; | |
3066 if (pendingFutures != null) { | |
3067 pendingFutures.remove(pendingFuture); | |
3068 if (pendingFutures.isEmpty) { | |
3069 _pendingFutureSources.remove(pendingFuture.source); | |
3070 } | |
3071 } | |
3072 } | |
3073 | |
3074 /** | |
3075 * Compute the transitive closure of all libraries that depend on the given | |
3076 * [library] by adding such libraries to the given collection of | |
3077 * [librariesToInvalidate]. | |
3078 */ | |
3079 void _computeAllLibrariesDependingOn( | |
3080 Source library, HashSet<Source> librariesToInvalidate) { | |
3081 if (librariesToInvalidate.add(library)) { | |
3082 for (Source dependentLibrary in getLibrariesDependingOn(library)) { | |
3083 _computeAllLibrariesDependingOn( | |
3084 dependentLibrary, librariesToInvalidate); | |
3085 } | |
3086 } | |
3087 } | |
3088 | |
3089 /** | |
3090 * Return the priority that should be used when the source associated with | |
3091 * the given [dartEntry] is added to the work manager. | |
3092 */ | |
3093 SourcePriority _computePriority(DartEntry dartEntry) { | |
3094 SourceKind kind = dartEntry.kind; | |
3095 if (kind == SourceKind.LIBRARY) { | |
3096 return SourcePriority.LIBRARY; | |
3097 } else if (kind == SourceKind.PART) { | |
3098 return SourcePriority.NORMAL_PART; | |
3099 } | |
3100 return SourcePriority.UNKNOWN; | |
3101 } | |
3102 | |
3103 /** | |
3104 * Given the encoded form of a source ([encoding]), use the source factory to | |
3105 * reconstitute the original source. | |
3106 */ | |
3107 Source _computeSourceFromEncoding(String encoding) => | |
3108 _sourceFactory.fromEncoding(encoding); | |
3109 | |
3110 /** | |
3111 * Return `true` if the given list of [sources] contains the given | |
3112 * [targetSource]. | |
3113 */ | |
3114 bool _contains(List<Source> sources, Source targetSource) { | |
3115 for (Source source in sources) { | |
3116 if (source == targetSource) { | |
3117 return true; | |
3118 } | |
3119 } | |
3120 return false; | |
3121 } | |
3122 | |
3123 /** | |
3124 * Return `true` if the given list of [sources] contains any of the given | |
3125 * [targetSources]. | |
3126 */ | |
3127 bool _containsAny(List<Source> sources, List<Source> targetSources) { | |
3128 for (Source targetSource in targetSources) { | |
3129 if (_contains(sources, targetSource)) { | |
3130 return true; | |
3131 } | |
3132 } | |
3133 return false; | |
3134 } | |
3135 | |
3136 /** | |
3137 * Set the contents of the given [source] to the given [contents] and mark the | |
3138 * source as having changed. The additional [offset], [oldLength] and | |
3139 * [newLength] information is used by the context to determine what reanalysis | |
3140 * is necessary. The method [setChangedContents] triggers a source changed | |
3141 * event where as this method does not. | |
3142 */ | |
3143 bool _contentRangeChanged(Source source, String contents, int offset, | |
3144 int oldLength, int newLength) { | |
3145 bool changed = false; | |
3146 String originalContents = _contentCache.setContents(source, contents); | |
3147 if (contents != null) { | |
3148 if (contents != originalContents) { | |
3149 if (_options.incremental) { | |
3150 _incrementalAnalysisCache = IncrementalAnalysisCache.update( | |
3151 _incrementalAnalysisCache, source, originalContents, contents, | |
3152 offset, oldLength, newLength, _getReadableSourceEntry(source)); | |
3153 } | |
3154 _sourceChanged(source); | |
3155 changed = true; | |
3156 SourceEntry sourceEntry = _cache.get(source); | |
3157 if (sourceEntry != null) { | |
3158 sourceEntry.modificationTime = | |
3159 _contentCache.getModificationStamp(source); | |
3160 sourceEntry.setValue(SourceEntry.CONTENT, contents); | |
3161 } | |
3162 } | |
3163 } else if (originalContents != null) { | |
3164 _incrementalAnalysisCache = | |
3165 IncrementalAnalysisCache.clear(_incrementalAnalysisCache, source); | |
3166 _sourceChanged(source); | |
3167 changed = true; | |
3168 } | |
3169 return changed; | |
3170 } | |
3171 | |
3172 /** | |
3173 * Set the contents of the given [source] to the given [contents] and mark the | |
3174 * source as having changed. This has the effect of overriding the default | |
3175 * contents of the source. If the contents are `null` the override is removed | |
3176 * so that the default contents will be returned. If [notify] is true, a | |
3177 * source changed event is triggered. | |
3178 */ | |
3179 void _contentsChanged(Source source, String contents, bool notify) { | |
3180 String originalContents = _contentCache.setContents(source, contents); | |
3181 handleContentsChanged(source, originalContents, contents, notify); | |
3182 } | |
3183 | |
3184 /** | |
3185 * Create a [GenerateDartErrorsTask] for the given [unitSource], marking the | |
3186 * verification errors as being in-process. The compilation unit and the | |
3187 * library can be the same if the compilation unit is the defining compilation | |
3188 * unit of the library. | |
3189 */ | |
3190 AnalysisContextImpl_TaskData _createGenerateDartErrorsTask(Source unitSource, | |
3191 DartEntry unitEntry, Source librarySource, DartEntry libraryEntry) { | |
3192 if (unitEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) != | |
3193 CacheState.VALID || | |
3194 libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { | |
3195 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3196 } | |
3197 CompilationUnit unit = | |
3198 unitEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource); | |
3199 if (unit == null) { | |
3200 CaughtException exception = new CaughtException(new AnalysisException( | |
3201 "Entry has VALID state for RESOLVED_UNIT but null value for ${unit
Source.fullName} in ${librarySource.fullName}"), | |
3202 null); | |
3203 AnalysisEngine.instance.logger.logInformation( | |
3204 exception.toString(), exception); | |
3205 unitEntry.recordResolutionError(exception); | |
3206 return new AnalysisContextImpl_TaskData(null, false); | |
3207 } | |
3208 LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); | |
3209 return new AnalysisContextImpl_TaskData( | |
3210 new GenerateDartErrorsTask(this, unitSource, unit, libraryElement), | |
3211 false); | |
3212 } | |
3213 | |
3214 /** | |
3215 * Create a [GenerateDartHintsTask] for the given [source], marking the hints | |
3216 * as being in-process. | |
3217 */ | |
3218 AnalysisContextImpl_TaskData _createGenerateDartHintsTask(Source source, | |
3219 DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) { | |
3220 if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { | |
3221 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3222 } | |
3223 LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); | |
3224 CompilationUnitElement definingUnit = | |
3225 libraryElement.definingCompilationUnit; | |
3226 List<CompilationUnitElement> parts = libraryElement.parts; | |
3227 List<TimestampedData<CompilationUnit>> units = | |
3228 new List<TimestampedData<CompilationUnit>>(parts.length + 1); | |
3229 units[0] = _getResolvedUnit(definingUnit, librarySource); | |
3230 if (units[0] == null) { | |
3231 // TODO(brianwilkerson) We should return a ResolveDartUnitTask | |
3232 // (unless there are multiple ASTs that need to be resolved). | |
3233 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3234 } | |
3235 for (int i = 0; i < parts.length; i++) { | |
3236 units[i + 1] = _getResolvedUnit(parts[i], librarySource); | |
3237 if (units[i + 1] == null) { | |
3238 // TODO(brianwilkerson) We should return a ResolveDartUnitTask | |
3239 // (unless there are multiple ASTs that need to be resolved). | |
3240 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3241 } | |
3242 } | |
3243 return new AnalysisContextImpl_TaskData( | |
3244 new GenerateDartHintsTask(this, units, libraryElement), false); | |
3245 } | |
3246 | |
3247 /** | |
3248 * Create a [GenerateDartLintsTask] for the given [source], marking the lints | |
3249 * as being in-process. | |
3250 */ | |
3251 AnalysisContextImpl_TaskData _createGenerateDartLintsTask(Source source, | |
3252 DartEntry dartEntry, Source librarySource, DartEntry libraryEntry) { | |
3253 if (libraryEntry.getState(DartEntry.ELEMENT) != CacheState.VALID) { | |
3254 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3255 } | |
3256 LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT); | |
3257 CompilationUnitElement definingUnit = | |
3258 libraryElement.definingCompilationUnit; | |
3259 List<CompilationUnitElement> parts = libraryElement.parts; | |
3260 List<TimestampedData<CompilationUnit>> units = | |
3261 new List<TimestampedData<CompilationUnit>>(parts.length + 1); | |
3262 units[0] = _getResolvedUnit(definingUnit, librarySource); | |
3263 if (units[0] == null) { | |
3264 // TODO(brianwilkerson) We should return a ResolveDartUnitTask | |
3265 // (unless there are multiple ASTs that need to be resolved). | |
3266 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3267 } | |
3268 for (int i = 0; i < parts.length; i++) { | |
3269 units[i + 1] = _getResolvedUnit(parts[i], librarySource); | |
3270 if (units[i + 1] == null) { | |
3271 // TODO(brianwilkerson) We should return a ResolveDartUnitTask | |
3272 // (unless there are multiple ASTs that need to be resolved). | |
3273 return _createResolveDartLibraryTask(librarySource, libraryEntry); | |
3274 } | |
3275 } | |
3276 //TODO(pquitslund): revisit if we need all units or whether one will do | |
3277 return new AnalysisContextImpl_TaskData( | |
3278 new GenerateDartLintsTask(this, units, libraryElement), false); | |
3279 } | |
3280 | |
3281 /** | |
3282 * Create a [GetContentTask] for the given [source], marking the content as | |
3283 * being in-process. | |
3284 */ | |
3285 AnalysisContextImpl_TaskData _createGetContentTask( | |
3286 Source source, SourceEntry sourceEntry) { | |
3287 return new AnalysisContextImpl_TaskData( | |
3288 new GetContentTask(this, source), false); | |
3289 } | |
3290 | |
3291 /** | |
3292 * Create a [ParseDartTask] for the given [source]. | |
3293 */ | |
3294 AnalysisContextImpl_TaskData _createParseDartTask( | |
3295 Source source, DartEntry dartEntry) { | |
3296 if (dartEntry.getState(DartEntry.TOKEN_STREAM) != CacheState.VALID || | |
3297 dartEntry.getState(SourceEntry.LINE_INFO) != CacheState.VALID) { | |
3298 return _createScanDartTask(source, dartEntry); | |
3299 } | |
3300 Token tokenStream = dartEntry.getValue(DartEntry.TOKEN_STREAM); | |
3301 dartEntry.setState(DartEntry.TOKEN_STREAM, CacheState.FLUSHED); | |
3302 return new AnalysisContextImpl_TaskData(new ParseDartTask(this, source, | |
3303 tokenStream, dartEntry.getValue(SourceEntry.LINE_INFO)), false); | |
3304 } | |
3305 | |
3306 /** | |
3307 * Create a [ParseHtmlTask] for the given [source]. | |
3308 */ | |
3309 AnalysisContextImpl_TaskData _createParseHtmlTask( | |
3310 Source source, HtmlEntry htmlEntry) { | |
3311 if (htmlEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { | |
3312 return _createGetContentTask(source, htmlEntry); | |
3313 } | |
3314 String content = htmlEntry.getValue(SourceEntry.CONTENT); | |
3315 htmlEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); | |
3316 return new AnalysisContextImpl_TaskData( | |
3317 new ParseHtmlTask(this, source, content), false); | |
3318 } | |
3319 | |
3320 /** | |
3321 * Create a [ResolveDartLibraryTask] for the given [source], marking ? as | |
3322 * being in-process. | |
3323 */ | |
3324 AnalysisContextImpl_TaskData _createResolveDartLibraryTask( | |
3325 Source source, DartEntry dartEntry) { | |
3326 try { | |
3327 AnalysisContextImpl_CycleBuilder builder = | |
3328 new AnalysisContextImpl_CycleBuilder(this); | |
3329 PerformanceStatistics.cycles.makeCurrentWhile(() { | |
3330 builder.computeCycleContaining(source); | |
3331 }); | |
3332 AnalysisContextImpl_TaskData taskData = builder.taskData; | |
3333 if (taskData != null) { | |
3334 return taskData; | |
3335 } | |
3336 return new AnalysisContextImpl_TaskData(new ResolveDartLibraryCycleTask( | |
3337 this, source, source, builder.librariesInCycle), false); | |
3338 } on AnalysisException catch (exception, stackTrace) { | |
3339 dartEntry | |
3340 .recordResolutionError(new CaughtException(exception, stackTrace)); | |
3341 AnalysisEngine.instance.logger.logError( | |
3342 "Internal error trying to create a ResolveDartLibraryTask", | |
3343 new CaughtException(exception, stackTrace)); | |
3344 } | |
3345 return new AnalysisContextImpl_TaskData(null, false); | |
3346 } | |
3347 | |
3348 /** | |
3349 * Create a [ResolveHtmlTask] for the given [source], marking the resolved | |
3350 * unit as being in-process. | |
3351 */ | |
3352 AnalysisContextImpl_TaskData _createResolveHtmlTask( | |
3353 Source source, HtmlEntry htmlEntry) { | |
3354 if (htmlEntry.getState(HtmlEntry.PARSED_UNIT) != CacheState.VALID) { | |
3355 return _createParseHtmlTask(source, htmlEntry); | |
3356 } | |
3357 return new AnalysisContextImpl_TaskData(new ResolveHtmlTask(this, source, | |
3358 htmlEntry.modificationTime, | |
3359 htmlEntry.getValue(HtmlEntry.PARSED_UNIT)), false); | |
3360 } | |
3361 | |
3362 /** | |
3363 * Create a [ScanDartTask] for the given [source], marking the scan errors as | |
3364 * being in-process. | |
3365 */ | |
3366 AnalysisContextImpl_TaskData _createScanDartTask( | |
3367 Source source, DartEntry dartEntry) { | |
3368 if (dartEntry.getState(SourceEntry.CONTENT) != CacheState.VALID) { | |
3369 return _createGetContentTask(source, dartEntry); | |
3370 } | |
3371 String content = dartEntry.getValue(SourceEntry.CONTENT); | |
3372 dartEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); | |
3373 return new AnalysisContextImpl_TaskData( | |
3374 new ScanDartTask(this, source, content), false); | |
3375 } | |
3376 | |
3377 /** | |
3378 * Create a source entry for the given [source]. Return the source entry that | |
3379 * was created, or `null` if the source should not be tracked by this context. | |
3380 */ | |
3381 SourceEntry _createSourceEntry(Source source, bool explicitlyAdded) { | |
3382 String name = source.shortName; | |
3383 if (AnalysisEngine.isHtmlFileName(name)) { | |
3384 HtmlEntry htmlEntry = new HtmlEntry(); | |
3385 htmlEntry.modificationTime = getModificationStamp(source); | |
3386 htmlEntry.explicitlyAdded = explicitlyAdded; | |
3387 _cache.put(source, htmlEntry); | |
3388 return htmlEntry; | |
3389 } else { | |
3390 DartEntry dartEntry = new DartEntry(); | |
3391 dartEntry.modificationTime = getModificationStamp(source); | |
3392 dartEntry.explicitlyAdded = explicitlyAdded; | |
3393 _cache.put(source, dartEntry); | |
3394 return dartEntry; | |
3395 } | |
3396 } | |
3397 | |
3398 /** | |
3399 * Return a list containing all of the change notices that are waiting to be | |
3400 * returned. If there are no notices, then return either `null` or an empty | |
3401 * list, depending on the value of [nullIfEmpty]. | |
3402 */ | |
3403 List<ChangeNotice> _getChangeNotices(bool nullIfEmpty) { | |
3404 if (_pendingNotices.isEmpty) { | |
3405 if (nullIfEmpty) { | |
3406 return null; | |
3407 } | |
3408 return ChangeNoticeImpl.EMPTY_LIST; | |
3409 } | |
3410 List<ChangeNotice> notices = new List.from(_pendingNotices.values); | |
3411 _pendingNotices.clear(); | |
3412 return notices; | |
3413 } | |
3414 | |
3415 /** | |
3416 * Given a source for a Dart file and the library that contains it, return the | |
3417 * data represented by the given descriptor that is associated with that | |
3418 * source. This method assumes that the data can be produced by generating | |
3419 * hints for the library if it is not already cached. | |
3420 * | |
3421 * Throws an [AnalysisException] if data could not be returned because the | |
3422 * source could not be resolved. | |
3423 * | |
3424 * <b>Note:</b> This method cannot be used in an async environment. | |
3425 */ | |
3426 Object _getDartHintData(Source unitSource, Source librarySource, | |
3427 DartEntry dartEntry, DataDescriptor descriptor) { | |
3428 dartEntry = | |
3429 _cacheDartHintData(unitSource, librarySource, dartEntry, descriptor); | |
3430 if (identical(descriptor, DartEntry.ELEMENT)) { | |
3431 return dartEntry.getValue(descriptor); | |
3432 } | |
3433 return dartEntry.getValueInLibrary(descriptor, librarySource); | |
3434 } | |
3435 | |
3436 /** | |
3437 * Given a source for a Dart file and the library that contains it, return the | |
3438 * data represented by the given descriptor that is associated with that | |
3439 * source. This method assumes that the data can be produced by generating | |
3440 * lints for the library if it is not already cached. | |
3441 * | |
3442 * Throws an [AnalysisException] if data could not be returned because the | |
3443 * source could not be resolved. | |
3444 * | |
3445 * <b>Note:</b> This method cannot be used in an async environment. | |
3446 */ | |
3447 Object _getDartLintData(Source unitSource, Source librarySource, | |
3448 DartEntry dartEntry, DataDescriptor descriptor) { | |
3449 dartEntry = | |
3450 _cacheDartLintData(unitSource, librarySource, dartEntry, descriptor); | |
3451 if (identical(descriptor, DartEntry.ELEMENT)) { | |
3452 return dartEntry.getValue(descriptor); | |
3453 } | |
3454 return dartEntry.getValueInLibrary(descriptor, librarySource); | |
3455 } | |
3456 | |
3457 /** | |
3458 * Given a source for a Dart file, return the data represented by the given | |
3459 * descriptor that is associated with that source. This method assumes that | |
3460 * the data can be produced by parsing the source if it is not already cached. | |
3461 * | |
3462 * Throws an [AnalysisException] if data could not be returned because the | |
3463 * source could not be parsed. | |
3464 * | |
3465 * <b>Note:</b> This method cannot be used in an async environment. | |
3466 */ | |
3467 Object _getDartParseData( | |
3468 Source source, DartEntry dartEntry, DataDescriptor descriptor) { | |
3469 dartEntry = _cacheDartParseData(source, dartEntry, descriptor); | |
3470 if (identical(descriptor, DartEntry.PARSED_UNIT)) { | |
3471 _accessedAst(source); | |
3472 return dartEntry.anyParsedCompilationUnit; | |
3473 } | |
3474 return dartEntry.getValue(descriptor); | |
3475 } | |
3476 | |
3477 /** | |
3478 * Given a source for a Dart file, return the data represented by the given | |
3479 * descriptor that is associated with that source, or the given default value | |
3480 * if the source is not a Dart file. This method assumes that the data can be | |
3481 * produced by parsing the source if it is not already cached. | |
3482 * | |
3483 * Throws an [AnalysisException] if data could not be returned because the | |
3484 * source could not be parsed. | |
3485 * | |
3486 * <b>Note:</b> This method cannot be used in an async environment. | |
3487 */ | |
3488 Object _getDartParseData2( | |
3489 Source source, DataDescriptor descriptor, Object defaultValue) { | |
3490 DartEntry dartEntry = _getReadableDartEntry(source); | |
3491 if (dartEntry == null) { | |
3492 return defaultValue; | |
3493 } | |
3494 try { | |
3495 return _getDartParseData(source, dartEntry, descriptor); | |
3496 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
3497 AnalysisEngine.instance.logger.logInformation( | |
3498 "Could not compute $descriptor", | |
3499 new CaughtException(exception, stackTrace)); | |
3500 return defaultValue; | |
3501 } | |
3502 } | |
3503 | |
3504 /** | |
3505 * Given a source for a Dart file and the library that contains it, return the | |
3506 * data represented by the given descriptor that is associated with that | |
3507 * source. This method assumes that the data can be produced by resolving the | |
3508 * source in the context of the library if it is not already cached. | |
3509 * | |
3510 * Throws an [AnalysisException] if data could not be returned because the | |
3511 * source could not be resolved. | |
3512 * | |
3513 * <b>Note:</b> This method cannot be used in an async environment. | |
3514 */ | |
3515 Object _getDartResolutionData(Source unitSource, Source librarySource, | |
3516 DartEntry dartEntry, DataDescriptor descriptor) { | |
3517 dartEntry = _cacheDartResolutionData( | |
3518 unitSource, librarySource, dartEntry, descriptor); | |
3519 if (identical(descriptor, DartEntry.ELEMENT)) { | |
3520 return dartEntry.getValue(descriptor); | |
3521 } else if (identical(descriptor, DartEntry.RESOLVED_UNIT)) { | |
3522 _accessedAst(unitSource); | |
3523 } | |
3524 return dartEntry.getValueInLibrary(descriptor, librarySource); | |
3525 } | |
3526 | |
3527 /** | |
3528 * Given a source for a Dart file and the library that contains it, return the | |
3529 * data represented by the given descriptor that is associated with that | |
3530 * source, or the given default value if the source is not a Dart file. This | |
3531 * method assumes that the data can be produced by resolving the source in the | |
3532 * context of the library if it is not already cached. | |
3533 * | |
3534 * Throws an [AnalysisException] if data could not be returned because the | |
3535 * source could not be resolved. | |
3536 * | |
3537 * <b>Note:</b> This method cannot be used in an async environment. | |
3538 */ | |
3539 Object _getDartResolutionData2(Source unitSource, Source librarySource, | |
3540 DataDescriptor descriptor, Object defaultValue) { | |
3541 DartEntry dartEntry = _getReadableDartEntry(unitSource); | |
3542 if (dartEntry == null) { | |
3543 return defaultValue; | |
3544 } | |
3545 try { | |
3546 return _getDartResolutionData( | |
3547 unitSource, librarySource, dartEntry, descriptor); | |
3548 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
3549 AnalysisEngine.instance.logger.logInformation( | |
3550 "Could not compute $descriptor", | |
3551 new CaughtException(exception, stackTrace)); | |
3552 return defaultValue; | |
3553 } | |
3554 } | |
3555 | |
3556 /** | |
3557 * Given a source for a Dart file, return the data represented by the given | |
3558 * descriptor that is associated with that source. This method assumes that | |
3559 * the data can be produced by scanning the source if it is not already | |
3560 * cached. | |
3561 * | |
3562 * Throws an [AnalysisException] if data could not be returned because the | |
3563 * source could not be scanned. | |
3564 * | |
3565 * <b>Note:</b> This method cannot be used in an async environment. | |
3566 */ | |
3567 Object _getDartScanData( | |
3568 Source source, DartEntry dartEntry, DataDescriptor descriptor) { | |
3569 dartEntry = _cacheDartScanData(source, dartEntry, descriptor); | |
3570 return dartEntry.getValue(descriptor); | |
3571 } | |
3572 | |
3573 /** | |
3574 * Given a source for a Dart file, return the data represented by the given | |
3575 * descriptor that is associated with that source, or the given default value | |
3576 * if the source is not a Dart file. This method assumes that the data can be | |
3577 * produced by scanning the source if it is not already cached. | |
3578 * | |
3579 * Throws an [AnalysisException] if data could not be returned because the | |
3580 * source could not be scanned. | |
3581 * | |
3582 * <b>Note:</b> This method cannot be used in an async environment. | |
3583 */ | |
3584 Object _getDartScanData2( | |
3585 Source source, DataDescriptor descriptor, Object defaultValue) { | |
3586 DartEntry dartEntry = _getReadableDartEntry(source); | |
3587 if (dartEntry == null) { | |
3588 return defaultValue; | |
3589 } | |
3590 try { | |
3591 return _getDartScanData(source, dartEntry, descriptor); | |
3592 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
3593 AnalysisEngine.instance.logger.logInformation( | |
3594 "Could not compute $descriptor", | |
3595 new CaughtException(exception, stackTrace)); | |
3596 return defaultValue; | |
3597 } | |
3598 } | |
3599 | |
3600 /** | |
3601 * Given a source for a Dart file and the library that contains it, return the | |
3602 * data represented by the given descriptor that is associated with that | |
3603 * source. This method assumes that the data can be produced by verifying the | |
3604 * source within the given library if it is not already cached. | |
3605 * | |
3606 * Throws an [AnalysisException] if data could not be returned because the | |
3607 * source could not be resolved. | |
3608 * | |
3609 * <b>Note:</b> This method cannot be used in an async environment. | |
3610 */ | |
3611 Object _getDartVerificationData(Source unitSource, Source librarySource, | |
3612 DartEntry dartEntry, DataDescriptor descriptor) { | |
3613 dartEntry = _cacheDartVerificationData( | |
3614 unitSource, librarySource, dartEntry, descriptor); | |
3615 return dartEntry.getValueInLibrary(descriptor, librarySource); | |
3616 } | |
3617 | |
3618 /** | |
3619 * Given a source for an HTML file, return the data represented by the given | |
3620 * descriptor that is associated with that source, or the given default value | |
3621 * if the source is not an HTML file. This method assumes that the data can be | |
3622 * produced by parsing the source if it is not already cached. | |
3623 * | |
3624 * Throws an [AnalysisException] if data could not be returned because the | |
3625 * source could not be parsed. | |
3626 * | |
3627 * <b>Note:</b> This method cannot be used in an async environment. | |
3628 */ | |
3629 Object _getHtmlParseData( | |
3630 Source source, DataDescriptor descriptor, Object defaultValue) { | |
3631 HtmlEntry htmlEntry = _getReadableHtmlEntry(source); | |
3632 if (htmlEntry == null) { | |
3633 return defaultValue; | |
3634 } | |
3635 htmlEntry = _cacheHtmlParseData(source, htmlEntry, descriptor); | |
3636 if (identical(descriptor, HtmlEntry.PARSED_UNIT)) { | |
3637 _accessedAst(source); | |
3638 return htmlEntry.anyParsedUnit; | |
3639 } | |
3640 return htmlEntry.getValue(descriptor); | |
3641 } | |
3642 | |
3643 /** | |
3644 * Given a source for an HTML file, return the data represented by the given | |
3645 * descriptor that is associated with that source, or the given default value | |
3646 * if the source is not an HTML file. This method assumes that the data can be | |
3647 * produced by resolving the source if it is not already cached. | |
3648 * | |
3649 * Throws an [AnalysisException] if data could not be returned because the | |
3650 * source could not be resolved. | |
3651 * | |
3652 * <b>Note:</b> This method cannot be used in an async environment. | |
3653 */ | |
3654 Object _getHtmlResolutionData( | |
3655 Source source, DataDescriptor descriptor, Object defaultValue) { | |
3656 HtmlEntry htmlEntry = _getReadableHtmlEntry(source); | |
3657 if (htmlEntry == null) { | |
3658 return defaultValue; | |
3659 } | |
3660 try { | |
3661 return _getHtmlResolutionData2(source, htmlEntry, descriptor); | |
3662 } on ObsoleteSourceAnalysisException catch (exception, stackTrace) { | |
3663 AnalysisEngine.instance.logger.logInformation( | |
3664 "Could not compute $descriptor", | |
3665 new CaughtException(exception, stackTrace)); | |
3666 return defaultValue; | |
3667 } | |
3668 } | |
3669 | |
3670 /** | |
3671 * Given a source for an HTML file, return the data represented by the given | |
3672 * descriptor that is associated with that source. This method assumes that | |
3673 * the data can be produced by resolving the source if it is not already | |
3674 * cached. | |
3675 * | |
3676 * Throws an [AnalysisException] if data could not be returned because the | |
3677 * source could not be resolved. | |
3678 * | |
3679 * <b>Note:</b> This method cannot be used in an async environment. | |
3680 */ | |
3681 Object _getHtmlResolutionData2( | |
3682 Source source, HtmlEntry htmlEntry, DataDescriptor descriptor) { | |
3683 htmlEntry = _cacheHtmlResolutionData(source, htmlEntry, descriptor); | |
3684 if (identical(descriptor, HtmlEntry.RESOLVED_UNIT)) { | |
3685 _accessedAst(source); | |
3686 } | |
3687 return htmlEntry.getValue(descriptor); | |
3688 } | |
3689 | |
3690 /** | |
3691 * Look at the given [source] to see whether a task needs to be performed | |
3692 * related to it. Return the task that should be performed, or `null` if there | |
3693 * is no more work to be done for the source. | |
3694 */ | |
3695 AnalysisContextImpl_TaskData _getNextAnalysisTaskForSource(Source source, | |
3696 SourceEntry sourceEntry, bool isPriority, bool hintsEnabled, | |
3697 bool lintsEnabled) { | |
3698 // Refuse to generate tasks for html based files that are above 1500 KB | |
3699 if (_isTooBigHtmlSourceEntry(source, sourceEntry)) { | |
3700 // TODO (jwren) we still need to report an error of some kind back to the | |
3701 // client. | |
3702 return new AnalysisContextImpl_TaskData(null, false); | |
3703 } | |
3704 if (sourceEntry == null) { | |
3705 return new AnalysisContextImpl_TaskData(null, false); | |
3706 } | |
3707 CacheState contentState = sourceEntry.getState(SourceEntry.CONTENT); | |
3708 if (contentState == CacheState.INVALID) { | |
3709 return _createGetContentTask(source, sourceEntry); | |
3710 } else if (contentState == CacheState.IN_PROCESS) { | |
3711 // We are already in the process of getting the content. | |
3712 // There's nothing else we can do with this source until that's complete. | |
3713 return new AnalysisContextImpl_TaskData(null, true); | |
3714 } else if (contentState == CacheState.ERROR) { | |
3715 // We have done all of the analysis we can for this source because we | |
3716 // cannot get its content. | |
3717 return new AnalysisContextImpl_TaskData(null, false); | |
3718 } | |
3719 if (sourceEntry is DartEntry) { | |
3720 DartEntry dartEntry = sourceEntry; | |
3721 CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS); | |
3722 if (scanErrorsState == CacheState.INVALID || | |
3723 (isPriority && scanErrorsState == CacheState.FLUSHED)) { | |
3724 return _createScanDartTask(source, dartEntry); | |
3725 } | |
3726 CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS); | |
3727 if (parseErrorsState == CacheState.INVALID || | |
3728 (isPriority && parseErrorsState == CacheState.FLUSHED)) { | |
3729 return _createParseDartTask(source, dartEntry); | |
3730 } | |
3731 if (isPriority && parseErrorsState != CacheState.ERROR) { | |
3732 if (!dartEntry.hasResolvableCompilationUnit) { | |
3733 return _createParseDartTask(source, dartEntry); | |
3734 } | |
3735 } | |
3736 SourceKind kind = dartEntry.getValue(DartEntry.SOURCE_KIND); | |
3737 if (kind == SourceKind.UNKNOWN) { | |
3738 return _createParseDartTask(source, dartEntry); | |
3739 } else if (kind == SourceKind.LIBRARY) { | |
3740 CacheState elementState = dartEntry.getState(DartEntry.ELEMENT); | |
3741 if (elementState == CacheState.INVALID) { | |
3742 return _createResolveDartLibraryTask(source, dartEntry); | |
3743 } | |
3744 } | |
3745 List<Source> librariesContaining = dartEntry.containingLibraries; | |
3746 for (Source librarySource in librariesContaining) { | |
3747 SourceEntry librarySourceEntry = _cache.get(librarySource); | |
3748 if (librarySourceEntry is DartEntry) { | |
3749 DartEntry libraryEntry = librarySourceEntry; | |
3750 CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT); | |
3751 if (elementState == CacheState.INVALID || | |
3752 (isPriority && elementState == CacheState.FLUSHED)) { | |
3753 // return createResolveDartLibraryTask(librarySource, (DartEntry) lib
raryEntry); | |
3754 return new AnalysisContextImpl_TaskData( | |
3755 new ResolveDartLibraryTask(this, source, librarySource), false); | |
3756 } | |
3757 CacheState resolvedUnitState = dartEntry.getStateInLibrary( | |
3758 DartEntry.RESOLVED_UNIT, librarySource); | |
3759 if (resolvedUnitState == CacheState.INVALID || | |
3760 (isPriority && resolvedUnitState == CacheState.FLUSHED)) { | |
3761 // | |
3762 // The commented out lines below are an optimization that doesn't | |
3763 // quite work yet. The problem is that if the source was not | |
3764 // resolved because it wasn't part of any library, then there won't | |
3765 // be any elements in the element model that we can use to resolve | |
3766 // it. | |
3767 // | |
3768 // LibraryElement libraryElement = libraryEntry.getValue(DartEntry.EL
EMENT); | |
3769 // if (libraryElement != null) { | |
3770 // return new ResolveDartUnitTask(this, source, libraryElement); | |
3771 // } | |
3772 // Possibly replace with: | |
3773 // return createResolveDartLibraryTask(librarySource, (DartEntry) li
braryEntry); | |
3774 return new AnalysisContextImpl_TaskData( | |
3775 new ResolveDartLibraryTask(this, source, librarySource), false); | |
3776 } | |
3777 if (shouldErrorsBeAnalyzed(source, dartEntry)) { | |
3778 CacheState verificationErrorsState = dartEntry.getStateInLibrary( | |
3779 DartEntry.VERIFICATION_ERRORS, librarySource); | |
3780 if (verificationErrorsState == CacheState.INVALID || | |
3781 (isPriority && verificationErrorsState == CacheState.FLUSHED)) { | |
3782 return _createGenerateDartErrorsTask( | |
3783 source, dartEntry, librarySource, libraryEntry); | |
3784 } | |
3785 if (hintsEnabled) { | |
3786 CacheState hintsState = | |
3787 dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource); | |
3788 if (hintsState == CacheState.INVALID || | |
3789 (isPriority && hintsState == CacheState.FLUSHED)) { | |
3790 return _createGenerateDartHintsTask( | |
3791 source, dartEntry, librarySource, libraryEntry); | |
3792 } | |
3793 } | |
3794 if (lintsEnabled) { | |
3795 CacheState lintsState = | |
3796 dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource); | |
3797 if (lintsState == CacheState.INVALID || | |
3798 (isPriority && lintsState == CacheState.FLUSHED)) { | |
3799 return _createGenerateDartLintsTask( | |
3800 source, dartEntry, librarySource, libraryEntry); | |
3801 } | |
3802 } | |
3803 } | |
3804 } | |
3805 } | |
3806 } else if (sourceEntry is HtmlEntry) { | |
3807 HtmlEntry htmlEntry = sourceEntry; | |
3808 CacheState parseErrorsState = htmlEntry.getState(HtmlEntry.PARSE_ERRORS); | |
3809 if (parseErrorsState == CacheState.INVALID || | |
3810 (isPriority && parseErrorsState == CacheState.FLUSHED)) { | |
3811 return _createParseHtmlTask(source, htmlEntry); | |
3812 } | |
3813 if (isPriority && parseErrorsState != CacheState.ERROR) { | |
3814 ht.HtmlUnit parsedUnit = htmlEntry.anyParsedUnit; | |
3815 if (parsedUnit == null) { | |
3816 return _createParseHtmlTask(source, htmlEntry); | |
3817 } | |
3818 } | |
3819 CacheState resolvedUnitState = | |
3820 htmlEntry.getState(HtmlEntry.RESOLVED_UNIT); | |
3821 if (resolvedUnitState == CacheState.INVALID || | |
3822 (isPriority && resolvedUnitState == CacheState.FLUSHED)) { | |
3823 return _createResolveHtmlTask(source, htmlEntry); | |
3824 } | |
3825 } | |
3826 return new AnalysisContextImpl_TaskData(null, false); | |
3827 } | |
3828 | |
3829 /** | |
3830 * Return the cache entry associated with the given [source], or `null` if the | |
3831 * source is not a Dart file. | |
3832 * | |
3833 * @param source the source for which a cache entry is being sought | |
3834 * @return the source cache entry associated with the given source | |
3835 */ | |
3836 DartEntry _getReadableDartEntry(Source source) { | |
3837 SourceEntry sourceEntry = _cache.get(source); | |
3838 if (sourceEntry == null) { | |
3839 sourceEntry = _createSourceEntry(source, false); | |
3840 } | |
3841 if (sourceEntry is DartEntry) { | |
3842 return sourceEntry; | |
3843 } | |
3844 return null; | |
3845 } | |
3846 | |
3847 /** | |
3848 * Return the cache entry associated with the given [source], or `null` if the | |
3849 * source is not an HTML file. | |
3850 */ | |
3851 HtmlEntry _getReadableHtmlEntry(Source source) { | |
3852 SourceEntry sourceEntry = _cache.get(source); | |
3853 if (sourceEntry == null) { | |
3854 sourceEntry = _createSourceEntry(source, false); | |
3855 } | |
3856 if (sourceEntry is HtmlEntry) { | |
3857 return sourceEntry; | |
3858 } | |
3859 return null; | |
3860 } | |
3861 | |
3862 /** | |
3863 * Return the cache entry associated with the given [source], creating it if | |
3864 * necessary. | |
3865 */ | |
3866 SourceEntry _getReadableSourceEntry(Source source) { | |
3867 SourceEntry sourceEntry = _cache.get(source); | |
3868 if (sourceEntry == null) { | |
3869 sourceEntry = _createSourceEntry(source, false); | |
3870 } | |
3871 return sourceEntry; | |
3872 } | |
3873 | |
3874 /** | |
3875 * Return a resolved compilation unit corresponding to the given [element] in | |
3876 * the library defined by the given [librarySource], or `null` if the | |
3877 * information is not cached. | |
3878 */ | |
3879 TimestampedData<CompilationUnit> _getResolvedUnit( | |
3880 CompilationUnitElement element, Source librarySource) { | |
3881 SourceEntry sourceEntry = _cache.get(element.source); | |
3882 if (sourceEntry is DartEntry) { | |
3883 DartEntry dartEntry = sourceEntry; | |
3884 if (dartEntry.getStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource) == | |
3885 CacheState.VALID) { | |
3886 return new TimestampedData<CompilationUnit>(dartEntry.modificationTime, | |
3887 dartEntry.getValueInLibrary( | |
3888 DartEntry.RESOLVED_UNIT, librarySource)); | |
3889 } | |
3890 } | |
3891 return null; | |
3892 } | |
3893 | |
3894 /** | |
3895 * Return a list containing all of the sources known to this context that have | |
3896 * the given [kind]. | |
3897 */ | |
3898 List<Source> _getSources(SourceKind kind) { | |
3899 List<Source> sources = new List<Source>(); | |
3900 MapIterator<Source, SourceEntry> iterator = _cache.iterator(); | |
3901 while (iterator.moveNext()) { | |
3902 if (iterator.value.kind == kind) { | |
3903 sources.add(iterator.key); | |
3904 } | |
3905 } | |
3906 return sources; | |
3907 } | |
3908 | |
3909 /** | |
3910 * Look at the given [source] to see whether a task needs to be performed | |
3911 * related to it. If so, add the source to the set of sources that need to be | |
3912 * processed. This method duplicates, and must therefore be kept in sync with, | |
3913 * [_getNextAnalysisTaskForSource]. This method is intended to be used for | |
3914 * testing purposes only. | |
3915 */ | |
3916 void _getSourcesNeedingProcessing(Source source, SourceEntry sourceEntry, | |
3917 bool isPriority, bool hintsEnabled, bool lintsEnabled, | |
3918 HashSet<Source> sources) { | |
3919 if (sourceEntry is DartEntry) { | |
3920 DartEntry dartEntry = sourceEntry; | |
3921 CacheState scanErrorsState = dartEntry.getState(DartEntry.SCAN_ERRORS); | |
3922 if (scanErrorsState == CacheState.INVALID || | |
3923 (isPriority && scanErrorsState == CacheState.FLUSHED)) { | |
3924 sources.add(source); | |
3925 return; | |
3926 } | |
3927 CacheState parseErrorsState = dartEntry.getState(DartEntry.PARSE_ERRORS); | |
3928 if (parseErrorsState == CacheState.INVALID || | |
3929 (isPriority && parseErrorsState == CacheState.FLUSHED)) { | |
3930 sources.add(source); | |
3931 return; | |
3932 } | |
3933 if (isPriority) { | |
3934 if (!dartEntry.hasResolvableCompilationUnit) { | |
3935 sources.add(source); | |
3936 return; | |
3937 } | |
3938 } | |
3939 for (Source librarySource in getLibrariesContaining(source)) { | |
3940 SourceEntry libraryEntry = _cache.get(librarySource); | |
3941 if (libraryEntry is DartEntry) { | |
3942 CacheState elementState = libraryEntry.getState(DartEntry.ELEMENT); | |
3943 if (elementState == CacheState.INVALID || | |
3944 (isPriority && elementState == CacheState.FLUSHED)) { | |
3945 sources.add(source); | |
3946 return; | |
3947 } | |
3948 CacheState resolvedUnitState = dartEntry.getStateInLibrary( | |
3949 DartEntry.RESOLVED_UNIT, librarySource); | |
3950 if (resolvedUnitState == CacheState.INVALID || | |
3951 (isPriority && resolvedUnitState == CacheState.FLUSHED)) { | |
3952 LibraryElement libraryElement = | |
3953 libraryEntry.getValue(DartEntry.ELEMENT); | |
3954 if (libraryElement != null) { | |
3955 sources.add(source); | |
3956 return; | |
3957 } | |
3958 } | |
3959 if (shouldErrorsBeAnalyzed(source, dartEntry)) { | |
3960 CacheState verificationErrorsState = dartEntry.getStateInLibrary( | |
3961 DartEntry.VERIFICATION_ERRORS, librarySource); | |
3962 if (verificationErrorsState == CacheState.INVALID || | |
3963 (isPriority && verificationErrorsState == CacheState.FLUSHED)) { | |
3964 LibraryElement libraryElement = | |
3965 libraryEntry.getValue(DartEntry.ELEMENT); | |
3966 if (libraryElement != null) { | |
3967 sources.add(source); | |
3968 return; | |
3969 } | |
3970 } | |
3971 if (hintsEnabled) { | |
3972 CacheState hintsState = | |
3973 dartEntry.getStateInLibrary(DartEntry.HINTS, librarySource); | |
3974 if (hintsState == CacheState.INVALID || | |
3975 (isPriority && hintsState == CacheState.FLUSHED)) { | |
3976 LibraryElement libraryElement = | |
3977 libraryEntry.getValue(DartEntry.ELEMENT); | |
3978 if (libraryElement != null) { | |
3979 sources.add(source); | |
3980 return; | |
3981 } | |
3982 } | |
3983 } | |
3984 if (lintsEnabled) { | |
3985 CacheState lintsState = | |
3986 dartEntry.getStateInLibrary(DartEntry.LINTS, librarySource); | |
3987 if (lintsState == CacheState.INVALID || | |
3988 (isPriority && lintsState == CacheState.FLUSHED)) { | |
3989 LibraryElement libraryElement = | |
3990 libraryEntry.getValue(DartEntry.ELEMENT); | |
3991 if (libraryElement != null) { | |
3992 sources.add(source); | |
3993 return; | |
3994 } | |
3995 } | |
3996 } | |
3997 } | |
3998 } | |
3999 } | |
4000 } else if (sourceEntry is HtmlEntry) { | |
4001 HtmlEntry htmlEntry = sourceEntry; | |
4002 CacheState parsedUnitState = htmlEntry.getState(HtmlEntry.PARSED_UNIT); | |
4003 if (parsedUnitState == CacheState.INVALID || | |
4004 (isPriority && parsedUnitState == CacheState.FLUSHED)) { | |
4005 sources.add(source); | |
4006 return; | |
4007 } | |
4008 CacheState resolvedUnitState = | |
4009 htmlEntry.getState(HtmlEntry.RESOLVED_UNIT); | |
4010 if (resolvedUnitState == CacheState.INVALID || | |
4011 (isPriority && resolvedUnitState == CacheState.FLUSHED)) { | |
4012 sources.add(source); | |
4013 return; | |
4014 } | |
4015 } | |
4016 } | |
4017 | |
4018 /** | |
4019 * Invalidate all of the resolution results computed by this context. The flag | |
4020 * [invalidateUris] should be `true` if the cached results of converting URIs | |
4021 * to source files should also be invalidated. | |
4022 */ | |
4023 void _invalidateAllLocalResolutionInformation(bool invalidateUris) { | |
4024 HashMap<Source, List<Source>> oldPartMap = | |
4025 new HashMap<Source, List<Source>>(); | |
4026 MapIterator<Source, SourceEntry> iterator = _privatePartition.iterator(); | |
4027 while (iterator.moveNext()) { | |
4028 Source source = iterator.key; | |
4029 SourceEntry sourceEntry = iterator.value; | |
4030 if (sourceEntry is HtmlEntry) { | |
4031 HtmlEntry htmlEntry = sourceEntry; | |
4032 htmlEntry.invalidateAllResolutionInformation(invalidateUris); | |
4033 iterator.value = htmlEntry; | |
4034 _workManager.add(source, SourcePriority.HTML); | |
4035 } else if (sourceEntry is DartEntry) { | |
4036 DartEntry dartEntry = sourceEntry; | |
4037 oldPartMap[source] = dartEntry.getValue(DartEntry.INCLUDED_PARTS); | |
4038 dartEntry.invalidateAllResolutionInformation(invalidateUris); | |
4039 iterator.value = dartEntry; | |
4040 _workManager.add(source, _computePriority(dartEntry)); | |
4041 } | |
4042 } | |
4043 _removeFromPartsUsingMap(oldPartMap); | |
4044 } | |
4045 | |
4046 /** | |
4047 * In response to a change to at least one of the compilation units in the | |
4048 * library defined by the given [librarySource], invalidate any results that | |
4049 * are dependent on the result of resolving that library. | |
4050 * | |
4051 * <b>Note:</b> Any cache entries that were accessed before this method was | |
4052 * invoked must be re-accessed after this method returns. | |
4053 */ | |
4054 void _invalidateLibraryResolution(Source librarySource) { | |
4055 // TODO(brianwilkerson) This could be optimized. There's no need to flush | |
4056 // all of these entries if the public namespace hasn't changed, which will | |
4057 // be a fairly common case. The question is whether we can afford the time | |
4058 // to compute the namespace to look for differences. | |
4059 DartEntry libraryEntry = _getReadableDartEntry(librarySource); | |
4060 if (libraryEntry != null) { | |
4061 List<Source> includedParts = | |
4062 libraryEntry.getValue(DartEntry.INCLUDED_PARTS); | |
4063 libraryEntry.invalidateAllResolutionInformation(false); | |
4064 _workManager.add(librarySource, SourcePriority.LIBRARY); | |
4065 for (Source partSource in includedParts) { | |
4066 SourceEntry partEntry = _cache.get(partSource); | |
4067 if (partEntry is DartEntry) { | |
4068 partEntry.invalidateAllResolutionInformation(false); | |
4069 } | |
4070 } | |
4071 } | |
4072 } | |
4073 | |
4074 /** | |
4075 * Return `true` if the given [library] is, or depends on, 'dart:html'. The | |
4076 * [visitedLibraries] is a collection of the libraries that have been visited, | |
4077 * used to prevent infinite recursion. | |
4078 */ | |
4079 bool _isClient(LibraryElement library, Source htmlSource, | |
4080 HashSet<LibraryElement> visitedLibraries) { | |
4081 if (visitedLibraries.contains(library)) { | |
4082 return false; | |
4083 } | |
4084 if (library.source == htmlSource) { | |
4085 return true; | |
4086 } | |
4087 visitedLibraries.add(library); | |
4088 for (LibraryElement imported in library.importedLibraries) { | |
4089 if (_isClient(imported, htmlSource, visitedLibraries)) { | |
4090 return true; | |
4091 } | |
4092 } | |
4093 for (LibraryElement exported in library.exportedLibraries) { | |
4094 if (_isClient(exported, htmlSource, visitedLibraries)) { | |
4095 return true; | |
4096 } | |
4097 } | |
4098 return false; | |
4099 } | |
4100 | |
4101 bool _isTooBigHtmlSourceEntry(Source source, SourceEntry sourceEntry) => | |
4102 false; | |
4103 | |
4104 /** | |
4105 * Log the given debugging [message]. | |
4106 */ | |
4107 void _logInformation(String message) { | |
4108 AnalysisEngine.instance.logger.logInformation(message); | |
4109 } | |
4110 | |
4111 // /** | |
4112 // * Notify all of the analysis listeners that the given source is no longer i
ncluded in the set of | |
4113 // * sources that are being analyzed. | |
4114 // * | |
4115 // * @param source the source that is no longer being analyzed | |
4116 // */ | |
4117 // void _notifyExcludedSource(Source source) { | |
4118 // int count = _listeners.length; | |
4119 // for (int i = 0; i < count; i++) { | |
4120 // _listeners[i].excludedSource(this, source); | |
4121 // } | |
4122 // } | |
4123 | |
4124 // /** | |
4125 // * Notify all of the analysis listeners that the given source is now include
d in the set of | |
4126 // * sources that are being analyzed. | |
4127 // * | |
4128 // * @param source the source that is now being analyzed | |
4129 // */ | |
4130 // void _notifyIncludedSource(Source source) { | |
4131 // int count = _listeners.length; | |
4132 // for (int i = 0; i < count; i++) { | |
4133 // _listeners[i].includedSource(this, source); | |
4134 // } | |
4135 // } | |
4136 | |
4137 // /** | |
4138 // * Notify all of the analysis listeners that the given Dart source was parse
d. | |
4139 // * | |
4140 // * @param source the source that was parsed | |
4141 // * @param unit the result of parsing the source | |
4142 // */ | |
4143 // void _notifyParsedDart(Source source, CompilationUnit unit) { | |
4144 // int count = _listeners.length; | |
4145 // for (int i = 0; i < count; i++) { | |
4146 // _listeners[i].parsedDart(this, source, unit); | |
4147 // } | |
4148 // } | |
4149 | |
4150 // /** | |
4151 // * Notify all of the analysis listeners that the given HTML source was parse
d. | |
4152 // * | |
4153 // * @param source the source that was parsed | |
4154 // * @param unit the result of parsing the source | |
4155 // */ | |
4156 // void _notifyParsedHtml(Source source, ht.HtmlUnit unit) { | |
4157 // int count = _listeners.length; | |
4158 // for (int i = 0; i < count; i++) { | |
4159 // _listeners[i].parsedHtml(this, source, unit); | |
4160 // } | |
4161 // } | |
4162 | |
4163 // /** | |
4164 // * Notify all of the analysis listeners that the given Dart source was resol
ved. | |
4165 // * | |
4166 // * @param source the source that was resolved | |
4167 // * @param unit the result of resolving the source | |
4168 // */ | |
4169 // void _notifyResolvedDart(Source source, CompilationUnit unit) { | |
4170 // int count = _listeners.length; | |
4171 // for (int i = 0; i < count; i++) { | |
4172 // _listeners[i].resolvedDart(this, source, unit); | |
4173 // } | |
4174 // } | |
4175 | |
4176 // /** | |
4177 // * Notify all of the analysis listeners that the given HTML source was resol
ved. | |
4178 // * | |
4179 // * @param source the source that was resolved | |
4180 // * @param unit the result of resolving the source | |
4181 // */ | |
4182 // void _notifyResolvedHtml(Source source, ht.HtmlUnit unit) { | |
4183 // int count = _listeners.length; | |
4184 // for (int i = 0; i < count; i++) { | |
4185 // _listeners[i].resolvedHtml(this, source, unit); | |
4186 // } | |
4187 // } | |
4188 | |
4189 /** | |
4190 * Notify all of the analysis listeners that a task is about to be performed. | |
4191 */ | |
4192 void _notifyAboutToPerformTask(String taskDescription) { | |
4193 int count = _listeners.length; | |
4194 for (int i = 0; i < count; i++) { | |
4195 _listeners[i].aboutToPerformTask(this, taskDescription); | |
4196 } | |
4197 } | |
4198 | |
4199 /** | |
4200 * Notify all of the analysis listeners that the errors associated with the | |
4201 * given [source] has been updated to the given [errors]. | |
4202 */ | |
4203 void _notifyErrors( | |
4204 Source source, List<AnalysisError> errors, LineInfo lineInfo) { | |
4205 int count = _listeners.length; | |
4206 for (int i = 0; i < count; i++) { | |
4207 _listeners[i].computedErrors(this, source, errors, lineInfo); | |
4208 } | |
4209 } | |
4210 | |
4211 /** | |
4212 * Given that the given [source] (with the corresponding [sourceEntry]) has | |
4213 * been invalidated, invalidate all of the libraries that depend on it. | |
4214 */ | |
4215 void _propagateInvalidation(Source source, SourceEntry sourceEntry) { | |
4216 if (sourceEntry is HtmlEntry) { | |
4217 HtmlEntry htmlEntry = sourceEntry; | |
4218 htmlEntry.modificationTime = getModificationStamp(source); | |
4219 htmlEntry.invalidateAllInformation(); | |
4220 _cache.removedAst(source); | |
4221 _workManager.add(source, SourcePriority.HTML); | |
4222 } else if (sourceEntry is DartEntry) { | |
4223 List<Source> containingLibraries = getLibrariesContaining(source); | |
4224 List<Source> dependentLibraries = getLibrariesDependingOn(source); | |
4225 HashSet<Source> librariesToInvalidate = new HashSet<Source>(); | |
4226 for (Source containingLibrary in containingLibraries) { | |
4227 _computeAllLibrariesDependingOn( | |
4228 containingLibrary, librariesToInvalidate); | |
4229 } | |
4230 for (Source dependentLibrary in dependentLibraries) { | |
4231 _computeAllLibrariesDependingOn( | |
4232 dependentLibrary, librariesToInvalidate); | |
4233 } | |
4234 for (Source library in librariesToInvalidate) { | |
4235 _invalidateLibraryResolution(library); | |
4236 } | |
4237 DartEntry dartEntry = _cache.get(source); | |
4238 _removeFromParts(source, dartEntry); | |
4239 dartEntry.modificationTime = getModificationStamp(source); | |
4240 dartEntry.invalidateAllInformation(); | |
4241 _cache.removedAst(source); | |
4242 _workManager.add(source, SourcePriority.UNKNOWN); | |
4243 } | |
4244 // reset unit in the notification, it is out of date now | |
4245 ChangeNoticeImpl notice = _pendingNotices[source]; | |
4246 if (notice != null) { | |
4247 notice.resolvedDartUnit = null; | |
4248 notice.resolvedHtmlUnit = null; | |
4249 } | |
4250 } | |
4251 | |
4252 /** | |
4253 * Given a [dartEntry] and a [library] element, record the library element and | |
4254 * other information gleaned from the element in the cache entry. | |
4255 */ | |
4256 void _recordElementData(DartEntry dartEntry, LibraryElement library, | |
4257 Source librarySource, Source htmlSource) { | |
4258 dartEntry.setValue(DartEntry.ELEMENT, library); | |
4259 dartEntry.setValue(DartEntry.IS_LAUNCHABLE, library.entryPoint != null); | |
4260 dartEntry.setValue(DartEntry.IS_CLIENT, | |
4261 _isClient(library, htmlSource, new HashSet<LibraryElement>())); | |
4262 } | |
4263 | |
4264 /** | |
4265 * Record the results produced by performing a [task] and return the cache | |
4266 * entry associated with the results. | |
4267 */ | |
4268 DartEntry _recordGenerateDartErrorsTask(GenerateDartErrorsTask task) { | |
4269 Source source = task.source; | |
4270 DartEntry dartEntry = _cache.get(source); | |
4271 Source librarySource = task.libraryElement.source; | |
4272 CaughtException thrownException = task.exception; | |
4273 if (thrownException != null) { | |
4274 dartEntry.recordVerificationErrorInLibrary( | |
4275 librarySource, thrownException); | |
4276 throw new AnalysisException('<rethrow>', thrownException); | |
4277 } | |
4278 dartEntry.setValueInLibrary( | |
4279 DartEntry.VERIFICATION_ERRORS, librarySource, task.errors); | |
4280 ChangeNoticeImpl notice = getNotice(source); | |
4281 LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); | |
4282 notice.setErrors(dartEntry.allErrors, lineInfo); | |
4283 return dartEntry; | |
4284 } | |
4285 | |
4286 /** | |
4287 * Record the results produced by performing a [task] and return the cache | |
4288 * entry associated with the results. | |
4289 */ | |
4290 DartEntry _recordGenerateDartHintsTask(GenerateDartHintsTask task) { | |
4291 Source librarySource = task.libraryElement.source; | |
4292 CaughtException thrownException = task.exception; | |
4293 DartEntry libraryEntry = null; | |
4294 HashMap<Source, List<AnalysisError>> hintMap = task.hintMap; | |
4295 if (hintMap == null) { | |
4296 // We don't have any information about which sources to mark as invalid | |
4297 // other than the library source. | |
4298 DartEntry libraryEntry = _cache.get(librarySource); | |
4299 if (thrownException == null) { | |
4300 String message = "GenerateDartHintsTask returned a null hint map " | |
4301 "without throwing an exception: ${librarySource.fullName}"; | |
4302 thrownException = | |
4303 new CaughtException(new AnalysisException(message), null); | |
4304 } | |
4305 libraryEntry.recordHintErrorInLibrary(librarySource, thrownException); | |
4306 throw new AnalysisException('<rethrow>', thrownException); | |
4307 } | |
4308 hintMap.forEach((Source unitSource, List<AnalysisError> hints) { | |
4309 DartEntry dartEntry = _cache.get(unitSource); | |
4310 if (unitSource == librarySource) { | |
4311 libraryEntry = dartEntry; | |
4312 } | |
4313 if (thrownException == null) { | |
4314 dartEntry.setValueInLibrary(DartEntry.HINTS, librarySource, hints); | |
4315 ChangeNoticeImpl notice = getNotice(unitSource); | |
4316 LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); | |
4317 notice.setErrors(dartEntry.allErrors, lineInfo); | |
4318 } else { | |
4319 dartEntry.recordHintErrorInLibrary(librarySource, thrownException); | |
4320 } | |
4321 }); | |
4322 if (thrownException != null) { | |
4323 throw new AnalysisException('<rethrow>', thrownException); | |
4324 } | |
4325 return libraryEntry; | |
4326 } | |
4327 | |
4328 /** | |
4329 * Record the results produced by performing a [task] and return the cache | |
4330 * entry associated with the results. | |
4331 */ | |
4332 DartEntry _recordGenerateDartLintsTask(GenerateDartLintsTask task) { | |
4333 Source librarySource = task.libraryElement.source; | |
4334 CaughtException thrownException = task.exception; | |
4335 DartEntry libraryEntry = null; | |
4336 HashMap<Source, List<AnalysisError>> lintMap = task.lintMap; | |
4337 if (lintMap == null) { | |
4338 // We don't have any information about which sources to mark as invalid | |
4339 // other than the library source. | |
4340 DartEntry libraryEntry = _cache.get(librarySource); | |
4341 if (thrownException == null) { | |
4342 String message = "GenerateDartLintsTask returned a null lint map " | |
4343 "without throwing an exception: ${librarySource.fullName}"; | |
4344 thrownException = | |
4345 new CaughtException(new AnalysisException(message), null); | |
4346 } | |
4347 libraryEntry.recordLintErrorInLibrary(librarySource, thrownException); | |
4348 throw new AnalysisException('<rethrow>', thrownException); | |
4349 } | |
4350 lintMap.forEach((Source unitSource, List<AnalysisError> lints) { | |
4351 DartEntry dartEntry = _cache.get(unitSource); | |
4352 if (unitSource == librarySource) { | |
4353 libraryEntry = dartEntry; | |
4354 } | |
4355 if (thrownException == null) { | |
4356 dartEntry.setValueInLibrary(DartEntry.LINTS, librarySource, lints); | |
4357 ChangeNoticeImpl notice = getNotice(unitSource); | |
4358 LineInfo lineInfo = dartEntry.getValue(SourceEntry.LINE_INFO); | |
4359 notice.setErrors(dartEntry.allErrors, lineInfo); | |
4360 } else { | |
4361 dartEntry.recordLintErrorInLibrary(librarySource, thrownException); | |
4362 } | |
4363 }); | |
4364 if (thrownException != null) { | |
4365 throw new AnalysisException('<rethrow>', thrownException); | |
4366 } | |
4367 return libraryEntry; | |
4368 } | |
4369 | |
4370 /** | |
4371 * Record the results produced by performing a [task] and return the cache | |
4372 * entry associated with the results. | |
4373 */ | |
4374 SourceEntry _recordGetContentsTask(GetContentTask task) { | |
4375 if (!task.isComplete) { | |
4376 return null; | |
4377 } | |
4378 Source source = task.source; | |
4379 SourceEntry sourceEntry = _cache.get(source); | |
4380 CaughtException thrownException = task.exception; | |
4381 if (thrownException != null) { | |
4382 sourceEntry.recordContentError(thrownException); | |
4383 { | |
4384 sourceEntry.setValue(SourceEntry.CONTENT_ERRORS, task.errors); | |
4385 ChangeNoticeImpl notice = getNotice(source); | |
4386 notice.setErrors(sourceEntry.allErrors, null); | |
4387 } | |
4388 _workManager.remove(source); | |
4389 throw new AnalysisException('<rethrow>', thrownException); | |
4390 } | |
4391 sourceEntry.modificationTime = task.modificationTime; | |
4392 sourceEntry.setValue(SourceEntry.CONTENT, task.content); | |
4393 return sourceEntry; | |
4394 } | |
4395 | |
4396 /** | |
4397 * Record the results produced by performing a [task] and return the cache | |
4398 * entry associated with the results. | |
4399 */ | |
4400 DartEntry _recordIncrementalAnalysisTaskResults( | |
4401 IncrementalAnalysisTask task) { | |
4402 CompilationUnit unit = task.compilationUnit; | |
4403 if (unit != null) { | |
4404 ChangeNoticeImpl notice = getNotice(task.source); | |
4405 notice.resolvedDartUnit = unit; | |
4406 _incrementalAnalysisCache = | |
4407 IncrementalAnalysisCache.cacheResult(task.cache, unit); | |
4408 } | |
4409 return null; | |
4410 } | |
4411 | |
4412 /** | |
4413 * Record the results produced by performing a [task] and return the cache | |
4414 * entry associated with the results. | |
4415 */ | |
4416 DartEntry _recordParseDartTaskResults(ParseDartTask task) { | |
4417 Source source = task.source; | |
4418 DartEntry dartEntry = _cache.get(source); | |
4419 _removeFromParts(source, dartEntry); | |
4420 CaughtException thrownException = task.exception; | |
4421 if (thrownException != null) { | |
4422 _removeFromParts(source, dartEntry); | |
4423 dartEntry.recordParseError(thrownException); | |
4424 _cache.removedAst(source); | |
4425 throw new AnalysisException('<rethrow>', thrownException); | |
4426 } | |
4427 if (task.hasNonPartOfDirective) { | |
4428 dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); | |
4429 dartEntry.containingLibrary = source; | |
4430 _workManager.add(source, SourcePriority.LIBRARY); | |
4431 } else if (task.hasPartOfDirective) { | |
4432 dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART); | |
4433 dartEntry.removeContainingLibrary(source); | |
4434 _workManager.add(source, SourcePriority.NORMAL_PART); | |
4435 } else { | |
4436 // The file contains no directives. | |
4437 List<Source> containingLibraries = dartEntry.containingLibraries; | |
4438 if (containingLibraries.length > 1 || | |
4439 (containingLibraries.length == 1 && | |
4440 containingLibraries[0] != source)) { | |
4441 dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.PART); | |
4442 dartEntry.removeContainingLibrary(source); | |
4443 _workManager.add(source, SourcePriority.NORMAL_PART); | |
4444 } else { | |
4445 dartEntry.setValue(DartEntry.SOURCE_KIND, SourceKind.LIBRARY); | |
4446 dartEntry.containingLibrary = source; | |
4447 _workManager.add(source, SourcePriority.LIBRARY); | |
4448 } | |
4449 } | |
4450 List<Source> newParts = task.includedSources; | |
4451 for (int i = 0; i < newParts.length; i++) { | |
4452 Source partSource = newParts[i]; | |
4453 DartEntry partEntry = _getReadableDartEntry(partSource); | |
4454 if (partEntry != null && !identical(partEntry, dartEntry)) { | |
4455 // TODO(brianwilkerson) Change the kind of the "part" if it was marked | |
4456 // as a library and it has no directives. | |
4457 partEntry.addContainingLibrary(source); | |
4458 } | |
4459 } | |
4460 dartEntry.setValue(DartEntry.PARSED_UNIT, task.compilationUnit); | |
4461 dartEntry.setValue(DartEntry.PARSE_ERRORS, task.errors); | |
4462 dartEntry.setValue(DartEntry.EXPORTED_LIBRARIES, task.exportedSources); | |
4463 dartEntry.setValue(DartEntry.IMPORTED_LIBRARIES, task.importedSources); | |
4464 dartEntry.setValue(DartEntry.INCLUDED_PARTS, newParts); | |
4465 _cache.storedAst(source); | |
4466 ChangeNoticeImpl notice = getNotice(source); | |
4467 if (notice.resolvedDartUnit == null) { | |
4468 notice.parsedDartUnit = task.compilationUnit; | |
4469 } | |
4470 notice.setErrors(dartEntry.allErrors, task.lineInfo); | |
4471 // Verify that the incrementally parsed and resolved unit in the incremental | |
4472 // cache is structurally equivalent to the fully parsed unit | |
4473 _incrementalAnalysisCache = IncrementalAnalysisCache.verifyStructure( | |
4474 _incrementalAnalysisCache, source, task.compilationUnit); | |
4475 return dartEntry; | |
4476 } | |
4477 | |
4478 /** | |
4479 * Record the results produced by performing a [task] and return the cache | |
4480 * entry associated with the results. | |
4481 */ | |
4482 HtmlEntry _recordParseHtmlTaskResults(ParseHtmlTask task) { | |
4483 Source source = task.source; | |
4484 HtmlEntry htmlEntry = _cache.get(source); | |
4485 CaughtException thrownException = task.exception; | |
4486 if (thrownException != null) { | |
4487 htmlEntry.recordParseError(thrownException); | |
4488 _cache.removedAst(source); | |
4489 throw new AnalysisException('<rethrow>', thrownException); | |
4490 } | |
4491 LineInfo lineInfo = task.lineInfo; | |
4492 htmlEntry.setValue(SourceEntry.LINE_INFO, lineInfo); | |
4493 htmlEntry.setValue(HtmlEntry.PARSED_UNIT, task.htmlUnit); | |
4494 htmlEntry.setValue(HtmlEntry.PARSE_ERRORS, task.errors); | |
4495 htmlEntry.setValue( | |
4496 HtmlEntry.REFERENCED_LIBRARIES, task.referencedLibraries); | |
4497 _cache.storedAst(source); | |
4498 ChangeNoticeImpl notice = getNotice(source); | |
4499 notice.setErrors(htmlEntry.allErrors, lineInfo); | |
4500 return htmlEntry; | |
4501 } | |
4502 | |
4503 /** | |
4504 * Record the results produced by performing a [task] and return the cache | |
4505 * entry associated with the results. | |
4506 */ | |
4507 DartEntry _recordResolveDartUnitTaskResults(ResolveDartUnitTask task) { | |
4508 Source unitSource = task.source; | |
4509 DartEntry dartEntry = _cache.get(unitSource); | |
4510 Source librarySource = task.librarySource; | |
4511 CaughtException thrownException = task.exception; | |
4512 if (thrownException != null) { | |
4513 dartEntry.recordResolutionErrorInLibrary(librarySource, thrownException); | |
4514 _cache.removedAst(unitSource); | |
4515 throw new AnalysisException('<rethrow>', thrownException); | |
4516 } | |
4517 dartEntry.setValueInLibrary( | |
4518 DartEntry.RESOLVED_UNIT, librarySource, task.resolvedUnit); | |
4519 _cache.storedAst(unitSource); | |
4520 return dartEntry; | |
4521 } | |
4522 | |
4523 /** | |
4524 * Record the results produced by performing a [task] and return the cache | |
4525 * entry associated with the results. | |
4526 */ | |
4527 HtmlEntry _recordResolveHtmlTaskResults(ResolveHtmlTask task) { | |
4528 Source source = task.source; | |
4529 HtmlEntry htmlEntry = _cache.get(source); | |
4530 CaughtException thrownException = task.exception; | |
4531 if (thrownException != null) { | |
4532 htmlEntry.recordResolutionError(thrownException); | |
4533 _cache.removedAst(source); | |
4534 throw new AnalysisException('<rethrow>', thrownException); | |
4535 } | |
4536 htmlEntry.setState(HtmlEntry.PARSED_UNIT, CacheState.FLUSHED); | |
4537 htmlEntry.setValue(HtmlEntry.RESOLVED_UNIT, task.resolvedUnit); | |
4538 htmlEntry.setValue(HtmlEntry.ELEMENT, task.element); | |
4539 htmlEntry.setValue(HtmlEntry.RESOLUTION_ERRORS, task.resolutionErrors); | |
4540 _cache.storedAst(source); | |
4541 ChangeNoticeImpl notice = getNotice(source); | |
4542 notice.resolvedHtmlUnit = task.resolvedUnit; | |
4543 LineInfo lineInfo = htmlEntry.getValue(SourceEntry.LINE_INFO); | |
4544 notice.setErrors(htmlEntry.allErrors, lineInfo); | |
4545 return htmlEntry; | |
4546 } | |
4547 | |
4548 /** | |
4549 * Record the results produced by performing a [task] and return the cache | |
4550 * entry associated with the results. | |
4551 */ | |
4552 DartEntry _recordScanDartTaskResults(ScanDartTask task) { | |
4553 Source source = task.source; | |
4554 DartEntry dartEntry = _cache.get(source); | |
4555 CaughtException thrownException = task.exception; | |
4556 if (thrownException != null) { | |
4557 _removeFromParts(source, dartEntry); | |
4558 dartEntry.recordScanError(thrownException); | |
4559 _cache.removedAst(source); | |
4560 throw new AnalysisException('<rethrow>', thrownException); | |
4561 } | |
4562 LineInfo lineInfo = task.lineInfo; | |
4563 dartEntry.setValue(SourceEntry.LINE_INFO, lineInfo); | |
4564 dartEntry.setValue(DartEntry.TOKEN_STREAM, task.tokenStream); | |
4565 dartEntry.setValue(DartEntry.SCAN_ERRORS, task.errors); | |
4566 _cache.storedAst(source); | |
4567 ChangeNoticeImpl notice = getNotice(source); | |
4568 notice.setErrors(dartEntry.allErrors, lineInfo); | |
4569 return dartEntry; | |
4570 } | |
4571 | |
4572 /** | |
4573 * Remove the given [librarySource] from the list of containing libraries for | |
4574 * all of the parts referenced by the given [dartEntry]. | |
4575 */ | |
4576 void _removeFromParts(Source librarySource, DartEntry dartEntry) { | |
4577 List<Source> oldParts = dartEntry.getValue(DartEntry.INCLUDED_PARTS); | |
4578 for (int i = 0; i < oldParts.length; i++) { | |
4579 Source partSource = oldParts[i]; | |
4580 DartEntry partEntry = _getReadableDartEntry(partSource); | |
4581 if (partEntry != null && !identical(partEntry, dartEntry)) { | |
4582 partEntry.removeContainingLibrary(librarySource); | |
4583 if (partEntry.containingLibraries.length == 0 && !exists(partSource)) { | |
4584 _cache.remove(partSource); | |
4585 } | |
4586 } | |
4587 } | |
4588 } | |
4589 | |
4590 /** | |
4591 * Remove the given libraries that are keys in the given map from the list of | |
4592 * containing libraries for each of the parts in the corresponding value. | |
4593 */ | |
4594 void _removeFromPartsUsingMap(HashMap<Source, List<Source>> oldPartMap) { | |
4595 oldPartMap.forEach((Source librarySource, List<Source> oldParts) { | |
4596 for (int i = 0; i < oldParts.length; i++) { | |
4597 Source partSource = oldParts[i]; | |
4598 if (partSource != librarySource) { | |
4599 DartEntry partEntry = _getReadableDartEntry(partSource); | |
4600 if (partEntry != null) { | |
4601 partEntry.removeContainingLibrary(librarySource); | |
4602 if (partEntry.containingLibraries.length == 0 && | |
4603 !exists(partSource)) { | |
4604 _cache.remove(partSource); | |
4605 } | |
4606 } | |
4607 } | |
4608 } | |
4609 }); | |
4610 } | |
4611 | |
4612 /** | |
4613 * Remove the given [source] from the priority order if it is in the list. | |
4614 */ | |
4615 void _removeFromPriorityOrder(Source source) { | |
4616 int count = _priorityOrder.length; | |
4617 List<Source> newOrder = new List<Source>(); | |
4618 for (int i = 0; i < count; i++) { | |
4619 if (_priorityOrder[i] != source) { | |
4620 newOrder.add(_priorityOrder[i]); | |
4621 } | |
4622 } | |
4623 if (newOrder.length < count) { | |
4624 analysisPriorityOrder = newOrder; | |
4625 } | |
4626 } | |
4627 | |
4628 /** | |
4629 * Create an entry for the newly added [source] and invalidate any sources | |
4630 * that referenced the source before it existed. | |
4631 */ | |
4632 void _sourceAvailable(Source source) { | |
4633 SourceEntry sourceEntry = _cache.get(source); | |
4634 if (sourceEntry == null) { | |
4635 sourceEntry = _createSourceEntry(source, true); | |
4636 } else { | |
4637 _propagateInvalidation(source, sourceEntry); | |
4638 sourceEntry = _cache.get(source); | |
4639 } | |
4640 if (sourceEntry is HtmlEntry) { | |
4641 _workManager.add(source, SourcePriority.HTML); | |
4642 } else if (sourceEntry is DartEntry) { | |
4643 _workManager.add(source, _computePriority(sourceEntry)); | |
4644 } | |
4645 } | |
4646 | |
4647 /** | |
4648 * Invalidate the [source] that was changed and any sources that referenced | |
4649 * the source before it existed. | |
4650 */ | |
4651 void _sourceChanged(Source source) { | |
4652 SourceEntry sourceEntry = _cache.get(source); | |
4653 // If the source is removed, we don't care about it. | |
4654 if (sourceEntry == null) { | |
4655 return; | |
4656 } | |
4657 // Check if the content of the source is the same as it was the last time. | |
4658 String sourceContent = sourceEntry.getValue(SourceEntry.CONTENT); | |
4659 if (sourceContent != null) { | |
4660 sourceEntry.setState(SourceEntry.CONTENT, CacheState.FLUSHED); | |
4661 try { | |
4662 TimestampedData<String> fileContents = getContents(source); | |
4663 if (fileContents.data == sourceContent) { | |
4664 return; | |
4665 } | |
4666 } catch (e) {} | |
4667 } | |
4668 // We have to invalidate the cache. | |
4669 _propagateInvalidation(source, sourceEntry); | |
4670 } | |
4671 | |
4672 /** | |
4673 * Record that the give [source] has been deleted. | |
4674 */ | |
4675 void _sourceDeleted(Source source) { | |
4676 SourceEntry sourceEntry = _cache.get(source); | |
4677 if (sourceEntry is HtmlEntry) { | |
4678 HtmlEntry htmlEntry = sourceEntry; | |
4679 htmlEntry.recordContentError(new CaughtException( | |
4680 new AnalysisException("This source was marked as being deleted"), | |
4681 null)); | |
4682 } else if (sourceEntry is DartEntry) { | |
4683 DartEntry dartEntry = sourceEntry; | |
4684 HashSet<Source> libraries = new HashSet<Source>(); | |
4685 for (Source librarySource in getLibrariesContaining(source)) { | |
4686 libraries.add(librarySource); | |
4687 for (Source dependentLibrary | |
4688 in getLibrariesDependingOn(librarySource)) { | |
4689 libraries.add(dependentLibrary); | |
4690 } | |
4691 } | |
4692 for (Source librarySource in libraries) { | |
4693 _invalidateLibraryResolution(librarySource); | |
4694 } | |
4695 dartEntry.recordContentError(new CaughtException( | |
4696 new AnalysisException("This source was marked as being deleted"), | |
4697 null)); | |
4698 } | |
4699 _workManager.remove(source); | |
4700 _removeFromPriorityOrder(source); | |
4701 } | |
4702 | |
4703 /** | |
4704 * Record that the given [source] has been removed. | |
4705 */ | |
4706 void _sourceRemoved(Source source) { | |
4707 SourceEntry sourceEntry = _cache.get(source); | |
4708 if (sourceEntry is HtmlEntry) {} else if (sourceEntry is DartEntry) { | |
4709 HashSet<Source> libraries = new HashSet<Source>(); | |
4710 for (Source librarySource in getLibrariesContaining(source)) { | |
4711 libraries.add(librarySource); | |
4712 for (Source dependentLibrary | |
4713 in getLibrariesDependingOn(librarySource)) { | |
4714 libraries.add(dependentLibrary); | |
4715 } | |
4716 } | |
4717 for (Source librarySource in libraries) { | |
4718 _invalidateLibraryResolution(librarySource); | |
4719 } | |
4720 } | |
4721 _cache.remove(source); | |
4722 _workManager.remove(source); | |
4723 _removeFromPriorityOrder(source); | |
4724 } | |
4725 | |
4726 /** | |
4727 * TODO(scheglov) A hackish, limited incremental resolution implementation. | |
4728 */ | |
4729 bool _tryPoorMansIncrementalResolution(Source unitSource, String newCode) { | |
4730 return PerformanceStatistics.incrementalAnalysis.makeCurrentWhile(() { | |
4731 incrementalResolutionValidation_lastUnitSource = null; | |
4732 incrementalResolutionValidation_lastLibrarySource = null; | |
4733 incrementalResolutionValidation_lastUnit = null; | |
4734 // prepare the entry | |
4735 DartEntry dartEntry = _cache.get(unitSource); | |
4736 if (dartEntry == null) { | |
4737 return false; | |
4738 } | |
4739 // prepare the (only) library source | |
4740 List<Source> librarySources = getLibrariesContaining(unitSource); | |
4741 if (librarySources.length != 1) { | |
4742 return false; | |
4743 } | |
4744 Source librarySource = librarySources[0]; | |
4745 // prepare the library element | |
4746 LibraryElement libraryElement = getLibraryElement(librarySource); | |
4747 if (libraryElement == null) { | |
4748 return false; | |
4749 } | |
4750 // prepare the existing unit | |
4751 CompilationUnit oldUnit = | |
4752 getResolvedCompilationUnit2(unitSource, librarySource); | |
4753 if (oldUnit == null) { | |
4754 return false; | |
4755 } | |
4756 // do resolution | |
4757 Stopwatch perfCounter = new Stopwatch()..start(); | |
4758 PoorMansIncrementalResolver resolver = new PoorMansIncrementalResolver( | |
4759 typeProvider, unitSource, getReadableSourceEntryOrNull(unitSource), | |
4760 null, null, oldUnit, analysisOptions.incrementalApi, analysisOptions); | |
4761 bool success = resolver.resolve(newCode); | |
4762 AnalysisEngine.instance.instrumentationService.logPerformance( | |
4763 AnalysisPerformanceKind.INCREMENTAL, perfCounter, | |
4764 'success=$success,context_id=$_id,code_length=${newCode.length}'); | |
4765 if (!success) { | |
4766 return false; | |
4767 } | |
4768 // if validation, remember the result, but throw it away | |
4769 if (analysisOptions.incrementalValidation) { | |
4770 incrementalResolutionValidation_lastUnitSource = oldUnit.element.source; | |
4771 incrementalResolutionValidation_lastLibrarySource = | |
4772 oldUnit.element.library.source; | |
4773 incrementalResolutionValidation_lastUnit = oldUnit; | |
4774 return false; | |
4775 } | |
4776 // prepare notice | |
4777 { | |
4778 LineInfo lineInfo = getLineInfo(unitSource); | |
4779 ChangeNoticeImpl notice = getNotice(unitSource); | |
4780 notice.resolvedDartUnit = oldUnit; | |
4781 notice.setErrors(dartEntry.allErrors, lineInfo); | |
4782 } | |
4783 // OK | |
4784 return true; | |
4785 }); | |
4786 } | |
4787 | |
4788 void _validateLastIncrementalResolutionResult() { | |
4789 if (incrementalResolutionValidation_lastUnitSource == null || | |
4790 incrementalResolutionValidation_lastLibrarySource == null || | |
4791 incrementalResolutionValidation_lastUnit == null) { | |
4792 return; | |
4793 } | |
4794 CompilationUnit fullUnit = getResolvedCompilationUnit2( | |
4795 incrementalResolutionValidation_lastUnitSource, | |
4796 incrementalResolutionValidation_lastLibrarySource); | |
4797 if (fullUnit != null) { | |
4798 try { | |
4799 assertSameResolution( | |
4800 incrementalResolutionValidation_lastUnit, fullUnit); | |
4801 } on IncrementalResolutionMismatch catch (mismatch, stack) { | |
4802 String failure = mismatch.message; | |
4803 String message = | |
4804 'Incremental resolution mismatch:\n$failure\nat\n$stack'; | |
4805 AnalysisEngine.instance.logger.logError(message); | |
4806 } | |
4807 } | |
4808 incrementalResolutionValidation_lastUnitSource = null; | |
4809 incrementalResolutionValidation_lastLibrarySource = null; | |
4810 incrementalResolutionValidation_lastUnit = null; | |
4811 } | |
4812 } | |
4813 | |
4814 /** | |
4815 * An object used by an analysis context to record the results of a task. | |
4816 */ | |
4817 class AnalysisContextImpl_AnalysisTaskResultRecorder | |
4818 implements AnalysisTaskVisitor<SourceEntry> { | |
4819 final AnalysisContextImpl AnalysisContextImpl_this; | |
4820 | |
4821 AnalysisContextImpl_AnalysisTaskResultRecorder(this.AnalysisContextImpl_this); | |
4822 | |
4823 @override | |
4824 DartEntry visitGenerateDartErrorsTask(GenerateDartErrorsTask task) => | |
4825 AnalysisContextImpl_this._recordGenerateDartErrorsTask(task); | |
4826 | |
4827 @override | |
4828 DartEntry visitGenerateDartHintsTask(GenerateDartHintsTask task) => | |
4829 AnalysisContextImpl_this._recordGenerateDartHintsTask(task); | |
4830 | |
4831 @override | |
4832 DartEntry visitGenerateDartLintsTask(GenerateDartLintsTask task) => | |
4833 AnalysisContextImpl_this._recordGenerateDartLintsTask(task); | |
4834 | |
4835 @override | |
4836 SourceEntry visitGetContentTask(GetContentTask task) => | |
4837 AnalysisContextImpl_this._recordGetContentsTask(task); | |
4838 | |
4839 @override | |
4840 DartEntry visitIncrementalAnalysisTask(IncrementalAnalysisTask task) => | |
4841 AnalysisContextImpl_this._recordIncrementalAnalysisTaskResults(task); | |
4842 | |
4843 @override | |
4844 DartEntry visitParseDartTask(ParseDartTask task) => | |
4845 AnalysisContextImpl_this._recordParseDartTaskResults(task); | |
4846 | |
4847 @override | |
4848 HtmlEntry visitParseHtmlTask(ParseHtmlTask task) => | |
4849 AnalysisContextImpl_this._recordParseHtmlTaskResults(task); | |
4850 | |
4851 @override | |
4852 DartEntry visitResolveDartLibraryCycleTask( | |
4853 ResolveDartLibraryCycleTask task) => | |
4854 AnalysisContextImpl_this.recordResolveDartLibraryCycleTaskResults(task); | |
4855 | |
4856 @override | |
4857 DartEntry visitResolveDartLibraryTask(ResolveDartLibraryTask task) => | |
4858 AnalysisContextImpl_this.recordResolveDartLibraryTaskResults(task); | |
4859 | |
4860 @override | |
4861 DartEntry visitResolveDartUnitTask(ResolveDartUnitTask task) => | |
4862 AnalysisContextImpl_this._recordResolveDartUnitTaskResults(task); | |
4863 | |
4864 @override | |
4865 HtmlEntry visitResolveHtmlTask(ResolveHtmlTask task) => | |
4866 AnalysisContextImpl_this._recordResolveHtmlTaskResults(task); | |
4867 | |
4868 @override | |
4869 DartEntry visitScanDartTask(ScanDartTask task) => | |
4870 AnalysisContextImpl_this._recordScanDartTaskResults(task); | |
4871 } | |
4872 | |
4873 class AnalysisContextImpl_ContextRetentionPolicy | |
4874 implements CacheRetentionPolicy { | |
4875 final AnalysisContextImpl AnalysisContextImpl_this; | |
4876 | |
4877 AnalysisContextImpl_ContextRetentionPolicy(this.AnalysisContextImpl_this); | |
4878 | |
4879 @override | |
4880 RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) { | |
4881 int priorityCount = AnalysisContextImpl_this._priorityOrder.length; | |
4882 for (int i = 0; i < priorityCount; i++) { | |
4883 if (source == AnalysisContextImpl_this._priorityOrder[i]) { | |
4884 return RetentionPriority.HIGH; | |
4885 } | |
4886 } | |
4887 if (AnalysisContextImpl_this._neededForResolution != null && | |
4888 AnalysisContextImpl_this._neededForResolution.contains(source)) { | |
4889 return RetentionPriority.HIGH; | |
4890 } | |
4891 if (sourceEntry is DartEntry) { | |
4892 DartEntry dartEntry = sourceEntry; | |
4893 if (_astIsNeeded(dartEntry)) { | |
4894 return RetentionPriority.MEDIUM; | |
4895 } | |
4896 } | |
4897 return RetentionPriority.LOW; | |
4898 } | |
4899 | |
4900 bool _astIsNeeded(DartEntry dartEntry) => | |
4901 dartEntry.hasInvalidData(DartEntry.HINTS) || | |
4902 dartEntry.hasInvalidData(DartEntry.LINTS) || | |
4903 dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) || | |
4904 dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS); | |
4905 } | |
4906 | |
4907 /** | |
4908 * An object used to construct a list of the libraries that must be resolved | |
4909 * together in order to resolve any one of the libraries. | |
4910 */ | |
4911 class AnalysisContextImpl_CycleBuilder { | |
4912 final AnalysisContextImpl AnalysisContextImpl_this; | |
4913 | |
4914 /** | |
4915 * A table mapping the sources of the defining compilation units of libraries | |
4916 * to the representation of the library that has the information needed to | |
4917 * resolve the library. | |
4918 */ | |
4919 HashMap<Source, ResolvableLibrary> _libraryMap = | |
4920 new HashMap<Source, ResolvableLibrary>(); | |
4921 | |
4922 /** | |
4923 * The dependency graph used to compute the libraries in the cycle. | |
4924 */ | |
4925 DirectedGraph<ResolvableLibrary> _dependencyGraph; | |
4926 | |
4927 /** | |
4928 * A list containing the libraries that are ready to be resolved. | |
4929 */ | |
4930 List<ResolvableLibrary> _librariesInCycle; | |
4931 | |
4932 /** | |
4933 * The analysis task that needs to be performed before the cycle of libraries | |
4934 * can be resolved, or `null` if the libraries are ready to be resolved. | |
4935 */ | |
4936 AnalysisContextImpl_TaskData _taskData; | |
4937 | |
4938 /** | |
4939 * Initialize a newly created cycle builder. | |
4940 */ | |
4941 AnalysisContextImpl_CycleBuilder(this.AnalysisContextImpl_this) : super(); | |
4942 | |
4943 /** | |
4944 * Return a list containing the libraries that are ready to be resolved | |
4945 * (assuming that [getTaskData] returns `null`). | |
4946 */ | |
4947 List<ResolvableLibrary> get librariesInCycle => _librariesInCycle; | |
4948 | |
4949 /** | |
4950 * Return a representation of an analysis task that needs to be performed | |
4951 * before the cycle of libraries can be resolved, or `null` if the libraries | |
4952 * are ready to be resolved. | |
4953 */ | |
4954 AnalysisContextImpl_TaskData get taskData => _taskData; | |
4955 | |
4956 /** | |
4957 * Compute a list of the libraries that need to be resolved together in orde | |
4958 * to resolve the given [librarySource]. | |
4959 */ | |
4960 void computeCycleContaining(Source librarySource) { | |
4961 // | |
4962 // Create the object representing the library being resolved. | |
4963 // | |
4964 ResolvableLibrary targetLibrary = _createLibrary(librarySource); | |
4965 // | |
4966 // Compute the set of libraries that need to be resolved together. | |
4967 // | |
4968 _dependencyGraph = new DirectedGraph<ResolvableLibrary>(); | |
4969 _computeLibraryDependencies(targetLibrary); | |
4970 if (_taskData != null) { | |
4971 return; | |
4972 } | |
4973 _librariesInCycle = _dependencyGraph.findCycleContaining(targetLibrary); | |
4974 // | |
4975 // Ensure that all of the data needed to resolve them has been computed. | |
4976 // | |
4977 _ensureImportsAndExports(); | |
4978 if (_taskData != null) { | |
4979 // At least one imported library needs to be resolved before the target | |
4980 // library. | |
4981 AnalysisTask task = _taskData.task; | |
4982 if (task is ResolveDartLibraryTask) { | |
4983 AnalysisContextImpl_this._workManager.addFirst( | |
4984 task.librarySource, SourcePriority.LIBRARY); | |
4985 } | |
4986 return; | |
4987 } | |
4988 _computePartsInCycle(librarySource); | |
4989 if (_taskData != null) { | |
4990 // At least one part needs to be parsed. | |
4991 return; | |
4992 } | |
4993 // All of the AST's necessary to perform a resolution of the library cycle | |
4994 // have been gathered, so it is no longer necessary to retain them in the | |
4995 // cache. | |
4996 AnalysisContextImpl_this._neededForResolution = null; | |
4997 } | |
4998 | |
4999 bool _addDependency(ResolvableLibrary dependant, Source dependency, | |
5000 List<ResolvableLibrary> dependencyList) { | |
5001 if (dependant.librarySource == dependency) { | |
5002 // Don't add a dependency of a library on itself; there's no point. | |
5003 return true; | |
5004 } | |
5005 ResolvableLibrary importedLibrary = _libraryMap[dependency]; | |
5006 if (importedLibrary == null) { | |
5007 importedLibrary = _createLibraryOrNull(dependency); | |
5008 if (importedLibrary != null) { | |
5009 _computeLibraryDependencies(importedLibrary); | |
5010 if (_taskData != null) { | |
5011 return false; | |
5012 } | |
5013 } | |
5014 } | |
5015 if (importedLibrary != null) { | |
5016 if (dependencyList != null) { | |
5017 dependencyList.add(importedLibrary); | |
5018 } | |
5019 _dependencyGraph.addEdge(dependant, importedLibrary); | |
5020 } | |
5021 return true; | |
5022 } | |
5023 | |
5024 /** | |
5025 * Recursively traverse the libraries reachable from the given [library], | |
5026 * creating instances of the class [Library] to represent them, and record the | |
5027 * references in the library objects. | |
5028 * | |
5029 * Throws an [AnalysisException] if some portion of the library graph could | |
5030 * not be traversed. | |
5031 */ | |
5032 void _computeLibraryDependencies(ResolvableLibrary library) { | |
5033 Source librarySource = library.librarySource; | |
5034 DartEntry dartEntry = | |
5035 AnalysisContextImpl_this._getReadableDartEntry(librarySource); | |
5036 List<Source> importedSources = | |
5037 _getSources(librarySource, dartEntry, DartEntry.IMPORTED_LIBRARIES); | |
5038 if (_taskData != null) { | |
5039 return; | |
5040 } | |
5041 List<Source> exportedSources = | |
5042 _getSources(librarySource, dartEntry, DartEntry.EXPORTED_LIBRARIES); | |
5043 if (_taskData != null) { | |
5044 return; | |
5045 } | |
5046 _computeLibraryDependenciesFromDirectives( | |
5047 library, importedSources, exportedSources); | |
5048 } | |
5049 | |
5050 /** | |
5051 * Recursively traverse the libraries reachable from the given [library], | |
5052 * creating instances of the class [Library] to represent them, and record the | |
5053 * references in the library objects. The [importedSources] is a list | |
5054 * containing the sources that are imported into the given library. The | |
5055 * [exportedSources] is a list containing the sources that are exported from | |
5056 * the given library. | |
5057 */ | |
5058 void _computeLibraryDependenciesFromDirectives(ResolvableLibrary library, | |
5059 List<Source> importedSources, List<Source> exportedSources) { | |
5060 int importCount = importedSources.length; | |
5061 List<ResolvableLibrary> importedLibraries = new List<ResolvableLibrary>(); | |
5062 bool explicitlyImportsCore = false; | |
5063 bool importsAsync = false; | |
5064 for (int i = 0; i < importCount; i++) { | |
5065 Source importedSource = importedSources[i]; | |
5066 if (importedSource == AnalysisContextImpl_this._coreLibrarySource) { | |
5067 explicitlyImportsCore = true; | |
5068 } else if (importedSource == | |
5069 AnalysisContextImpl_this._asyncLibrarySource) { | |
5070 importsAsync = true; | |
5071 } | |
5072 if (!_addDependency(library, importedSource, importedLibraries)) { | |
5073 return; | |
5074 } | |
5075 } | |
5076 library.explicitlyImportsCore = explicitlyImportsCore; | |
5077 if (!explicitlyImportsCore) { | |
5078 if (!_addDependency(library, AnalysisContextImpl_this._coreLibrarySource, | |
5079 importedLibraries)) { | |
5080 return; | |
5081 } | |
5082 } | |
5083 if (!importsAsync) { | |
5084 // Add a dependency on async to ensure that the Future element will be | |
5085 // built before we generate errors and warnings for async methods. Also | |
5086 // include it in importedLibraries, so that it will be picked up by | |
5087 // LibraryResolver2._buildLibraryMap(). | |
5088 // TODO(paulberry): this is a bit of a hack, since the async library | |
5089 // isn't actually being imported. Also, it's not clear whether it should | |
5090 // be necessary: in theory, dart:core already (indirectly) imports | |
5091 // dart:async, so if core has been built, async should have been built | |
5092 // too. However, removing this code causes unit test failures. | |
5093 if (!_addDependency(library, AnalysisContextImpl_this._asyncLibrarySource, | |
5094 importedLibraries)) { | |
5095 return; | |
5096 } | |
5097 } | |
5098 library.importedLibraries = importedLibraries; | |
5099 int exportCount = exportedSources.length; | |
5100 if (exportCount > 0) { | |
5101 List<ResolvableLibrary> exportedLibraries = new List<ResolvableLibrary>(); | |
5102 for (int i = 0; i < exportCount; i++) { | |
5103 Source exportedSource = exportedSources[i]; | |
5104 if (!_addDependency(library, exportedSource, exportedLibraries)) { | |
5105 return; | |
5106 } | |
5107 } | |
5108 library.exportedLibraries = exportedLibraries; | |
5109 } | |
5110 } | |
5111 | |
5112 /** | |
5113 * Gather the resolvable AST structures for each of the compilation units in | |
5114 * each of the libraries in the cycle. This is done in two phases: first we | |
5115 * ensure that we have cached an AST structure for each compilation unit, then | |
5116 * we gather them. We split the work this way because getting the AST | |
5117 * structures can change the state of the cache in such a way that we would | |
5118 * have more work to do if any compilation unit didn't have a resolvable AST | |
5119 * structure. | |
5120 */ | |
5121 void _computePartsInCycle(Source librarySource) { | |
5122 int count = _librariesInCycle.length; | |
5123 List<CycleBuilder_LibraryPair> libraryData = | |
5124 new List<CycleBuilder_LibraryPair>(); | |
5125 for (int i = 0; i < count; i++) { | |
5126 ResolvableLibrary library = _librariesInCycle[i]; | |
5127 libraryData.add(new CycleBuilder_LibraryPair( | |
5128 library, _ensurePartsInLibrary(library))); | |
5129 } | |
5130 AnalysisContextImpl_this._neededForResolution = _gatherSources(libraryData); | |
5131 if (AnalysisContextImpl._TRACE_PERFORM_TASK) { | |
5132 print( | |
5133 " preserve resolution data for ${AnalysisContextImpl_this._neededForR
esolution.length} sources while resolving ${librarySource.fullName}"); | |
5134 } | |
5135 if (_taskData != null) { | |
5136 return; | |
5137 } | |
5138 for (int i = 0; i < count; i++) { | |
5139 _computePartsInLibrary(libraryData[i]); | |
5140 } | |
5141 } | |
5142 | |
5143 /** | |
5144 * Gather the resolvable compilation units for each of the compilation units | |
5145 * in the library represented by the [libraryPair]. | |
5146 */ | |
5147 void _computePartsInLibrary(CycleBuilder_LibraryPair libraryPair) { | |
5148 ResolvableLibrary library = libraryPair.library; | |
5149 List<CycleBuilder_SourceEntryPair> entryPairs = libraryPair.entryPairs; | |
5150 int count = entryPairs.length; | |
5151 List<ResolvableCompilationUnit> units = | |
5152 new List<ResolvableCompilationUnit>(count); | |
5153 for (int i = 0; i < count; i++) { | |
5154 CycleBuilder_SourceEntryPair entryPair = entryPairs[i]; | |
5155 Source source = entryPair.source; | |
5156 DartEntry dartEntry = entryPair.entry; | |
5157 units[i] = new ResolvableCompilationUnit( | |
5158 source, dartEntry.resolvableCompilationUnit); | |
5159 } | |
5160 library.resolvableCompilationUnits = units; | |
5161 } | |
5162 | |
5163 /** | |
5164 * Create an object to represent the information about the library defined by | |
5165 * the compilation unit with the given [librarySource]. | |
5166 */ | |
5167 ResolvableLibrary _createLibrary(Source librarySource) { | |
5168 ResolvableLibrary library = new ResolvableLibrary(librarySource); | |
5169 SourceEntry sourceEntry = | |
5170 AnalysisContextImpl_this._cache.get(librarySource); | |
5171 if (sourceEntry is DartEntry) { | |
5172 LibraryElementImpl libraryElement = | |
5173 sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl; | |
5174 if (libraryElement != null) { | |
5175 library.libraryElement = libraryElement; | |
5176 } | |
5177 } | |
5178 _libraryMap[librarySource] = library; | |
5179 return library; | |
5180 } | |
5181 | |
5182 /** | |
5183 * Create an object to represent the information about the library defined by | |
5184 * the compilation unit with the given [librarySource]. | |
5185 */ | |
5186 ResolvableLibrary _createLibraryOrNull(Source librarySource) { | |
5187 ResolvableLibrary library = new ResolvableLibrary(librarySource); | |
5188 SourceEntry sourceEntry = | |
5189 AnalysisContextImpl_this._cache.get(librarySource); | |
5190 if (sourceEntry is DartEntry) { | |
5191 LibraryElementImpl libraryElement = | |
5192 sourceEntry.getValue(DartEntry.ELEMENT) as LibraryElementImpl; | |
5193 if (libraryElement != null) { | |
5194 library.libraryElement = libraryElement; | |
5195 } | |
5196 } | |
5197 _libraryMap[librarySource] = library; | |
5198 return library; | |
5199 } | |
5200 | |
5201 /** | |
5202 * Ensure that the given [library] has an element model built for it. If | |
5203 * another task needs to be executed first in order to build the element | |
5204 * model, that task is placed in [taskData]. | |
5205 */ | |
5206 void _ensureElementModel(ResolvableLibrary library) { | |
5207 Source librarySource = library.librarySource; | |
5208 DartEntry libraryEntry = | |
5209 AnalysisContextImpl_this._getReadableDartEntry(librarySource); | |
5210 if (libraryEntry != null && | |
5211 libraryEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) { | |
5212 AnalysisContextImpl_this._workManager.addFirst( | |
5213 librarySource, SourcePriority.LIBRARY); | |
5214 if (_taskData == null) { | |
5215 _taskData = AnalysisContextImpl_this._createResolveDartLibraryTask( | |
5216 librarySource, libraryEntry); | |
5217 } | |
5218 } | |
5219 } | |
5220 | |
5221 /** | |
5222 * Ensure that all of the libraries that are exported by the given [library] | |
5223 * (but are not themselves in the cycle) have element models built for them. | |
5224 * If another task needs to be executed first in order to build the element | |
5225 * model, that task is placed in [taskData]. | |
5226 */ | |
5227 void _ensureExports( | |
5228 ResolvableLibrary library, HashSet<Source> visitedLibraries) { | |
5229 List<ResolvableLibrary> dependencies = library.exports; | |
5230 int dependencyCount = dependencies.length; | |
5231 for (int i = 0; i < dependencyCount; i++) { | |
5232 ResolvableLibrary dependency = dependencies[i]; | |
5233 if (!_librariesInCycle.contains(dependency) && | |
5234 visitedLibraries.add(dependency.librarySource)) { | |
5235 if (dependency.libraryElement == null) { | |
5236 _ensureElementModel(dependency); | |
5237 } else { | |
5238 _ensureExports(dependency, visitedLibraries); | |
5239 } | |
5240 if (_taskData != null) { | |
5241 return; | |
5242 } | |
5243 } | |
5244 } | |
5245 } | |
5246 | |
5247 /** | |
5248 * Ensure that all of the libraries that are exported by the given [library] | |
5249 * (but are not themselves in the cycle) have element models built for them. | |
5250 * If another task needs to be executed first in order to build the element | |
5251 * model, that task is placed in [taskData]. | |
5252 */ | |
5253 void _ensureImports(ResolvableLibrary library) { | |
5254 List<ResolvableLibrary> dependencies = library.imports; | |
5255 int dependencyCount = dependencies.length; | |
5256 for (int i = 0; i < dependencyCount; i++) { | |
5257 ResolvableLibrary dependency = dependencies[i]; | |
5258 if (!_librariesInCycle.contains(dependency) && | |
5259 dependency.libraryElement == null) { | |
5260 _ensureElementModel(dependency); | |
5261 if (_taskData != null) { | |
5262 return; | |
5263 } | |
5264 } | |
5265 } | |
5266 } | |
5267 | |
5268 /** | |
5269 * Ensure that all of the libraries that are either imported or exported by | |
5270 * libraries in the cycle (but are not themselves in the cycle) have element | |
5271 * models built for them. | |
5272 */ | |
5273 void _ensureImportsAndExports() { | |
5274 HashSet<Source> visitedLibraries = new HashSet<Source>(); | |
5275 int libraryCount = _librariesInCycle.length; | |
5276 for (int i = 0; i < libraryCount; i++) { | |
5277 ResolvableLibrary library = _librariesInCycle[i]; | |
5278 _ensureImports(library); | |
5279 if (_taskData != null) { | |
5280 return; | |
5281 } | |
5282 _ensureExports(library, visitedLibraries); | |
5283 if (_taskData != null) { | |
5284 return; | |
5285 } | |
5286 } | |
5287 } | |
5288 | |
5289 /** | |
5290 * Ensure that there is a resolvable compilation unit available for all of the | |
5291 * compilation units in the given [library]. | |
5292 */ | |
5293 List<CycleBuilder_SourceEntryPair> _ensurePartsInLibrary( | |
5294 ResolvableLibrary library) { | |
5295 List<CycleBuilder_SourceEntryPair> pairs = | |
5296 new List<CycleBuilder_SourceEntryPair>(); | |
5297 Source librarySource = library.librarySource; | |
5298 DartEntry libraryEntry = | |
5299 AnalysisContextImpl_this._getReadableDartEntry(librarySource); | |
5300 if (libraryEntry == null) { | |
5301 throw new AnalysisException( | |
5302 "Cannot find entry for ${librarySource.fullName}"); | |
5303 } else if (libraryEntry.getState(DartEntry.PARSED_UNIT) == | |
5304 CacheState.ERROR) { | |
5305 String message = | |
5306 "Cannot compute parsed unit for ${librarySource.fullName}"; | |
5307 CaughtException exception = libraryEntry.exception; | |
5308 if (exception == null) { | |
5309 throw new AnalysisException(message); | |
5310 } | |
5311 throw new AnalysisException( | |
5312 message, new CaughtException(exception, null)); | |
5313 } | |
5314 _ensureResolvableCompilationUnit(librarySource, libraryEntry); | |
5315 pairs.add(new CycleBuilder_SourceEntryPair(librarySource, libraryEntry)); | |
5316 List<Source> partSources = | |
5317 _getSources(librarySource, libraryEntry, DartEntry.INCLUDED_PARTS); | |
5318 int count = partSources.length; | |
5319 for (int i = 0; i < count; i++) { | |
5320 Source partSource = partSources[i]; | |
5321 DartEntry partEntry = | |
5322 AnalysisContextImpl_this._getReadableDartEntry(partSource); | |
5323 if (partEntry != null && | |
5324 partEntry.getState(DartEntry.PARSED_UNIT) != CacheState.ERROR) { | |
5325 _ensureResolvableCompilationUnit(partSource, partEntry); | |
5326 pairs.add(new CycleBuilder_SourceEntryPair(partSource, partEntry)); | |
5327 } | |
5328 } | |
5329 return pairs; | |
5330 } | |
5331 | |
5332 /** | |
5333 * Ensure that there is a resolvable compilation unit available for the given | |
5334 * [source]. | |
5335 */ | |
5336 void _ensureResolvableCompilationUnit(Source source, DartEntry dartEntry) { | |
5337 // The entry will be null if the source represents a non-Dart file. | |
5338 if (dartEntry != null && !dartEntry.hasResolvableCompilationUnit) { | |
5339 if (_taskData == null) { | |
5340 _taskData = | |
5341 AnalysisContextImpl_this._createParseDartTask(source, dartEntry); | |
5342 } | |
5343 } | |
5344 } | |
5345 | |
5346 HashSet<Source> _gatherSources(List<CycleBuilder_LibraryPair> libraryData) { | |
5347 int libraryCount = libraryData.length; | |
5348 HashSet<Source> sources = new HashSet<Source>(); | |
5349 for (int i = 0; i < libraryCount; i++) { | |
5350 List<CycleBuilder_SourceEntryPair> entryPairs = libraryData[i].entryPairs; | |
5351 int entryCount = entryPairs.length; | |
5352 for (int j = 0; j < entryCount; j++) { | |
5353 sources.add(entryPairs[j].source); | |
5354 } | |
5355 } | |
5356 return sources; | |
5357 } | |
5358 | |
5359 /** | |
5360 * Return the sources described by the given [descriptor]. | |
5361 */ | |
5362 List<Source> _getSources(Source source, DartEntry dartEntry, | |
5363 DataDescriptor<List<Source>> descriptor) { | |
5364 if (dartEntry == null) { | |
5365 return Source.EMPTY_LIST; | |
5366 } | |
5367 CacheState exportState = dartEntry.getState(descriptor); | |
5368 if (exportState == CacheState.ERROR) { | |
5369 return Source.EMPTY_LIST; | |
5370 } else if (exportState != CacheState.VALID) { | |
5371 if (_taskData == null) { | |
5372 _taskData = | |
5373 AnalysisContextImpl_this._createParseDartTask(source, dartEntry); | |
5374 } | |
5375 return Source.EMPTY_LIST; | |
5376 } | |
5377 return dartEntry.getValue(descriptor); | |
5378 } | |
5379 } | |
5380 | |
5381 /** | |
5382 * Information about the next task to be performed. Each data has an implicit | |
5383 * associated source: the source that might need to be analyzed. There are | |
5384 * essentially three states that can be represented: | |
5385 * | |
5386 * * If [getTask] returns a non-`null` value, then that is the task that should | |
5387 * be executed to further analyze the associated source. | |
5388 * * Otherwise, if [isBlocked] returns `true`, then there is no work that can be | |
5389 * done, but analysis for the associated source is not complete. | |
5390 * * Otherwise, [getDependentSource] should return a source that needs to be | |
5391 * analyzed before the analysis of the associated source can be completed. | |
5392 */ | |
5393 class AnalysisContextImpl_TaskData { | |
5394 /** | |
5395 * The task that is to be performed. | |
5396 */ | |
5397 final AnalysisTask task; | |
5398 | |
5399 /** | |
5400 * A flag indicating whether the associated source is blocked waiting for its | |
5401 * contents to be loaded. | |
5402 */ | |
5403 final bool _blocked; | |
5404 | |
5405 /** | |
5406 * Initialize a newly created data holder. | |
5407 */ | |
5408 AnalysisContextImpl_TaskData(this.task, this._blocked); | |
5409 | |
5410 /** | |
5411 * Return `true` if the associated source is blocked waiting for its contents | |
5412 * to be loaded. | |
5413 */ | |
5414 bool get isBlocked => _blocked; | |
5415 | |
5416 @override | |
5417 String toString() { | |
5418 if (task == null) { | |
5419 return "blocked: $_blocked"; | |
5420 } | |
5421 return task.toString(); | |
5422 } | |
5423 } | |
5424 | |
5425 /** | |
5426 * Statistics and information about a single [AnalysisContext]. | |
5427 */ | |
5428 abstract class AnalysisContextStatistics { | |
5429 /** | |
5430 * Return the statistics for each kind of cached data. | |
5431 */ | |
5432 List<AnalysisContextStatistics_CacheRow> get cacheRows; | |
5433 | |
5434 /** | |
5435 * Return the exceptions that caused some entries to have a state of | |
5436 * [CacheState.ERROR]. | |
5437 */ | |
5438 List<CaughtException> get exceptions; | |
5439 | |
5440 /** | |
5441 * Return information about each of the partitions in the cache. | |
5442 */ | |
5443 List<AnalysisContextStatistics_PartitionData> get partitionData; | |
5444 | |
5445 /** | |
5446 * Return a list containing all of the sources in the cache. | |
5447 */ | |
5448 List<Source> get sources; | |
5449 } | |
5450 | |
5451 /** | |
5452 * Information about single piece of data in the cache. | |
5453 */ | |
5454 abstract class AnalysisContextStatistics_CacheRow { | |
5455 /** | |
5456 * List of possible states which can be queried. | |
5457 */ | |
5458 static const List<CacheState> STATES = const <CacheState>[ | |
5459 CacheState.ERROR, | |
5460 CacheState.FLUSHED, | |
5461 CacheState.IN_PROCESS, | |
5462 CacheState.INVALID, | |
5463 CacheState.VALID | |
5464 ]; | |
5465 | |
5466 /** | |
5467 * Return the number of entries whose state is [CacheState.ERROR]. | |
5468 */ | |
5469 int get errorCount; | |
5470 | |
5471 /** | |
5472 * Return the number of entries whose state is [CacheState.FLUSHED]. | |
5473 */ | |
5474 int get flushedCount; | |
5475 | |
5476 /** | |
5477 * Return the number of entries whose state is [CacheState.IN_PROCESS]. | |
5478 */ | |
5479 int get inProcessCount; | |
5480 | |
5481 /** | |
5482 * Return the number of entries whose state is [CacheState.INVALID]. | |
5483 */ | |
5484 int get invalidCount; | |
5485 | |
5486 /** | |
5487 * Return the name of the data represented by this object. | |
5488 */ | |
5489 String get name; | |
5490 | |
5491 /** | |
5492 * Return the number of entries whose state is [CacheState.VALID]. | |
5493 */ | |
5494 int get validCount; | |
5495 | |
5496 /** | |
5497 * Return the number of entries whose state is [state]. | |
5498 */ | |
5499 int getCount(CacheState state); | |
5500 } | |
5501 | |
5502 /** | |
5503 * Information about a single partition in the cache. | |
5504 */ | |
5505 abstract class AnalysisContextStatistics_PartitionData { | |
5506 /** | |
5507 * Return the number of entries in the partition that have an AST structure in | |
5508 * one state or another. | |
5509 */ | |
5510 int get astCount; | |
5511 | |
5512 /** | |
5513 * Return the total number of entries in the partition. | |
5514 */ | |
5515 int get totalCount; | |
5516 } | |
5517 | |
5518 /** | |
5519 * Implementation of the [AnalysisContextStatistics]. | |
5520 */ | |
5521 class AnalysisContextStatisticsImpl implements AnalysisContextStatistics { | |
5522 Map<String, AnalysisContextStatistics_CacheRow> _dataMap = | |
5523 new HashMap<String, AnalysisContextStatistics_CacheRow>(); | |
5524 | |
5525 List<Source> _sources = new List<Source>(); | |
5526 | |
5527 HashSet<CaughtException> _exceptions = new HashSet<CaughtException>(); | |
5528 | |
5529 List<AnalysisContextStatistics_PartitionData> _partitionData; | |
5530 | |
5531 @override | |
5532 List<AnalysisContextStatistics_CacheRow> get cacheRows => | |
5533 _dataMap.values.toList(); | |
5534 | |
5535 @override | |
5536 List<CaughtException> get exceptions => new List.from(_exceptions); | |
5537 | |
5538 @override | |
5539 List<AnalysisContextStatistics_PartitionData> get partitionData => | |
5540 _partitionData; | |
5541 | |
5542 /** | |
5543 * Set the partition data returned by this object to the given data. | |
5544 */ | |
5545 void set partitionData(List<AnalysisContextStatistics_PartitionData> data) { | |
5546 _partitionData = data; | |
5547 } | |
5548 | |
5549 @override | |
5550 List<Source> get sources => _sources; | |
5551 | |
5552 void addSource(Source source) { | |
5553 _sources.add(source); | |
5554 } | |
5555 | |
5556 void _internalPutCacheItem(Source source, SourceEntry dartEntry, | |
5557 DataDescriptor rowDesc, CacheState state) { | |
5558 String rowName = rowDesc.toString(); | |
5559 AnalysisContextStatisticsImpl_CacheRowImpl row = | |
5560 _dataMap[rowName] as AnalysisContextStatisticsImpl_CacheRowImpl; | |
5561 if (row == null) { | |
5562 row = new AnalysisContextStatisticsImpl_CacheRowImpl(rowName); | |
5563 _dataMap[rowName] = row; | |
5564 } | |
5565 row._incState(state); | |
5566 if (state == CacheState.ERROR) { | |
5567 CaughtException exception = dartEntry.exception; | |
5568 if (exception != null) { | |
5569 _exceptions.add(exception); | |
5570 } | |
5571 } | |
5572 } | |
5573 } | |
5574 | |
5575 class AnalysisContextStatisticsImpl_CacheRowImpl | |
5576 implements AnalysisContextStatistics_CacheRow { | |
5577 final String name; | |
5578 | |
5579 Map<CacheState, int> _counts = <CacheState, int>{}; | |
5580 | |
5581 AnalysisContextStatisticsImpl_CacheRowImpl(this.name); | |
5582 | |
5583 @override | |
5584 int get errorCount => getCount(CacheState.ERROR); | |
5585 | |
5586 @override | |
5587 int get flushedCount => getCount(CacheState.FLUSHED); | |
5588 | |
5589 @override | |
5590 int get hashCode => name.hashCode; | |
5591 | |
5592 @override | |
5593 int get inProcessCount => getCount(CacheState.IN_PROCESS); | |
5594 | |
5595 @override | |
5596 int get invalidCount => getCount(CacheState.INVALID); | |
5597 | |
5598 @override | |
5599 int get validCount => getCount(CacheState.VALID); | |
5600 | |
5601 @override | |
5602 bool operator ==(Object obj) => | |
5603 obj is AnalysisContextStatisticsImpl_CacheRowImpl && obj.name == name; | |
5604 | |
5605 @override | |
5606 int getCount(CacheState state) { | |
5607 int count = _counts[state]; | |
5608 if (count != null) { | |
5609 return count; | |
5610 } else { | |
5611 return 0; | |
5612 } | |
5613 } | |
5614 | |
5615 void _incState(CacheState state) { | |
5616 if (_counts[state] == null) { | |
5617 _counts[state] = 1; | |
5618 } else { | |
5619 _counts[state]++; | |
5620 } | |
5621 } | |
5622 } | |
5623 | |
5624 class AnalysisContextStatisticsImpl_PartitionDataImpl | |
5625 implements AnalysisContextStatistics_PartitionData { | |
5626 final int astCount; | |
5627 | |
5628 final int totalCount; | |
5629 | |
5630 AnalysisContextStatisticsImpl_PartitionDataImpl( | |
5631 this.astCount, this.totalCount); | |
5632 } | |
5633 | |
5634 /** | |
5635 * A representation of changes to the types of analysis that should be | |
5636 * performed. | |
5637 */ | |
5638 class AnalysisDelta { | |
5639 /** | |
5640 * A mapping from source to what type of analysis should be performed on that | |
5641 * source. | |
5642 */ | |
5643 HashMap<Source, AnalysisLevel> _analysisMap = | |
5644 new HashMap<Source, AnalysisLevel>(); | |
5645 | |
5646 /** | |
5647 * Return a collection of the sources that have been added. This is equivalent | |
5648 * to calling [getAnalysisLevels] and collecting all sources that do not have | |
5649 * an analysis level of [AnalysisLevel.NONE]. | |
5650 */ | |
5651 List<Source> get addedSources { | |
5652 List<Source> result = new List<Source>(); | |
5653 _analysisMap.forEach((Source source, AnalysisLevel level) { | |
5654 if (level != AnalysisLevel.NONE) { | |
5655 result.add(source); | |
5656 } | |
5657 }); | |
5658 return result; | |
5659 } | |
5660 | |
5661 /** | |
5662 * Return a mapping of sources to the level of analysis that should be | |
5663 * performed. | |
5664 */ | |
5665 Map<Source, AnalysisLevel> get analysisLevels => _analysisMap; | |
5666 | |
5667 /** | |
5668 * Record that the given [source] should be analyzed at the given [level]. | |
5669 */ | |
5670 void setAnalysisLevel(Source source, AnalysisLevel level) { | |
5671 _analysisMap[source] = level; | |
5672 } | |
5673 | |
5674 @override | |
5675 String toString() { | |
5676 StringBuffer buffer = new StringBuffer(); | |
5677 bool needsSeparator = _appendSources(buffer, false, AnalysisLevel.ALL); | |
5678 needsSeparator = | |
5679 _appendSources(buffer, needsSeparator, AnalysisLevel.RESOLVED); | |
5680 _appendSources(buffer, needsSeparator, AnalysisLevel.NONE); | |
5681 return buffer.toString(); | |
5682 } | |
5683 | |
5684 /** | |
5685 * Appendto the given [buffer] all sources with the given analysis [level], | |
5686 * prefixed with a label and a separator if [needsSeparator] is `true`. | |
5687 */ | |
5688 bool _appendSources( | |
5689 StringBuffer buffer, bool needsSeparator, AnalysisLevel level) { | |
5690 bool first = true; | |
5691 _analysisMap.forEach((Source source, AnalysisLevel sourceLevel) { | |
5692 if (sourceLevel == level) { | |
5693 if (first) { | |
5694 first = false; | |
5695 if (needsSeparator) { | |
5696 buffer.write("; "); | |
5697 } | |
5698 buffer.write(level); | |
5699 buffer.write(" "); | |
5700 } else { | |
5701 buffer.write(", "); | |
5702 } | |
5703 buffer.write(source.fullName); | |
5704 } | |
5705 }); | |
5706 return needsSeparator || !first; | |
5707 } | |
5708 } | |
5709 | |
5710 /** | |
5711 * The entry point for the functionality provided by the analysis engine. There | |
5712 * is a single instance of this class. | |
5713 */ | |
5714 class AnalysisEngine { | |
5715 /** | |
5716 * The suffix used for Dart source files. | |
5717 */ | |
5718 static const String SUFFIX_DART = "dart"; | |
5719 | |
5720 /** | |
5721 * The short suffix used for HTML files. | |
5722 */ | |
5723 static const String SUFFIX_HTM = "htm"; | |
5724 | |
5725 /** | |
5726 * The long suffix used for HTML files. | |
5727 */ | |
5728 static const String SUFFIX_HTML = "html"; | |
5729 | |
5730 /** | |
5731 * The unique instance of this class. | |
5732 */ | |
5733 static final AnalysisEngine instance = new AnalysisEngine._(); | |
5734 | |
5735 /** | |
5736 * The logger that should receive information about errors within the analysis | |
5737 * engine. | |
5738 */ | |
5739 Logger _logger = Logger.NULL; | |
5740 | |
5741 /** | |
5742 * The plugin that defines the extension points and extensions that are define
d by | |
5743 * command-line applications using the analysis engine. | |
5744 */ | |
5745 final CommandLinePlugin commandLinePlugin = new CommandLinePlugin(); | |
5746 | |
5747 /** | |
5748 * The plugin that defines the extension points and extensions that are | |
5749 * inherently defined by the analysis engine. | |
5750 */ | |
5751 final EnginePlugin enginePlugin = new EnginePlugin(); | |
5752 | |
5753 /*** | |
5754 * The plugin that defines the extension points and extensions that are define
d | |
5755 * by applications that want to consume options defined in the analysis | |
5756 * options file. | |
5757 */ | |
5758 final OptionsPlugin optionsPlugin = new OptionsPlugin(); | |
5759 | |
5760 /** | |
5761 * The instrumentation service that is to be used by this analysis engine. | |
5762 */ | |
5763 InstrumentationService _instrumentationService = | |
5764 InstrumentationService.NULL_SERVICE; | |
5765 | |
5766 /** | |
5767 * The list of supported plugins for processing by clients. | |
5768 */ | |
5769 List<Plugin> _supportedPlugins; | |
5770 | |
5771 /** | |
5772 * The partition manager being used to manage the shared partitions. | |
5773 */ | |
5774 final PartitionManager partitionManager = new PartitionManager(); | |
5775 | |
5776 /** | |
5777 * The partition manager being used to manage the shared partitions. | |
5778 */ | |
5779 final newContext.PartitionManager partitionManager_new = | |
5780 new newContext.PartitionManager(); | |
5781 | |
5782 /** | |
5783 * A flag indicating whether the (new) task model should be used to perform | |
5784 * analysis. | |
5785 */ | |
5786 bool useTaskModel = false; | |
5787 | |
5788 /** | |
5789 * A flag indicating whether the task model should attempt to limit | |
5790 * invalidation after a change. | |
5791 */ | |
5792 bool limitInvalidationInTaskModel = false; | |
5793 | |
5794 /** | |
5795 * The task manager used to manage the tasks used to analyze code. | |
5796 */ | |
5797 TaskManager _taskManager; | |
5798 | |
5799 AnalysisEngine._(); | |
5800 | |
5801 /** | |
5802 * Return the instrumentation service that is to be used by this analysis | |
5803 * engine. | |
5804 */ | |
5805 InstrumentationService get instrumentationService => _instrumentationService; | |
5806 | |
5807 /** | |
5808 * Set the instrumentation service that is to be used by this analysis engine | |
5809 * to the given [service]. | |
5810 */ | |
5811 void set instrumentationService(InstrumentationService service) { | |
5812 if (service == null) { | |
5813 _instrumentationService = InstrumentationService.NULL_SERVICE; | |
5814 } else { | |
5815 _instrumentationService = service; | |
5816 } | |
5817 } | |
5818 | |
5819 /** | |
5820 * Return the logger that should receive information about errors within the | |
5821 * analysis engine. | |
5822 */ | |
5823 Logger get logger => _logger; | |
5824 | |
5825 /** | |
5826 * Set the logger that should receive information about errors within the | |
5827 * analysis engine to the given [logger]. | |
5828 */ | |
5829 void set logger(Logger logger) { | |
5830 this._logger = logger == null ? Logger.NULL : logger; | |
5831 } | |
5832 | |
5833 /** | |
5834 * Return the list of supported plugins for processing by clients. | |
5835 */ | |
5836 List<Plugin> get supportedPlugins { | |
5837 if (_supportedPlugins == null) { | |
5838 _supportedPlugins = <Plugin>[ | |
5839 enginePlugin, | |
5840 commandLinePlugin, | |
5841 optionsPlugin | |
5842 ]; | |
5843 } | |
5844 return _supportedPlugins; | |
5845 } | |
5846 | |
5847 /** | |
5848 * Return the task manager used to manage the tasks used to analyze code. | |
5849 */ | |
5850 TaskManager get taskManager { | |
5851 if (_taskManager == null) { | |
5852 if (enginePlugin.taskExtensionPoint == null) { | |
5853 // The plugin wasn't used, so tasks are not registered. | |
5854 new ExtensionManager().processPlugins([enginePlugin]); | |
5855 } | |
5856 _taskManager = new TaskManager(); | |
5857 _taskManager.addTaskDescriptors(enginePlugin.taskDescriptors); | |
5858 // TODO(brianwilkerson) Create a way to associate different results with | |
5859 // different file suffixes, then make this pluggable. | |
5860 _taskManager.addGeneralResult(DART_ERRORS); | |
5861 } | |
5862 return _taskManager; | |
5863 } | |
5864 | |
5865 /** | |
5866 * Clear any caches holding on to analysis results so that a full re-analysis | |
5867 * will be performed the next time an analysis context is created. | |
5868 */ | |
5869 void clearCaches() { | |
5870 partitionManager.clearCache(); | |
5871 } | |
5872 | |
5873 /** | |
5874 * Create and return a new context in which analysis can be performed. | |
5875 */ | |
5876 AnalysisContext createAnalysisContext() { | |
5877 if (useTaskModel) { | |
5878 return new newContext.AnalysisContextImpl(); | |
5879 } | |
5880 return new AnalysisContextImpl(); | |
5881 } | |
5882 | |
5883 /** | |
5884 * Return `true` if the given [fileName] is assumed to contain Dart source | |
5885 * code. | |
5886 */ | |
5887 static bool isDartFileName(String fileName) { | |
5888 if (fileName == null) { | |
5889 return false; | |
5890 } | |
5891 return javaStringEqualsIgnoreCase( | |
5892 FileNameUtilities.getExtension(fileName), SUFFIX_DART); | |
5893 } | |
5894 | |
5895 /** | |
5896 * Return `true` if the given [fileName] is assumed to contain HTML. | |
5897 */ | |
5898 static bool isHtmlFileName(String fileName) { | |
5899 if (fileName == null) { | |
5900 return false; | |
5901 } | |
5902 String extension = FileNameUtilities.getExtension(fileName); | |
5903 return javaStringEqualsIgnoreCase(extension, SUFFIX_HTML) || | |
5904 javaStringEqualsIgnoreCase(extension, SUFFIX_HTM); | |
5905 } | |
5906 } | |
5907 | |
5908 /** | |
5909 * The analysis errors and line information for the errors. | |
5910 */ | |
5911 abstract class AnalysisErrorInfo { | |
5912 /** | |
5913 * Return the errors that as a result of the analysis, or `null` if there were | |
5914 * no errors. | |
5915 */ | |
5916 List<AnalysisError> get errors; | |
5917 | |
5918 /** | |
5919 * Return the line information associated with the errors, or `null` if there | |
5920 * were no errors. | |
5921 */ | |
5922 LineInfo get lineInfo; | |
5923 } | |
5924 | |
5925 /** | |
5926 * The analysis errors and line info associated with a source. | |
5927 */ | |
5928 class AnalysisErrorInfoImpl implements AnalysisErrorInfo { | |
5929 /** | |
5930 * The analysis errors associated with a source, or `null` if there are no | |
5931 * errors. | |
5932 */ | |
5933 final List<AnalysisError> errors; | |
5934 | |
5935 /** | |
5936 * The line information associated with the errors, or `null` if there are no | |
5937 * errors. | |
5938 */ | |
5939 final LineInfo lineInfo; | |
5940 | |
5941 /** | |
5942 * Initialize an newly created error info with the given [errors] and | |
5943 * [lineInfo]. | |
5944 */ | |
5945 AnalysisErrorInfoImpl(this.errors, this.lineInfo); | |
5946 } | |
5947 | |
5948 /** | |
5949 * The levels at which a source can be analyzed. | |
5950 */ | |
5951 class AnalysisLevel extends Enum<AnalysisLevel> { | |
5952 /** | |
5953 * Indicates a source should be fully analyzed. | |
5954 */ | |
5955 static const AnalysisLevel ALL = const AnalysisLevel('ALL', 0); | |
5956 | |
5957 /** | |
5958 * Indicates a source should be resolved and that errors, warnings and hints a
re needed. | |
5959 */ | |
5960 static const AnalysisLevel ERRORS = const AnalysisLevel('ERRORS', 1); | |
5961 | |
5962 /** | |
5963 * Indicates a source should be resolved, but that errors, warnings and hints
are not needed. | |
5964 */ | |
5965 static const AnalysisLevel RESOLVED = const AnalysisLevel('RESOLVED', 2); | |
5966 | |
5967 /** | |
5968 * Indicates a source is not of interest to the client. | |
5969 */ | |
5970 static const AnalysisLevel NONE = const AnalysisLevel('NONE', 3); | |
5971 | |
5972 static const List<AnalysisLevel> values = const [ALL, ERRORS, RESOLVED, NONE]; | |
5973 | |
5974 const AnalysisLevel(String name, int ordinal) : super(name, ordinal); | |
5975 } | |
5976 | |
5977 /** | |
5978 * An object that is listening for results being produced by an analysis | |
5979 * context. | |
5980 */ | |
5981 abstract class AnalysisListener { | |
5982 /** | |
5983 * Reports that a task, described by the given [taskDescription] is about to | |
5984 * be performed by the given [context]. | |
5985 */ | |
5986 void aboutToPerformTask(AnalysisContext context, String taskDescription); | |
5987 | |
5988 /** | |
5989 * Reports that the [errors] associated with the given [source] in the given | |
5990 * [context] has been updated to the given errors. The [lineInfo] is the line | |
5991 * information associated with the source. | |
5992 */ | |
5993 void computedErrors(AnalysisContext context, Source source, | |
5994 List<AnalysisError> errors, LineInfo lineInfo); | |
5995 | |
5996 /** | |
5997 * Reports that the given [source] is no longer included in the set of sources | |
5998 * that are being analyzed by the given analysis [context]. | |
5999 */ | |
6000 void excludedSource(AnalysisContext context, Source source); | |
6001 | |
6002 /** | |
6003 * Reports that the given [source] is now included in the set of sources that | |
6004 * are being analyzed by the given analysis [context]. | |
6005 */ | |
6006 void includedSource(AnalysisContext context, Source source); | |
6007 | |
6008 /** | |
6009 * Reports that the given Dart [source] was parsed in the given [context], | |
6010 * producing the given [unit]. | |
6011 */ | |
6012 void parsedDart(AnalysisContext context, Source source, CompilationUnit unit); | |
6013 | |
6014 /** | |
6015 * Reports that the given HTML [source] was parsed in the given [context]. | |
6016 */ | |
6017 @deprecated | |
6018 void parsedHtml(AnalysisContext context, Source source, ht.HtmlUnit unit); | |
6019 | |
6020 /** | |
6021 * Reports that the given Dart [source] was resolved in the given [context]. | |
6022 */ | |
6023 void resolvedDart( | |
6024 AnalysisContext context, Source source, CompilationUnit unit); | |
6025 | |
6026 /** | |
6027 * Reports that the given HTML [source] was resolved in the given [context]. | |
6028 */ | |
6029 @deprecated | |
6030 void resolvedHtml(AnalysisContext context, Source source, ht.HtmlUnit unit); | |
6031 } | |
6032 | |
6033 /** | |
6034 * Futures returned by [AnalysisContext] for pending analysis results will | |
6035 * complete with this error if it is determined that analysis results will | |
6036 * never become available (e.g. because the requested source is not subject to | |
6037 * analysis, or because the requested source is a part file which is not a part | |
6038 * of any known library). | |
6039 */ | |
6040 class AnalysisNotScheduledError implements Exception {} | |
6041 | |
6042 /** | |
6043 * A set of analysis options used to control the behavior of an analysis | |
6044 * context. | |
6045 */ | |
6046 abstract class AnalysisOptions { | |
6047 /** | |
6048 * If analysis is to parse and analyze all function bodies, return `true`. | |
6049 * If analysis is to skip all function bodies, return `false`. If analysis | |
6050 * is to parse and analyze function bodies in some sources and not in others, | |
6051 * throw an exception. | |
6052 * | |
6053 * This getter is deprecated; consider using [analyzeFunctionBodiesPredicate] | |
6054 * instead. | |
6055 */ | |
6056 @deprecated // Use this.analyzeFunctionBodiesPredicate | |
6057 bool get analyzeFunctionBodies; | |
6058 | |
6059 /** | |
6060 * Function that returns `true` if analysis is to parse and analyze function | |
6061 * bodies for a given source. | |
6062 */ | |
6063 AnalyzeFunctionBodiesPredicate get analyzeFunctionBodiesPredicate; | |
6064 | |
6065 /** | |
6066 * Return the maximum number of sources for which AST structures should be | |
6067 * kept in the cache. | |
6068 */ | |
6069 int get cacheSize; | |
6070 | |
6071 /** | |
6072 * Return `true` if analysis is to generate dart2js related hint results. | |
6073 */ | |
6074 bool get dart2jsHint; | |
6075 | |
6076 /** | |
6077 * Return `true` if analysis is to include the new async support. | |
6078 */ | |
6079 @deprecated // Always true | |
6080 bool get enableAsync; | |
6081 | |
6082 /** | |
6083 * Return `true` if analysis is to include the new deferred loading support. | |
6084 */ | |
6085 @deprecated // Always true | |
6086 bool get enableDeferredLoading; | |
6087 | |
6088 /** | |
6089 * Return `true` if analysis is to include the new enum support. | |
6090 */ | |
6091 @deprecated // Always true | |
6092 bool get enableEnum; | |
6093 | |
6094 /** | |
6095 * Return `true` to enable generic methods (DEP 22). | |
6096 */ | |
6097 bool get enableGenericMethods => null; | |
6098 | |
6099 /** | |
6100 * Return `true` to enable null-aware operators (DEP 9). | |
6101 */ | |
6102 @deprecated // Always true | |
6103 bool get enableNullAwareOperators; | |
6104 | |
6105 /** | |
6106 * Return `true` to strictly follow the specification when generating | |
6107 * warnings on "call" methods (fixes dartbug.com/21938). | |
6108 */ | |
6109 bool get enableStrictCallChecks; | |
6110 | |
6111 /** | |
6112 * Return `true` if errors, warnings and hints should be generated for sources | |
6113 * that are implicitly being analyzed. The default value is `true`. | |
6114 */ | |
6115 bool get generateImplicitErrors; | |
6116 | |
6117 /** | |
6118 * Return `true` if errors, warnings and hints should be generated for sources | |
6119 * in the SDK. The default value is `false`. | |
6120 */ | |
6121 bool get generateSdkErrors; | |
6122 | |
6123 /** | |
6124 * Return `true` if analysis is to generate hint results (e.g. type inference | |
6125 * based information and pub best practices). | |
6126 */ | |
6127 bool get hint; | |
6128 | |
6129 /** | |
6130 * Return `true` if incremental analysis should be used. | |
6131 */ | |
6132 bool get incremental; | |
6133 | |
6134 /** | |
6135 * A flag indicating whether incremental analysis should be used for API | |
6136 * changes. | |
6137 */ | |
6138 bool get incrementalApi; | |
6139 | |
6140 /** | |
6141 * A flag indicating whether validation should be performed after incremental | |
6142 * analysis. | |
6143 */ | |
6144 bool get incrementalValidation; | |
6145 | |
6146 /** | |
6147 * Return `true` if analysis is to generate lint warnings. | |
6148 */ | |
6149 bool get lint; | |
6150 | |
6151 /** | |
6152 * Return `true` if analysis is to parse comments. | |
6153 */ | |
6154 bool get preserveComments; | |
6155 } | |
6156 | |
6157 /** | |
6158 * A set of analysis options used to control the behavior of an analysis | |
6159 * context. | |
6160 */ | |
6161 class AnalysisOptionsImpl implements AnalysisOptions { | |
6162 /** | |
6163 * The maximum number of sources for which data should be kept in the cache. | |
6164 */ | |
6165 static const int DEFAULT_CACHE_SIZE = 64; | |
6166 | |
6167 /** | |
6168 * The default value for enabling deferred loading. | |
6169 */ | |
6170 @deprecated | |
6171 static bool DEFAULT_ENABLE_DEFERRED_LOADING = true; | |
6172 | |
6173 /** | |
6174 * The default value for enabling enum support. | |
6175 */ | |
6176 @deprecated | |
6177 static bool DEFAULT_ENABLE_ENUM = true; | |
6178 | |
6179 /** | |
6180 * A predicate indicating whether analysis is to parse and analyze function | |
6181 * bodies. | |
6182 */ | |
6183 AnalyzeFunctionBodiesPredicate _analyzeFunctionBodiesPredicate = | |
6184 _analyzeAllFunctionBodies; | |
6185 | |
6186 /** | |
6187 * The maximum number of sources for which AST structures should be kept in | |
6188 * the cache. | |
6189 */ | |
6190 int cacheSize = DEFAULT_CACHE_SIZE; | |
6191 | |
6192 /** | |
6193 * A flag indicating whether analysis is to generate dart2js related hint | |
6194 * results. | |
6195 */ | |
6196 bool dart2jsHint = true; | |
6197 | |
6198 /** | |
6199 * A flag indicating whether generic methods are to be supported (DEP 22). | |
6200 */ | |
6201 bool enableGenericMethods = false; | |
6202 | |
6203 /** | |
6204 * A flag indicating whether analysis is to strictly follow the specification | |
6205 * when generating warnings on "call" methods (fixes dartbug.com/21938). | |
6206 */ | |
6207 bool enableStrictCallChecks = false; | |
6208 | |
6209 /** | |
6210 * A flag indicating whether errors, warnings and hints should be generated | |
6211 * for sources that are implicitly being analyzed. | |
6212 */ | |
6213 bool generateImplicitErrors = true; | |
6214 | |
6215 /** | |
6216 * A flag indicating whether errors, warnings and hints should be generated | |
6217 * for sources in the SDK. | |
6218 */ | |
6219 bool generateSdkErrors = false; | |
6220 | |
6221 /** | |
6222 * A flag indicating whether analysis is to generate hint results (e.g. type | |
6223 * inference based information and pub best practices). | |
6224 */ | |
6225 bool hint = true; | |
6226 | |
6227 /** | |
6228 * A flag indicating whether incremental analysis should be used. | |
6229 */ | |
6230 bool incremental = false; | |
6231 | |
6232 /** | |
6233 * A flag indicating whether incremental analysis should be used for API | |
6234 * changes. | |
6235 */ | |
6236 bool incrementalApi = false; | |
6237 | |
6238 /** | |
6239 * A flag indicating whether validation should be performed after incremental | |
6240 * analysis. | |
6241 */ | |
6242 bool incrementalValidation = false; | |
6243 | |
6244 /** | |
6245 * A flag indicating whether analysis is to generate lint warnings. | |
6246 */ | |
6247 bool lint = false; | |
6248 | |
6249 /** | |
6250 * A flag indicating whether analysis is to parse comments. | |
6251 */ | |
6252 bool preserveComments = true; | |
6253 | |
6254 /** | |
6255 * Initialize a newly created set of analysis options to have their default | |
6256 * values. | |
6257 */ | |
6258 AnalysisOptionsImpl(); | |
6259 | |
6260 /** | |
6261 * Initialize a newly created set of analysis options to have the same values | |
6262 * as those in the given set of analysis [options]. | |
6263 */ | |
6264 @deprecated // Use new AnalysisOptionsImpl.from(options) | |
6265 AnalysisOptionsImpl.con1(AnalysisOptions options) { | |
6266 analyzeFunctionBodiesPredicate = options.analyzeFunctionBodiesPredicate; | |
6267 cacheSize = options.cacheSize; | |
6268 dart2jsHint = options.dart2jsHint; | |
6269 enableStrictCallChecks = options.enableStrictCallChecks; | |
6270 generateImplicitErrors = options.generateImplicitErrors; | |
6271 generateSdkErrors = options.generateSdkErrors; | |
6272 hint = options.hint; | |
6273 incremental = options.incremental; | |
6274 incrementalApi = options.incrementalApi; | |
6275 incrementalValidation = options.incrementalValidation; | |
6276 lint = options.lint; | |
6277 preserveComments = options.preserveComments; | |
6278 } | |
6279 | |
6280 /** | |
6281 * Initialize a newly created set of analysis options to have the same values | |
6282 * as those in the given set of analysis [options]. | |
6283 */ | |
6284 AnalysisOptionsImpl.from(AnalysisOptions options) { | |
6285 analyzeFunctionBodiesPredicate = options.analyzeFunctionBodiesPredicate; | |
6286 cacheSize = options.cacheSize; | |
6287 dart2jsHint = options.dart2jsHint; | |
6288 enableStrictCallChecks = options.enableStrictCallChecks; | |
6289 generateImplicitErrors = options.generateImplicitErrors; | |
6290 generateSdkErrors = options.generateSdkErrors; | |
6291 hint = options.hint; | |
6292 incremental = options.incremental; | |
6293 incrementalApi = options.incrementalApi; | |
6294 incrementalValidation = options.incrementalValidation; | |
6295 lint = options.lint; | |
6296 preserveComments = options.preserveComments; | |
6297 } | |
6298 | |
6299 bool get analyzeFunctionBodies { | |
6300 if (identical(analyzeFunctionBodiesPredicate, _analyzeAllFunctionBodies)) { | |
6301 return true; | |
6302 } else if (identical( | |
6303 analyzeFunctionBodiesPredicate, _analyzeNoFunctionBodies)) { | |
6304 return false; | |
6305 } else { | |
6306 throw new StateError('analyzeFunctionBodiesPredicate in use'); | |
6307 } | |
6308 } | |
6309 | |
6310 set analyzeFunctionBodies(bool value) { | |
6311 if (value) { | |
6312 analyzeFunctionBodiesPredicate = _analyzeAllFunctionBodies; | |
6313 } else { | |
6314 analyzeFunctionBodiesPredicate = _analyzeNoFunctionBodies; | |
6315 } | |
6316 } | |
6317 | |
6318 @override | |
6319 AnalyzeFunctionBodiesPredicate get analyzeFunctionBodiesPredicate => | |
6320 _analyzeFunctionBodiesPredicate; | |
6321 | |
6322 set analyzeFunctionBodiesPredicate(AnalyzeFunctionBodiesPredicate value) { | |
6323 if (value == null) { | |
6324 throw new ArgumentError.notNull('analyzeFunctionBodiesPredicate'); | |
6325 } | |
6326 _analyzeFunctionBodiesPredicate = value; | |
6327 } | |
6328 | |
6329 @deprecated | |
6330 @override | |
6331 bool get enableAsync => true; | |
6332 | |
6333 @deprecated | |
6334 void set enableAsync(bool enable) { | |
6335 // Async support cannot be disabled | |
6336 } | |
6337 | |
6338 @deprecated | |
6339 @override | |
6340 bool get enableDeferredLoading => true; | |
6341 | |
6342 @deprecated | |
6343 void set enableDeferredLoading(bool enable) { | |
6344 // Deferred loading support cannot be disabled | |
6345 } | |
6346 | |
6347 @deprecated | |
6348 @override | |
6349 bool get enableEnum => true; | |
6350 | |
6351 @deprecated | |
6352 void set enableEnum(bool enable) { | |
6353 // Enum support cannot be disabled | |
6354 } | |
6355 | |
6356 @deprecated | |
6357 @override | |
6358 bool get enableNullAwareOperators => true; | |
6359 | |
6360 @deprecated | |
6361 void set enableNullAwareOperators(bool enable) { | |
6362 // Null-aware operator support cannot be disabled | |
6363 } | |
6364 | |
6365 /** | |
6366 * Predicate used for [analyzeFunctionBodiesPredicate] when | |
6367 * [analyzeFunctionBodies] is set to `true`. | |
6368 */ | |
6369 static bool _analyzeAllFunctionBodies(Source _) => true; | |
6370 | |
6371 /** | |
6372 * Predicate used for [analyzeFunctionBodiesPredicate] when | |
6373 * [analyzeFunctionBodies] is set to `false`. | |
6374 */ | |
6375 static bool _analyzeNoFunctionBodies(Source _) => false; | |
6376 } | |
6377 | |
6378 /** | |
6379 * | |
6380 */ | |
6381 class AnalysisResult { | |
6382 /** | |
6383 * The change notices associated with this result, or `null` if there were no | |
6384 * changes and there is no more work to be done. | |
6385 */ | |
6386 final List<ChangeNotice> _notices; | |
6387 | |
6388 /** | |
6389 * The number of milliseconds required to determine which task was to be | |
6390 * performed. | |
6391 */ | |
6392 final int getTime; | |
6393 | |
6394 /** | |
6395 * The name of the class of the task that was performed. | |
6396 */ | |
6397 final String taskClassName; | |
6398 | |
6399 /** | |
6400 * The number of milliseconds required to perform the task. | |
6401 */ | |
6402 final int performTime; | |
6403 | |
6404 /** | |
6405 * Initialize a newly created analysis result to have the given values. The | |
6406 * [notices] is the change notices associated with this result. The [getTime] | |
6407 * is the number of milliseconds required to determine which task was to be | |
6408 * performed. The [taskClassName] is the name of the class of the task that | |
6409 * was performed. The [performTime] is the number of milliseconds required to | |
6410 * perform the task. | |
6411 */ | |
6412 AnalysisResult( | |
6413 this._notices, this.getTime, this.taskClassName, this.performTime); | |
6414 | |
6415 /** | |
6416 * Return the change notices associated with this result, or `null` if there | |
6417 * were no changes and there is no more work to be done. | |
6418 */ | |
6419 List<ChangeNotice> get changeNotices => _notices; | |
6420 | |
6421 /** | |
6422 * Return `true` if there is more to be performed after the task that was | |
6423 * performed. | |
6424 */ | |
6425 bool get hasMoreWork => _notices != null; | |
6426 } | |
6427 | |
6428 /** | |
6429 * An analysis task. | |
6430 */ | |
6431 abstract class AnalysisTask { | |
6432 /** | |
6433 * The context in which the task is to be performed. | |
6434 */ | |
6435 final InternalAnalysisContext context; | |
6436 | |
6437 /** | |
6438 * The exception that was thrown while performing this task, or `null` if the | |
6439 * task completed successfully. | |
6440 */ | |
6441 CaughtException _thrownException; | |
6442 | |
6443 /** | |
6444 * Initialize a newly created task to perform analysis within the given | |
6445 * [context]. | |
6446 */ | |
6447 AnalysisTask(this.context); | |
6448 | |
6449 /** | |
6450 * Return the exception that was thrown while performing this task, or `null` | |
6451 * if the task completed successfully. | |
6452 */ | |
6453 CaughtException get exception => _thrownException; | |
6454 | |
6455 /** | |
6456 * Return a textual description of this task. | |
6457 */ | |
6458 String get taskDescription; | |
6459 | |
6460 /** | |
6461 * Use the given [visitor] to visit this task. Throws an [AnalysisException] | |
6462 * if the visitor throws the exception. | |
6463 */ | |
6464 accept(AnalysisTaskVisitor visitor); | |
6465 | |
6466 /** | |
6467 * Perform this analysis task, protected by an exception handler. Throws an | |
6468 * [AnalysisException] if an exception occurs while performing the task. | |
6469 */ | |
6470 void internalPerform(); | |
6471 | |
6472 /** | |
6473 * Perform this analysis task and use the given [visitor] to visit this task | |
6474 * after it has completed. Throws an [AnalysisException] if the visitor throws | |
6475 * the exception. | |
6476 */ | |
6477 Object perform(AnalysisTaskVisitor visitor) { | |
6478 try { | |
6479 _safelyPerform(); | |
6480 } on AnalysisException catch (exception, stackTrace) { | |
6481 _thrownException = new CaughtException(exception, stackTrace); | |
6482 AnalysisEngine.instance.logger.logInformation( | |
6483 "Task failed: $taskDescription", | |
6484 new CaughtException(exception, stackTrace)); | |
6485 } | |
6486 return PerformanceStatistics.analysisTaskVisitor | |
6487 .makeCurrentWhile(() => accept(visitor)); | |
6488 } | |
6489 | |
6490 @override | |
6491 String toString() => taskDescription; | |
6492 | |
6493 /** | |
6494 * Perform this analysis task, ensuring that all exceptions are wrapped in an | |
6495 * [AnalysisException]. Throws an [AnalysisException] if any exception occurs | |
6496 * while performing the task | |
6497 */ | |
6498 void _safelyPerform() { | |
6499 try { | |
6500 String contextName = context.name; | |
6501 if (contextName == null) { | |
6502 contextName = 'unnamed'; | |
6503 } | |
6504 AnalysisEngine.instance.instrumentationService.logAnalysisTask( | |
6505 contextName, taskDescription); | |
6506 internalPerform(); | |
6507 } on AnalysisException { | |
6508 rethrow; | |
6509 } catch (exception, stackTrace) { | |
6510 throw new AnalysisException( | |
6511 exception.toString(), new CaughtException(exception, stackTrace)); | |
6512 } | |
6513 } | |
6514 } | |
6515 | |
6516 /** | |
6517 * An object used to visit tasks. While tasks are not structured in any | |
6518 * interesting way, this class provides the ability to dispatch to an | |
6519 * appropriate method. | |
6520 */ | |
6521 abstract class AnalysisTaskVisitor<E> { | |
6522 /** | |
6523 * Visit the given [task], returning the result of the visit. This method will | |
6524 * throw an AnalysisException if the visitor throws an exception. | |
6525 */ | |
6526 E visitGenerateDartErrorsTask(GenerateDartErrorsTask task); | |
6527 | |
6528 /** | |
6529 * Visit the given [task], returning the result of the visit. This method will | |
6530 * throw an AnalysisException if the visitor throws an exception. | |
6531 */ | |
6532 E visitGenerateDartHintsTask(GenerateDartHintsTask task); | |
6533 | |
6534 /** | |
6535 * Visit the given [task], returning the result of the visit. This method will | |
6536 * throw an AnalysisException if the visitor throws an exception. | |
6537 */ | |
6538 E visitGenerateDartLintsTask(GenerateDartLintsTask task); | |
6539 | |
6540 /** | |
6541 * Visit the given [task], returning the result of the visit. This method will | |
6542 * throw an AnalysisException if the visitor throws an exception. | |
6543 */ | |
6544 E visitGetContentTask(GetContentTask task); | |
6545 | |
6546 /** | |
6547 * Visit the given [task], returning the result of the visit. This method will | |
6548 * throw an AnalysisException if the visitor throws an exception. | |
6549 */ | |
6550 E visitIncrementalAnalysisTask( | |
6551 IncrementalAnalysisTask incrementalAnalysisTask); | |
6552 | |
6553 /** | |
6554 * Visit the given [task], returning the result of the visit. This method will | |
6555 * throw an AnalysisException if the visitor throws an exception. | |
6556 */ | |
6557 E visitParseDartTask(ParseDartTask task); | |
6558 | |
6559 /** | |
6560 * Visit the given [task], returning the result of the visit. This method will | |
6561 * throw an AnalysisException if the visitor throws an exception. | |
6562 */ | |
6563 E visitParseHtmlTask(ParseHtmlTask task); | |
6564 | |
6565 /** | |
6566 * Visit the given [task], returning the result of the visit. This method will | |
6567 * throw an AnalysisException if the visitor throws an exception. | |
6568 */ | |
6569 E visitResolveDartLibraryCycleTask(ResolveDartLibraryCycleTask task); | |
6570 | |
6571 /** | |
6572 * Visit the given [task], returning the result of the visit. This method will | |
6573 * throw an AnalysisException if the visitor throws an exception. | |
6574 */ | |
6575 E visitResolveDartLibraryTask(ResolveDartLibraryTask task); | |
6576 | |
6577 /** | |
6578 * Visit the given [task], returning the result of the visit. This method will | |
6579 * throw an AnalysisException if the visitor throws an exception. | |
6580 */ | |
6581 E visitResolveDartUnitTask(ResolveDartUnitTask task); | |
6582 | |
6583 /** | |
6584 * Visit the given [task], returning the result of the visit. This method will | |
6585 * throw an AnalysisException if the visitor throws an exception. | |
6586 */ | |
6587 E visitResolveHtmlTask(ResolveHtmlTask task); | |
6588 | |
6589 /** | |
6590 * Visit the given [task], returning the result of the visit. This method will | |
6591 * throw an AnalysisException if the visitor throws an exception. | |
6592 */ | |
6593 E visitScanDartTask(ScanDartTask task); | |
6594 } | |
6595 | |
6596 /** | |
6597 * A `CachedResult` is a single analysis result that is stored in a | |
6598 * [SourceEntry]. | |
6599 */ | |
6600 class CachedResult<E> { | |
6601 /** | |
6602 * The state of the cached value. | |
6603 */ | |
6604 CacheState state; | |
6605 | |
6606 /** | |
6607 * The value being cached, or `null` if there is no value (for example, when | |
6608 * the [state] is [CacheState.INVALID]. | |
6609 */ | |
6610 E value; | |
6611 | |
6612 /** | |
6613 * Initialize a newly created result holder to represent the value of data | |
6614 * described by the given [descriptor]. | |
6615 */ | |
6616 CachedResult(DataDescriptor descriptor) { | |
6617 state = CacheState.INVALID; | |
6618 value = descriptor.defaultValue; | |
6619 } | |
6620 } | |
6621 | |
6622 /** | |
6623 * A single partition in an LRU cache of information related to analysis. | |
6624 */ | |
6625 abstract class CachePartition { | |
6626 /** | |
6627 * The context that owns this partition. Multiple contexts can reference a | |
6628 * partition, but only one context can own it. | |
6629 */ | |
6630 final InternalAnalysisContext context; | |
6631 | |
6632 /** | |
6633 * The maximum number of sources for which AST structures should be kept in | |
6634 * the cache. | |
6635 */ | |
6636 int _maxCacheSize = 0; | |
6637 | |
6638 /** | |
6639 * The policy used to determine which pieces of data to remove from the cache. | |
6640 */ | |
6641 final CacheRetentionPolicy _retentionPolicy; | |
6642 | |
6643 /** | |
6644 * A table mapping the sources belonging to this partition to the information | |
6645 * known about those sources. | |
6646 */ | |
6647 HashMap<Source, SourceEntry> _sourceMap = new HashMap<Source, SourceEntry>(); | |
6648 | |
6649 /** | |
6650 * A list containing the most recently accessed sources with the most recently | |
6651 * used at the end of the list. When more sources are added than the maximum | |
6652 * allowed then the least recently used source will be removed and will have | |
6653 * it's cached AST structure flushed. | |
6654 */ | |
6655 List<Source> _recentlyUsed; | |
6656 | |
6657 /** | |
6658 * Initialize a newly created cache to maintain at most [maxCacheSize] AST | |
6659 * structures in the cache. The cache is owned by the give [context], and the | |
6660 * [retentionPolicy] will be used to determine which pieces of data to remove | |
6661 * from the cache. | |
6662 */ | |
6663 CachePartition(this.context, int maxCacheSize, this._retentionPolicy) { | |
6664 this._maxCacheSize = maxCacheSize; | |
6665 _recentlyUsed = new List<Source>(); | |
6666 } | |
6667 | |
6668 /** | |
6669 * Return the number of entries in this partition that have an AST associated | |
6670 * with them. | |
6671 */ | |
6672 int get astSize { | |
6673 int astSize = 0; | |
6674 int count = _recentlyUsed.length; | |
6675 for (int i = 0; i < count; i++) { | |
6676 Source source = _recentlyUsed[i]; | |
6677 SourceEntry sourceEntry = _sourceMap[source]; | |
6678 if (sourceEntry is DartEntry) { | |
6679 if (sourceEntry.anyParsedCompilationUnit != null) { | |
6680 astSize++; | |
6681 } | |
6682 } else if (sourceEntry is HtmlEntry) { | |
6683 if (sourceEntry.anyParsedUnit != null) { | |
6684 astSize++; | |
6685 } | |
6686 } | |
6687 } | |
6688 return astSize; | |
6689 } | |
6690 | |
6691 /** | |
6692 * Return a table mapping the sources known to the context to the information | |
6693 * known about the source. | |
6694 * | |
6695 * <b>Note:</b> This method is only visible for use by [AnalysisCache] and | |
6696 * should not be used for any other purpose. | |
6697 */ | |
6698 Map<Source, SourceEntry> get map => _sourceMap; | |
6699 | |
6700 /** | |
6701 * Set the maximum size of the cache to the given [size]. | |
6702 */ | |
6703 void set maxCacheSize(int size) { | |
6704 _maxCacheSize = size; | |
6705 while (_recentlyUsed.length > _maxCacheSize) { | |
6706 if (!_flushAstFromCache()) { | |
6707 break; | |
6708 } | |
6709 } | |
6710 } | |
6711 | |
6712 /** | |
6713 * Record that the AST associated with the given source was just read from the | |
6714 * cache. | |
6715 */ | |
6716 void accessedAst(Source source) { | |
6717 if (_recentlyUsed.remove(source)) { | |
6718 _recentlyUsed.add(source); | |
6719 return; | |
6720 } | |
6721 while (_recentlyUsed.length >= _maxCacheSize) { | |
6722 if (!_flushAstFromCache()) { | |
6723 break; | |
6724 } | |
6725 } | |
6726 _recentlyUsed.add(source); | |
6727 } | |
6728 | |
6729 /** | |
6730 * Return `true` if the given [source] is contained in this partition. | |
6731 */ | |
6732 bool contains(Source source); | |
6733 | |
6734 /** | |
6735 * Return the entry associated with the given [source]. | |
6736 */ | |
6737 SourceEntry get(Source source) => _sourceMap[source]; | |
6738 | |
6739 /** | |
6740 * Return an iterator returning all of the map entries mapping sources to | |
6741 * cache entries. | |
6742 */ | |
6743 MapIterator<Source, SourceEntry> iterator() => | |
6744 new SingleMapIterator<Source, SourceEntry>(_sourceMap); | |
6745 | |
6746 /** | |
6747 * Associate the given [entry] with the given [source]. | |
6748 */ | |
6749 void put(Source source, SourceEntry entry) { | |
6750 entry.fixExceptionState(); | |
6751 _sourceMap[source] = entry; | |
6752 } | |
6753 | |
6754 /** | |
6755 * Remove all information related to the given [source] from this cache. | |
6756 */ | |
6757 void remove(Source source) { | |
6758 _recentlyUsed.remove(source); | |
6759 _sourceMap.remove(source); | |
6760 } | |
6761 | |
6762 /** | |
6763 * Record that the AST associated with the given [source] was just removed | |
6764 * from the cache. | |
6765 */ | |
6766 void removedAst(Source source) { | |
6767 _recentlyUsed.remove(source); | |
6768 } | |
6769 | |
6770 /** | |
6771 * Return the number of sources that are mapped to cache entries. | |
6772 */ | |
6773 int size() => _sourceMap.length; | |
6774 | |
6775 /** | |
6776 * Record that the AST associated with the given [source] was just stored to | |
6777 * the cache. | |
6778 */ | |
6779 void storedAst(Source source) { | |
6780 if (_recentlyUsed.contains(source)) { | |
6781 return; | |
6782 } | |
6783 while (_recentlyUsed.length >= _maxCacheSize) { | |
6784 if (!_flushAstFromCache()) { | |
6785 break; | |
6786 } | |
6787 } | |
6788 _recentlyUsed.add(source); | |
6789 } | |
6790 | |
6791 /** | |
6792 * Attempt to flush one AST structure from the cache. Return `true` if a | |
6793 * structure was flushed. | |
6794 */ | |
6795 bool _flushAstFromCache() { | |
6796 Source removedSource = _removeAstToFlush(); | |
6797 if (removedSource == null) { | |
6798 return false; | |
6799 } | |
6800 SourceEntry sourceEntry = _sourceMap[removedSource]; | |
6801 if (sourceEntry is HtmlEntry) { | |
6802 HtmlEntry htmlEntry = sourceEntry; | |
6803 htmlEntry.flushAstStructures(); | |
6804 } else if (sourceEntry is DartEntry) { | |
6805 DartEntry dartEntry = sourceEntry; | |
6806 dartEntry.flushAstStructures(); | |
6807 } | |
6808 return true; | |
6809 } | |
6810 | |
6811 /** | |
6812 * Remove and return one source from the list of recently used sources whose | |
6813 * AST structure can be flushed from the cache. The source that will be | |
6814 * returned will be the source that has been unreferenced for the longest | |
6815 * period of time but that is not a priority for analysis. | |
6816 */ | |
6817 Source _removeAstToFlush() { | |
6818 int sourceToRemove = -1; | |
6819 for (int i = 0; i < _recentlyUsed.length; i++) { | |
6820 Source source = _recentlyUsed[i]; | |
6821 RetentionPriority priority = | |
6822 _retentionPolicy.getAstPriority(source, _sourceMap[source]); | |
6823 if (priority == RetentionPriority.LOW) { | |
6824 return _recentlyUsed.removeAt(i); | |
6825 } else if (priority == RetentionPriority.MEDIUM && sourceToRemove < 0) { | |
6826 sourceToRemove = i; | |
6827 } | |
6828 } | |
6829 if (sourceToRemove < 0) { | |
6830 // This happens if the retention policy returns a priority of HIGH for all | |
6831 // of the sources that have been recently used. This is the case, for | |
6832 // example, when the list of priority sources is bigger than the current | |
6833 // cache size. | |
6834 return null; | |
6835 } | |
6836 return _recentlyUsed.removeAt(sourceToRemove); | |
6837 } | |
6838 } | |
6839 | |
6840 /** | |
6841 * An object used to determine how important it is for data to be retained in | |
6842 * the analysis cache. | |
6843 */ | |
6844 abstract class CacheRetentionPolicy { | |
6845 /** | |
6846 * Return the priority of retaining the AST structure for the given [source]. | |
6847 */ | |
6848 RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry); | |
6849 } | |
6850 | |
6851 /** | |
6852 * The possible states of cached data. | |
6853 */ | |
6854 class CacheState extends Enum<CacheState> { | |
6855 /** | |
6856 * The data is not in the cache and the last time an attempt was made to | |
6857 * compute the data an exception occurred, making it pointless to attempt to | |
6858 * compute the data again. | |
6859 * | |
6860 * Valid Transitions: | |
6861 * * [INVALID] if a source was modified that might cause the data to be | |
6862 * computable | |
6863 */ | |
6864 static const CacheState ERROR = const CacheState('ERROR', 0); | |
6865 | |
6866 /** | |
6867 * The data is not in the cache because it was flushed from the cache in order | |
6868 * to control memory usage. If the data is recomputed, results do not need to | |
6869 * be reported. | |
6870 * | |
6871 * Valid Transitions: | |
6872 * * [IN_PROCESS] if the data is being recomputed | |
6873 * * [INVALID] if a source was modified that causes the data to need to be | |
6874 * recomputed | |
6875 */ | |
6876 static const CacheState FLUSHED = const CacheState('FLUSHED', 1); | |
6877 | |
6878 /** | |
6879 * The data might or might not be in the cache but is in the process of being | |
6880 * recomputed. | |
6881 * | |
6882 * Valid Transitions: | |
6883 * * [ERROR] if an exception occurred while trying to compute the data | |
6884 * * [VALID] if the data was successfully computed and stored in the cache | |
6885 */ | |
6886 static const CacheState IN_PROCESS = const CacheState('IN_PROCESS', 2); | |
6887 | |
6888 /** | |
6889 * The data is not in the cache and needs to be recomputed so that results can | |
6890 * be reported. | |
6891 * | |
6892 * Valid Transitions: | |
6893 * * [IN_PROCESS] if an attempt is being made to recompute the data | |
6894 */ | |
6895 static const CacheState INVALID = const CacheState('INVALID', 3); | |
6896 | |
6897 /** | |
6898 * The data is in the cache and up-to-date. | |
6899 * | |
6900 * Valid Transitions: | |
6901 * * [FLUSHED] if the data is removed in order to manage memory usage | |
6902 * * [INVALID] if a source was modified in such a way as to invalidate the | |
6903 * previous data | |
6904 */ | |
6905 static const CacheState VALID = const CacheState('VALID', 4); | |
6906 | |
6907 static const List<CacheState> values = const [ | |
6908 ERROR, | |
6909 FLUSHED, | |
6910 IN_PROCESS, | |
6911 INVALID, | |
6912 VALID | |
6913 ]; | |
6914 | |
6915 const CacheState(String name, int ordinal) : super(name, ordinal); | |
6916 } | |
6917 | |
6918 /** | |
6919 * An object that represents a change to the analysis results associated with a | |
6920 * given source. | |
6921 */ | |
6922 abstract class ChangeNotice implements AnalysisErrorInfo { | |
6923 /** | |
6924 * The parsed, but maybe not resolved Dart AST that changed as a result of | |
6925 * the analysis, or `null` if the AST was not changed. | |
6926 */ | |
6927 CompilationUnit get parsedDartUnit; | |
6928 | |
6929 /** | |
6930 * The fully resolved Dart AST that changed as a result of the analysis, or | |
6931 * `null` if the AST was not changed. | |
6932 */ | |
6933 CompilationUnit get resolvedDartUnit; | |
6934 | |
6935 /** | |
6936 * The fully resolved HTML AST that changed as a result of the analysis, or | |
6937 * `null` if the AST was not changed. | |
6938 */ | |
6939 @deprecated | |
6940 ht.HtmlUnit get resolvedHtmlUnit; | |
6941 | |
6942 /** | |
6943 * Return the source for which the result is being reported. | |
6944 */ | |
6945 Source get source; | |
6946 } | |
6947 | |
6948 /** | |
6949 * An implementation of a [ChangeNotice]. | |
6950 */ | |
6951 class ChangeNoticeImpl implements ChangeNotice { | |
6952 /** | |
6953 * An empty list of change notices. | |
6954 */ | |
6955 static const List<ChangeNoticeImpl> EMPTY_LIST = const <ChangeNoticeImpl>[]; | |
6956 | |
6957 /** | |
6958 * The source for which the result is being reported. | |
6959 */ | |
6960 final Source source; | |
6961 | |
6962 /** | |
6963 * The parsed, but maybe not resolved Dart AST that changed as a result of | |
6964 * the analysis, or `null` if the AST was not changed. | |
6965 */ | |
6966 CompilationUnit parsedDartUnit; | |
6967 | |
6968 /** | |
6969 * The fully resolved Dart AST that changed as a result of the analysis, or | |
6970 * `null` if the AST was not changed. | |
6971 */ | |
6972 CompilationUnit resolvedDartUnit; | |
6973 | |
6974 /** | |
6975 * The fully resolved HTML AST that changed as a result of the analysis, or | |
6976 * `null` if the AST was not changed. | |
6977 */ | |
6978 @deprecated | |
6979 ht.HtmlUnit resolvedHtmlUnit; | |
6980 | |
6981 /** | |
6982 * The errors that changed as a result of the analysis, or `null` if errors | |
6983 * were not changed. | |
6984 */ | |
6985 List<AnalysisError> _errors; | |
6986 | |
6987 /** | |
6988 * The line information associated with the source, or `null` if errors were | |
6989 * not changed. | |
6990 */ | |
6991 LineInfo _lineInfo; | |
6992 | |
6993 /** | |
6994 * Initialize a newly created notice associated with the given source. | |
6995 * | |
6996 * @param source the source for which the change is being reported | |
6997 */ | |
6998 ChangeNoticeImpl(this.source); | |
6999 | |
7000 @override | |
7001 List<AnalysisError> get errors => _errors; | |
7002 | |
7003 @override | |
7004 LineInfo get lineInfo => _lineInfo; | |
7005 | |
7006 /** | |
7007 * Set the errors that changed as a result of the analysis to the given | |
7008 * [errors] and set the line information to the given [lineInfo]. | |
7009 */ | |
7010 void setErrors(List<AnalysisError> errors, LineInfo lineInfo) { | |
7011 this._errors = errors; | |
7012 this._lineInfo = lineInfo; | |
7013 if (lineInfo == null) { | |
7014 AnalysisEngine.instance.logger.logInformation("No line info: $source", | |
7015 new CaughtException(new AnalysisException(), null)); | |
7016 } | |
7017 } | |
7018 | |
7019 @override | |
7020 String toString() => "Changes for ${source.fullName}"; | |
7021 } | |
7022 | |
7023 /** | |
7024 * An indication of which sources have been added, changed, removed, or deleted. | |
7025 * In the case of a changed source, there are multiple ways of indicating the | |
7026 * nature of the change. | |
7027 * | |
7028 * No source should be added to the change set more than once, either with the | |
7029 * same or a different kind of change. It does not make sense, for example, for | |
7030 * a source to be both added and removed, and it is redundant for a source to be | |
7031 * marked as changed in its entirety and changed in some specific range. | |
7032 */ | |
7033 class ChangeSet { | |
7034 /** | |
7035 * A list containing the sources that have been added. | |
7036 */ | |
7037 final List<Source> addedSources = new List<Source>(); | |
7038 | |
7039 /** | |
7040 * A list containing the sources that have been changed. | |
7041 */ | |
7042 final List<Source> changedSources = new List<Source>(); | |
7043 | |
7044 /** | |
7045 * A table mapping the sources whose content has been changed to the current | |
7046 * content of those sources. | |
7047 */ | |
7048 HashMap<Source, String> _changedContent = new HashMap<Source, String>(); | |
7049 | |
7050 /** | |
7051 * A table mapping the sources whose content has been changed within a single | |
7052 * range to the current content of those sources and information about the | |
7053 * affected range. | |
7054 */ | |
7055 final HashMap<Source, ChangeSet_ContentChange> changedRanges = | |
7056 new HashMap<Source, ChangeSet_ContentChange>(); | |
7057 | |
7058 /** | |
7059 * A list containing the sources that have been removed. | |
7060 */ | |
7061 final List<Source> removedSources = new List<Source>(); | |
7062 | |
7063 /** | |
7064 * A list containing the source containers specifying additional sources that | |
7065 * have been removed. | |
7066 */ | |
7067 final List<SourceContainer> removedContainers = new List<SourceContainer>(); | |
7068 | |
7069 /** | |
7070 * A list containing the sources that have been deleted. | |
7071 */ | |
7072 final List<Source> deletedSources = new List<Source>(); | |
7073 | |
7074 /** | |
7075 * Return a table mapping the sources whose content has been changed to the | |
7076 * current content of those sources. | |
7077 */ | |
7078 Map<Source, String> get changedContents => _changedContent; | |
7079 | |
7080 /** | |
7081 * Return `true` if this change set does not contain any changes. | |
7082 */ | |
7083 bool get isEmpty => addedSources.isEmpty && | |
7084 changedSources.isEmpty && | |
7085 _changedContent.isEmpty && | |
7086 changedRanges.isEmpty && | |
7087 removedSources.isEmpty && | |
7088 removedContainers.isEmpty && | |
7089 deletedSources.isEmpty; | |
7090 | |
7091 /** | |
7092 * Record that the specified [source] has been added and that its content is | |
7093 * the default contents of the source. | |
7094 */ | |
7095 void addedSource(Source source) { | |
7096 addedSources.add(source); | |
7097 } | |
7098 | |
7099 /** | |
7100 * Record that the specified [source] has been changed and that its content is | |
7101 * the given [contents]. | |
7102 */ | |
7103 void changedContent(Source source, String contents) { | |
7104 _changedContent[source] = contents; | |
7105 } | |
7106 | |
7107 /** | |
7108 * Record that the specified [source] has been changed and that its content is | |
7109 * the given [contents]. The [offset] is the offset into the current contents. | |
7110 * The [oldLength] is the number of characters in the original contents that | |
7111 * were replaced. The [newLength] is the number of characters in the | |
7112 * replacement text. | |
7113 */ | |
7114 void changedRange(Source source, String contents, int offset, int oldLength, | |
7115 int newLength) { | |
7116 changedRanges[source] = | |
7117 new ChangeSet_ContentChange(contents, offset, oldLength, newLength); | |
7118 } | |
7119 | |
7120 /** | |
7121 * Record that the specified [source] has been changed. If the content of the | |
7122 * source was previously overridden, this has no effect (the content remains | |
7123 * overridden). To cancel (or change) the override, use [changedContent] | |
7124 * instead. | |
7125 */ | |
7126 void changedSource(Source source) { | |
7127 changedSources.add(source); | |
7128 } | |
7129 | |
7130 /** | |
7131 * Record that the specified [source] has been deleted. | |
7132 */ | |
7133 void deletedSource(Source source) { | |
7134 deletedSources.add(source); | |
7135 } | |
7136 | |
7137 /** | |
7138 * Record that the specified source [container] has been removed. | |
7139 */ | |
7140 void removedContainer(SourceContainer container) { | |
7141 if (container != null) { | |
7142 removedContainers.add(container); | |
7143 } | |
7144 } | |
7145 | |
7146 /** | |
7147 * Record that the specified [source] has been removed. | |
7148 */ | |
7149 void removedSource(Source source) { | |
7150 if (source != null) { | |
7151 removedSources.add(source); | |
7152 } | |
7153 } | |
7154 | |
7155 @override | |
7156 String toString() { | |
7157 StringBuffer buffer = new StringBuffer(); | |
7158 bool needsSeparator = | |
7159 _appendSources(buffer, addedSources, false, "addedSources"); | |
7160 needsSeparator = _appendSources( | |
7161 buffer, changedSources, needsSeparator, "changedSources"); | |
7162 needsSeparator = _appendSources2( | |
7163 buffer, _changedContent, needsSeparator, "changedContent"); | |
7164 needsSeparator = | |
7165 _appendSources2(buffer, changedRanges, needsSeparator, "changedRanges"); | |
7166 needsSeparator = _appendSources( | |
7167 buffer, deletedSources, needsSeparator, "deletedSources"); | |
7168 needsSeparator = _appendSources( | |
7169 buffer, removedSources, needsSeparator, "removedSources"); | |
7170 int count = removedContainers.length; | |
7171 if (count > 0) { | |
7172 if (removedSources.isEmpty) { | |
7173 if (needsSeparator) { | |
7174 buffer.write("; "); | |
7175 } | |
7176 buffer.write("removed: from "); | |
7177 buffer.write(count); | |
7178 buffer.write(" containers"); | |
7179 } else { | |
7180 buffer.write(", and more from "); | |
7181 buffer.write(count); | |
7182 buffer.write(" containers"); | |
7183 } | |
7184 } | |
7185 return buffer.toString(); | |
7186 } | |
7187 | |
7188 /** | |
7189 * Append the given [sources] to the given [buffer], prefixed with the given | |
7190 * [label] and a separator if [needsSeparator] is `true`. Return `true` if | |
7191 * future lists of sources will need a separator. | |
7192 */ | |
7193 bool _appendSources(StringBuffer buffer, List<Source> sources, | |
7194 bool needsSeparator, String label) { | |
7195 if (sources.isEmpty) { | |
7196 return needsSeparator; | |
7197 } | |
7198 if (needsSeparator) { | |
7199 buffer.write("; "); | |
7200 } | |
7201 buffer.write(label); | |
7202 String prefix = " "; | |
7203 for (Source source in sources) { | |
7204 buffer.write(prefix); | |
7205 buffer.write(source.fullName); | |
7206 prefix = ", "; | |
7207 } | |
7208 return true; | |
7209 } | |
7210 | |
7211 /** | |
7212 * Append the given [sources] to the given [builder], prefixed with the given | |
7213 * [label] and a separator if [needsSeparator] is `true`. Return `true` if | |
7214 * future lists of sources will need a separator. | |
7215 */ | |
7216 bool _appendSources2(StringBuffer buffer, HashMap<Source, dynamic> sources, | |
7217 bool needsSeparator, String label) { | |
7218 if (sources.isEmpty) { | |
7219 return needsSeparator; | |
7220 } | |
7221 if (needsSeparator) { | |
7222 buffer.write("; "); | |
7223 } | |
7224 buffer.write(label); | |
7225 String prefix = " "; | |
7226 for (Source source in sources.keys.toSet()) { | |
7227 buffer.write(prefix); | |
7228 buffer.write(source.fullName); | |
7229 prefix = ", "; | |
7230 } | |
7231 return true; | |
7232 } | |
7233 } | |
7234 | |
7235 /** | |
7236 * A change to the content of a source. | |
7237 */ | |
7238 class ChangeSet_ContentChange { | |
7239 /** | |
7240 * The new contents of the source. | |
7241 */ | |
7242 final String contents; | |
7243 | |
7244 /** | |
7245 * The offset into the current contents. | |
7246 */ | |
7247 final int offset; | |
7248 | |
7249 /** | |
7250 * The number of characters in the original contents that were replaced | |
7251 */ | |
7252 final int oldLength; | |
7253 | |
7254 /** | |
7255 * The number of characters in the replacement text. | |
7256 */ | |
7257 final int newLength; | |
7258 | |
7259 /** | |
7260 * Initialize a newly created change object to represent a change to the | |
7261 * content of a source. The [contents] is the new contents of the source. The | |
7262 * [offse] ist the offset into the current contents. The [oldLength] is the | |
7263 * number of characters in the original contents that were replaced. The | |
7264 * [newLength] is the number of characters in the replacement text. | |
7265 */ | |
7266 ChangeSet_ContentChange( | |
7267 this.contents, this.offset, this.oldLength, this.newLength); | |
7268 } | |
7269 | |
7270 /** | |
7271 * [ComputedResult] describes a value computed for a [ResultDescriptor]. | |
7272 */ | |
7273 class ComputedResult<V> { | |
7274 /** | |
7275 * The context in which the value was computed. | |
7276 */ | |
7277 final AnalysisContext context; | |
7278 | |
7279 /** | |
7280 * The descriptor of the result which was computed. | |
7281 */ | |
7282 final ResultDescriptor<V> descriptor; | |
7283 | |
7284 /** | |
7285 * The target for which the result was computed. | |
7286 */ | |
7287 final AnalysisTarget target; | |
7288 | |
7289 /** | |
7290 * The computed value. | |
7291 */ | |
7292 final V value; | |
7293 | |
7294 ComputedResult(this.context, this.descriptor, this.target, this.value); | |
7295 | |
7296 @override | |
7297 String toString() => '$descriptor of $target in $context'; | |
7298 } | |
7299 | |
7300 /** | |
7301 * A pair containing a library and a list of the (source, entry) pairs for | |
7302 * compilation units in the library. | |
7303 */ | |
7304 class CycleBuilder_LibraryPair { | |
7305 /** | |
7306 * The library containing the compilation units. | |
7307 */ | |
7308 ResolvableLibrary library; | |
7309 | |
7310 /** | |
7311 * The (source, entry) pairs representing the compilation units in the | |
7312 * library. | |
7313 */ | |
7314 List<CycleBuilder_SourceEntryPair> entryPairs; | |
7315 | |
7316 /** | |
7317 * Initialize a newly created pair from the given [library] and [entryPairs]. | |
7318 */ | |
7319 CycleBuilder_LibraryPair(ResolvableLibrary library, | |
7320 List<CycleBuilder_SourceEntryPair> entryPairs) { | |
7321 this.library = library; | |
7322 this.entryPairs = entryPairs; | |
7323 } | |
7324 } | |
7325 | |
7326 /** | |
7327 * A pair containing a source and the cache entry associated with that source. | |
7328 * They are used to reduce the number of times an entry must be looked up in the | |
7329 * cache. | |
7330 */ | |
7331 class CycleBuilder_SourceEntryPair { | |
7332 /** | |
7333 * The source associated with the entry. | |
7334 */ | |
7335 Source source; | |
7336 | |
7337 /** | |
7338 * The entry associated with the source. | |
7339 */ | |
7340 DartEntry entry; | |
7341 | |
7342 /** | |
7343 * Initialize a newly created pair from the given [source] and [entry]. | |
7344 */ | |
7345 CycleBuilder_SourceEntryPair(Source source, DartEntry entry) { | |
7346 this.source = source; | |
7347 this.entry = entry; | |
7348 } | |
7349 } | |
7350 | |
7351 /** | |
7352 * The information cached by an analysis context about an individual Dart file. | |
7353 */ | |
7354 class DartEntry extends SourceEntry { | |
7355 /** | |
7356 * The data descriptor representing the element model representing a single | |
7357 * compilation unit. This model is incomplete and should not be used except as | |
7358 * input to another task. | |
7359 */ | |
7360 static final DataDescriptor<List<AnalysisError>> BUILT_ELEMENT = | |
7361 new DataDescriptor<List<AnalysisError>>("DartEntry.BUILT_ELEMENT"); | |
7362 | |
7363 /** | |
7364 * The data descriptor representing the AST structure after the element model | |
7365 * has been built (and declarations are resolved) but before other resolution | |
7366 * has been performed. | |
7367 */ | |
7368 static final DataDescriptor<CompilationUnit> BUILT_UNIT = | |
7369 new DataDescriptor<CompilationUnit>("DartEntry.BUILT_UNIT"); | |
7370 | |
7371 /** | |
7372 * The data descriptor representing the list of libraries that contain this | |
7373 * compilation unit. | |
7374 */ | |
7375 static final DataDescriptor<List<Source>> CONTAINING_LIBRARIES = | |
7376 new DataDescriptor<List<Source>>( | |
7377 "DartEntry.CONTAINING_LIBRARIES", Source.EMPTY_LIST); | |
7378 | |
7379 /** | |
7380 * The data descriptor representing the library element for the library. This | |
7381 * data is only available for Dart files that are the defining compilation | |
7382 * unit of a library. | |
7383 */ | |
7384 static final DataDescriptor<LibraryElement> ELEMENT = | |
7385 new DataDescriptor<LibraryElement>("DartEntry.ELEMENT"); | |
7386 | |
7387 /** | |
7388 * The data descriptor representing the list of exported libraries. This data | |
7389 * is only available for Dart files that are the defining compilation unit of | |
7390 * a library. | |
7391 */ | |
7392 static final DataDescriptor<List<Source>> EXPORTED_LIBRARIES = | |
7393 new DataDescriptor<List<Source>>( | |
7394 "DartEntry.EXPORTED_LIBRARIES", Source.EMPTY_LIST); | |
7395 | |
7396 /** | |
7397 * The data descriptor representing the hints resulting from auditing the | |
7398 * source. | |
7399 */ | |
7400 static final DataDescriptor<List<AnalysisError>> HINTS = | |
7401 new DataDescriptor<List<AnalysisError>>( | |
7402 "DartEntry.HINTS", AnalysisError.NO_ERRORS); | |
7403 | |
7404 /** | |
7405 * The data descriptor representing the list of imported libraries. This data | |
7406 * is only available for Dart files that are the defining compilation unit of | |
7407 * a library. | |
7408 */ | |
7409 static final DataDescriptor<List<Source>> IMPORTED_LIBRARIES = | |
7410 new DataDescriptor<List<Source>>( | |
7411 "DartEntry.IMPORTED_LIBRARIES", Source.EMPTY_LIST); | |
7412 | |
7413 /** | |
7414 * The data descriptor representing the list of included parts. This data is | |
7415 * only available for Dart files that are the defining compilation unit of a | |
7416 * library. | |
7417 */ | |
7418 static final DataDescriptor<List<Source>> INCLUDED_PARTS = | |
7419 new DataDescriptor<List<Source>>( | |
7420 "DartEntry.INCLUDED_PARTS", Source.EMPTY_LIST); | |
7421 | |
7422 /** | |
7423 * The data descriptor representing the client flag. This data is only | |
7424 * available for Dart files that are the defining compilation unit of a | |
7425 * library. | |
7426 */ | |
7427 static final DataDescriptor<bool> IS_CLIENT = | |
7428 new DataDescriptor<bool>("DartEntry.IS_CLIENT", false); | |
7429 | |
7430 /** | |
7431 * The data descriptor representing the launchable flag. This data is only | |
7432 * available for Dart files that are the defining compilation unit of a | |
7433 * library. | |
7434 */ | |
7435 static final DataDescriptor<bool> IS_LAUNCHABLE = | |
7436 new DataDescriptor<bool>("DartEntry.IS_LAUNCHABLE", false); | |
7437 | |
7438 /** | |
7439 * The data descriptor representing lint warnings resulting from auditing the | |
7440 * source. | |
7441 */ | |
7442 static final DataDescriptor<List<AnalysisError>> LINTS = | |
7443 new DataDescriptor<List<AnalysisError>>( | |
7444 "DartEntry.LINTS", AnalysisError.NO_ERRORS); | |
7445 | |
7446 /** | |
7447 * The data descriptor representing the errors resulting from parsing the | |
7448 * source. | |
7449 */ | |
7450 static final DataDescriptor<List<AnalysisError>> PARSE_ERRORS = | |
7451 new DataDescriptor<List<AnalysisError>>( | |
7452 "DartEntry.PARSE_ERRORS", AnalysisError.NO_ERRORS); | |
7453 | |
7454 /** | |
7455 * The data descriptor representing the parsed AST structure. | |
7456 */ | |
7457 static final DataDescriptor<CompilationUnit> PARSED_UNIT = | |
7458 new DataDescriptor<CompilationUnit>("DartEntry.PARSED_UNIT"); | |
7459 | |
7460 /** | |
7461 * The data descriptor representing the public namespace of the library. This | |
7462 * data is only available for Dart files that are the defining compilation | |
7463 * unit of a library. | |
7464 */ | |
7465 static final DataDescriptor<Namespace> PUBLIC_NAMESPACE = | |
7466 new DataDescriptor<Namespace>("DartEntry.PUBLIC_NAMESPACE"); | |
7467 | |
7468 /** | |
7469 * The data descriptor representing the errors resulting from resolving the | |
7470 * source. | |
7471 */ | |
7472 static final DataDescriptor<List<AnalysisError>> RESOLUTION_ERRORS = | |
7473 new DataDescriptor<List<AnalysisError>>( | |
7474 "DartEntry.RESOLUTION_ERRORS", AnalysisError.NO_ERRORS); | |
7475 | |
7476 /** | |
7477 * The data descriptor representing the resolved AST structure. | |
7478 */ | |
7479 static final DataDescriptor<CompilationUnit> RESOLVED_UNIT = | |
7480 new DataDescriptor<CompilationUnit>("DartEntry.RESOLVED_UNIT"); | |
7481 | |
7482 /** | |
7483 * The data descriptor representing the errors resulting from scanning the | |
7484 * source. | |
7485 */ | |
7486 static final DataDescriptor<List<AnalysisError>> SCAN_ERRORS = | |
7487 new DataDescriptor<List<AnalysisError>>( | |
7488 "DartEntry.SCAN_ERRORS", AnalysisError.NO_ERRORS); | |
7489 | |
7490 /** | |
7491 * The data descriptor representing the source kind. | |
7492 */ | |
7493 static final DataDescriptor<SourceKind> SOURCE_KIND = | |
7494 new DataDescriptor<SourceKind>( | |
7495 "DartEntry.SOURCE_KIND", SourceKind.UNKNOWN); | |
7496 | |
7497 /** | |
7498 * The data descriptor representing the token stream. | |
7499 */ | |
7500 static final DataDescriptor<Token> TOKEN_STREAM = | |
7501 new DataDescriptor<Token>("DartEntry.TOKEN_STREAM"); | |
7502 | |
7503 /** | |
7504 * The data descriptor representing the errors resulting from verifying the | |
7505 * source. | |
7506 */ | |
7507 static final DataDescriptor<List<AnalysisError>> VERIFICATION_ERRORS = | |
7508 new DataDescriptor<List<AnalysisError>>( | |
7509 "DartEntry.VERIFICATION_ERRORS", AnalysisError.NO_ERRORS); | |
7510 | |
7511 /** | |
7512 * The list of libraries that contain this compilation unit. The list will be | |
7513 * empty if there are no known libraries that contain this compilation unit. | |
7514 */ | |
7515 List<Source> _containingLibraries = new List<Source>(); | |
7516 | |
7517 /** | |
7518 * The information known as a result of resolving this compilation unit as | |
7519 * part of the library that contains this unit. This field will never be | |
7520 * `null`. | |
7521 */ | |
7522 ResolutionState _resolutionState = new ResolutionState(); | |
7523 | |
7524 /** | |
7525 * Return all of the errors associated with the compilation unit that are | |
7526 * currently cached. | |
7527 */ | |
7528 List<AnalysisError> get allErrors { | |
7529 List<AnalysisError> errors = new List<AnalysisError>(); | |
7530 errors.addAll(super.allErrors); | |
7531 errors.addAll(getValue(SCAN_ERRORS)); | |
7532 errors.addAll(getValue(PARSE_ERRORS)); | |
7533 ResolutionState state = _resolutionState; | |
7534 while (state != null) { | |
7535 errors.addAll(state.getValue(RESOLUTION_ERRORS)); | |
7536 errors.addAll(state.getValue(VERIFICATION_ERRORS)); | |
7537 errors.addAll(state.getValue(HINTS)); | |
7538 errors.addAll(state.getValue(LINTS)); | |
7539 state = state._nextState; | |
7540 } | |
7541 if (errors.length == 0) { | |
7542 return AnalysisError.NO_ERRORS; | |
7543 } | |
7544 return errors; | |
7545 } | |
7546 | |
7547 /** | |
7548 * Return a valid parsed compilation unit, either an unresolved AST structure | |
7549 * or the result of resolving the AST structure in the context of some | |
7550 * library, or `null` if there is no parsed compilation unit available. | |
7551 */ | |
7552 CompilationUnit get anyParsedCompilationUnit { | |
7553 if (getState(PARSED_UNIT) == CacheState.VALID) { | |
7554 return getValue(PARSED_UNIT); | |
7555 } | |
7556 ResolutionState state = _resolutionState; | |
7557 while (state != null) { | |
7558 if (state.getState(BUILT_UNIT) == CacheState.VALID) { | |
7559 return state.getValue(BUILT_UNIT); | |
7560 } | |
7561 state = state._nextState; | |
7562 } | |
7563 | |
7564 return anyResolvedCompilationUnit; | |
7565 } | |
7566 | |
7567 /** | |
7568 * Return the result of resolving the compilation unit as part of any library, | |
7569 * or `null` if there is no cached resolved compilation unit. | |
7570 */ | |
7571 CompilationUnit get anyResolvedCompilationUnit { | |
7572 ResolutionState state = _resolutionState; | |
7573 while (state != null) { | |
7574 if (state.getState(RESOLVED_UNIT) == CacheState.VALID) { | |
7575 return state.getValue(RESOLVED_UNIT); | |
7576 } | |
7577 state = state._nextState; | |
7578 } | |
7579 return null; | |
7580 } | |
7581 | |
7582 /** | |
7583 * The libraries that are known to contain this part. | |
7584 */ | |
7585 List<Source> get containingLibraries => _containingLibraries; | |
7586 | |
7587 /** | |
7588 * Set the list of libraries that contain this compilation unit to contain | |
7589 * only the given [librarySource]. This method should only be invoked on | |
7590 * entries that represent a library. | |
7591 */ | |
7592 void set containingLibrary(Source librarySource) { | |
7593 _containingLibraries.clear(); | |
7594 _containingLibraries.add(librarySource); | |
7595 } | |
7596 | |
7597 @override | |
7598 List<DataDescriptor> get descriptors { | |
7599 List<DataDescriptor> result = super.descriptors; | |
7600 result.addAll(<DataDescriptor>[ | |
7601 DartEntry.SOURCE_KIND, | |
7602 DartEntry.CONTAINING_LIBRARIES, | |
7603 DartEntry.PARSE_ERRORS, | |
7604 DartEntry.PARSED_UNIT, | |
7605 DartEntry.SCAN_ERRORS, | |
7606 DartEntry.SOURCE_KIND, | |
7607 DartEntry.TOKEN_STREAM | |
7608 ]); | |
7609 SourceKind kind = getValue(DartEntry.SOURCE_KIND); | |
7610 if (kind == SourceKind.LIBRARY) { | |
7611 result.addAll(<DataDescriptor>[ | |
7612 DartEntry.ELEMENT, | |
7613 DartEntry.EXPORTED_LIBRARIES, | |
7614 DartEntry.IMPORTED_LIBRARIES, | |
7615 DartEntry.INCLUDED_PARTS, | |
7616 DartEntry.IS_CLIENT, | |
7617 DartEntry.IS_LAUNCHABLE, | |
7618 DartEntry.PUBLIC_NAMESPACE | |
7619 ]); | |
7620 } | |
7621 return result; | |
7622 } | |
7623 | |
7624 /** | |
7625 * Return `true` if this entry has an AST structure that can be resolved, even | |
7626 * if it needs to be copied. Returning `true` implies that the method | |
7627 * [resolvableCompilationUnit] will return a non-`null` result. | |
7628 */ | |
7629 bool get hasResolvableCompilationUnit { | |
7630 if (getState(PARSED_UNIT) == CacheState.VALID) { | |
7631 return true; | |
7632 } | |
7633 ResolutionState state = _resolutionState; | |
7634 while (state != null) { | |
7635 if (state.getState(BUILT_UNIT) == CacheState.VALID || | |
7636 state.getState(RESOLVED_UNIT) == CacheState.VALID) { | |
7637 return true; | |
7638 } | |
7639 state = state._nextState; | |
7640 } | |
7641 | |
7642 return false; | |
7643 } | |
7644 | |
7645 @override | |
7646 SourceKind get kind => getValue(SOURCE_KIND); | |
7647 | |
7648 /** | |
7649 * The library sources containing the receiver's source. | |
7650 */ | |
7651 List<Source> get librariesContaining { | |
7652 ResolutionState state = _resolutionState; | |
7653 List<Source> result = new List<Source>(); | |
7654 while (state != null) { | |
7655 if (state._librarySource != null) { | |
7656 result.add(state._librarySource); | |
7657 } | |
7658 state = state._nextState; | |
7659 } | |
7660 return result; | |
7661 } | |
7662 | |
7663 /** | |
7664 * Get a list of all the library-dependent descriptors for which values may | |
7665 * be stored in this SourceEntry. | |
7666 */ | |
7667 List<DataDescriptor> get libraryDescriptors { | |
7668 return <DataDescriptor>[ | |
7669 DartEntry.BUILT_ELEMENT, | |
7670 DartEntry.BUILT_UNIT, | |
7671 DartEntry.RESOLUTION_ERRORS, | |
7672 DartEntry.RESOLVED_UNIT, | |
7673 DartEntry.VERIFICATION_ERRORS, | |
7674 DartEntry.HINTS, | |
7675 DartEntry.LINTS | |
7676 ]; | |
7677 } | |
7678 | |
7679 /** | |
7680 * A compilation unit that has not been accessed by any other client and can | |
7681 * therefore safely be modified by the reconciler, or `null` if the source has | |
7682 * not been parsed. | |
7683 */ | |
7684 CompilationUnit get resolvableCompilationUnit { | |
7685 if (getState(PARSED_UNIT) == CacheState.VALID) { | |
7686 CompilationUnit unit = getValue(PARSED_UNIT); | |
7687 setState(PARSED_UNIT, CacheState.FLUSHED); | |
7688 return unit; | |
7689 } | |
7690 ResolutionState state = _resolutionState; | |
7691 while (state != null) { | |
7692 if (state.getState(BUILT_UNIT) == CacheState.VALID) { | |
7693 // TODO(brianwilkerson) We're cloning the structure to remove any | |
7694 // previous resolution data, but I'm not sure that's necessary. | |
7695 return state.getValue(BUILT_UNIT).accept(new AstCloner()); | |
7696 } | |
7697 if (state.getState(RESOLVED_UNIT) == CacheState.VALID) { | |
7698 return state.getValue(RESOLVED_UNIT).accept(new AstCloner()); | |
7699 } | |
7700 state = state._nextState; | |
7701 } | |
7702 return null; | |
7703 } | |
7704 | |
7705 /** | |
7706 * Add the given [librarySource] to the list of libraries that contain this | |
7707 * part. This method should only be invoked on entries that represent a part. | |
7708 */ | |
7709 void addContainingLibrary(Source librarySource) { | |
7710 _containingLibraries.add(librarySource); | |
7711 } | |
7712 | |
7713 /** | |
7714 * Flush any AST structures being maintained by this entry. | |
7715 */ | |
7716 void flushAstStructures() { | |
7717 _flush(TOKEN_STREAM); | |
7718 _flush(PARSED_UNIT); | |
7719 _resolutionState.flushAstStructures(); | |
7720 } | |
7721 | |
7722 /** | |
7723 * Return the state of the data represented by the given [descriptor] in the | |
7724 * context of the given [librarySource]. | |
7725 */ | |
7726 CacheState getStateInLibrary( | |
7727 DataDescriptor descriptor, Source librarySource) { | |
7728 if (!_isValidLibraryDescriptor(descriptor)) { | |
7729 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
7730 } | |
7731 ResolutionState state = _resolutionState; | |
7732 while (state != null) { | |
7733 if (librarySource == state._librarySource) { | |
7734 return state.getState(descriptor); | |
7735 } | |
7736 state = state._nextState; | |
7737 } | |
7738 return CacheState.INVALID; | |
7739 } | |
7740 | |
7741 /** | |
7742 * Return the value of the data represented by the given [descriptor] in the | |
7743 * context of the given [librarySource], or `null` if the data represented by | |
7744 * the descriptor is not in the cache. | |
7745 */ | |
7746 Object getValueInLibrary(DataDescriptor descriptor, Source librarySource) { | |
7747 if (!_isValidLibraryDescriptor(descriptor)) { | |
7748 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
7749 } | |
7750 ResolutionState state = _resolutionState; | |
7751 while (state != null) { | |
7752 if (librarySource == state._librarySource) { | |
7753 return state.getValue(descriptor); | |
7754 } | |
7755 state = state._nextState; | |
7756 } | |
7757 return descriptor.defaultValue; | |
7758 } | |
7759 | |
7760 /** | |
7761 * Return `true` if the data represented by the given [descriptor] is marked | |
7762 * as being invalid. If the descriptor represents library-specific data then | |
7763 * this method will return `true` if the data associated with any library it | |
7764 * marked as invalid. | |
7765 */ | |
7766 bool hasInvalidData(DataDescriptor descriptor) { | |
7767 if (_isValidDescriptor(descriptor)) { | |
7768 return getState(descriptor) == CacheState.INVALID; | |
7769 } else if (_isValidLibraryDescriptor(descriptor)) { | |
7770 ResolutionState state = _resolutionState; | |
7771 while (state != null) { | |
7772 if (state.getState(descriptor) == CacheState.INVALID) { | |
7773 return true; | |
7774 } | |
7775 state = state._nextState; | |
7776 } | |
7777 } | |
7778 return false; | |
7779 } | |
7780 | |
7781 @override | |
7782 void invalidateAllInformation() { | |
7783 super.invalidateAllInformation(); | |
7784 setState(SCAN_ERRORS, CacheState.INVALID); | |
7785 setState(TOKEN_STREAM, CacheState.INVALID); | |
7786 setState(SOURCE_KIND, CacheState.INVALID); | |
7787 setState(PARSE_ERRORS, CacheState.INVALID); | |
7788 setState(PARSED_UNIT, CacheState.INVALID); | |
7789 _discardCachedResolutionInformation(true); | |
7790 } | |
7791 | |
7792 /** | |
7793 * Invalidate all of the resolution information associated with the | |
7794 * compilation unit. The flag [invalidateUris] should be `true` if the cached | |
7795 * results of converting URIs to source files should also be invalidated. | |
7796 */ | |
7797 void invalidateAllResolutionInformation(bool invalidateUris) { | |
7798 if (getState(PARSED_UNIT) == CacheState.FLUSHED) { | |
7799 ResolutionState state = _resolutionState; | |
7800 while (state != null) { | |
7801 if (state.getState(BUILT_UNIT) == CacheState.VALID) { | |
7802 CompilationUnit unit = state.getValue(BUILT_UNIT); | |
7803 setValue(PARSED_UNIT, unit.accept(new AstCloner())); | |
7804 break; | |
7805 } else if (state.getState(RESOLVED_UNIT) == CacheState.VALID) { | |
7806 CompilationUnit unit = state.getValue(RESOLVED_UNIT); | |
7807 setValue(PARSED_UNIT, unit.accept(new AstCloner())); | |
7808 break; | |
7809 } | |
7810 state = state._nextState; | |
7811 } | |
7812 } | |
7813 _discardCachedResolutionInformation(invalidateUris); | |
7814 } | |
7815 | |
7816 /** | |
7817 * Invalidate all of the parse and resolution information associated with | |
7818 * this source. | |
7819 */ | |
7820 void invalidateParseInformation() { | |
7821 setState(SOURCE_KIND, CacheState.INVALID); | |
7822 setState(PARSE_ERRORS, CacheState.INVALID); | |
7823 setState(PARSED_UNIT, CacheState.INVALID); | |
7824 _containingLibraries.clear(); | |
7825 _discardCachedResolutionInformation(true); | |
7826 } | |
7827 | |
7828 /** | |
7829 * Record that an [exception] occurred while attempting to build the element | |
7830 * model for the source represented by this entry in the context of the given | |
7831 * [library]. This will set the state of all resolution-based information as | |
7832 * being in error, but will not change the state of any parse results. | |
7833 */ | |
7834 void recordBuildElementErrorInLibrary( | |
7835 Source librarySource, CaughtException exception) { | |
7836 setStateInLibrary(BUILT_ELEMENT, librarySource, CacheState.ERROR); | |
7837 setStateInLibrary(BUILT_UNIT, librarySource, CacheState.ERROR); | |
7838 recordResolutionErrorInLibrary(librarySource, exception); | |
7839 } | |
7840 | |
7841 @override | |
7842 void recordContentError(CaughtException exception) { | |
7843 super.recordContentError(exception); | |
7844 recordScanError(exception); | |
7845 } | |
7846 | |
7847 /** | |
7848 * Record that an error occurred while attempting to generate hints for the | |
7849 * source represented by this entry. This will set the state of all | |
7850 * verification information as being in error. The [librarySource] is the | |
7851 * source of the library in which hints were being generated. The [exception] | |
7852 * is the exception that shows where the error occurred. | |
7853 */ | |
7854 void recordHintErrorInLibrary( | |
7855 Source librarySource, CaughtException exception) { | |
7856 this.exception = exception; | |
7857 ResolutionState state = _getOrCreateResolutionState(librarySource); | |
7858 state.recordHintError(); | |
7859 } | |
7860 | |
7861 /** | |
7862 * Record that an error occurred while attempting to generate lints for the | |
7863 * source represented by this entry. This will set the state of all | |
7864 * verification information as being in error. The [librarySource] is the | |
7865 * source of the library in which lints were being generated. The [exception] | |
7866 * is the exception that shows where the error occurred. | |
7867 */ | |
7868 void recordLintErrorInLibrary( | |
7869 Source librarySource, CaughtException exception) { | |
7870 this.exception = exception; | |
7871 ResolutionState state = _getOrCreateResolutionState(librarySource); | |
7872 state.recordLintError(); | |
7873 } | |
7874 | |
7875 /** | |
7876 * Record that an [exception] occurred while attempting to scan or parse the | |
7877 * entry represented by this entry. This will set the state of all information
, | |
7878 * including any resolution-based information, as being in error. | |
7879 */ | |
7880 void recordParseError(CaughtException exception) { | |
7881 setState(SOURCE_KIND, CacheState.ERROR); | |
7882 setState(PARSE_ERRORS, CacheState.ERROR); | |
7883 setState(PARSED_UNIT, CacheState.ERROR); | |
7884 setState(EXPORTED_LIBRARIES, CacheState.ERROR); | |
7885 setState(IMPORTED_LIBRARIES, CacheState.ERROR); | |
7886 setState(INCLUDED_PARTS, CacheState.ERROR); | |
7887 recordResolutionError(exception); | |
7888 } | |
7889 | |
7890 /** | |
7891 * Record that an [exception] occurred while attempting to resolve the source | |
7892 * represented by this entry. This will set the state of all resolution-based | |
7893 * information as being in error, but will not change the state of any parse | |
7894 * results. | |
7895 */ | |
7896 void recordResolutionError(CaughtException exception) { | |
7897 this.exception = exception; | |
7898 setState(ELEMENT, CacheState.ERROR); | |
7899 setState(IS_CLIENT, CacheState.ERROR); | |
7900 setState(IS_LAUNCHABLE, CacheState.ERROR); | |
7901 setState(PUBLIC_NAMESPACE, CacheState.ERROR); | |
7902 _resolutionState.recordResolutionErrorsInAllLibraries(); | |
7903 } | |
7904 | |
7905 /** | |
7906 * Record that an error occurred while attempting to resolve the source | |
7907 * represented by this entry. This will set the state of all resolution-based | |
7908 * information as being in error, but will not change the state of any parse | |
7909 * results. The [librarySource] is the source of the library in which | |
7910 * resolution was being performed. The [exception] is the exception that shows | |
7911 * where the error occurred. | |
7912 */ | |
7913 void recordResolutionErrorInLibrary( | |
7914 Source librarySource, CaughtException exception) { | |
7915 this.exception = exception; | |
7916 setState(ELEMENT, CacheState.ERROR); | |
7917 setState(IS_CLIENT, CacheState.ERROR); | |
7918 setState(IS_LAUNCHABLE, CacheState.ERROR); | |
7919 setState(PUBLIC_NAMESPACE, CacheState.ERROR); | |
7920 ResolutionState state = _getOrCreateResolutionState(librarySource); | |
7921 state.recordResolutionError(); | |
7922 } | |
7923 | |
7924 /** | |
7925 * Record that an [exception] occurred while attempting to scan or parse the | |
7926 * entry represented by this entry. This will set the state of all | |
7927 * information, including any resolution-based information, as being in error. | |
7928 */ | |
7929 @override | |
7930 void recordScanError(CaughtException exception) { | |
7931 super.recordScanError(exception); | |
7932 setState(SCAN_ERRORS, CacheState.ERROR); | |
7933 setState(TOKEN_STREAM, CacheState.ERROR); | |
7934 recordParseError(exception); | |
7935 } | |
7936 | |
7937 /** | |
7938 * Record that an [exception] occurred while attempting to generate errors and | |
7939 * warnings for the source represented by this entry. This will set the state | |
7940 * of all verification information as being in error. The [librarySource] is | |
7941 * the source of the library in which verification was being performed. The | |
7942 * [exception] is the exception that shows where the error occurred. | |
7943 */ | |
7944 void recordVerificationErrorInLibrary( | |
7945 Source librarySource, CaughtException exception) { | |
7946 this.exception = exception; | |
7947 ResolutionState state = _getOrCreateResolutionState(librarySource); | |
7948 state.recordVerificationError(); | |
7949 } | |
7950 | |
7951 /** | |
7952 * Remove the given [library] from the list of libraries that contain this | |
7953 * part. This method should only be invoked on entries that represent a part. | |
7954 */ | |
7955 void removeContainingLibrary(Source library) { | |
7956 _containingLibraries.remove(library); | |
7957 } | |
7958 | |
7959 /** | |
7960 * Remove any resolution information associated with this compilation unit | |
7961 * being part of the given [library], presumably because it is no longer part | |
7962 * of the library. | |
7963 */ | |
7964 void removeResolution(Source library) { | |
7965 if (library != null) { | |
7966 if (library == _resolutionState._librarySource) { | |
7967 if (_resolutionState._nextState == null) { | |
7968 _resolutionState.invalidateAllResolutionInformation(); | |
7969 } else { | |
7970 _resolutionState = _resolutionState._nextState; | |
7971 } | |
7972 } else { | |
7973 ResolutionState priorState = _resolutionState; | |
7974 ResolutionState state = _resolutionState._nextState; | |
7975 while (state != null) { | |
7976 if (library == state._librarySource) { | |
7977 priorState._nextState = state._nextState; | |
7978 break; | |
7979 } | |
7980 priorState = state; | |
7981 state = state._nextState; | |
7982 } | |
7983 } | |
7984 } | |
7985 } | |
7986 | |
7987 /** | |
7988 * Set the state of the data represented by the given [descriptor] in the | |
7989 * context of the given [library] to the given [state]. | |
7990 */ | |
7991 void setStateInLibrary( | |
7992 DataDescriptor descriptor, Source library, CacheState state) { | |
7993 if (!_isValidLibraryDescriptor(descriptor)) { | |
7994 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
7995 } | |
7996 ResolutionState resolutionState = _getOrCreateResolutionState(library); | |
7997 resolutionState.setState(descriptor, state); | |
7998 } | |
7999 | |
8000 /** | |
8001 * Set the value of the data represented by the given [descriptor] in the | |
8002 * context of the given [library] to the given [value], and set the state of | |
8003 * that data to [CacheState.VALID]. | |
8004 */ | |
8005 void setValueInLibrary( | |
8006 DataDescriptor descriptor, Source library, Object value) { | |
8007 if (!_isValidLibraryDescriptor(descriptor)) { | |
8008 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
8009 } | |
8010 ResolutionState state = _getOrCreateResolutionState(library); | |
8011 state.setValue(descriptor, value); | |
8012 } | |
8013 | |
8014 /** | |
8015 * Invalidate all of the resolution information associated with the | |
8016 * compilation unit. The flag [invalidateUris] should be `true` if the cached | |
8017 * results of converting URIs to source files should also be invalidated. | |
8018 */ | |
8019 void _discardCachedResolutionInformation(bool invalidateUris) { | |
8020 setState(ELEMENT, CacheState.INVALID); | |
8021 setState(IS_CLIENT, CacheState.INVALID); | |
8022 setState(IS_LAUNCHABLE, CacheState.INVALID); | |
8023 setState(PUBLIC_NAMESPACE, CacheState.INVALID); | |
8024 _resolutionState.invalidateAllResolutionInformation(); | |
8025 if (invalidateUris) { | |
8026 setState(EXPORTED_LIBRARIES, CacheState.INVALID); | |
8027 setState(IMPORTED_LIBRARIES, CacheState.INVALID); | |
8028 setState(INCLUDED_PARTS, CacheState.INVALID); | |
8029 } | |
8030 } | |
8031 | |
8032 /** | |
8033 * Return a resolution state for the specified [library], creating one as | |
8034 * necessary. | |
8035 */ | |
8036 ResolutionState _getOrCreateResolutionState(Source library) { | |
8037 ResolutionState state = _resolutionState; | |
8038 if (state._librarySource == null) { | |
8039 state._librarySource = library; | |
8040 return state; | |
8041 } | |
8042 while (state._librarySource != library) { | |
8043 if (state._nextState == null) { | |
8044 ResolutionState newState = new ResolutionState(); | |
8045 newState._librarySource = library; | |
8046 state._nextState = newState; | |
8047 return newState; | |
8048 } | |
8049 state = state._nextState; | |
8050 } | |
8051 return state; | |
8052 } | |
8053 | |
8054 @override | |
8055 bool _isValidDescriptor(DataDescriptor descriptor) { | |
8056 return descriptor == CONTAINING_LIBRARIES || | |
8057 descriptor == ELEMENT || | |
8058 descriptor == EXPORTED_LIBRARIES || | |
8059 descriptor == IMPORTED_LIBRARIES || | |
8060 descriptor == INCLUDED_PARTS || | |
8061 descriptor == IS_CLIENT || | |
8062 descriptor == IS_LAUNCHABLE || | |
8063 descriptor == PARSED_UNIT || | |
8064 descriptor == PARSE_ERRORS || | |
8065 descriptor == PUBLIC_NAMESPACE || | |
8066 descriptor == SCAN_ERRORS || | |
8067 descriptor == SOURCE_KIND || | |
8068 descriptor == TOKEN_STREAM || | |
8069 super._isValidDescriptor(descriptor); | |
8070 } | |
8071 | |
8072 /** | |
8073 * Return `true` if the [descriptor] is valid for this entry when the data is | |
8074 * relative to a library. | |
8075 */ | |
8076 bool _isValidLibraryDescriptor(DataDescriptor descriptor) { | |
8077 return descriptor == BUILT_ELEMENT || | |
8078 descriptor == BUILT_UNIT || | |
8079 descriptor == HINTS || | |
8080 descriptor == LINTS || | |
8081 descriptor == RESOLUTION_ERRORS || | |
8082 descriptor == RESOLVED_UNIT || | |
8083 descriptor == VERIFICATION_ERRORS; | |
8084 } | |
8085 | |
8086 @override | |
8087 bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) { | |
8088 bool needsSeparator = super._writeDiffOn(buffer, oldEntry); | |
8089 if (oldEntry is! DartEntry) { | |
8090 if (needsSeparator) { | |
8091 buffer.write("; "); | |
8092 } | |
8093 buffer.write("entry type changed; was "); | |
8094 buffer.write(oldEntry.runtimeType.toString()); | |
8095 return true; | |
8096 } | |
8097 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "tokenStream", | |
8098 DartEntry.TOKEN_STREAM, oldEntry); | |
8099 needsSeparator = _writeStateDiffOn( | |
8100 buffer, needsSeparator, "scanErrors", DartEntry.SCAN_ERRORS, oldEntry); | |
8101 needsSeparator = _writeStateDiffOn( | |
8102 buffer, needsSeparator, "sourceKind", DartEntry.SOURCE_KIND, oldEntry); | |
8103 needsSeparator = _writeStateDiffOn( | |
8104 buffer, needsSeparator, "parsedUnit", DartEntry.PARSED_UNIT, oldEntry); | |
8105 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "parseErrors", | |
8106 DartEntry.PARSE_ERRORS, oldEntry); | |
8107 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
8108 "importedLibraries", DartEntry.IMPORTED_LIBRARIES, oldEntry); | |
8109 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
8110 "exportedLibraries", DartEntry.EXPORTED_LIBRARIES, oldEntry); | |
8111 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "includedParts", | |
8112 DartEntry.INCLUDED_PARTS, oldEntry); | |
8113 needsSeparator = _writeStateDiffOn( | |
8114 buffer, needsSeparator, "element", DartEntry.ELEMENT, oldEntry); | |
8115 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
8116 "publicNamespace", DartEntry.PUBLIC_NAMESPACE, oldEntry); | |
8117 needsSeparator = _writeStateDiffOn( | |
8118 buffer, needsSeparator, "clientServer", DartEntry.IS_CLIENT, oldEntry); | |
8119 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "launchable", | |
8120 DartEntry.IS_LAUNCHABLE, oldEntry); | |
8121 // TODO(brianwilkerson) Add better support for containingLibraries. | |
8122 // It would be nice to be able to report on size-preserving changes. | |
8123 int oldLibraryCount = (oldEntry as DartEntry)._containingLibraries.length; | |
8124 int libraryCount = _containingLibraries.length; | |
8125 if (oldLibraryCount != libraryCount) { | |
8126 if (needsSeparator) { | |
8127 buffer.write("; "); | |
8128 } | |
8129 buffer.write("containingLibraryCount = "); | |
8130 buffer.write(oldLibraryCount); | |
8131 buffer.write(" -> "); | |
8132 buffer.write(libraryCount); | |
8133 needsSeparator = true; | |
8134 } | |
8135 // | |
8136 // Report change to the per-library state. | |
8137 // | |
8138 HashMap<Source, ResolutionState> oldStateMap = | |
8139 new HashMap<Source, ResolutionState>(); | |
8140 ResolutionState state = (oldEntry as DartEntry)._resolutionState; | |
8141 while (state != null) { | |
8142 Source librarySource = state._librarySource; | |
8143 if (librarySource != null) { | |
8144 oldStateMap[librarySource] = state; | |
8145 } | |
8146 state = state._nextState; | |
8147 } | |
8148 state = _resolutionState; | |
8149 while (state != null) { | |
8150 Source librarySource = state._librarySource; | |
8151 if (librarySource != null) { | |
8152 ResolutionState oldState = oldStateMap.remove(librarySource); | |
8153 if (oldState == null) { | |
8154 if (needsSeparator) { | |
8155 buffer.write("; "); | |
8156 } | |
8157 buffer.write("added resolution for "); | |
8158 buffer.write(librarySource.fullName); | |
8159 needsSeparator = true; | |
8160 } else { | |
8161 needsSeparator = oldState._writeDiffOn( | |
8162 buffer, needsSeparator, oldEntry as DartEntry); | |
8163 } | |
8164 } | |
8165 state = state._nextState; | |
8166 } | |
8167 for (Source librarySource in oldStateMap.keys.toSet()) { | |
8168 if (needsSeparator) { | |
8169 buffer.write("; "); | |
8170 } | |
8171 buffer.write("removed resolution for "); | |
8172 buffer.write(librarySource.fullName); | |
8173 needsSeparator = true; | |
8174 } | |
8175 return needsSeparator; | |
8176 } | |
8177 | |
8178 @override | |
8179 void _writeOn(StringBuffer buffer) { | |
8180 buffer.write("Dart: "); | |
8181 super._writeOn(buffer); | |
8182 _writeStateOn(buffer, "tokenStream", TOKEN_STREAM); | |
8183 _writeStateOn(buffer, "scanErrors", SCAN_ERRORS); | |
8184 _writeStateOn(buffer, "sourceKind", SOURCE_KIND); | |
8185 _writeStateOn(buffer, "parsedUnit", PARSED_UNIT); | |
8186 _writeStateOn(buffer, "parseErrors", PARSE_ERRORS); | |
8187 _writeStateOn(buffer, "exportedLibraries", EXPORTED_LIBRARIES); | |
8188 _writeStateOn(buffer, "importedLibraries", IMPORTED_LIBRARIES); | |
8189 _writeStateOn(buffer, "includedParts", INCLUDED_PARTS); | |
8190 _writeStateOn(buffer, "element", ELEMENT); | |
8191 _writeStateOn(buffer, "publicNamespace", PUBLIC_NAMESPACE); | |
8192 _writeStateOn(buffer, "clientServer", IS_CLIENT); | |
8193 _writeStateOn(buffer, "launchable", IS_LAUNCHABLE); | |
8194 _resolutionState._writeOn(buffer); | |
8195 } | |
8196 } | |
8197 | |
8198 /** | |
8199 * An immutable constant representing data that can be stored in the cache. | |
8200 */ | |
8201 class DataDescriptor<E> { | |
8202 /** | |
8203 * The next artificial hash code. | |
8204 */ | |
8205 static int _NEXT_HASH_CODE = 0; | |
8206 | |
8207 /** | |
8208 * The artifitial hash code for this object. | |
8209 */ | |
8210 final int _hashCode = _NEXT_HASH_CODE++; | |
8211 | |
8212 /** | |
8213 * The name of the descriptor, used for debugging purposes. | |
8214 */ | |
8215 final String _name; | |
8216 | |
8217 /** | |
8218 * The default value used when the data does not exist. | |
8219 */ | |
8220 final E defaultValue; | |
8221 | |
8222 /** | |
8223 * Initialize a newly created descriptor to have the given [name] and | |
8224 * [defaultValue]. | |
8225 */ | |
8226 DataDescriptor(this._name, [this.defaultValue = null]); | |
8227 | |
8228 @override | |
8229 int get hashCode => _hashCode; | |
8230 | |
8231 @override | |
8232 String toString() => _name; | |
8233 } | |
8234 | |
8235 /** | |
8236 * A retention policy that will keep AST's in the cache if there is analysis | |
8237 * information that needs to be computed for a source, where the computation is | |
8238 * dependent on having the AST. | |
8239 */ | |
8240 class DefaultRetentionPolicy implements CacheRetentionPolicy { | |
8241 /** | |
8242 * An instance of this class that can be shared. | |
8243 */ | |
8244 static DefaultRetentionPolicy POLICY = new DefaultRetentionPolicy(); | |
8245 | |
8246 /** | |
8247 * Return `true` if there is analysis information in the given [dartEntry] | |
8248 * that needs to be computed, where the computation is dependent on having the | |
8249 * AST. | |
8250 */ | |
8251 bool astIsNeeded(DartEntry dartEntry) => | |
8252 dartEntry.hasInvalidData(DartEntry.HINTS) || | |
8253 dartEntry.hasInvalidData(DartEntry.LINTS) || | |
8254 dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) || | |
8255 dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS); | |
8256 | |
8257 @override | |
8258 RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) { | |
8259 if (sourceEntry is DartEntry) { | |
8260 DartEntry dartEntry = sourceEntry; | |
8261 if (astIsNeeded(dartEntry)) { | |
8262 return RetentionPriority.MEDIUM; | |
8263 } | |
8264 } | |
8265 return RetentionPriority.LOW; | |
8266 } | |
8267 } | |
8268 | |
8269 /** | |
8270 * Instances of the class `GenerateDartErrorsTask` generate errors and warnings
for a single | |
8271 * Dart source. | |
8272 */ | |
8273 class GenerateDartErrorsTask extends AnalysisTask { | |
8274 /** | |
8275 * The source for which errors and warnings are to be produced. | |
8276 */ | |
8277 final Source source; | |
8278 | |
8279 /** | |
8280 * The compilation unit used to resolve the dependencies. | |
8281 */ | |
8282 final CompilationUnit _unit; | |
8283 | |
8284 /** | |
8285 * The element model for the library containing the source. | |
8286 */ | |
8287 final LibraryElement libraryElement; | |
8288 | |
8289 /** | |
8290 * The errors that were generated for the source. | |
8291 */ | |
8292 List<AnalysisError> _errors; | |
8293 | |
8294 /** | |
8295 * Initialize a newly created task to perform analysis within the given contex
t. | |
8296 * | |
8297 * @param context the context in which the task is to be performed | |
8298 * @param source the source for which errors and warnings are to be produced | |
8299 * @param unit the compilation unit used to resolve the dependencies | |
8300 * @param libraryElement the element model for the library containing the sour
ce | |
8301 */ | |
8302 GenerateDartErrorsTask(InternalAnalysisContext context, this.source, | |
8303 this._unit, this.libraryElement) | |
8304 : super(context); | |
8305 | |
8306 /** | |
8307 * Return the errors that were generated for the source. | |
8308 * | |
8309 * @return the errors that were generated for the source | |
8310 */ | |
8311 List<AnalysisError> get errors => _errors; | |
8312 | |
8313 @override | |
8314 String get taskDescription => | |
8315 "generate errors and warnings for ${source.fullName}"; | |
8316 | |
8317 @override | |
8318 accept(AnalysisTaskVisitor visitor) => | |
8319 visitor.visitGenerateDartErrorsTask(this); | |
8320 | |
8321 @override | |
8322 void internalPerform() { | |
8323 PerformanceStatistics.errors.makeCurrentWhile(() { | |
8324 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
8325 ErrorReporter errorReporter = new ErrorReporter(errorListener, source); | |
8326 TypeProvider typeProvider = context.typeProvider; | |
8327 // | |
8328 // Validate the directives | |
8329 // | |
8330 validateDirectives(context, source, _unit, errorListener); | |
8331 // | |
8332 // Use the ConstantVerifier to verify the use of constants. | |
8333 // This needs to happen before using the ErrorVerifier because some error | |
8334 // codes need the computed constant values. | |
8335 // | |
8336 // TODO(paulberry): as a temporary workaround for issue 21572, | |
8337 // ConstantVerifier is being run right after ConstantValueComputer, so we | |
8338 // don't need to run it here. Once issue 21572 is fixed, re-enable the | |
8339 // call to ConstantVerifier. | |
8340 // ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
libraryElement, typeProvider); | |
8341 // _unit.accept(constantVerifier); | |
8342 // | |
8343 // Use the ErrorVerifier to compute the rest of the errors. | |
8344 // | |
8345 ErrorVerifier errorVerifier = new ErrorVerifier(errorReporter, | |
8346 libraryElement, typeProvider, new InheritanceManager(libraryElement)); | |
8347 _unit.accept(errorVerifier); | |
8348 _errors = errorListener.getErrorsForSource(source); | |
8349 }); | |
8350 } | |
8351 | |
8352 /** | |
8353 * Check each directive in the given compilation unit to see if the referenced
source exists and | |
8354 * report an error if it does not. | |
8355 * | |
8356 * @param context the context in which the library exists | |
8357 * @param librarySource the source representing the library containing the dir
ectives | |
8358 * @param unit the compilation unit containing the directives to be validated | |
8359 * @param errorListener the error listener to which errors should be reported | |
8360 */ | |
8361 static void validateDirectives(AnalysisContext context, Source librarySource, | |
8362 CompilationUnit unit, AnalysisErrorListener errorListener) { | |
8363 for (Directive directive in unit.directives) { | |
8364 if (directive is UriBasedDirective) { | |
8365 validateReferencedSource( | |
8366 context, librarySource, directive, errorListener); | |
8367 } | |
8368 } | |
8369 } | |
8370 | |
8371 /** | |
8372 * Check the given directive to see if the referenced source exists and report
an error if it does | |
8373 * not. | |
8374 * | |
8375 * @param context the context in which the library exists | |
8376 * @param librarySource the source representing the library containing the dir
ective | |
8377 * @param directive the directive to be verified | |
8378 * @param errorListener the error listener to which errors should be reported | |
8379 */ | |
8380 static void validateReferencedSource(AnalysisContext context, | |
8381 Source librarySource, UriBasedDirective directive, | |
8382 AnalysisErrorListener errorListener) { | |
8383 Source source = directive.source; | |
8384 if (source != null) { | |
8385 if (context.exists(source)) { | |
8386 return; | |
8387 } | |
8388 } else { | |
8389 // Don't report errors already reported by ParseDartTask.resolveDirective | |
8390 if (directive.validate() != null) { | |
8391 return; | |
8392 } | |
8393 } | |
8394 StringLiteral uriLiteral = directive.uri; | |
8395 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
8396 uriLiteral.length, CompileTimeErrorCode.URI_DOES_NOT_EXIST, | |
8397 [directive.uriContent])); | |
8398 } | |
8399 } | |
8400 | |
8401 /** | |
8402 * Instances of the class `GenerateDartHintsTask` generate hints for a single Da
rt library. | |
8403 */ | |
8404 class GenerateDartHintsTask extends AnalysisTask { | |
8405 /** | |
8406 * The compilation units that comprise the library, with the defining compilat
ion unit appearing | |
8407 * first in the list. | |
8408 */ | |
8409 final List<TimestampedData<CompilationUnit>> _units; | |
8410 | |
8411 /** | |
8412 * The element model for the library being analyzed. | |
8413 */ | |
8414 final LibraryElement libraryElement; | |
8415 | |
8416 /** | |
8417 * A table mapping the sources that were analyzed to the hints that were | |
8418 * generated for the sources. | |
8419 */ | |
8420 HashMap<Source, List<AnalysisError>> _hintMap; | |
8421 | |
8422 /** | |
8423 * Initialize a newly created task to perform analysis within the given contex
t. | |
8424 * | |
8425 * @param context the context in which the task is to be performed | |
8426 * @param units the compilation units that comprise the library, with the defi
ning compilation | |
8427 * unit appearing first in the list | |
8428 * @param libraryElement the element model for the library being analyzed | |
8429 */ | |
8430 GenerateDartHintsTask( | |
8431 InternalAnalysisContext context, this._units, this.libraryElement) | |
8432 : super(context); | |
8433 | |
8434 /** | |
8435 * Return a table mapping the sources that were analyzed to the hints that wer
e generated for the | |
8436 * sources, or `null` if the task has not been performed or if the analysis di
d not complete | |
8437 * normally. | |
8438 * | |
8439 * @return a table mapping the sources that were analyzed to the hints that we
re generated for the | |
8440 * sources | |
8441 */ | |
8442 HashMap<Source, List<AnalysisError>> get hintMap => _hintMap; | |
8443 | |
8444 @override | |
8445 String get taskDescription { | |
8446 Source librarySource = libraryElement.source; | |
8447 if (librarySource == null) { | |
8448 return "generate Dart hints for library without source"; | |
8449 } | |
8450 return "generate Dart hints for ${librarySource.fullName}"; | |
8451 } | |
8452 | |
8453 @override | |
8454 accept(AnalysisTaskVisitor visitor) => | |
8455 visitor.visitGenerateDartHintsTask(this); | |
8456 | |
8457 @override | |
8458 void internalPerform() { | |
8459 // | |
8460 // Gather the compilation units. | |
8461 // | |
8462 int unitCount = _units.length; | |
8463 List<CompilationUnit> compilationUnits = | |
8464 new List<CompilationUnit>(unitCount); | |
8465 for (int i = 0; i < unitCount; i++) { | |
8466 compilationUnits[i] = _units[i].data; | |
8467 } | |
8468 // | |
8469 // Analyze all of the units. | |
8470 // | |
8471 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
8472 HintGenerator hintGenerator = | |
8473 new HintGenerator(compilationUnits, context, errorListener); | |
8474 hintGenerator.generateForLibrary(); | |
8475 // | |
8476 // Store the results. | |
8477 // | |
8478 _hintMap = new HashMap<Source, List<AnalysisError>>(); | |
8479 for (int i = 0; i < unitCount; i++) { | |
8480 Source source = _units[i].data.element.source; | |
8481 _hintMap[source] = errorListener.getErrorsForSource(source); | |
8482 } | |
8483 } | |
8484 } | |
8485 | |
8486 /// Generates lint feedback for a single Dart library. | |
8487 class GenerateDartLintsTask extends AnalysisTask { | |
8488 | |
8489 ///The compilation units that comprise the library, with the defining | |
8490 ///compilation unit appearing first in the list. | |
8491 final List<TimestampedData<CompilationUnit>> _units; | |
8492 | |
8493 /// The element model for the library being analyzed. | |
8494 final LibraryElement libraryElement; | |
8495 | |
8496 /// A mapping of analyzed sources to their associated lint warnings. | |
8497 /// May be [null] if the task has not been performed or if analysis did not | |
8498 /// complete normally. | |
8499 HashMap<Source, List<AnalysisError>> lintMap; | |
8500 | |
8501 /// Initialize a newly created task to perform lint checking over these | |
8502 /// [_units] belonging to this [libraryElement] within the given [context]. | |
8503 GenerateDartLintsTask(context, this._units, this.libraryElement) | |
8504 : super(context); | |
8505 | |
8506 @override | |
8507 String get taskDescription { | |
8508 Source librarySource = libraryElement.source; | |
8509 return (librarySource == null) | |
8510 ? "generate Dart lints for library without source" | |
8511 : "generate Dart lints for ${librarySource.fullName}"; | |
8512 } | |
8513 | |
8514 @override | |
8515 accept(AnalysisTaskVisitor visitor) => | |
8516 visitor.visitGenerateDartLintsTask(this); | |
8517 | |
8518 @override | |
8519 void internalPerform() { | |
8520 Iterable<CompilationUnit> compilationUnits = | |
8521 _units.map((TimestampedData<CompilationUnit> unit) => unit.data); | |
8522 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
8523 LintGenerator lintGenerator = | |
8524 new LintGenerator(compilationUnits, errorListener); | |
8525 lintGenerator.generate(); | |
8526 | |
8527 lintMap = new HashMap<Source, List<AnalysisError>>(); | |
8528 compilationUnits.forEach((CompilationUnit unit) { | |
8529 Source source = unit.element.source; | |
8530 lintMap[source] = errorListener.getErrorsForSource(source); | |
8531 }); | |
8532 } | |
8533 } | |
8534 | |
8535 /** | |
8536 * Instances of the class `GetContentTask` get the contents of a source. | |
8537 */ | |
8538 class GetContentTask extends AnalysisTask { | |
8539 /** | |
8540 * The source to be read. | |
8541 */ | |
8542 final Source source; | |
8543 | |
8544 /** | |
8545 * A flag indicating whether this task is complete. | |
8546 */ | |
8547 bool _complete = false; | |
8548 | |
8549 /** | |
8550 * The contents of the source. | |
8551 */ | |
8552 String _content; | |
8553 | |
8554 /** | |
8555 * The errors that were produced by getting the source content. | |
8556 */ | |
8557 final List<AnalysisError> errors = <AnalysisError>[]; | |
8558 | |
8559 /** | |
8560 * The time at which the contents of the source were last modified. | |
8561 */ | |
8562 int _modificationTime = -1; | |
8563 | |
8564 /** | |
8565 * Initialize a newly created task to perform analysis within the given contex
t. | |
8566 * | |
8567 * @param context the context in which the task is to be performed | |
8568 * @param source the source to be parsed | |
8569 * @param contentData the time-stamped contents of the source | |
8570 */ | |
8571 GetContentTask(InternalAnalysisContext context, this.source) | |
8572 : super(context) { | |
8573 if (source == null) { | |
8574 throw new IllegalArgumentException("Cannot get contents of null source"); | |
8575 } | |
8576 } | |
8577 | |
8578 /** | |
8579 * Return the contents of the source, or `null` if the task has not completed
or if there | |
8580 * was an exception while getting the contents. | |
8581 * | |
8582 * @return the contents of the source | |
8583 */ | |
8584 String get content => _content; | |
8585 | |
8586 /** | |
8587 * Return `true` if this task is complete. Unlike most tasks, this task is all
owed to be | |
8588 * visited more than once in order to support asynchronous IO. If the task is
not complete when it | |
8589 * is visited synchronously as part of the [AnalysisTask.perform] | |
8590 * method, it will be visited again, using the same visitor, when the IO opera
tion has been | |
8591 * performed. | |
8592 * | |
8593 * @return `true` if this task is complete | |
8594 */ | |
8595 bool get isComplete => _complete; | |
8596 | |
8597 /** | |
8598 * Return the time at which the contents of the source that was parsed were la
st modified, or a | |
8599 * negative value if the task has not yet been performed or if an exception oc
curred. | |
8600 * | |
8601 * @return the time at which the contents of the source that was parsed were l
ast modified | |
8602 */ | |
8603 int get modificationTime => _modificationTime; | |
8604 | |
8605 @override | |
8606 String get taskDescription => "get contents of ${source.fullName}"; | |
8607 | |
8608 @override | |
8609 accept(AnalysisTaskVisitor visitor) => visitor.visitGetContentTask(this); | |
8610 | |
8611 @override | |
8612 void internalPerform() { | |
8613 _complete = true; | |
8614 try { | |
8615 TimestampedData<String> data = context.getContents(source); | |
8616 _content = data.data; | |
8617 _modificationTime = data.modificationTime; | |
8618 AnalysisEngine.instance.instrumentationService.logFileRead( | |
8619 source.fullName, _modificationTime, _content); | |
8620 } catch (exception, stackTrace) { | |
8621 errors.add(new AnalysisError( | |
8622 source, 0, 0, ScannerErrorCode.UNABLE_GET_CONTENT, [exception])); | |
8623 throw new AnalysisException("Could not get contents of $source", | |
8624 new CaughtException(exception, stackTrace)); | |
8625 } | |
8626 } | |
8627 } | |
8628 | |
8629 /** | |
8630 * The information cached by an analysis context about an individual HTML file. | |
8631 */ | |
8632 class HtmlEntry extends SourceEntry { | |
8633 /** | |
8634 * The data descriptor representing the HTML element. | |
8635 */ | |
8636 static final DataDescriptor<HtmlElement> ELEMENT = | |
8637 new DataDescriptor<HtmlElement>("HtmlEntry.ELEMENT"); | |
8638 | |
8639 /** | |
8640 * The data descriptor representing the hints resulting from auditing the | |
8641 * source. | |
8642 */ | |
8643 static final DataDescriptor<List<AnalysisError>> HINTS = | |
8644 new DataDescriptor<List<AnalysisError>>( | |
8645 "HtmlEntry.HINTS", AnalysisError.NO_ERRORS); | |
8646 | |
8647 /** | |
8648 * The data descriptor representing the errors resulting from parsing the | |
8649 * source. | |
8650 */ | |
8651 static final DataDescriptor<List<AnalysisError>> PARSE_ERRORS = | |
8652 new DataDescriptor<List<AnalysisError>>( | |
8653 "HtmlEntry.PARSE_ERRORS", AnalysisError.NO_ERRORS); | |
8654 | |
8655 /** | |
8656 * The data descriptor representing the parsed AST structure. | |
8657 */ | |
8658 static final DataDescriptor<ht.HtmlUnit> PARSED_UNIT = | |
8659 new DataDescriptor<ht.HtmlUnit>("HtmlEntry.PARSED_UNIT"); | |
8660 | |
8661 /** | |
8662 * The data descriptor representing the resolved AST structure. | |
8663 */ | |
8664 static final DataDescriptor<ht.HtmlUnit> RESOLVED_UNIT = | |
8665 new DataDescriptor<ht.HtmlUnit>("HtmlEntry.RESOLVED_UNIT"); | |
8666 | |
8667 /** | |
8668 * The data descriptor representing the list of referenced libraries. | |
8669 */ | |
8670 static final DataDescriptor<List<Source>> REFERENCED_LIBRARIES = | |
8671 new DataDescriptor<List<Source>>( | |
8672 "HtmlEntry.REFERENCED_LIBRARIES", Source.EMPTY_LIST); | |
8673 | |
8674 /** | |
8675 * The data descriptor representing the errors resulting from resolving the | |
8676 * source. | |
8677 */ | |
8678 static final DataDescriptor<List<AnalysisError>> RESOLUTION_ERRORS = | |
8679 new DataDescriptor<List<AnalysisError>>( | |
8680 "HtmlEntry.RESOLUTION_ERRORS", AnalysisError.NO_ERRORS); | |
8681 | |
8682 /** | |
8683 * Return all of the errors associated with the HTML file that are currently | |
8684 * cached. | |
8685 */ | |
8686 List<AnalysisError> get allErrors { | |
8687 List<AnalysisError> errors = new List<AnalysisError>(); | |
8688 errors.addAll(super.allErrors); | |
8689 errors.addAll(getValue(PARSE_ERRORS)); | |
8690 errors.addAll(getValue(RESOLUTION_ERRORS)); | |
8691 errors.addAll(getValue(HINTS)); | |
8692 if (errors.length == 0) { | |
8693 return AnalysisError.NO_ERRORS; | |
8694 } | |
8695 return errors; | |
8696 } | |
8697 | |
8698 /** | |
8699 * Return a valid parsed unit, either an unresolved AST structure or the | |
8700 * result of resolving the AST structure, or `null` if there is no parsed unit | |
8701 * available. | |
8702 */ | |
8703 ht.HtmlUnit get anyParsedUnit { | |
8704 if (getState(PARSED_UNIT) == CacheState.VALID) { | |
8705 return getValue(PARSED_UNIT); | |
8706 } | |
8707 if (getState(RESOLVED_UNIT) == CacheState.VALID) { | |
8708 return getValue(RESOLVED_UNIT); | |
8709 } | |
8710 return null; | |
8711 } | |
8712 | |
8713 @override | |
8714 List<DataDescriptor> get descriptors { | |
8715 List<DataDescriptor> result = super.descriptors; | |
8716 result.addAll([ | |
8717 HtmlEntry.ELEMENT, | |
8718 HtmlEntry.PARSE_ERRORS, | |
8719 HtmlEntry.PARSED_UNIT, | |
8720 HtmlEntry.RESOLUTION_ERRORS, | |
8721 HtmlEntry.RESOLVED_UNIT, | |
8722 HtmlEntry.HINTS | |
8723 ]); | |
8724 return result; | |
8725 } | |
8726 | |
8727 @override | |
8728 SourceKind get kind => SourceKind.HTML; | |
8729 | |
8730 /** | |
8731 * Flush any AST structures being maintained by this entry. | |
8732 */ | |
8733 void flushAstStructures() { | |
8734 _flush(PARSED_UNIT); | |
8735 _flush(RESOLVED_UNIT); | |
8736 } | |
8737 | |
8738 @override | |
8739 void invalidateAllInformation() { | |
8740 super.invalidateAllInformation(); | |
8741 setState(PARSE_ERRORS, CacheState.INVALID); | |
8742 setState(PARSED_UNIT, CacheState.INVALID); | |
8743 setState(RESOLVED_UNIT, CacheState.INVALID); | |
8744 invalidateAllResolutionInformation(true); | |
8745 } | |
8746 | |
8747 /** | |
8748 * Invalidate all of the resolution information associated with the HTML file. | |
8749 * If [invalidateUris] is `true`, the cached results of converting URIs to | |
8750 * source files should also be invalidated. | |
8751 */ | |
8752 void invalidateAllResolutionInformation(bool invalidateUris) { | |
8753 setState(RESOLVED_UNIT, CacheState.INVALID); | |
8754 setState(ELEMENT, CacheState.INVALID); | |
8755 setState(RESOLUTION_ERRORS, CacheState.INVALID); | |
8756 setState(HINTS, CacheState.INVALID); | |
8757 if (invalidateUris) { | |
8758 setState(REFERENCED_LIBRARIES, CacheState.INVALID); | |
8759 } | |
8760 } | |
8761 | |
8762 /** | |
8763 * Invalidate all of the parse and resolution information associated with | |
8764 * this source. | |
8765 */ | |
8766 void invalidateParseInformation() { | |
8767 setState(PARSE_ERRORS, CacheState.INVALID); | |
8768 setState(PARSED_UNIT, CacheState.INVALID); | |
8769 invalidateAllResolutionInformation(true); | |
8770 } | |
8771 | |
8772 @override | |
8773 void recordContentError(CaughtException exception) { | |
8774 super.recordContentError(exception); | |
8775 recordParseError(exception); | |
8776 } | |
8777 | |
8778 /** | |
8779 * Record that an [exception] was encountered while attempting to parse the | |
8780 * source associated with this entry. | |
8781 */ | |
8782 void recordParseError(CaughtException exception) { | |
8783 // If the scanning and parsing of HTML are separated, | |
8784 // the following line can be removed. | |
8785 recordScanError(exception); | |
8786 setState(PARSE_ERRORS, CacheState.ERROR); | |
8787 setState(PARSED_UNIT, CacheState.ERROR); | |
8788 setState(REFERENCED_LIBRARIES, CacheState.ERROR); | |
8789 recordResolutionError(exception); | |
8790 } | |
8791 | |
8792 /** | |
8793 * Record that an [exception] was encountered while attempting to resolve the | |
8794 * source associated with this entry. | |
8795 */ | |
8796 void recordResolutionError(CaughtException exception) { | |
8797 this.exception = exception; | |
8798 setState(RESOLVED_UNIT, CacheState.ERROR); | |
8799 setState(ELEMENT, CacheState.ERROR); | |
8800 setState(RESOLUTION_ERRORS, CacheState.ERROR); | |
8801 setState(HINTS, CacheState.ERROR); | |
8802 } | |
8803 | |
8804 @override | |
8805 bool _isValidDescriptor(DataDescriptor descriptor) { | |
8806 return descriptor == ELEMENT || | |
8807 descriptor == HINTS || | |
8808 descriptor == PARSED_UNIT || | |
8809 descriptor == PARSE_ERRORS || | |
8810 descriptor == REFERENCED_LIBRARIES || | |
8811 descriptor == RESOLUTION_ERRORS || | |
8812 descriptor == RESOLVED_UNIT || | |
8813 super._isValidDescriptor(descriptor); | |
8814 } | |
8815 | |
8816 @override | |
8817 bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) { | |
8818 bool needsSeparator = super._writeDiffOn(buffer, oldEntry); | |
8819 if (oldEntry is! HtmlEntry) { | |
8820 if (needsSeparator) { | |
8821 buffer.write("; "); | |
8822 } | |
8823 buffer.write("entry type changed; was "); | |
8824 buffer.write(oldEntry.runtimeType); | |
8825 return true; | |
8826 } | |
8827 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "parseErrors", | |
8828 HtmlEntry.PARSE_ERRORS, oldEntry); | |
8829 needsSeparator = _writeStateDiffOn( | |
8830 buffer, needsSeparator, "parsedUnit", HtmlEntry.PARSED_UNIT, oldEntry); | |
8831 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "resolvedUnit", | |
8832 HtmlEntry.RESOLVED_UNIT, oldEntry); | |
8833 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
8834 "resolutionErrors", HtmlEntry.RESOLUTION_ERRORS, oldEntry); | |
8835 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
8836 "referencedLibraries", HtmlEntry.REFERENCED_LIBRARIES, oldEntry); | |
8837 needsSeparator = _writeStateDiffOn( | |
8838 buffer, needsSeparator, "element", HtmlEntry.ELEMENT, oldEntry); | |
8839 return needsSeparator; | |
8840 } | |
8841 | |
8842 @override | |
8843 void _writeOn(StringBuffer buffer) { | |
8844 buffer.write("Html: "); | |
8845 super._writeOn(buffer); | |
8846 _writeStateOn(buffer, "parseErrors", PARSE_ERRORS); | |
8847 _writeStateOn(buffer, "parsedUnit", PARSED_UNIT); | |
8848 _writeStateOn(buffer, "resolvedUnit", RESOLVED_UNIT); | |
8849 _writeStateOn(buffer, "resolutionErrors", RESOLUTION_ERRORS); | |
8850 _writeStateOn(buffer, "referencedLibraries", REFERENCED_LIBRARIES); | |
8851 _writeStateOn(buffer, "element", ELEMENT); | |
8852 } | |
8853 } | |
8854 | |
8855 /** | |
8856 * Instances of the class `IncrementalAnalysisCache` hold information used to pe
rform | |
8857 * incremental analysis. | |
8858 * | |
8859 * See [AnalysisContextImpl.setChangedContents]. | |
8860 */ | |
8861 class IncrementalAnalysisCache { | |
8862 final Source librarySource; | |
8863 | |
8864 final Source source; | |
8865 | |
8866 final String oldContents; | |
8867 | |
8868 final CompilationUnit resolvedUnit; | |
8869 | |
8870 String _newContents; | |
8871 | |
8872 int _offset = 0; | |
8873 | |
8874 int _oldLength = 0; | |
8875 | |
8876 int _newLength = 0; | |
8877 | |
8878 IncrementalAnalysisCache(this.librarySource, this.source, this.resolvedUnit, | |
8879 this.oldContents, String newContents, int offset, int oldLength, | |
8880 int newLength) { | |
8881 this._newContents = newContents; | |
8882 this._offset = offset; | |
8883 this._oldLength = oldLength; | |
8884 this._newLength = newLength; | |
8885 } | |
8886 | |
8887 /** | |
8888 * Determine if the cache contains source changes that need to be analyzed | |
8889 * | |
8890 * @return `true` if the cache contains changes to be analyzed, else `false` | |
8891 */ | |
8892 bool get hasWork => _oldLength > 0 || _newLength > 0; | |
8893 | |
8894 /** | |
8895 * Return the current contents for the receiver's source. | |
8896 * | |
8897 * @return the contents (not `null`) | |
8898 */ | |
8899 String get newContents => _newContents; | |
8900 | |
8901 /** | |
8902 * Return the number of characters in the replacement text. | |
8903 * | |
8904 * @return the replacement length (zero or greater) | |
8905 */ | |
8906 int get newLength => _newLength; | |
8907 | |
8908 /** | |
8909 * Return the character position of the first changed character. | |
8910 * | |
8911 * @return the offset (zero or greater) | |
8912 */ | |
8913 int get offset => _offset; | |
8914 | |
8915 /** | |
8916 * Return the number of characters that were replaced. | |
8917 * | |
8918 * @return the replaced length (zero or greater) | |
8919 */ | |
8920 int get oldLength => _oldLength; | |
8921 | |
8922 /** | |
8923 * Determine if the incremental analysis result can be cached for the next inc
remental analysis. | |
8924 * | |
8925 * @param cache the prior incremental analysis cache | |
8926 * @param unit the incrementally updated compilation unit | |
8927 * @return the cache used for incremental analysis or `null` if incremental an
alysis results | |
8928 * cannot be cached for the next incremental analysis | |
8929 */ | |
8930 static IncrementalAnalysisCache cacheResult( | |
8931 IncrementalAnalysisCache cache, CompilationUnit unit) { | |
8932 if (cache != null && unit != null) { | |
8933 return new IncrementalAnalysisCache(cache.librarySource, cache.source, | |
8934 unit, cache._newContents, cache._newContents, 0, 0, 0); | |
8935 } | |
8936 return null; | |
8937 } | |
8938 | |
8939 /** | |
8940 * Determine if the cache should be cleared. | |
8941 * | |
8942 * @param cache the prior cache or `null` if none | |
8943 * @param source the source being updated (not `null`) | |
8944 * @return the cache used for incremental analysis or `null` if incremental an
alysis cannot | |
8945 * be performed | |
8946 */ | |
8947 static IncrementalAnalysisCache clear( | |
8948 IncrementalAnalysisCache cache, Source source) { | |
8949 if (cache == null || cache.source == source) { | |
8950 return null; | |
8951 } | |
8952 return cache; | |
8953 } | |
8954 | |
8955 /** | |
8956 * Determine if incremental analysis can be performed from the given informati
on. | |
8957 * | |
8958 * @param cache the prior cache or `null` if none | |
8959 * @param source the source being updated (not `null`) | |
8960 * @param oldContents the original source contents prior to this update (may b
e `null`) | |
8961 * @param newContents the new contents after this incremental change (not `nul
l`) | |
8962 * @param offset the offset at which the change occurred | |
8963 * @param oldLength the length of the text being replaced | |
8964 * @param newLength the length of the replacement text | |
8965 * @param sourceEntry the cached entry for the given source or `null` if none | |
8966 * @return the cache used for incremental analysis or `null` if incremental an
alysis cannot | |
8967 * be performed | |
8968 */ | |
8969 static IncrementalAnalysisCache update(IncrementalAnalysisCache cache, | |
8970 Source source, String oldContents, String newContents, int offset, | |
8971 int oldLength, int newLength, SourceEntry sourceEntry) { | |
8972 // Determine the cache resolved unit | |
8973 Source librarySource = null; | |
8974 CompilationUnit unit = null; | |
8975 if (sourceEntry is DartEntry) { | |
8976 DartEntry dartEntry = sourceEntry; | |
8977 List<Source> librarySources = dartEntry.librariesContaining; | |
8978 if (librarySources.length == 1) { | |
8979 librarySource = librarySources[0]; | |
8980 if (librarySource != null) { | |
8981 unit = dartEntry.getValueInLibrary( | |
8982 DartEntry.RESOLVED_UNIT, librarySource); | |
8983 } | |
8984 } | |
8985 } | |
8986 // Create a new cache if there is not an existing cache or the source is | |
8987 // different or a new resolved compilation unit is available. | |
8988 if (cache == null || cache.source != source || unit != null) { | |
8989 if (unit == null) { | |
8990 return null; | |
8991 } | |
8992 if (oldContents == null) { | |
8993 if (oldLength != 0) { | |
8994 return null; | |
8995 } | |
8996 oldContents = | |
8997 "${newContents.substring(0, offset)}${newContents.substring(offset +
newLength)}"; | |
8998 } | |
8999 return new IncrementalAnalysisCache(librarySource, source, unit, | |
9000 oldContents, newContents, offset, oldLength, newLength); | |
9001 } | |
9002 // Update the existing cache if the change is contiguous | |
9003 if (cache._oldLength == 0 && cache._newLength == 0) { | |
9004 cache._offset = offset; | |
9005 cache._oldLength = oldLength; | |
9006 cache._newLength = newLength; | |
9007 } else { | |
9008 if (cache._offset > offset || offset > cache._offset + cache._newLength) { | |
9009 return null; | |
9010 } | |
9011 cache._newLength += newLength - oldLength; | |
9012 } | |
9013 cache._newContents = newContents; | |
9014 return cache; | |
9015 } | |
9016 | |
9017 /** | |
9018 * Verify that the incrementally parsed and resolved unit in the incremental c
ache is structurally | |
9019 * equivalent to the fully parsed unit. | |
9020 * | |
9021 * @param cache the prior cache or `null` if none | |
9022 * @param source the source of the compilation unit that was parsed (not `null
`) | |
9023 * @param unit the compilation unit that was just parsed | |
9024 * @return the cache used for incremental analysis or `null` if incremental an
alysis results | |
9025 * cannot be cached for the next incremental analysis | |
9026 */ | |
9027 static IncrementalAnalysisCache verifyStructure( | |
9028 IncrementalAnalysisCache cache, Source source, CompilationUnit unit) { | |
9029 if (cache != null && unit != null && cache.source == source) { | |
9030 if (!AstComparator.equalNodes(cache.resolvedUnit, unit)) { | |
9031 return null; | |
9032 } | |
9033 } | |
9034 return cache; | |
9035 } | |
9036 } | |
9037 | |
9038 /** | |
9039 * Instances of the class `IncrementalAnalysisTask` incrementally update existin
g analysis. | |
9040 */ | |
9041 class IncrementalAnalysisTask extends AnalysisTask { | |
9042 /** | |
9043 * The information used to perform incremental analysis. | |
9044 */ | |
9045 final IncrementalAnalysisCache cache; | |
9046 | |
9047 /** | |
9048 * The compilation unit that was produced by incrementally updating the existi
ng unit. | |
9049 */ | |
9050 CompilationUnit _updatedUnit; | |
9051 | |
9052 /** | |
9053 * Initialize a newly created task to perform analysis within the given contex
t. | |
9054 * | |
9055 * @param context the context in which the task is to be performed | |
9056 * @param cache the incremental analysis cache used to perform the analysis | |
9057 */ | |
9058 IncrementalAnalysisTask(InternalAnalysisContext context, this.cache) | |
9059 : super(context); | |
9060 | |
9061 /** | |
9062 * Return the compilation unit that was produced by incrementally updating the
existing | |
9063 * compilation unit, or `null` if the task has not yet been performed, could n
ot be | |
9064 * performed, or if an exception occurred. | |
9065 * | |
9066 * @return the compilation unit | |
9067 */ | |
9068 CompilationUnit get compilationUnit => _updatedUnit; | |
9069 | |
9070 /** | |
9071 * Return the source that is to be incrementally analyzed. | |
9072 * | |
9073 * @return the source | |
9074 */ | |
9075 Source get source => cache != null ? cache.source : null; | |
9076 | |
9077 @override | |
9078 String get taskDescription => | |
9079 "incremental analysis ${cache != null ? cache.source : "null"}"; | |
9080 | |
9081 /** | |
9082 * Return the type provider used for incremental resolution. | |
9083 * | |
9084 * @return the type provider (or `null` if an exception occurs) | |
9085 */ | |
9086 TypeProvider get typeProvider { | |
9087 try { | |
9088 return context.typeProvider; | |
9089 } on AnalysisException { | |
9090 return null; | |
9091 } | |
9092 } | |
9093 | |
9094 @override | |
9095 accept(AnalysisTaskVisitor visitor) => | |
9096 visitor.visitIncrementalAnalysisTask(this); | |
9097 | |
9098 @override | |
9099 void internalPerform() { | |
9100 if (cache == null) { | |
9101 return; | |
9102 } | |
9103 // Only handle small changes | |
9104 if (cache.oldLength > 0 || cache.newLength > 30) { | |
9105 return; | |
9106 } | |
9107 // Produce an updated token stream | |
9108 CharacterReader reader = new CharSequenceReader(cache.newContents); | |
9109 BooleanErrorListener errorListener = new BooleanErrorListener(); | |
9110 IncrementalScanner scanner = new IncrementalScanner( | |
9111 cache.source, reader, errorListener, context.analysisOptions); | |
9112 scanner.rescan(cache.resolvedUnit.beginToken, cache.offset, cache.oldLength, | |
9113 cache.newLength); | |
9114 if (errorListener.errorReported) { | |
9115 return; | |
9116 } | |
9117 // Produce an updated AST | |
9118 IncrementalParser parser = new IncrementalParser( | |
9119 cache.source, scanner.tokenMap, AnalysisErrorListener.NULL_LISTENER); | |
9120 _updatedUnit = parser.reparse(cache.resolvedUnit, scanner.leftToken, | |
9121 scanner.rightToken, cache.offset, cache.offset + cache.oldLength); | |
9122 // Update the resolution | |
9123 TypeProvider typeProvider = this.typeProvider; | |
9124 if (_updatedUnit != null && typeProvider != null) { | |
9125 CompilationUnitElement element = _updatedUnit.element; | |
9126 if (element != null) { | |
9127 LibraryElement library = element.library; | |
9128 if (library != null) { | |
9129 IncrementalResolver resolver = new IncrementalResolver(null, null, | |
9130 null, element, cache.offset, cache.oldLength, cache.newLength); | |
9131 resolver.resolve(parser.updatedNode); | |
9132 } | |
9133 } | |
9134 } | |
9135 } | |
9136 } | |
9137 | |
9138 /** | |
9139 * Additional behavior for an analysis context that is required by internal | |
9140 * users of the context. | |
9141 */ | |
9142 abstract class InternalAnalysisContext implements AnalysisContext { | |
9143 /** | |
9144 * A table mapping the sources known to the context to the information known | |
9145 * about the source. | |
9146 * | |
9147 * TODO(scheglov) add the type, once we have only one cache. | |
9148 */ | |
9149 dynamic get analysisCache; | |
9150 | |
9151 /** | |
9152 * Allow the client to supply its own content cache. This will take the | |
9153 * place of the content cache created by default, allowing clients to share | |
9154 * the content cache between contexts. | |
9155 */ | |
9156 set contentCache(ContentCache value); | |
9157 | |
9158 /** | |
9159 * Return a list of the explicit targets being analyzed by this context. | |
9160 */ | |
9161 List<AnalysisTarget> get explicitTargets; | |
9162 | |
9163 /** | |
9164 * A factory to override how [LibraryResolver] is created. | |
9165 */ | |
9166 LibraryResolverFactory get libraryResolverFactory; | |
9167 | |
9168 /** | |
9169 * Return a list containing all of the sources that have been marked as | |
9170 * priority sources. Clients must not modify the returned list. | |
9171 */ | |
9172 List<Source> get prioritySources; | |
9173 | |
9174 /** | |
9175 * Return a list of the priority targets being analyzed by this context. | |
9176 */ | |
9177 List<AnalysisTarget> get priorityTargets; | |
9178 | |
9179 /** | |
9180 * The partition that contains analysis results that are not shared with other | |
9181 * contexts. | |
9182 * | |
9183 * TODO(scheglov) add the type, once we have only one cache. | |
9184 */ | |
9185 dynamic get privateAnalysisCachePartition; | |
9186 | |
9187 /** | |
9188 * A factory to override how [ResolverVisitor] is created. | |
9189 */ | |
9190 ResolverVisitorFactory get resolverVisitorFactory; | |
9191 | |
9192 /** | |
9193 * Returns a statistics about this context. | |
9194 */ | |
9195 AnalysisContextStatistics get statistics; | |
9196 | |
9197 /** | |
9198 * Sets the [TypeProvider] for this context. | |
9199 */ | |
9200 void set typeProvider(TypeProvider typeProvider); | |
9201 | |
9202 /** | |
9203 * A factory to override how [TypeResolverVisitor] is created. | |
9204 */ | |
9205 TypeResolverVisitorFactory get typeResolverVisitorFactory; | |
9206 | |
9207 /** | |
9208 * Return a list containing the sources of the libraries that are exported by | |
9209 * the library with the given [source]. The list will be empty if the given | |
9210 * source is invalid, if the given source does not represent a library, or if | |
9211 * the library does not export any other libraries. | |
9212 * | |
9213 * Throws an [AnalysisException] if the exported libraries could not be | |
9214 * computed. | |
9215 */ | |
9216 List<Source> computeExportedLibraries(Source source); | |
9217 | |
9218 /** | |
9219 * Return a list containing the sources of the libraries that are imported by | |
9220 * the library with the given [source]. The list will be empty if the given | |
9221 * source is invalid, if the given source does not represent a library, or if | |
9222 * the library does not import any other libraries. | |
9223 * | |
9224 * Throws an [AnalysisException] if the imported libraries could not be | |
9225 * computed. | |
9226 */ | |
9227 List<Source> computeImportedLibraries(Source source); | |
9228 | |
9229 /** | |
9230 * Return an AST structure corresponding to the given [source], but ensure | |
9231 * that the structure has not already been resolved and will not be resolved | |
9232 * by any other threads or in any other library. | |
9233 * | |
9234 * Throws an [AnalysisException] if the analysis could not be performed. | |
9235 * | |
9236 * <b>Note:</b> This method cannot be used in an async environment | |
9237 */ | |
9238 CompilationUnit computeResolvableCompilationUnit(Source source); | |
9239 | |
9240 /** | |
9241 * Return all the resolved [CompilationUnit]s for the given [source] if not | |
9242 * flushed, otherwise return `null` and ensures that the [CompilationUnit]s | |
9243 * will be eventually returned to the client from [performAnalysisTask]. | |
9244 */ | |
9245 List<CompilationUnit> ensureResolvedDartUnits(Source source); | |
9246 | |
9247 /** | |
9248 * Return the cache entry associated with the given [target]. | |
9249 */ | |
9250 cache.CacheEntry getCacheEntry(AnalysisTarget target); | |
9251 | |
9252 /** | |
9253 * Return context that owns the given [source]. | |
9254 */ | |
9255 InternalAnalysisContext getContextFor(Source source); | |
9256 | |
9257 /** | |
9258 * Return a change notice for the given [source], creating one if one does not | |
9259 * already exist. | |
9260 */ | |
9261 ChangeNoticeImpl getNotice(Source source); | |
9262 | |
9263 /** | |
9264 * Return a namespace containing mappings for all of the public names defined | |
9265 * by the given [library]. | |
9266 */ | |
9267 Namespace getPublicNamespace(LibraryElement library); | |
9268 | |
9269 /** | |
9270 * Respond to a change which has been made to the given [source] file. | |
9271 * [originalContents] is the former contents of the file, and [newContents] | |
9272 * is the updated contents. If [notify] is true, a source changed event is | |
9273 * triggered. | |
9274 * | |
9275 * Normally it should not be necessary for clients to call this function, | |
9276 * since it will be automatically invoked in response to a call to | |
9277 * [applyChanges] or [setContents]. However, if this analysis context is | |
9278 * sharing its content cache with other contexts, then the client must | |
9279 * manually update the content cache and call this function for each context. | |
9280 * | |
9281 * Return `true` if the change was significant to this context (i.e. [source] | |
9282 * is either implicitly or explicitly analyzed by this context, and a change | |
9283 * actually occurred). | |
9284 */ | |
9285 bool handleContentsChanged( | |
9286 Source source, String originalContents, String newContents, bool notify); | |
9287 | |
9288 /** | |
9289 * Given an [elementMap] mapping the source for the libraries represented by | |
9290 * the corresponding elements to the elements representing the libraries, | |
9291 * record those mappings. | |
9292 */ | |
9293 void recordLibraryElements(Map<Source, LibraryElement> elementMap); | |
9294 | |
9295 /** | |
9296 * Return `true` if errors should be produced for the given [source]. | |
9297 * The [entry] associated with the source is passed in for efficiency. | |
9298 * | |
9299 * TODO(scheglov) remove [entry] after migration to the new task model. | |
9300 * It is not used there anyway. | |
9301 */ | |
9302 bool shouldErrorsBeAnalyzed(Source source, Object entry); | |
9303 | |
9304 /** | |
9305 * For testing only: flush all representations of the AST (both resolved and | |
9306 * unresolved) for the given [source] out of the cache. | |
9307 */ | |
9308 void test_flushAstStructures(Source source); | |
9309 | |
9310 /** | |
9311 * Call the given callback function for eache cache item in the context. | |
9312 */ | |
9313 @deprecated | |
9314 void visitCacheItems(void callback(Source source, SourceEntry dartEntry, | |
9315 DataDescriptor rowDesc, CacheState state)); | |
9316 | |
9317 /** | |
9318 * Visit all entries of the content cache. | |
9319 */ | |
9320 void visitContentCache(ContentCacheVisitor visitor); | |
9321 } | |
9322 | |
9323 /** | |
9324 * An object that can be used to receive information about errors within the | |
9325 * analysis engine. Implementations usually write this information to a file, | |
9326 * but can also record the information for later use (such as during testing) or | |
9327 * even ignore the information. | |
9328 */ | |
9329 abstract class Logger { | |
9330 /** | |
9331 * A logger that ignores all logging. | |
9332 */ | |
9333 static final Logger NULL = new NullLogger(); | |
9334 | |
9335 /** | |
9336 * Log the given message as an error. The [message] is expected to be an | |
9337 * explanation of why the error occurred or what it means. The [exception] is | |
9338 * expected to be the reason for the error. At least one argument must be | |
9339 * provided. | |
9340 */ | |
9341 void logError(String message, [CaughtException exception]); | |
9342 | |
9343 /** | |
9344 * Log the given [exception] as one representing an error. The [message] is an | |
9345 * explanation of why the error occurred or what it means. | |
9346 */ | |
9347 @deprecated // Use logError(message, exception) | |
9348 void logError2(String message, Object exception); | |
9349 | |
9350 /** | |
9351 * Log the given informational message. The [message] is expected to be an | |
9352 * explanation of why the error occurred or what it means. The [exception] is | |
9353 * expected to be the reason for the error. | |
9354 */ | |
9355 void logInformation(String message, [CaughtException exception]); | |
9356 | |
9357 /** | |
9358 * Log the given [exception] as one representing an informational message. The | |
9359 * [message] is an explanation of why the error occurred or what it means. | |
9360 */ | |
9361 @deprecated // Use logInformation(message, exception) | |
9362 void logInformation2(String message, Object exception); | |
9363 } | |
9364 | |
9365 /** | |
9366 * An implementation of [Logger] that does nothing. | |
9367 */ | |
9368 class NullLogger implements Logger { | |
9369 @override | |
9370 void logError(String message, [CaughtException exception]) {} | |
9371 | |
9372 @override | |
9373 void logError2(String message, Object exception) {} | |
9374 | |
9375 @override | |
9376 void logInformation(String message, [CaughtException exception]) {} | |
9377 | |
9378 @override | |
9379 void logInformation2(String message, Object exception) {} | |
9380 } | |
9381 | |
9382 /** | |
9383 * An exception created when an analysis attempt fails because a source was | |
9384 * deleted between the time the analysis started and the time the results of the | |
9385 * analysis were ready to be recorded. | |
9386 */ | |
9387 class ObsoleteSourceAnalysisException extends AnalysisException { | |
9388 /** | |
9389 * The source that was removed while it was being analyzed. | |
9390 */ | |
9391 Source _source; | |
9392 | |
9393 /** | |
9394 * Initialize a newly created exception to represent the removal of the given | |
9395 * [source]. | |
9396 */ | |
9397 ObsoleteSourceAnalysisException(Source source) : super( | |
9398 "The source '${source.fullName}' was removed while it was being analyz
ed") { | |
9399 this._source = source; | |
9400 } | |
9401 | |
9402 /** | |
9403 * Return the source that was removed while it was being analyzed. | |
9404 */ | |
9405 Source get source => _source; | |
9406 } | |
9407 | |
9408 /** | |
9409 * Instances of the class `ParseDartTask` parse a specific source as a Dart file
. | |
9410 */ | |
9411 class ParseDartTask extends AnalysisTask { | |
9412 /** | |
9413 * The source to be parsed. | |
9414 */ | |
9415 final Source source; | |
9416 | |
9417 /** | |
9418 * The head of the token stream used for parsing. | |
9419 */ | |
9420 final Token _tokenStream; | |
9421 | |
9422 /** | |
9423 * The line information associated with the source. | |
9424 */ | |
9425 final LineInfo lineInfo; | |
9426 | |
9427 /** | |
9428 * The compilation unit that was produced by parsing the source. | |
9429 */ | |
9430 CompilationUnit _unit; | |
9431 | |
9432 /** | |
9433 * A flag indicating whether the source contains a 'part of' directive. | |
9434 */ | |
9435 bool _containsPartOfDirective = false; | |
9436 | |
9437 /** | |
9438 * A flag indicating whether the source contains any directive other than a 'p
art of' directive. | |
9439 */ | |
9440 bool _containsNonPartOfDirective = false; | |
9441 | |
9442 /** | |
9443 * A set containing the sources referenced by 'export' directives. | |
9444 */ | |
9445 HashSet<Source> _exportedSources = new HashSet<Source>(); | |
9446 | |
9447 /** | |
9448 * A set containing the sources referenced by 'import' directives. | |
9449 */ | |
9450 HashSet<Source> _importedSources = new HashSet<Source>(); | |
9451 | |
9452 /** | |
9453 * A set containing the sources referenced by 'part' directives. | |
9454 */ | |
9455 HashSet<Source> _includedSources = new HashSet<Source>(); | |
9456 | |
9457 /** | |
9458 * The errors that were produced by scanning and parsing the source. | |
9459 */ | |
9460 List<AnalysisError> _errors = AnalysisError.NO_ERRORS; | |
9461 | |
9462 /** | |
9463 * Initialize a newly created task to perform analysis within the given contex
t. | |
9464 * | |
9465 * @param context the context in which the task is to be performed | |
9466 * @param source the source to be parsed | |
9467 * @param tokenStream the head of the token stream used for parsing | |
9468 * @param lineInfo the line information associated with the source | |
9469 */ | |
9470 ParseDartTask(InternalAnalysisContext context, this.source, this._tokenStream, | |
9471 this.lineInfo) | |
9472 : super(context); | |
9473 | |
9474 /** | |
9475 * Return the compilation unit that was produced by parsing the source, or `nu
ll` if the | |
9476 * task has not yet been performed or if an exception occurred. | |
9477 * | |
9478 * @return the compilation unit that was produced by parsing the source | |
9479 */ | |
9480 CompilationUnit get compilationUnit => _unit; | |
9481 | |
9482 /** | |
9483 * Return the errors that were produced by scanning and parsing the source, or
an empty list if | |
9484 * the task has not yet been performed or if an exception occurred. | |
9485 * | |
9486 * @return the errors that were produced by scanning and parsing the source | |
9487 */ | |
9488 List<AnalysisError> get errors => _errors; | |
9489 | |
9490 /** | |
9491 * Return a list containing the sources referenced by 'export' directives, or
an empty list if | |
9492 * the task has not yet been performed or if an exception occurred. | |
9493 * | |
9494 * @return an list containing the sources referenced by 'export' directives | |
9495 */ | |
9496 List<Source> get exportedSources => _toArray(_exportedSources); | |
9497 | |
9498 /** | |
9499 * Return `true` if the source contains any directive other than a 'part of' d
irective, or | |
9500 * `false` if the task has not yet been performed or if an exception occurred. | |
9501 * | |
9502 * @return `true` if the source contains any directive other than a 'part of'
directive | |
9503 */ | |
9504 bool get hasNonPartOfDirective => _containsNonPartOfDirective; | |
9505 | |
9506 /** | |
9507 * Return `true` if the source contains a 'part of' directive, or `false` if t
he task | |
9508 * has not yet been performed or if an exception occurred. | |
9509 * | |
9510 * @return `true` if the source contains a 'part of' directive | |
9511 */ | |
9512 bool get hasPartOfDirective => _containsPartOfDirective; | |
9513 | |
9514 /** | |
9515 * Return a list containing the sources referenced by 'import' directives, or
an empty list if | |
9516 * the task has not yet been performed or if an exception occurred. | |
9517 * | |
9518 * @return a list containing the sources referenced by 'import' directives | |
9519 */ | |
9520 List<Source> get importedSources => _toArray(_importedSources); | |
9521 | |
9522 /** | |
9523 * Return a list containing the sources referenced by 'part' directives, or an
empty list if | |
9524 * the task has not yet been performed or if an exception occurred. | |
9525 * | |
9526 * @return a list containing the sources referenced by 'part' directives | |
9527 */ | |
9528 List<Source> get includedSources => _toArray(_includedSources); | |
9529 | |
9530 @override | |
9531 String get taskDescription { | |
9532 if (source == null) { | |
9533 return "parse as dart null source"; | |
9534 } | |
9535 return "parse as dart ${source.fullName}"; | |
9536 } | |
9537 | |
9538 @override | |
9539 accept(AnalysisTaskVisitor visitor) => visitor.visitParseDartTask(this); | |
9540 | |
9541 @override | |
9542 void internalPerform() { | |
9543 // | |
9544 // Then parse the token stream. | |
9545 // | |
9546 PerformanceStatistics.parse.makeCurrentWhile(() { | |
9547 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
9548 Parser parser = new Parser(source, errorListener); | |
9549 AnalysisOptions options = context.analysisOptions; | |
9550 parser.parseFunctionBodies = | |
9551 options.analyzeFunctionBodiesPredicate(source); | |
9552 parser.parseGenericMethods = options.enableGenericMethods; | |
9553 _unit = parser.parseCompilationUnit(_tokenStream); | |
9554 _unit.lineInfo = lineInfo; | |
9555 AnalysisContext analysisContext = context; | |
9556 for (Directive directive in _unit.directives) { | |
9557 if (directive is PartOfDirective) { | |
9558 _containsPartOfDirective = true; | |
9559 } else { | |
9560 _containsNonPartOfDirective = true; | |
9561 if (directive is UriBasedDirective) { | |
9562 Source referencedSource = resolveDirective( | |
9563 analysisContext, source, directive, errorListener); | |
9564 if (referencedSource != null) { | |
9565 if (directive is ExportDirective) { | |
9566 _exportedSources.add(referencedSource); | |
9567 } else if (directive is ImportDirective) { | |
9568 _importedSources.add(referencedSource); | |
9569 } else if (directive is PartDirective) { | |
9570 if (referencedSource != source) { | |
9571 _includedSources.add(referencedSource); | |
9572 } | |
9573 } else { | |
9574 throw new AnalysisException( | |
9575 "$runtimeType failed to handle a ${directive.runtimeType}"); | |
9576 } | |
9577 } | |
9578 } | |
9579 } | |
9580 } | |
9581 _errors = errorListener.getErrorsForSource(source); | |
9582 }); | |
9583 } | |
9584 | |
9585 /** | |
9586 * Efficiently convert the given set of [sources] to a list. | |
9587 */ | |
9588 List<Source> _toArray(HashSet<Source> sources) { | |
9589 int size = sources.length; | |
9590 if (size == 0) { | |
9591 return Source.EMPTY_LIST; | |
9592 } | |
9593 return new List.from(sources); | |
9594 } | |
9595 | |
9596 /** | |
9597 * Return the result of resolving the URI of the given URI-based directive aga
inst the URI of the | |
9598 * given library, or `null` if the URI is not valid. | |
9599 * | |
9600 * @param context the context in which the resolution is to be performed | |
9601 * @param librarySource the source representing the library containing the dir
ective | |
9602 * @param directive the directive which URI should be resolved | |
9603 * @param errorListener the error listener to which errors should be reported | |
9604 * @return the result of resolving the URI against the URI of the library | |
9605 */ | |
9606 static Source resolveDirective(AnalysisContext context, Source librarySource, | |
9607 UriBasedDirective directive, AnalysisErrorListener errorListener) { | |
9608 StringLiteral uriLiteral = directive.uri; | |
9609 String uriContent = uriLiteral.stringValue; | |
9610 if (uriContent != null) { | |
9611 uriContent = uriContent.trim(); | |
9612 directive.uriContent = uriContent; | |
9613 } | |
9614 UriValidationCode code = directive.validate(); | |
9615 if (code == null) { | |
9616 String encodedUriContent = Uri.encodeFull(uriContent); | |
9617 try { | |
9618 Source source = | |
9619 context.sourceFactory.resolveUri(librarySource, encodedUriContent); | |
9620 directive.source = source; | |
9621 return source; | |
9622 } on JavaIOException { | |
9623 code = UriValidationCode.INVALID_URI; | |
9624 } | |
9625 } | |
9626 if (code == UriValidationCode.URI_WITH_DART_EXT_SCHEME) { | |
9627 return null; | |
9628 } | |
9629 if (code == UriValidationCode.URI_WITH_INTERPOLATION) { | |
9630 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
9631 uriLiteral.length, CompileTimeErrorCode.URI_WITH_INTERPOLATION)); | |
9632 return null; | |
9633 } | |
9634 if (code == UriValidationCode.INVALID_URI) { | |
9635 errorListener.onError(new AnalysisError(librarySource, uriLiteral.offset, | |
9636 uriLiteral.length, CompileTimeErrorCode.INVALID_URI, [uriContent])); | |
9637 return null; | |
9638 } | |
9639 throw new RuntimeException( | |
9640 message: "Failed to handle validation code: $code"); | |
9641 } | |
9642 } | |
9643 | |
9644 /** | |
9645 * Instances of the class `ParseHtmlTask` parse a specific source as an HTML fil
e. | |
9646 */ | |
9647 class ParseHtmlTask extends AnalysisTask { | |
9648 /** | |
9649 * The name of the 'src' attribute in a HTML tag. | |
9650 */ | |
9651 static String _ATTRIBUTE_SRC = "src"; | |
9652 | |
9653 /** | |
9654 * The name of the 'script' tag in an HTML file. | |
9655 */ | |
9656 static String _TAG_SCRIPT = "script"; | |
9657 | |
9658 /** | |
9659 * The source to be parsed. | |
9660 */ | |
9661 final Source source; | |
9662 | |
9663 /** | |
9664 * The contents of the source. | |
9665 */ | |
9666 final String _content; | |
9667 | |
9668 /** | |
9669 * The line information that was produced. | |
9670 */ | |
9671 LineInfo _lineInfo; | |
9672 | |
9673 /** | |
9674 * The HTML unit that was produced by parsing the source. | |
9675 */ | |
9676 ht.HtmlUnit _unit; | |
9677 | |
9678 /** | |
9679 * The errors that were produced by scanning and parsing the source. | |
9680 */ | |
9681 List<AnalysisError> _errors = AnalysisError.NO_ERRORS; | |
9682 | |
9683 /** | |
9684 * A list containing the sources of the libraries that are referenced within t
he HTML. | |
9685 */ | |
9686 List<Source> _referencedLibraries = Source.EMPTY_LIST; | |
9687 | |
9688 /** | |
9689 * Initialize a newly created task to perform analysis within the given contex
t. | |
9690 * | |
9691 * @param context the context in which the task is to be performed | |
9692 * @param source the source to be parsed | |
9693 * @param content the contents of the source | |
9694 */ | |
9695 ParseHtmlTask(InternalAnalysisContext context, this.source, this._content) | |
9696 : super(context); | |
9697 | |
9698 /** | |
9699 * Return the errors that were produced by scanning and parsing the source, or
`null` if the | |
9700 * task has not yet been performed or if an exception occurred. | |
9701 * | |
9702 * @return the errors that were produced by scanning and parsing the source | |
9703 */ | |
9704 List<AnalysisError> get errors => _errors; | |
9705 | |
9706 /** | |
9707 * Return the HTML unit that was produced by parsing the source. | |
9708 * | |
9709 * @return the HTML unit that was produced by parsing the source | |
9710 */ | |
9711 ht.HtmlUnit get htmlUnit => _unit; | |
9712 | |
9713 /** | |
9714 * Return the sources of libraries that are referenced in the specified HTML f
ile. | |
9715 * | |
9716 * @return the sources of libraries that are referenced in the HTML file | |
9717 */ | |
9718 List<Source> get librarySources { | |
9719 List<Source> libraries = new List<Source>(); | |
9720 _unit.accept(new ParseHtmlTask_getLibrarySources(this, libraries)); | |
9721 if (libraries.isEmpty) { | |
9722 return Source.EMPTY_LIST; | |
9723 } | |
9724 return libraries; | |
9725 } | |
9726 | |
9727 /** | |
9728 * Return the line information that was produced, or `null` if the task has no
t yet been | |
9729 * performed or if an exception occurred. | |
9730 * | |
9731 * @return the line information that was produced | |
9732 */ | |
9733 LineInfo get lineInfo => _lineInfo; | |
9734 | |
9735 /** | |
9736 * Return a list containing the sources of the libraries that are referenced w
ithin the HTML. | |
9737 * | |
9738 * @return the sources of the libraries that are referenced within the HTML | |
9739 */ | |
9740 List<Source> get referencedLibraries => _referencedLibraries; | |
9741 | |
9742 @override | |
9743 String get taskDescription { | |
9744 if (source == null) { | |
9745 return "parse as html null source"; | |
9746 } | |
9747 return "parse as html ${source.fullName}"; | |
9748 } | |
9749 | |
9750 @override | |
9751 accept(AnalysisTaskVisitor visitor) => visitor.visitParseHtmlTask(this); | |
9752 | |
9753 @override | |
9754 void internalPerform() { | |
9755 try { | |
9756 ht.AbstractScanner scanner = new ht.StringScanner(source, _content); | |
9757 scanner.passThroughElements = <String>[_TAG_SCRIPT]; | |
9758 ht.Token token = scanner.tokenize(); | |
9759 _lineInfo = new LineInfo(scanner.lineStarts); | |
9760 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
9761 _unit = new ht.HtmlParser(source, errorListener, context.analysisOptions) | |
9762 .parse(token, _lineInfo); | |
9763 _unit.accept(new RecursiveXmlVisitor_ParseHtmlTask_internalPerform( | |
9764 this, errorListener)); | |
9765 _errors = errorListener.getErrorsForSource(source); | |
9766 _referencedLibraries = librarySources; | |
9767 } catch (exception, stackTrace) { | |
9768 throw new AnalysisException( | |
9769 "Exception", new CaughtException(exception, stackTrace)); | |
9770 } | |
9771 } | |
9772 | |
9773 /** | |
9774 * Resolves directives in the given [CompilationUnit]. | |
9775 */ | |
9776 void _resolveScriptDirectives( | |
9777 CompilationUnit script, AnalysisErrorListener errorListener) { | |
9778 if (script == null) { | |
9779 return; | |
9780 } | |
9781 AnalysisContext analysisContext = context; | |
9782 for (Directive directive in script.directives) { | |
9783 if (directive is UriBasedDirective) { | |
9784 ParseDartTask.resolveDirective( | |
9785 analysisContext, source, directive, errorListener); | |
9786 } | |
9787 } | |
9788 } | |
9789 } | |
9790 | |
9791 class ParseHtmlTask_getLibrarySources extends ht.RecursiveXmlVisitor<Object> { | |
9792 final ParseHtmlTask _task; | |
9793 | |
9794 List<Source> libraries; | |
9795 | |
9796 ParseHtmlTask_getLibrarySources(this._task, this.libraries) : super(); | |
9797 | |
9798 @override | |
9799 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
9800 ht.XmlAttributeNode scriptAttribute = null; | |
9801 for (ht.XmlAttributeNode attribute in node.attributes) { | |
9802 if (javaStringEqualsIgnoreCase( | |
9803 attribute.name, ParseHtmlTask._ATTRIBUTE_SRC)) { | |
9804 scriptAttribute = attribute; | |
9805 } | |
9806 } | |
9807 if (scriptAttribute != null) { | |
9808 try { | |
9809 Uri uri = Uri.parse(scriptAttribute.text); | |
9810 String fileName = uri.path; | |
9811 Source librarySource = | |
9812 _task.context.sourceFactory.resolveUri(_task.source, fileName); | |
9813 if (_task.context.exists(librarySource)) { | |
9814 libraries.add(librarySource); | |
9815 } | |
9816 } on FormatException { | |
9817 // ignored - invalid URI reported during resolution phase | |
9818 } | |
9819 } | |
9820 return super.visitHtmlScriptTagNode(node); | |
9821 } | |
9822 } | |
9823 | |
9824 /** | |
9825 * An object that manages the partitions that can be shared between analysis | |
9826 * contexts. | |
9827 */ | |
9828 class PartitionManager { | |
9829 /** | |
9830 * The default cache size for a Dart SDK partition. | |
9831 */ | |
9832 static int _DEFAULT_SDK_CACHE_SIZE = 256; | |
9833 | |
9834 /** | |
9835 * A table mapping SDK's to the partitions used for those SDK's. | |
9836 */ | |
9837 HashMap<DartSdk, SdkCachePartition> _sdkPartitions = | |
9838 new HashMap<DartSdk, SdkCachePartition>(); | |
9839 | |
9840 /** | |
9841 * Clear any cached data being maintained by this manager. | |
9842 */ | |
9843 void clearCache() { | |
9844 _sdkPartitions.clear(); | |
9845 } | |
9846 | |
9847 /** | |
9848 * Return the partition being used for the given [sdk], creating the partition | |
9849 * if necessary. | |
9850 */ | |
9851 SdkCachePartition forSdk(DartSdk sdk) { | |
9852 // Call sdk.context now, because when it creates a new | |
9853 // InternalAnalysisContext instance, it calls forSdk() again, so creates an | |
9854 // SdkCachePartition instance. | |
9855 // So, if we initialize context after "partition == null", we end up | |
9856 // with two SdkCachePartition instances. | |
9857 InternalAnalysisContext sdkContext = sdk.context; | |
9858 // Check cache for an existing partition. | |
9859 SdkCachePartition partition = _sdkPartitions[sdk]; | |
9860 if (partition == null) { | |
9861 partition = new SdkCachePartition(sdkContext, _DEFAULT_SDK_CACHE_SIZE); | |
9862 _sdkPartitions[sdk] = partition; | |
9863 } | |
9864 return partition; | |
9865 } | |
9866 } | |
9867 | |
9868 /** | |
9869 * Representation of a pending computation which is based on the results of | |
9870 * analysis that may or may not have been completed. | |
9871 */ | |
9872 class PendingFuture<T> { | |
9873 /** | |
9874 * The context in which this computation runs. | |
9875 */ | |
9876 final AnalysisContextImpl _context; | |
9877 | |
9878 /** | |
9879 * The source used by this computation to compute its value. | |
9880 */ | |
9881 final Source source; | |
9882 | |
9883 /** | |
9884 * The function which implements the computation. | |
9885 */ | |
9886 final PendingFutureComputer<T> _computeValue; | |
9887 | |
9888 /** | |
9889 * The completer that should be completed once the computation has succeeded. | |
9890 */ | |
9891 CancelableCompleter<T> _completer; | |
9892 | |
9893 PendingFuture(this._context, this.source, this._computeValue) { | |
9894 _completer = new CancelableCompleter<T>(_onCancel); | |
9895 } | |
9896 | |
9897 /** | |
9898 * Retrieve the future which will be completed when this object is | |
9899 * successfully evaluated. | |
9900 */ | |
9901 CancelableFuture<T> get future => _completer.future; | |
9902 | |
9903 /** | |
9904 * Execute [_computeValue], passing it the given [sourceEntry], and complete | |
9905 * the pending future if it's appropriate to do so. If the pending future is | |
9906 * completed by this call, true is returned; otherwise false is returned. | |
9907 * | |
9908 * Once this function has returned true, it should not be called again. | |
9909 * | |
9910 * Other than completing the future, this method is free of side effects. | |
9911 * Note that any code the client has attached to the future will be executed | |
9912 * in a microtask, so there is no danger of side effects occurring due to | |
9913 * client callbacks. | |
9914 */ | |
9915 bool evaluate(SourceEntry sourceEntry) { | |
9916 assert(!_completer.isCompleted); | |
9917 try { | |
9918 T result = _computeValue(sourceEntry); | |
9919 if (result == null) { | |
9920 return false; | |
9921 } else { | |
9922 _completer.complete(result); | |
9923 return true; | |
9924 } | |
9925 } catch (exception, stackTrace) { | |
9926 _completer.completeError(exception, stackTrace); | |
9927 return true; | |
9928 } | |
9929 } | |
9930 | |
9931 /** | |
9932 * No further analysis updates are expected which affect this future, so | |
9933 * complete it with an AnalysisNotScheduledError in order to avoid | |
9934 * deadlocking the client. | |
9935 */ | |
9936 void forciblyComplete() { | |
9937 try { | |
9938 throw new AnalysisNotScheduledError(); | |
9939 } catch (exception, stackTrace) { | |
9940 _completer.completeError(exception, stackTrace); | |
9941 } | |
9942 } | |
9943 | |
9944 void _onCancel() { | |
9945 _context._cancelFuture(this); | |
9946 } | |
9947 } | |
9948 | |
9949 /** | |
9950 * Container with global [AnalysisContext] performance statistics. | |
9951 */ | |
9952 class PerformanceStatistics { | |
9953 /** | |
9954 * The [PerformanceTag] for time spent in reading files. | |
9955 */ | |
9956 static PerformanceTag io = new PerformanceTag('io'); | |
9957 | |
9958 /** | |
9959 * The [PerformanceTag] for time spent in scanning. | |
9960 */ | |
9961 static PerformanceTag scan = new PerformanceTag('scan'); | |
9962 | |
9963 /** | |
9964 * The [PerformanceTag] for time spent in parsing. | |
9965 */ | |
9966 static PerformanceTag parse = new PerformanceTag('parse'); | |
9967 | |
9968 /** | |
9969 * The [PerformanceTag] for time spent in resolving. | |
9970 */ | |
9971 static PerformanceTag resolve = new PerformanceTag('resolve'); | |
9972 | |
9973 /** | |
9974 * The [PerformanceTag] for time spent in error verifier. | |
9975 */ | |
9976 static PerformanceTag errors = new PerformanceTag('errors'); | |
9977 | |
9978 /** | |
9979 * The [PerformanceTag] for time spent in hints generator. | |
9980 */ | |
9981 static PerformanceTag hints = new PerformanceTag('hints'); | |
9982 | |
9983 /** | |
9984 * The [PerformanceTag] for time spent in linting. | |
9985 */ | |
9986 static PerformanceTag lint = new PerformanceTag('lint'); | |
9987 | |
9988 /** | |
9989 * The [PerformanceTag] for time spent computing cycles. | |
9990 */ | |
9991 static PerformanceTag cycles = new PerformanceTag('cycles'); | |
9992 | |
9993 /** | |
9994 * The [PerformanceTag] for time spent in other phases of analysis. | |
9995 */ | |
9996 static PerformanceTag performAnaysis = new PerformanceTag('performAnaysis'); | |
9997 | |
9998 /** | |
9999 * The [PerformanceTag] for time spent in the analysis task visitor after | |
10000 * tasks are complete. | |
10001 */ | |
10002 static PerformanceTag analysisTaskVisitor = | |
10003 new PerformanceTag('analysisTaskVisitor'); | |
10004 | |
10005 /** | |
10006 * The [PerformanceTag] for time spent in the getter | |
10007 * AnalysisContextImpl.nextAnalysisTask. | |
10008 */ | |
10009 static var nextTask = new PerformanceTag('nextAnalysisTask'); | |
10010 | |
10011 /** | |
10012 * The [PerformanceTag] for time spent during otherwise not accounted parts | |
10013 * incremental of analysis. | |
10014 */ | |
10015 static PerformanceTag incrementalAnalysis = | |
10016 new PerformanceTag('incrementalAnalysis'); | |
10017 } | |
10018 | |
10019 /** | |
10020 * An error listener that will record the errors that are reported to it in a | |
10021 * way that is appropriate for caching those errors within an analysis context. | |
10022 */ | |
10023 class RecordingErrorListener implements AnalysisErrorListener { | |
10024 /** | |
10025 * A map of sets containing the errors that were collected, keyed by each | |
10026 * source. | |
10027 */ | |
10028 Map<Source, HashSet<AnalysisError>> _errors = | |
10029 new HashMap<Source, HashSet<AnalysisError>>(); | |
10030 | |
10031 /** | |
10032 * Return the errors collected by the listener. | |
10033 */ | |
10034 List<AnalysisError> get errors { | |
10035 int numEntries = _errors.length; | |
10036 if (numEntries == 0) { | |
10037 return AnalysisError.NO_ERRORS; | |
10038 } | |
10039 List<AnalysisError> resultList = new List<AnalysisError>(); | |
10040 for (HashSet<AnalysisError> errors in _errors.values) { | |
10041 resultList.addAll(errors); | |
10042 } | |
10043 return resultList; | |
10044 } | |
10045 | |
10046 /** | |
10047 * Add all of the errors recorded by the given [listener] to this listener. | |
10048 */ | |
10049 void addAll(RecordingErrorListener listener) { | |
10050 for (AnalysisError error in listener.errors) { | |
10051 onError(error); | |
10052 } | |
10053 } | |
10054 | |
10055 /** | |
10056 * Return the errors collected by the listener for the given [source]. | |
10057 */ | |
10058 List<AnalysisError> getErrorsForSource(Source source) { | |
10059 HashSet<AnalysisError> errorsForSource = _errors[source]; | |
10060 if (errorsForSource == null) { | |
10061 return AnalysisError.NO_ERRORS; | |
10062 } else { | |
10063 return new List.from(errorsForSource); | |
10064 } | |
10065 } | |
10066 | |
10067 @override | |
10068 void onError(AnalysisError error) { | |
10069 Source source = error.source; | |
10070 HashSet<AnalysisError> errorsForSource = _errors[source]; | |
10071 if (_errors[source] == null) { | |
10072 errorsForSource = new HashSet<AnalysisError>(); | |
10073 _errors[source] = errorsForSource; | |
10074 } | |
10075 errorsForSource.add(error); | |
10076 } | |
10077 } | |
10078 | |
10079 class RecursiveXmlVisitor_ParseHtmlTask_internalPerform | |
10080 extends ht.RecursiveXmlVisitor<Object> { | |
10081 final ParseHtmlTask ParseHtmlTask_this; | |
10082 | |
10083 RecordingErrorListener errorListener; | |
10084 | |
10085 RecursiveXmlVisitor_ParseHtmlTask_internalPerform( | |
10086 this.ParseHtmlTask_this, this.errorListener) | |
10087 : super(); | |
10088 | |
10089 @override | |
10090 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
10091 ParseHtmlTask_this._resolveScriptDirectives(node.script, errorListener); | |
10092 return null; | |
10093 } | |
10094 } | |
10095 | |
10096 class RecursiveXmlVisitor_ResolveHtmlTask_internalPerform | |
10097 extends ht.RecursiveXmlVisitor<Object> { | |
10098 final ResolveHtmlTask ResolveHtmlTask_this; | |
10099 | |
10100 RecordingErrorListener errorListener; | |
10101 | |
10102 RecursiveXmlVisitor_ResolveHtmlTask_internalPerform( | |
10103 this.ResolveHtmlTask_this, this.errorListener) | |
10104 : super(); | |
10105 | |
10106 @override | |
10107 Object visitHtmlScriptTagNode(ht.HtmlScriptTagNode node) { | |
10108 CompilationUnit script = node.script; | |
10109 if (script != null) { | |
10110 GenerateDartErrorsTask.validateDirectives(ResolveHtmlTask_this.context, | |
10111 ResolveHtmlTask_this.source, script, errorListener); | |
10112 } | |
10113 return null; | |
10114 } | |
10115 } | |
10116 | |
10117 /** | |
10118 * An visitor that removes any resolution information from an AST structure when | |
10119 * used to visit that structure. | |
10120 */ | |
10121 class ResolutionEraser extends GeneralizingAstVisitor<Object> { | |
10122 @override | |
10123 Object visitAssignmentExpression(AssignmentExpression node) { | |
10124 node.staticElement = null; | |
10125 node.propagatedElement = null; | |
10126 return super.visitAssignmentExpression(node); | |
10127 } | |
10128 | |
10129 @override | |
10130 Object visitBinaryExpression(BinaryExpression node) { | |
10131 node.staticElement = null; | |
10132 node.propagatedElement = null; | |
10133 return super.visitBinaryExpression(node); | |
10134 } | |
10135 | |
10136 @override | |
10137 Object visitBreakStatement(BreakStatement node) { | |
10138 node.target = null; | |
10139 return super.visitBreakStatement(node); | |
10140 } | |
10141 | |
10142 @override | |
10143 Object visitCompilationUnit(CompilationUnit node) { | |
10144 node.element = null; | |
10145 return super.visitCompilationUnit(node); | |
10146 } | |
10147 | |
10148 @override | |
10149 Object visitConstructorDeclaration(ConstructorDeclaration node) { | |
10150 node.element = null; | |
10151 return super.visitConstructorDeclaration(node); | |
10152 } | |
10153 | |
10154 @override | |
10155 Object visitConstructorName(ConstructorName node) { | |
10156 node.staticElement = null; | |
10157 return super.visitConstructorName(node); | |
10158 } | |
10159 | |
10160 @override | |
10161 Object visitContinueStatement(ContinueStatement node) { | |
10162 node.target = null; | |
10163 return super.visitContinueStatement(node); | |
10164 } | |
10165 | |
10166 @override | |
10167 Object visitDirective(Directive node) { | |
10168 node.element = null; | |
10169 return super.visitDirective(node); | |
10170 } | |
10171 | |
10172 @override | |
10173 Object visitExpression(Expression node) { | |
10174 node.staticType = null; | |
10175 node.propagatedType = null; | |
10176 return super.visitExpression(node); | |
10177 } | |
10178 | |
10179 @override | |
10180 Object visitFunctionExpression(FunctionExpression node) { | |
10181 node.element = null; | |
10182 return super.visitFunctionExpression(node); | |
10183 } | |
10184 | |
10185 @override | |
10186 Object visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | |
10187 node.staticElement = null; | |
10188 node.propagatedElement = null; | |
10189 return super.visitFunctionExpressionInvocation(node); | |
10190 } | |
10191 | |
10192 @override | |
10193 Object visitIndexExpression(IndexExpression node) { | |
10194 node.staticElement = null; | |
10195 node.propagatedElement = null; | |
10196 return super.visitIndexExpression(node); | |
10197 } | |
10198 | |
10199 @override | |
10200 Object visitInstanceCreationExpression(InstanceCreationExpression node) { | |
10201 node.staticElement = null; | |
10202 return super.visitInstanceCreationExpression(node); | |
10203 } | |
10204 | |
10205 @override | |
10206 Object visitPostfixExpression(PostfixExpression node) { | |
10207 node.staticElement = null; | |
10208 node.propagatedElement = null; | |
10209 return super.visitPostfixExpression(node); | |
10210 } | |
10211 | |
10212 @override | |
10213 Object visitPrefixExpression(PrefixExpression node) { | |
10214 node.staticElement = null; | |
10215 node.propagatedElement = null; | |
10216 return super.visitPrefixExpression(node); | |
10217 } | |
10218 | |
10219 @override | |
10220 Object visitRedirectingConstructorInvocation( | |
10221 RedirectingConstructorInvocation node) { | |
10222 node.staticElement = null; | |
10223 return super.visitRedirectingConstructorInvocation(node); | |
10224 } | |
10225 | |
10226 @override | |
10227 Object visitSimpleIdentifier(SimpleIdentifier node) { | |
10228 node.staticElement = null; | |
10229 node.propagatedElement = null; | |
10230 return super.visitSimpleIdentifier(node); | |
10231 } | |
10232 | |
10233 @override | |
10234 Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
10235 node.staticElement = null; | |
10236 return super.visitSuperConstructorInvocation(node); | |
10237 } | |
10238 | |
10239 /** | |
10240 * Remove any resolution information from the given AST structure. | |
10241 */ | |
10242 static void erase(AstNode node) { | |
10243 node.accept(new ResolutionEraser()); | |
10244 } | |
10245 } | |
10246 | |
10247 /** | |
10248 * The information produced by resolving a compilation unit as part of a | |
10249 * specific library. | |
10250 */ | |
10251 class ResolutionState { | |
10252 /** | |
10253 * The next resolution state or `null` if none. | |
10254 */ | |
10255 ResolutionState _nextState; | |
10256 | |
10257 /** | |
10258 * The source for the defining compilation unit of the library that contains | |
10259 * this unit. If this unit is the defining compilation unit for it's library, | |
10260 * then this will be the source for this unit. | |
10261 */ | |
10262 Source _librarySource; | |
10263 | |
10264 /** | |
10265 * A table mapping descriptors to the cached results for those descriptors. | |
10266 * If there is no entry for a given descriptor then the state is implicitly | |
10267 * [CacheState.INVALID] and the value is implicitly the default value. | |
10268 */ | |
10269 Map<DataDescriptor, CachedResult> resultMap = | |
10270 new HashMap<DataDescriptor, CachedResult>(); | |
10271 | |
10272 /** | |
10273 * Flush any AST structures being maintained by this state. | |
10274 */ | |
10275 void flushAstStructures() { | |
10276 _flush(DartEntry.BUILT_UNIT); | |
10277 _flush(DartEntry.RESOLVED_UNIT); | |
10278 if (_nextState != null) { | |
10279 _nextState.flushAstStructures(); | |
10280 } | |
10281 } | |
10282 | |
10283 /** | |
10284 * Return the state of the data represented by the given [descriptor]. | |
10285 */ | |
10286 CacheState getState(DataDescriptor descriptor) { | |
10287 CachedResult result = resultMap[descriptor]; | |
10288 if (result == null) { | |
10289 return CacheState.INVALID; | |
10290 } | |
10291 return result.state; | |
10292 } | |
10293 | |
10294 /** | |
10295 * Return the value of the data represented by the given [descriptor], or | |
10296 * `null` if the data represented by the descriptor is not valid. | |
10297 */ | |
10298 /*<V>*/ dynamic /*V*/ getValue(DataDescriptor /*<V>*/ descriptor) { | |
10299 CachedResult result = resultMap[descriptor]; | |
10300 if (result == null) { | |
10301 return descriptor.defaultValue; | |
10302 } | |
10303 return result.value; | |
10304 } | |
10305 | |
10306 /** | |
10307 * Return `true` if the state of any data value is [CacheState.ERROR]. | |
10308 */ | |
10309 bool hasErrorState() { | |
10310 for (CachedResult result in resultMap.values) { | |
10311 if (result.state == CacheState.ERROR) { | |
10312 return true; | |
10313 } | |
10314 } | |
10315 return false; | |
10316 } | |
10317 | |
10318 /** | |
10319 * Invalidate all of the resolution information associated with the compilatio
n unit. | |
10320 */ | |
10321 void invalidateAllResolutionInformation() { | |
10322 _nextState = null; | |
10323 _librarySource = null; | |
10324 setState(DartEntry.BUILT_UNIT, CacheState.INVALID); | |
10325 setState(DartEntry.BUILT_ELEMENT, CacheState.INVALID); | |
10326 setState(DartEntry.HINTS, CacheState.INVALID); | |
10327 setState(DartEntry.LINTS, CacheState.INVALID); | |
10328 setState(DartEntry.RESOLVED_UNIT, CacheState.INVALID); | |
10329 setState(DartEntry.RESOLUTION_ERRORS, CacheState.INVALID); | |
10330 setState(DartEntry.VERIFICATION_ERRORS, CacheState.INVALID); | |
10331 } | |
10332 | |
10333 /** | |
10334 * Record that an exception occurred while attempting to build the element | |
10335 * model for the source associated with this state. | |
10336 */ | |
10337 void recordBuildElementError() { | |
10338 setState(DartEntry.BUILT_UNIT, CacheState.ERROR); | |
10339 setState(DartEntry.BUILT_ELEMENT, CacheState.ERROR); | |
10340 recordResolutionError(); | |
10341 } | |
10342 | |
10343 /** | |
10344 * Record that an exception occurred while attempting to generate hints for | |
10345 * the source associated with this entry. This will set the state of all | |
10346 * verification information as being in error. | |
10347 */ | |
10348 void recordHintError() { | |
10349 setState(DartEntry.HINTS, CacheState.ERROR); | |
10350 } | |
10351 | |
10352 /** | |
10353 * Record that an exception occurred while attempting to generate lints for | |
10354 * the source associated with this entry. This will set the state of all | |
10355 * verification information as being in error. | |
10356 */ | |
10357 void recordLintError() { | |
10358 setState(DartEntry.LINTS, CacheState.ERROR); | |
10359 } | |
10360 | |
10361 /** | |
10362 * Record that an exception occurred while attempting to resolve the source | |
10363 * associated with this state. | |
10364 */ | |
10365 void recordResolutionError() { | |
10366 setState(DartEntry.RESOLVED_UNIT, CacheState.ERROR); | |
10367 setState(DartEntry.RESOLUTION_ERRORS, CacheState.ERROR); | |
10368 recordVerificationError(); | |
10369 } | |
10370 | |
10371 /** | |
10372 * Record that an exception occurred while attempting to scan or parse the | |
10373 * source associated with this entry. This will set the state of all | |
10374 * resolution-based information as being in error. | |
10375 */ | |
10376 void recordResolutionErrorsInAllLibraries() { | |
10377 recordBuildElementError(); | |
10378 if (_nextState != null) { | |
10379 _nextState.recordResolutionErrorsInAllLibraries(); | |
10380 } | |
10381 } | |
10382 | |
10383 /** | |
10384 * Record that an exception occurred while attempting to generate errors and | |
10385 * warnings for the source associated with this entry. This will set the state | |
10386 * of all verification information as being in error. | |
10387 */ | |
10388 void recordVerificationError() { | |
10389 setState(DartEntry.VERIFICATION_ERRORS, CacheState.ERROR); | |
10390 recordHintError(); | |
10391 } | |
10392 | |
10393 /** | |
10394 * Set the state of the data represented by the given [descriptor] to the | |
10395 * given [state]. | |
10396 */ | |
10397 void setState(DataDescriptor descriptor, CacheState state) { | |
10398 if (state == CacheState.VALID) { | |
10399 throw new ArgumentError("use setValue() to set the state to VALID"); | |
10400 } | |
10401 if (state == CacheState.INVALID) { | |
10402 resultMap.remove(descriptor); | |
10403 } else { | |
10404 CachedResult result = | |
10405 resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor)); | |
10406 result.state = state; | |
10407 if (state != CacheState.IN_PROCESS) { | |
10408 // | |
10409 // If the state is in-process, we can leave the current value in the | |
10410 // cache for any 'get' methods to access. | |
10411 // | |
10412 result.value = descriptor.defaultValue; | |
10413 } | |
10414 } | |
10415 } | |
10416 | |
10417 /** | |
10418 * Set the value of the data represented by the given [descriptor] to the | |
10419 * given [value]. | |
10420 */ | |
10421 void setValue(DataDescriptor /*<V>*/ descriptor, dynamic /*V*/ value) { | |
10422 CachedResult result = | |
10423 resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor)); | |
10424 SourceEntry.countTransition(descriptor, result); | |
10425 result.state = CacheState.VALID; | |
10426 result.value = value == null ? descriptor.defaultValue : value; | |
10427 } | |
10428 | |
10429 /** | |
10430 * Flush the value of the data described by the [descriptor]. | |
10431 */ | |
10432 void _flush(DataDescriptor descriptor) { | |
10433 CachedResult result = resultMap[descriptor]; | |
10434 if (result != null && result.state == CacheState.VALID) { | |
10435 result.state = CacheState.FLUSHED; | |
10436 result.value = descriptor.defaultValue; | |
10437 } | |
10438 } | |
10439 | |
10440 /** | |
10441 * Write a textual representation of the difference between the old entry and | |
10442 * this entry to the given string [buffer]. A separator will be written before | |
10443 * the first difference if [needsSeparator] is `true`. The [oldEntry] is the | |
10444 * entry that was replaced by this entry. Return `true` is a separator is | |
10445 * needed before writing any subsequent differences. | |
10446 */ | |
10447 bool _writeDiffOn( | |
10448 StringBuffer buffer, bool needsSeparator, DartEntry oldEntry) { | |
10449 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, "resolvedUnit", | |
10450 DartEntry.RESOLVED_UNIT, oldEntry); | |
10451 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
10452 "resolutionErrors", DartEntry.RESOLUTION_ERRORS, oldEntry); | |
10453 needsSeparator = _writeStateDiffOn(buffer, needsSeparator, | |
10454 "verificationErrors", DartEntry.VERIFICATION_ERRORS, oldEntry); | |
10455 needsSeparator = _writeStateDiffOn( | |
10456 buffer, needsSeparator, "hints", DartEntry.HINTS, oldEntry); | |
10457 needsSeparator = _writeStateDiffOn( | |
10458 buffer, needsSeparator, "lints", DartEntry.LINTS, oldEntry); | |
10459 return needsSeparator; | |
10460 } | |
10461 | |
10462 /** | |
10463 * Write a textual representation of this state to the given [buffer]. The | |
10464 * result will only be used for debugging purposes. | |
10465 */ | |
10466 void _writeOn(StringBuffer buffer) { | |
10467 if (_librarySource != null) { | |
10468 _writeStateOn(buffer, "builtElement", DartEntry.BUILT_ELEMENT); | |
10469 _writeStateOn(buffer, "builtUnit", DartEntry.BUILT_UNIT); | |
10470 _writeStateOn(buffer, "resolvedUnit", DartEntry.RESOLVED_UNIT); | |
10471 _writeStateOn(buffer, "resolutionErrors", DartEntry.RESOLUTION_ERRORS); | |
10472 _writeStateOn( | |
10473 buffer, "verificationErrors", DartEntry.VERIFICATION_ERRORS); | |
10474 _writeStateOn(buffer, "hints", DartEntry.HINTS); | |
10475 _writeStateOn(buffer, "lints", DartEntry.LINTS); | |
10476 if (_nextState != null) { | |
10477 _nextState._writeOn(buffer); | |
10478 } | |
10479 } | |
10480 } | |
10481 | |
10482 /** | |
10483 * Write a textual representation of the difference between the state of the | |
10484 * value described by the given [descriptor] between the [oldEntry] and this | |
10485 * entry to the given [buffer]. Return `true` if some difference was written. | |
10486 */ | |
10487 bool _writeStateDiffOn(StringBuffer buffer, bool needsSeparator, String label, | |
10488 DataDescriptor descriptor, SourceEntry oldEntry) { | |
10489 CacheState oldState = oldEntry.getState(descriptor); | |
10490 CacheState newState = getState(descriptor); | |
10491 if (oldState != newState) { | |
10492 if (needsSeparator) { | |
10493 buffer.write("; "); | |
10494 } | |
10495 buffer.write(label); | |
10496 buffer.write(" = "); | |
10497 buffer.write(oldState); | |
10498 buffer.write(" -> "); | |
10499 buffer.write(newState); | |
10500 return true; | |
10501 } | |
10502 return needsSeparator; | |
10503 } | |
10504 | |
10505 /** | |
10506 * Write a textual representation of the state of the value described by the | |
10507 * given [descriptor] to the given bugger, prefixed by the given [label] to | |
10508 * the given [buffer]. | |
10509 */ | |
10510 void _writeStateOn( | |
10511 StringBuffer buffer, String label, DataDescriptor descriptor) { | |
10512 CachedResult result = resultMap[descriptor]; | |
10513 buffer.write("; "); | |
10514 buffer.write(label); | |
10515 buffer.write(" = "); | |
10516 buffer.write(result == null ? CacheState.INVALID : result.state); | |
10517 } | |
10518 } | |
10519 | |
10520 /** | |
10521 * A compilation unit that is not referenced by any other objects. It is used by | |
10522 * the [LibraryResolver] to resolve a library. | |
10523 */ | |
10524 class ResolvableCompilationUnit { | |
10525 /** | |
10526 * The source of the compilation unit. | |
10527 */ | |
10528 final Source source; | |
10529 | |
10530 /** | |
10531 * The compilation unit. | |
10532 */ | |
10533 final CompilationUnit compilationUnit; | |
10534 | |
10535 /** | |
10536 * Initialize a newly created holder to hold the given [source] and | |
10537 * [compilationUnit]. | |
10538 */ | |
10539 ResolvableCompilationUnit(this.source, this.compilationUnit); | |
10540 } | |
10541 | |
10542 /** | |
10543 * Instances of the class `ResolveDartLibraryTask` resolve a specific Dart libra
ry. | |
10544 */ | |
10545 class ResolveDartLibraryCycleTask extends AnalysisTask { | |
10546 /** | |
10547 * The source representing the file whose compilation unit is to be returned.
TODO(brianwilkerson) | |
10548 * This should probably be removed, but is being left in for now to ease the t
ransition. | |
10549 */ | |
10550 final Source unitSource; | |
10551 | |
10552 /** | |
10553 * The source representing the library to be resolved. | |
10554 */ | |
10555 final Source librarySource; | |
10556 | |
10557 /** | |
10558 * The libraries that are part of the cycle containing the library to be resol
ved. | |
10559 */ | |
10560 final List<ResolvableLibrary> _librariesInCycle; | |
10561 | |
10562 /** | |
10563 * The library resolver holding information about the libraries that were reso
lved. | |
10564 */ | |
10565 LibraryResolver2 _resolver; | |
10566 | |
10567 /** | |
10568 * Initialize a newly created task to perform analysis within the given contex
t. | |
10569 * | |
10570 * @param context the context in which the task is to be performed | |
10571 * @param unitSource the source representing the file whose compilation unit i
s to be returned | |
10572 * @param librarySource the source representing the library to be resolved | |
10573 * @param librariesInCycle the libraries that are part of the cycle containing
the library to be | |
10574 * resolved | |
10575 */ | |
10576 ResolveDartLibraryCycleTask(InternalAnalysisContext context, this.unitSource, | |
10577 this.librarySource, this._librariesInCycle) | |
10578 : super(context); | |
10579 | |
10580 /** | |
10581 * Return the library resolver holding information about the libraries that we
re resolved. | |
10582 * | |
10583 * @return the library resolver holding information about the libraries that w
ere resolved | |
10584 */ | |
10585 LibraryResolver2 get libraryResolver => _resolver; | |
10586 | |
10587 @override | |
10588 String get taskDescription { | |
10589 if (librarySource == null) { | |
10590 return "resolve library null source"; | |
10591 } | |
10592 return "resolve library ${librarySource.fullName}"; | |
10593 } | |
10594 | |
10595 @override | |
10596 accept(AnalysisTaskVisitor visitor) => | |
10597 visitor.visitResolveDartLibraryCycleTask(this); | |
10598 | |
10599 @override | |
10600 void internalPerform() { | |
10601 _resolver = new LibraryResolver2(context); | |
10602 _resolver.resolveLibrary(librarySource, _librariesInCycle); | |
10603 } | |
10604 } | |
10605 | |
10606 /** | |
10607 * Instances of the class `ResolveDartLibraryTask` resolve a specific Dart libra
ry. | |
10608 */ | |
10609 class ResolveDartLibraryTask extends AnalysisTask { | |
10610 /** | |
10611 * The source representing the file whose compilation unit is to be returned. | |
10612 */ | |
10613 final Source unitSource; | |
10614 | |
10615 /** | |
10616 * The source representing the library to be resolved. | |
10617 */ | |
10618 final Source librarySource; | |
10619 | |
10620 /** | |
10621 * The library resolver holding information about the libraries that were reso
lved. | |
10622 */ | |
10623 LibraryResolver _resolver; | |
10624 | |
10625 /** | |
10626 * Initialize a newly created task to perform analysis within the given contex
t. | |
10627 * | |
10628 * @param context the context in which the task is to be performed | |
10629 * @param unitSource the source representing the file whose compilation unit i
s to be returned | |
10630 * @param librarySource the source representing the library to be resolved | |
10631 */ | |
10632 ResolveDartLibraryTask( | |
10633 InternalAnalysisContext context, this.unitSource, this.librarySource) | |
10634 : super(context); | |
10635 | |
10636 /** | |
10637 * Return the library resolver holding information about the libraries that we
re resolved. | |
10638 * | |
10639 * @return the library resolver holding information about the libraries that w
ere resolved | |
10640 */ | |
10641 LibraryResolver get libraryResolver => _resolver; | |
10642 | |
10643 @override | |
10644 String get taskDescription { | |
10645 if (librarySource == null) { | |
10646 return "resolve library null source"; | |
10647 } | |
10648 return "resolve library ${librarySource.fullName}"; | |
10649 } | |
10650 | |
10651 @override | |
10652 accept(AnalysisTaskVisitor visitor) => | |
10653 visitor.visitResolveDartLibraryTask(this); | |
10654 | |
10655 @override | |
10656 void internalPerform() { | |
10657 LibraryResolverFactory resolverFactory = context.libraryResolverFactory; | |
10658 _resolver = resolverFactory == null | |
10659 ? new LibraryResolver(context) | |
10660 : resolverFactory(context); | |
10661 _resolver.resolveLibrary(librarySource, true); | |
10662 } | |
10663 } | |
10664 | |
10665 /** | |
10666 * Instances of the class `ResolveDartUnitTask` resolve a single Dart file based
on a existing | |
10667 * element model. | |
10668 */ | |
10669 class ResolveDartUnitTask extends AnalysisTask { | |
10670 /** | |
10671 * The source that is to be resolved. | |
10672 */ | |
10673 final Source source; | |
10674 | |
10675 /** | |
10676 * The element model for the library containing the source. | |
10677 */ | |
10678 final LibraryElement _libraryElement; | |
10679 | |
10680 /** | |
10681 * The compilation unit that was resolved by this task. | |
10682 */ | |
10683 CompilationUnit _resolvedUnit; | |
10684 | |
10685 /** | |
10686 * Initialize a newly created task to perform analysis within the given contex
t. | |
10687 * | |
10688 * @param context the context in which the task is to be performed | |
10689 * @param source the source to be parsed | |
10690 * @param libraryElement the element model for the library containing the sour
ce | |
10691 */ | |
10692 ResolveDartUnitTask( | |
10693 InternalAnalysisContext context, this.source, this._libraryElement) | |
10694 : super(context); | |
10695 | |
10696 /** | |
10697 * Return the source for the library containing the source that is to be resol
ved. | |
10698 * | |
10699 * @return the source for the library containing the source that is to be reso
lved | |
10700 */ | |
10701 Source get librarySource => _libraryElement.source; | |
10702 | |
10703 /** | |
10704 * Return the compilation unit that was resolved by this task. | |
10705 * | |
10706 * @return the compilation unit that was resolved by this task | |
10707 */ | |
10708 CompilationUnit get resolvedUnit => _resolvedUnit; | |
10709 | |
10710 @override | |
10711 String get taskDescription { | |
10712 Source librarySource = _libraryElement.source; | |
10713 if (librarySource == null) { | |
10714 return "resolve unit null source"; | |
10715 } | |
10716 return "resolve unit ${librarySource.fullName}"; | |
10717 } | |
10718 | |
10719 @override | |
10720 accept(AnalysisTaskVisitor visitor) => visitor.visitResolveDartUnitTask(this); | |
10721 | |
10722 @override | |
10723 void internalPerform() { | |
10724 TypeProvider typeProvider = _libraryElement.context.typeProvider; | |
10725 CompilationUnit unit = context.computeResolvableCompilationUnit(source); | |
10726 if (unit == null) { | |
10727 throw new AnalysisException( | |
10728 "Internal error: computeResolvableCompilationUnit returned a value wit
hout a parsed Dart unit"); | |
10729 } | |
10730 // | |
10731 // Resolve names in declarations. | |
10732 // | |
10733 new DeclarationResolver().resolve(unit, _find(_libraryElement, source)); | |
10734 // | |
10735 // Resolve the type names. | |
10736 // | |
10737 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
10738 TypeResolverVisitor typeResolverVisitor = new TypeResolverVisitor( | |
10739 _libraryElement, source, typeProvider, errorListener); | |
10740 unit.accept(typeResolverVisitor); | |
10741 // | |
10742 // Resolve the rest of the structure | |
10743 // | |
10744 InheritanceManager inheritanceManager = | |
10745 new InheritanceManager(_libraryElement); | |
10746 ResolverVisitor resolverVisitor = new ResolverVisitor( | |
10747 _libraryElement, source, typeProvider, errorListener, | |
10748 inheritanceManager: inheritanceManager); | |
10749 unit.accept(resolverVisitor); | |
10750 // | |
10751 // Perform additional error checking. | |
10752 // | |
10753 PerformanceStatistics.errors.makeCurrentWhile(() { | |
10754 ErrorReporter errorReporter = new ErrorReporter(errorListener, source); | |
10755 ErrorVerifier errorVerifier = new ErrorVerifier( | |
10756 errorReporter, _libraryElement, typeProvider, inheritanceManager); | |
10757 unit.accept(errorVerifier); | |
10758 // TODO(paulberry): as a temporary workaround for issue 21572, | |
10759 // ConstantVerifier is being run right after ConstantValueComputer, so we | |
10760 // don't need to run it here. Once issue 21572 is fixed, re-enable the | |
10761 // call to ConstantVerifier. | |
10762 // ConstantVerifier constantVerifier = new ConstantVerifier(errorReporter,
_libraryElement, typeProvider); | |
10763 // unit.accept(constantVerifier); | |
10764 }); | |
10765 // | |
10766 // Capture the results. | |
10767 // | |
10768 _resolvedUnit = unit; | |
10769 } | |
10770 | |
10771 /** | |
10772 * Search the compilation units that are part of the given library and return
the element | |
10773 * representing the compilation unit with the given source. Return `null` if t
here is no | |
10774 * such compilation unit. | |
10775 * | |
10776 * @param libraryElement the element representing the library being searched t
hrough | |
10777 * @param unitSource the source for the compilation unit whose element is to b
e returned | |
10778 * @return the element representing the compilation unit | |
10779 */ | |
10780 CompilationUnitElement _find( | |
10781 LibraryElement libraryElement, Source unitSource) { | |
10782 CompilationUnitElement element = libraryElement.definingCompilationUnit; | |
10783 if (element.source == unitSource) { | |
10784 return element; | |
10785 } | |
10786 for (CompilationUnitElement partElement in libraryElement.parts) { | |
10787 if (partElement.source == unitSource) { | |
10788 return partElement; | |
10789 } | |
10790 } | |
10791 return null; | |
10792 } | |
10793 } | |
10794 | |
10795 /** | |
10796 * Instances of the class `ResolveHtmlTask` resolve a specific source as an HTML
file. | |
10797 */ | |
10798 class ResolveHtmlTask extends AnalysisTask { | |
10799 /** | |
10800 * The source to be resolved. | |
10801 */ | |
10802 final Source source; | |
10803 | |
10804 /** | |
10805 * The time at which the contents of the source were last modified. | |
10806 */ | |
10807 final int modificationTime; | |
10808 | |
10809 /** | |
10810 * The HTML unit to be resolved. | |
10811 */ | |
10812 final ht.HtmlUnit _unit; | |
10813 | |
10814 /** | |
10815 * The [HtmlUnit] that was resolved by this task. | |
10816 */ | |
10817 ht.HtmlUnit _resolvedUnit; | |
10818 | |
10819 /** | |
10820 * The element produced by resolving the source. | |
10821 */ | |
10822 HtmlElement _element = null; | |
10823 | |
10824 /** | |
10825 * The resolution errors that were discovered while resolving the source. | |
10826 */ | |
10827 List<AnalysisError> _resolutionErrors = AnalysisError.NO_ERRORS; | |
10828 | |
10829 /** | |
10830 * Initialize a newly created task to perform analysis within the given contex
t. | |
10831 * | |
10832 * @param context the context in which the task is to be performed | |
10833 * @param source the source to be resolved | |
10834 * @param modificationTime the time at which the contents of the source were l
ast modified | |
10835 * @param unit the HTML unit to be resolved | |
10836 */ | |
10837 ResolveHtmlTask(InternalAnalysisContext context, this.source, | |
10838 this.modificationTime, this._unit) | |
10839 : super(context); | |
10840 | |
10841 HtmlElement get element => _element; | |
10842 | |
10843 List<AnalysisError> get resolutionErrors => _resolutionErrors; | |
10844 | |
10845 /** | |
10846 * Return the [HtmlUnit] that was resolved by this task. | |
10847 * | |
10848 * @return the [HtmlUnit] that was resolved by this task | |
10849 */ | |
10850 ht.HtmlUnit get resolvedUnit => _resolvedUnit; | |
10851 | |
10852 @override | |
10853 String get taskDescription { | |
10854 if (source == null) { | |
10855 return "resolve as html null source"; | |
10856 } | |
10857 return "resolve as html ${source.fullName}"; | |
10858 } | |
10859 | |
10860 @override | |
10861 accept(AnalysisTaskVisitor visitor) => visitor.visitResolveHtmlTask(this); | |
10862 | |
10863 @override | |
10864 void internalPerform() { | |
10865 // | |
10866 // Build the standard HTML element. | |
10867 // | |
10868 HtmlUnitBuilder builder = new HtmlUnitBuilder(context); | |
10869 _element = builder.buildHtmlElement(source, _unit); | |
10870 RecordingErrorListener errorListener = builder.errorListener; | |
10871 // | |
10872 // Validate the directives | |
10873 // | |
10874 _unit.accept(new RecursiveXmlVisitor_ResolveHtmlTask_internalPerform( | |
10875 this, errorListener)); | |
10876 // | |
10877 // Record all resolution errors. | |
10878 // | |
10879 _resolutionErrors = errorListener.getErrorsForSource(source); | |
10880 // | |
10881 // Remember the resolved unit. | |
10882 // | |
10883 _resolvedUnit = _unit; | |
10884 } | |
10885 } | |
10886 | |
10887 /** | |
10888 * The priority of data in the cache in terms of the desirability of retaining | |
10889 * some specified data about a specified source. | |
10890 */ | |
10891 class RetentionPriority extends Enum<RetentionPriority> { | |
10892 /** | |
10893 * A priority indicating that a given piece of data can be removed from the | |
10894 * cache without reservation. | |
10895 */ | |
10896 static const RetentionPriority LOW = const RetentionPriority('LOW', 0); | |
10897 | |
10898 /** | |
10899 * A priority indicating that a given piece of data should not be removed from | |
10900 * the cache unless there are no sources for which the corresponding data has | |
10901 * a lower priority. Currently used for data that is needed in order to finish | |
10902 * some outstanding analysis task. | |
10903 */ | |
10904 static const RetentionPriority MEDIUM = const RetentionPriority('MEDIUM', 1); | |
10905 | |
10906 /** | |
10907 * A priority indicating that a given piece of data should not be removed from | |
10908 * the cache. Currently used for data related to a priority source. | |
10909 */ | |
10910 static const RetentionPriority HIGH = const RetentionPriority('HIGH', 2); | |
10911 | |
10912 static const List<RetentionPriority> values = const [LOW, MEDIUM, HIGH]; | |
10913 | |
10914 const RetentionPriority(String name, int ordinal) : super(name, ordinal); | |
10915 } | |
10916 | |
10917 /** | |
10918 * Instances of the class `ScanDartTask` scan a specific source as a Dart file. | |
10919 */ | |
10920 class ScanDartTask extends AnalysisTask { | |
10921 /** | |
10922 * The source to be scanned. | |
10923 */ | |
10924 final Source source; | |
10925 | |
10926 /** | |
10927 * The contents of the source. | |
10928 */ | |
10929 final String _content; | |
10930 | |
10931 /** | |
10932 * The token stream that was produced by scanning the source. | |
10933 */ | |
10934 Token _tokenStream; | |
10935 | |
10936 /** | |
10937 * The line information that was produced. | |
10938 */ | |
10939 LineInfo _lineInfo; | |
10940 | |
10941 /** | |
10942 * The errors that were produced by scanning the source. | |
10943 */ | |
10944 List<AnalysisError> _errors = AnalysisError.NO_ERRORS; | |
10945 | |
10946 /** | |
10947 * Initialize a newly created task to perform analysis within the given contex
t. | |
10948 * | |
10949 * @param context the context in which the task is to be performed | |
10950 * @param source the source to be parsed | |
10951 * @param content the contents of the source | |
10952 */ | |
10953 ScanDartTask(InternalAnalysisContext context, this.source, this._content) | |
10954 : super(context); | |
10955 | |
10956 /** | |
10957 * Return the errors that were produced by scanning the source, or `null` if t
he task has | |
10958 * not yet been performed or if an exception occurred. | |
10959 * | |
10960 * @return the errors that were produced by scanning the source | |
10961 */ | |
10962 List<AnalysisError> get errors => _errors; | |
10963 | |
10964 /** | |
10965 * Return the line information that was produced, or `null` if the task has no
t yet been | |
10966 * performed or if an exception occurred. | |
10967 * | |
10968 * @return the line information that was produced | |
10969 */ | |
10970 LineInfo get lineInfo => _lineInfo; | |
10971 | |
10972 @override | |
10973 String get taskDescription { | |
10974 if (source == null) { | |
10975 return "scan as dart null source"; | |
10976 } | |
10977 return "scan as dart ${source.fullName}"; | |
10978 } | |
10979 | |
10980 /** | |
10981 * Return the token stream that was produced by scanning the source, or `null`
if the task | |
10982 * has not yet been performed or if an exception occurred. | |
10983 * | |
10984 * @return the token stream that was produced by scanning the source | |
10985 */ | |
10986 Token get tokenStream => _tokenStream; | |
10987 | |
10988 @override | |
10989 accept(AnalysisTaskVisitor visitor) => visitor.visitScanDartTask(this); | |
10990 | |
10991 @override | |
10992 void internalPerform() { | |
10993 PerformanceStatistics.scan.makeCurrentWhile(() { | |
10994 RecordingErrorListener errorListener = new RecordingErrorListener(); | |
10995 try { | |
10996 Scanner scanner = new Scanner( | |
10997 source, new CharSequenceReader(_content), errorListener); | |
10998 scanner.preserveComments = context.analysisOptions.preserveComments; | |
10999 _tokenStream = scanner.tokenize(); | |
11000 _lineInfo = new LineInfo(scanner.lineStarts); | |
11001 _errors = errorListener.getErrorsForSource(source); | |
11002 } catch (exception, stackTrace) { | |
11003 throw new AnalysisException( | |
11004 "Exception", new CaughtException(exception, stackTrace)); | |
11005 } | |
11006 }); | |
11007 } | |
11008 } | |
11009 | |
11010 /** | |
11011 * An [AnalysisContext] that only contains sources for a Dart SDK. | |
11012 */ | |
11013 class SdkAnalysisContext extends AnalysisContextImpl { | |
11014 @override | |
11015 AnalysisCache createCacheFromSourceFactory(SourceFactory factory) { | |
11016 if (factory == null) { | |
11017 return super.createCacheFromSourceFactory(factory); | |
11018 } | |
11019 DartSdk sdk = factory.dartSdk; | |
11020 if (sdk == null) { | |
11021 throw new IllegalArgumentException( | |
11022 "The source factory for an SDK analysis context must have a DartUriRes
olver"); | |
11023 } | |
11024 return new AnalysisCache( | |
11025 <CachePartition>[AnalysisEngine.instance.partitionManager.forSdk(sdk)]); | |
11026 } | |
11027 } | |
11028 | |
11029 /** | |
11030 * A cache partition that contains all of the sources in the SDK. | |
11031 */ | |
11032 class SdkCachePartition extends CachePartition { | |
11033 /** | |
11034 * Initialize a newly created partition. The [context] is the context that | |
11035 * owns this partition. The [maxCacheSize] is the maximum number of sources | |
11036 * for which AST structures should be kept in the cache. | |
11037 */ | |
11038 SdkCachePartition(InternalAnalysisContext context, int maxCacheSize) | |
11039 : super(context, maxCacheSize, DefaultRetentionPolicy.POLICY); | |
11040 | |
11041 @override | |
11042 bool contains(Source source) => source.isInSystemLibrary; | |
11043 } | |
11044 | |
11045 /** | |
11046 * The information cached by an analysis context about an individual source, no | |
11047 * matter what kind of source it is. | |
11048 */ | |
11049 abstract class SourceEntry { | |
11050 /** | |
11051 * The data descriptor representing the contents of the source. | |
11052 */ | |
11053 static final DataDescriptor<String> CONTENT = | |
11054 new DataDescriptor<String>("SourceEntry.CONTENT"); | |
11055 | |
11056 /** | |
11057 * The data descriptor representing the errors resulting from reading the | |
11058 * source content. | |
11059 */ | |
11060 static final DataDescriptor<List<AnalysisError>> CONTENT_ERRORS = | |
11061 new DataDescriptor<List<AnalysisError>>( | |
11062 "SourceEntry.CONTENT_ERRORS", AnalysisError.NO_ERRORS); | |
11063 | |
11064 /** | |
11065 * The data descriptor representing the line information. | |
11066 */ | |
11067 static final DataDescriptor<LineInfo> LINE_INFO = | |
11068 new DataDescriptor<LineInfo>("SourceEntry.LINE_INFO"); | |
11069 | |
11070 /** | |
11071 * The index of the flag indicating whether the source was explicitly added to | |
11072 * the context or whether the source was implicitly added because it was | |
11073 * referenced by another source. | |
11074 */ | |
11075 static int _EXPLICITLY_ADDED_FLAG = 0; | |
11076 | |
11077 /** | |
11078 * A table mapping data descriptors to a count of the number of times a value | |
11079 * was set when in a given state. | |
11080 */ | |
11081 static final Map<DataDescriptor, Map<CacheState, int>> transitionMap = | |
11082 new HashMap<DataDescriptor, Map<CacheState, int>>(); | |
11083 | |
11084 /** | |
11085 * The most recent time at which the state of the source matched the state | |
11086 * represented by this entry. | |
11087 */ | |
11088 int modificationTime = 0; | |
11089 | |
11090 /** | |
11091 * The exception that caused one or more values to have a state of | |
11092 * [CacheState.ERROR]. | |
11093 */ | |
11094 CaughtException exception; | |
11095 | |
11096 /** | |
11097 * A bit-encoding of boolean flags associated with this element. | |
11098 */ | |
11099 int _flags = 0; | |
11100 | |
11101 /** | |
11102 * A table mapping data descriptors to the cached results for those | |
11103 * descriptors. | |
11104 */ | |
11105 Map<DataDescriptor, CachedResult> resultMap = | |
11106 new HashMap<DataDescriptor, CachedResult>(); | |
11107 | |
11108 /** | |
11109 * Return all of the errors associated with this entry. | |
11110 */ | |
11111 List<AnalysisError> get allErrors { | |
11112 return getValue(CONTENT_ERRORS); | |
11113 } | |
11114 | |
11115 /** | |
11116 * Get a list of all the library-independent descriptors for which values may | |
11117 * be stored in this SourceEntry. | |
11118 */ | |
11119 List<DataDescriptor> get descriptors { | |
11120 return <DataDescriptor>[CONTENT, CONTENT_ERRORS, LINE_INFO]; | |
11121 } | |
11122 | |
11123 /** | |
11124 * Return `true` if the source was explicitly added to the context or `false` | |
11125 * if the source was implicitly added because it was referenced by another | |
11126 * source. | |
11127 */ | |
11128 bool get explicitlyAdded => _getFlag(_EXPLICITLY_ADDED_FLAG); | |
11129 | |
11130 /** | |
11131 * Set whether the source was explicitly added to the context to match the | |
11132 * [explicitlyAdded] flag. | |
11133 */ | |
11134 void set explicitlyAdded(bool explicitlyAdded) { | |
11135 _setFlag(_EXPLICITLY_ADDED_FLAG, explicitlyAdded); | |
11136 } | |
11137 | |
11138 /** | |
11139 * Return the kind of the source, or `null` if the kind is not currently | |
11140 * cached. | |
11141 */ | |
11142 SourceKind get kind; | |
11143 | |
11144 /** | |
11145 * Fix the state of the [exception] to match the current state of the entry. | |
11146 */ | |
11147 void fixExceptionState() { | |
11148 if (hasErrorState()) { | |
11149 if (exception == null) { | |
11150 // | |
11151 // This code should never be reached, but is a fail-safe in case an | |
11152 // exception is not recorded when it should be. | |
11153 // | |
11154 String message = "State set to ERROR without setting an exception"; | |
11155 exception = new CaughtException(new AnalysisException(message), null); | |
11156 } | |
11157 } else { | |
11158 exception = null; | |
11159 } | |
11160 } | |
11161 | |
11162 /** | |
11163 * Return a textual representation of the difference between the [oldEntry] | |
11164 * and this entry. The difference is represented as a sequence of fields whose | |
11165 * value would change if the old entry were converted into the new entry. | |
11166 */ | |
11167 String getDiff(SourceEntry oldEntry) { | |
11168 StringBuffer buffer = new StringBuffer(); | |
11169 _writeDiffOn(buffer, oldEntry); | |
11170 return buffer.toString(); | |
11171 } | |
11172 | |
11173 /** | |
11174 * Return the state of the data represented by the given [descriptor]. | |
11175 */ | |
11176 CacheState getState(DataDescriptor descriptor) { | |
11177 if (!_isValidDescriptor(descriptor)) { | |
11178 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
11179 } | |
11180 CachedResult result = resultMap[descriptor]; | |
11181 if (result == null) { | |
11182 return CacheState.INVALID; | |
11183 } | |
11184 return result.state; | |
11185 } | |
11186 | |
11187 /** | |
11188 * Return the value of the data represented by the given [descriptor], or | |
11189 * `null` if the data represented by the descriptor is not valid. | |
11190 */ | |
11191 /*<V>*/ dynamic /*V*/ getValue(DataDescriptor /*<V>*/ descriptor) { | |
11192 if (!_isValidDescriptor(descriptor)) { | |
11193 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
11194 } | |
11195 CachedResult result = resultMap[descriptor]; | |
11196 if (result == null) { | |
11197 return descriptor.defaultValue; | |
11198 } | |
11199 return result.value; | |
11200 } | |
11201 | |
11202 /** | |
11203 * Return `true` if the state of any data value is [CacheState.ERROR]. | |
11204 */ | |
11205 bool hasErrorState() { | |
11206 for (CachedResult result in resultMap.values) { | |
11207 if (result.state == CacheState.ERROR) { | |
11208 return true; | |
11209 } | |
11210 } | |
11211 return false; | |
11212 } | |
11213 | |
11214 /** | |
11215 * Invalidate all of the information associated with this source. | |
11216 */ | |
11217 void invalidateAllInformation() { | |
11218 setState(CONTENT, CacheState.INVALID); | |
11219 setState(CONTENT_ERRORS, CacheState.INVALID); | |
11220 setState(LINE_INFO, CacheState.INVALID); | |
11221 } | |
11222 | |
11223 /** | |
11224 * Record that an [exception] occurred while attempting to get the contents of | |
11225 * the source represented by this entry. This will set the state of all | |
11226 * information, including any resolution-based information, as being in error. | |
11227 */ | |
11228 void recordContentError(CaughtException exception) { | |
11229 setState(CONTENT, CacheState.ERROR); | |
11230 recordScanError(exception); | |
11231 } | |
11232 | |
11233 /** | |
11234 * Record that an [exception] occurred while attempting to scan or parse the | |
11235 * entry represented by this entry. This will set the state of all | |
11236 * information, including any resolution-based information, as being in error. | |
11237 */ | |
11238 void recordScanError(CaughtException exception) { | |
11239 this.exception = exception; | |
11240 setState(LINE_INFO, CacheState.ERROR); | |
11241 } | |
11242 | |
11243 /** | |
11244 * Set the state of the data represented by the given [descriptor] to the | |
11245 * given [state]. | |
11246 */ | |
11247 void setState(DataDescriptor descriptor, CacheState state) { | |
11248 if (!_isValidDescriptor(descriptor)) { | |
11249 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
11250 } | |
11251 if (state == CacheState.VALID) { | |
11252 throw new ArgumentError("use setValue() to set the state to VALID"); | |
11253 } | |
11254 _validateStateChange(descriptor, state); | |
11255 if (state == CacheState.INVALID) { | |
11256 resultMap.remove(descriptor); | |
11257 } else { | |
11258 CachedResult result = | |
11259 resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor)); | |
11260 result.state = state; | |
11261 if (state != CacheState.IN_PROCESS) { | |
11262 // | |
11263 // If the state is in-process, we can leave the current value in the | |
11264 // cache for any 'get' methods to access. | |
11265 // | |
11266 result.value = descriptor.defaultValue; | |
11267 } | |
11268 } | |
11269 } | |
11270 | |
11271 /** | |
11272 * Set the value of the data represented by the given [descriptor] to the | |
11273 * given [value]. | |
11274 */ | |
11275 void setValue(DataDescriptor /*<V>*/ descriptor, dynamic /*V*/ value) { | |
11276 if (!_isValidDescriptor(descriptor)) { | |
11277 throw new ArgumentError("Invalid descriptor: $descriptor"); | |
11278 } | |
11279 _validateStateChange(descriptor, CacheState.VALID); | |
11280 CachedResult result = | |
11281 resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor)); | |
11282 countTransition(descriptor, result); | |
11283 result.state = CacheState.VALID; | |
11284 result.value = value == null ? descriptor.defaultValue : value; | |
11285 } | |
11286 | |
11287 @override | |
11288 String toString() { | |
11289 StringBuffer buffer = new StringBuffer(); | |
11290 _writeOn(buffer); | |
11291 return buffer.toString(); | |
11292 } | |
11293 | |
11294 /** | |
11295 * Flush the value of the data described by the [descriptor]. | |
11296 */ | |
11297 void _flush(DataDescriptor descriptor) { | |
11298 CachedResult result = resultMap[descriptor]; | |
11299 if (result != null && result.state == CacheState.VALID) { | |
11300 _validateStateChange(descriptor, CacheState.FLUSHED); | |
11301 result.state = CacheState.FLUSHED; | |
11302 result.value = descriptor.defaultValue; | |
11303 } | |
11304 } | |
11305 | |
11306 /** | |
11307 * Return the value of the flag with the given [index]. | |
11308 */ | |
11309 bool _getFlag(int index) => BooleanArray.get(_flags, index); | |
11310 | |
11311 /** | |
11312 * Return `true` if the [descriptor] is valid for this entry. | |
11313 */ | |
11314 bool _isValidDescriptor(DataDescriptor descriptor) { | |
11315 return descriptor == CONTENT || | |
11316 descriptor == CONTENT_ERRORS || | |
11317 descriptor == LINE_INFO; | |
11318 } | |
11319 | |
11320 /** | |
11321 * Set the value of the flag with the given [index] to the given [value]. | |
11322 */ | |
11323 void _setFlag(int index, bool value) { | |
11324 _flags = BooleanArray.set(_flags, index, value); | |
11325 } | |
11326 | |
11327 /** | |
11328 * If the state of the value described by the given [descriptor] is changing | |
11329 * from ERROR to anything else, capture the information. This is an attempt to | |
11330 * discover the underlying cause of a long-standing bug. | |
11331 */ | |
11332 void _validateStateChange(DataDescriptor descriptor, CacheState newState) { | |
11333 // TODO(brianwilkerson) Decide whether we still want to capture this data. | |
11334 // if (descriptor != CONTENT) { | |
11335 // return; | |
11336 // } | |
11337 // CachedResult result = resultMap[CONTENT]; | |
11338 // if (result != null && result.state == CacheState.ERROR) { | |
11339 // String message = | |
11340 // "contentState changing from ${result.state} to $newState"; | |
11341 // InstrumentationBuilder builder = | |
11342 // Instrumentation.builder2("SourceEntry-validateStateChange"); | |
11343 // builder.data3("message", message); | |
11344 // //builder.data("source", source.getFullName()); | |
11345 // builder.record(new CaughtException(new AnalysisException(message), null)
); | |
11346 // builder.log(); | |
11347 // } | |
11348 } | |
11349 | |
11350 /** | |
11351 * Write a textual representation of the difference between the [oldEntry] and | |
11352 * this entry to the given string [buffer]. Return `true` if some difference | |
11353 * was written. | |
11354 */ | |
11355 bool _writeDiffOn(StringBuffer buffer, SourceEntry oldEntry) { | |
11356 bool needsSeparator = false; | |
11357 CaughtException oldException = oldEntry.exception; | |
11358 if (!identical(oldException, exception)) { | |
11359 buffer.write("exception = "); | |
11360 buffer.write(oldException.runtimeType); | |
11361 buffer.write(" -> "); | |
11362 buffer.write(exception.runtimeType); | |
11363 needsSeparator = true; | |
11364 } | |
11365 int oldModificationTime = oldEntry.modificationTime; | |
11366 if (oldModificationTime != modificationTime) { | |
11367 if (needsSeparator) { | |
11368 buffer.write("; "); | |
11369 } | |
11370 buffer.write("time = "); | |
11371 buffer.write(oldModificationTime); | |
11372 buffer.write(" -> "); | |
11373 buffer.write(modificationTime); | |
11374 needsSeparator = true; | |
11375 } | |
11376 needsSeparator = | |
11377 _writeStateDiffOn(buffer, needsSeparator, "content", CONTENT, oldEntry); | |
11378 needsSeparator = _writeStateDiffOn( | |
11379 buffer, needsSeparator, "contentErrors", CONTENT_ERRORS, oldEntry); | |
11380 needsSeparator = _writeStateDiffOn( | |
11381 buffer, needsSeparator, "lineInfo", LINE_INFO, oldEntry); | |
11382 return needsSeparator; | |
11383 } | |
11384 | |
11385 /** | |
11386 * Write a textual representation of this entry to the given [buffer]. The | |
11387 * result should only be used for debugging purposes. | |
11388 */ | |
11389 void _writeOn(StringBuffer buffer) { | |
11390 buffer.write("time = "); | |
11391 buffer.write(modificationTime); | |
11392 _writeStateOn(buffer, "content", CONTENT); | |
11393 _writeStateOn(buffer, "contentErrors", CONTENT_ERRORS); | |
11394 _writeStateOn(buffer, "lineInfo", LINE_INFO); | |
11395 } | |
11396 | |
11397 /** | |
11398 * Write a textual representation of the difference between the state of the | |
11399 * value described by the given [descriptor] between the [oldEntry] and this | |
11400 * entry to the given [buffer]. Return `true` if some difference was written. | |
11401 */ | |
11402 bool _writeStateDiffOn(StringBuffer buffer, bool needsSeparator, String label, | |
11403 DataDescriptor descriptor, SourceEntry oldEntry) { | |
11404 CacheState oldState = oldEntry.getState(descriptor); | |
11405 CacheState newState = getState(descriptor); | |
11406 if (oldState != newState) { | |
11407 if (needsSeparator) { | |
11408 buffer.write("; "); | |
11409 } | |
11410 buffer.write(label); | |
11411 buffer.write(" = "); | |
11412 buffer.write(oldState); | |
11413 buffer.write(" -> "); | |
11414 buffer.write(newState); | |
11415 return true; | |
11416 } | |
11417 return needsSeparator; | |
11418 } | |
11419 | |
11420 /** | |
11421 * Write a textual representation of the state of the value described by the | |
11422 * given [descriptor] to the given bugger, prefixed by the given [label] to | |
11423 * the given [buffer]. | |
11424 */ | |
11425 void _writeStateOn( | |
11426 StringBuffer buffer, String label, DataDescriptor descriptor) { | |
11427 CachedResult result = resultMap[descriptor]; | |
11428 buffer.write("; "); | |
11429 buffer.write(label); | |
11430 buffer.write(" = "); | |
11431 buffer.write(result == null ? CacheState.INVALID : result.state); | |
11432 } | |
11433 | |
11434 /** | |
11435 * Increment the count of the number of times that data represented by the | |
11436 * given [descriptor] was transitioned from the current state (as found in the | |
11437 * given [result] to a valid state. | |
11438 */ | |
11439 static void countTransition(DataDescriptor descriptor, CachedResult result) { | |
11440 Map<CacheState, int> countMap = transitionMap.putIfAbsent( | |
11441 descriptor, () => new HashMap<CacheState, int>()); | |
11442 int count = countMap[result.state]; | |
11443 countMap[result.state] = count == null ? 1 : count + 1; | |
11444 } | |
11445 } | |
11446 | |
11447 /** | |
11448 * The priority levels used to return sources in an optimal order. A smaller | |
11449 * ordinal value equates to a higher priority. | |
11450 */ | |
11451 class SourcePriority extends Enum<SourcePriority> { | |
11452 /** | |
11453 * Used for a Dart source that is known to be a part contained in a library | |
11454 * that was recently resolved. These parts are given a higher priority because | |
11455 * there is a high probability that their AST structure is still in the cache | |
11456 * and therefore would not need to be re-created. | |
11457 */ | |
11458 static const SourcePriority PRIORITY_PART = | |
11459 const SourcePriority('PRIORITY_PART', 0); | |
11460 | |
11461 /** | |
11462 * Used for a Dart source that is known to be a library. | |
11463 */ | |
11464 static const SourcePriority LIBRARY = const SourcePriority('LIBRARY', 1); | |
11465 | |
11466 /** | |
11467 * Used for a Dart source whose kind is unknown. | |
11468 */ | |
11469 static const SourcePriority UNKNOWN = const SourcePriority('UNKNOWN', 2); | |
11470 | |
11471 /** | |
11472 * Used for a Dart source that is known to be a part but whose library has not | |
11473 * yet been resolved. | |
11474 */ | |
11475 static const SourcePriority NORMAL_PART = | |
11476 const SourcePriority('NORMAL_PART', 3); | |
11477 | |
11478 /** | |
11479 * Used for an HTML source. | |
11480 */ | |
11481 static const SourcePriority HTML = const SourcePriority('HTML', 4); | |
11482 | |
11483 static const List<SourcePriority> values = const [ | |
11484 PRIORITY_PART, | |
11485 LIBRARY, | |
11486 UNKNOWN, | |
11487 NORMAL_PART, | |
11488 HTML | |
11489 ]; | |
11490 | |
11491 const SourcePriority(String name, int ordinal) : super(name, ordinal); | |
11492 } | |
11493 | |
11494 /** | |
11495 * [SourcesChangedEvent] indicates which sources have been added, removed, | |
11496 * or whose contents have changed. | |
11497 */ | |
11498 class SourcesChangedEvent { | |
11499 /** | |
11500 * The internal representation of what has changed. Clients should not access | |
11501 * this field directly. | |
11502 */ | |
11503 final ChangeSet _changeSet; | |
11504 | |
11505 /** | |
11506 * Construct an instance representing the given changes. | |
11507 */ | |
11508 SourcesChangedEvent(ChangeSet changeSet) : _changeSet = changeSet; | |
11509 | |
11510 /** | |
11511 * Construct an instance representing a source content change. | |
11512 */ | |
11513 factory SourcesChangedEvent.changedContent(Source source, String contents) { | |
11514 ChangeSet changeSet = new ChangeSet(); | |
11515 changeSet.changedContent(source, contents); | |
11516 return new SourcesChangedEvent(changeSet); | |
11517 } | |
11518 | |
11519 /** | |
11520 * Construct an instance representing a source content change. | |
11521 */ | |
11522 factory SourcesChangedEvent.changedRange(Source source, String contents, | |
11523 int offset, int oldLength, int newLength) { | |
11524 ChangeSet changeSet = new ChangeSet(); | |
11525 changeSet.changedRange(source, contents, offset, oldLength, newLength); | |
11526 return new SourcesChangedEvent(changeSet); | |
11527 } | |
11528 | |
11529 /** | |
11530 * Return the collection of sources for which content has changed. | |
11531 */ | |
11532 Iterable<Source> get changedSources { | |
11533 List<Source> changedSources = new List.from(_changeSet.changedSources); | |
11534 changedSources.addAll(_changeSet.changedContents.keys); | |
11535 changedSources.addAll(_changeSet.changedRanges.keys); | |
11536 return changedSources; | |
11537 } | |
11538 | |
11539 /** | |
11540 * Return `true` if any sources were added. | |
11541 */ | |
11542 bool get wereSourcesAdded => _changeSet.addedSources.length > 0; | |
11543 | |
11544 /** | |
11545 * Return `true` if any sources were removed or deleted. | |
11546 */ | |
11547 bool get wereSourcesRemovedOrDeleted => | |
11548 _changeSet.removedSources.length > 0 || | |
11549 _changeSet.removedContainers.length > 0 || | |
11550 _changeSet.deletedSources.length > 0; | |
11551 } | |
11552 | |
11553 /** | |
11554 * Analysis data for which we have a modification time. | |
11555 */ | |
11556 class TimestampedData<E> { | |
11557 /** | |
11558 * The modification time of the source from which the data was created. | |
11559 */ | |
11560 final int modificationTime; | |
11561 | |
11562 /** | |
11563 * The data that was created from the source. | |
11564 */ | |
11565 final E data; | |
11566 | |
11567 /** | |
11568 * Initialize a newly created holder to associate the given [data] with the | |
11569 * given [modificationTime]. | |
11570 */ | |
11571 TimestampedData(this.modificationTime, this.data); | |
11572 } | |
11573 | |
11574 /** | |
11575 * A cache partition that contains all sources not contained in other | |
11576 * partitions. | |
11577 */ | |
11578 class UniversalCachePartition extends CachePartition { | |
11579 /** | |
11580 * Initialize a newly created partition. The [context] is the context that | |
11581 * owns this partition. The [maxCacheSize] is the maximum number of sources | |
11582 * for which AST structures should be kept in the cache. The [retentionPolicy] | |
11583 * is the policy used to determine which pieces of data to remove from the | |
11584 * cache. | |
11585 */ | |
11586 UniversalCachePartition(InternalAnalysisContext context, int maxCacheSize, | |
11587 CacheRetentionPolicy retentionPolicy) | |
11588 : super(context, maxCacheSize, retentionPolicy); | |
11589 | |
11590 @override | |
11591 bool contains(Source source) => true; | |
11592 } | |
11593 | |
11594 /** | |
11595 * The unique instances of the class `WaitForAsyncTask` represents a state in wh
ich there is | |
11596 * no analysis work that can be done until some asynchronous task (such as IO) h
as completed, but | |
11597 * where analysis is not yet complete. | |
11598 */ | |
11599 class WaitForAsyncTask extends AnalysisTask { | |
11600 /** | |
11601 * The unique instance of this class. | |
11602 */ | |
11603 static WaitForAsyncTask _UniqueInstance = new WaitForAsyncTask(); | |
11604 | |
11605 /** | |
11606 * Return the unique instance of this class. | |
11607 * | |
11608 * @return the unique instance of this class | |
11609 */ | |
11610 static WaitForAsyncTask get instance => _UniqueInstance; | |
11611 | |
11612 /** | |
11613 * Prevent the creation of instances of this class. | |
11614 */ | |
11615 WaitForAsyncTask() : super(null); | |
11616 | |
11617 @override | |
11618 String get taskDescription => "Waiting for async analysis"; | |
11619 | |
11620 @override | |
11621 accept(AnalysisTaskVisitor visitor) => null; | |
11622 | |
11623 @override | |
11624 void internalPerform() { | |
11625 // There is no work to be done. | |
11626 } | |
11627 } | |
11628 | |
11629 /** | |
11630 * An object that manages a list of sources that need to have analysis work | |
11631 * performed on them. | |
11632 */ | |
11633 class WorkManager { | |
11634 /** | |
11635 * A list containing the various queues is priority order. | |
11636 */ | |
11637 List<List<Source>> _workQueues; | |
11638 | |
11639 /** | |
11640 * Initialize a newly created manager to have no work queued up. | |
11641 */ | |
11642 WorkManager() { | |
11643 int queueCount = SourcePriority.values.length; | |
11644 _workQueues = new List<List<Source>>(queueCount); | |
11645 for (int i = 0; i < queueCount; i++) { | |
11646 _workQueues[i] = new List<Source>(); | |
11647 } | |
11648 } | |
11649 | |
11650 /** | |
11651 * Record that the given [source] needs to be analyzed. The [priority] level | |
11652 * is used to control when the source will be analyzed with respect to other | |
11653 * sources. If the source was previously added then it's priority is updated. | |
11654 * If it was previously added with the same priority then it's position in the | |
11655 * queue is unchanged. | |
11656 */ | |
11657 void add(Source source, SourcePriority priority) { | |
11658 int queueCount = _workQueues.length; | |
11659 int ordinal = priority.ordinal; | |
11660 for (int i = 0; i < queueCount; i++) { | |
11661 List<Source> queue = _workQueues[i]; | |
11662 if (i == ordinal) { | |
11663 if (!queue.contains(source)) { | |
11664 queue.add(source); | |
11665 } | |
11666 } else { | |
11667 queue.remove(source); | |
11668 } | |
11669 } | |
11670 } | |
11671 | |
11672 /** | |
11673 * Record that the given [source] needs to be analyzed. The [priority] level | |
11674 * is used to control when the source will be analyzed with respect to other | |
11675 * sources. If the source was previously added then it's priority is updated. | |
11676 * In either case, it will be analyzed before other sources of the same | |
11677 * priority. | |
11678 */ | |
11679 void addFirst(Source source, SourcePriority priority) { | |
11680 int queueCount = _workQueues.length; | |
11681 int ordinal = priority.ordinal; | |
11682 for (int i = 0; i < queueCount; i++) { | |
11683 List<Source> queue = _workQueues[i]; | |
11684 if (i == ordinal) { | |
11685 queue.remove(source); | |
11686 queue.insert(0, source); | |
11687 } else { | |
11688 queue.remove(source); | |
11689 } | |
11690 } | |
11691 } | |
11692 | |
11693 /** | |
11694 * Return an iterator that can be used to access the sources to be analyzed in | |
11695 * the order in which they should be analyzed. | |
11696 * | |
11697 * <b>Note:</b> As with other iterators, no sources can be added or removed | |
11698 * from this work manager while the iterator is being used. Unlike some | |
11699 * implementations, however, the iterator will not detect when this | |
11700 * requirement has been violated; it might work correctly, it might return the | |
11701 * wrong source, or it might throw an exception. | |
11702 */ | |
11703 WorkManager_WorkIterator iterator() => new WorkManager_WorkIterator(this); | |
11704 | |
11705 /** | |
11706 * Record that the given source is fully analyzed. | |
11707 */ | |
11708 void remove(Source source) { | |
11709 int queueCount = _workQueues.length; | |
11710 for (int i = 0; i < queueCount; i++) { | |
11711 _workQueues[i].remove(source); | |
11712 } | |
11713 } | |
11714 | |
11715 @override | |
11716 String toString() { | |
11717 StringBuffer buffer = new StringBuffer(); | |
11718 List<SourcePriority> priorities = SourcePriority.values; | |
11719 bool needsSeparator = false; | |
11720 int queueCount = _workQueues.length; | |
11721 for (int i = 0; i < queueCount; i++) { | |
11722 List<Source> queue = _workQueues[i]; | |
11723 if (!queue.isEmpty) { | |
11724 if (needsSeparator) { | |
11725 buffer.write("; "); | |
11726 } | |
11727 buffer.write(priorities[i]); | |
11728 buffer.write(": "); | |
11729 int queueSize = queue.length; | |
11730 for (int j = 0; j < queueSize; j++) { | |
11731 if (j > 0) { | |
11732 buffer.write(", "); | |
11733 } | |
11734 buffer.write(queue[j].fullName); | |
11735 } | |
11736 needsSeparator = true; | |
11737 } | |
11738 } | |
11739 return buffer.toString(); | |
11740 } | |
11741 } | |
11742 | |
11743 /** | |
11744 * An iterator that returns the sources in a work manager in the order in which | |
11745 * they are to be analyzed. | |
11746 */ | |
11747 class WorkManager_WorkIterator { | |
11748 final WorkManager _manager; | |
11749 | |
11750 /** | |
11751 * The index of the work queue through which we are currently iterating. | |
11752 */ | |
11753 int _queueIndex = 0; | |
11754 | |
11755 /** | |
11756 * The index of the next element of the work queue to be returned. | |
11757 */ | |
11758 int _index = -1; | |
11759 | |
11760 /** | |
11761 * Initialize a newly created iterator to be ready to return the first element | |
11762 * in the iteration. | |
11763 */ | |
11764 WorkManager_WorkIterator(this._manager) { | |
11765 _advance(); | |
11766 } | |
11767 | |
11768 /** | |
11769 * Return `true` if there is another [Source] available for processing. | |
11770 */ | |
11771 bool get hasNext => _queueIndex < _manager._workQueues.length; | |
11772 | |
11773 /** | |
11774 * Return the next [Source] available for processing and advance so that the | |
11775 * returned source will not be returned again. | |
11776 */ | |
11777 Source next() { | |
11778 if (!hasNext) { | |
11779 throw new NoSuchElementException(); | |
11780 } | |
11781 Source source = _manager._workQueues[_queueIndex][_index]; | |
11782 _advance(); | |
11783 return source; | |
11784 } | |
11785 | |
11786 /** | |
11787 * Increment the [index] and [queueIndex] so that they are either indicating | |
11788 * the next source to be returned or are indicating that there are no more | |
11789 * sources to be returned. | |
11790 */ | |
11791 void _advance() { | |
11792 _index++; | |
11793 if (_index >= _manager._workQueues[_queueIndex].length) { | |
11794 _index = 0; | |
11795 _queueIndex++; | |
11796 while (_queueIndex < _manager._workQueues.length && | |
11797 _manager._workQueues[_queueIndex].isEmpty) { | |
11798 _queueIndex++; | |
11799 } | |
11800 } | |
11801 } | |
11802 } | |
11803 | |
11804 /** | |
11805 * A helper class used to create futures for AnalysisContextImpl. Using a helper | |
11806 * class allows us to preserve the generic parameter T. | |
11807 */ | |
11808 class _AnalysisFutureHelper<T> { | |
11809 final AnalysisContextImpl _context; | |
11810 | |
11811 _AnalysisFutureHelper(this._context); | |
11812 | |
11813 /** | |
11814 * Return a future that will be completed with the result of calling | |
11815 * [computeValue]. If [computeValue] returns non-null, the future will be | |
11816 * completed immediately with the resulting value. If it returns null, then | |
11817 * it will be re-executed in the future, after the next time the cached | |
11818 * information for [source] has changed. If [computeValue] throws an | |
11819 * exception, the future will fail with that exception. | |
11820 * | |
11821 * If the [computeValue] still returns null after there is no further | |
11822 * analysis to be done for [source], then the future will be completed with | |
11823 * the error AnalysisNotScheduledError. | |
11824 * | |
11825 * Since [computeValue] will be called while the state of analysis is being | |
11826 * updated, it should be free of side effects so that it doesn't cause | |
11827 * reentrant changes to the analysis state. | |
11828 */ | |
11829 CancelableFuture<T> computeAsync( | |
11830 Source source, T computeValue(SourceEntry sourceEntry)) { | |
11831 if (_context.isDisposed) { | |
11832 // No further analysis is expected, so return a future that completes | |
11833 // immediately with AnalysisNotScheduledError. | |
11834 return new CancelableFuture.error(new AnalysisNotScheduledError()); | |
11835 } | |
11836 SourceEntry sourceEntry = _context.getReadableSourceEntryOrNull(source); | |
11837 if (sourceEntry == null) { | |
11838 return new CancelableFuture.error(new AnalysisNotScheduledError()); | |
11839 } | |
11840 PendingFuture pendingFuture = | |
11841 new PendingFuture<T>(_context, source, computeValue); | |
11842 if (!pendingFuture.evaluate(sourceEntry)) { | |
11843 _context._pendingFutureSources | |
11844 .putIfAbsent(source, () => <PendingFuture>[]) | |
11845 .add(pendingFuture); | |
11846 } | |
11847 return pendingFuture.future; | |
11848 } | |
11849 } | |
OLD | NEW |