| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library analyzer.task.model; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask; | |
| 10 import 'package:analyzer/src/generated/java_engine.dart'; | |
| 11 import 'package:analyzer/src/generated/source.dart'; | |
| 12 import 'package:analyzer/src/task/driver.dart'; | |
| 13 import 'package:analyzer/src/task/model.dart'; | |
| 14 | |
| 15 /** | |
| 16 * A function that converts the given [key] and [value] into a [TaskInput]. | |
| 17 */ | |
| 18 typedef TaskInput<E> BinaryFunction<K, V, E>(K key, V value); | |
| 19 | |
| 20 /** | |
| 21 * A function that takes an analysis [context] and an analysis [target] and | |
| 22 * returns an analysis task. Such functions are passed to a [TaskDescriptor] to | |
| 23 * be used to create the described task. | |
| 24 */ | |
| 25 typedef AnalysisTask BuildTask(AnalysisContext context, AnalysisTarget target); | |
| 26 | |
| 27 /** | |
| 28 * A function that takes the target for which a task will produce results and | |
| 29 * returns a map from input names to descriptions of the analysis results needed | |
| 30 * by the task in order for the task to be performed. Such functions are passed | |
| 31 * to a [TaskDescriptor] to be used to determine the inputs needed by the task. | |
| 32 */ | |
| 33 typedef Map<String, TaskInput> CreateTaskInputs(AnalysisTarget target); | |
| 34 | |
| 35 /** | |
| 36 * A function that converts an object of the type [B] into a [TaskInput]. | |
| 37 * This is used, for example, by a [ListTaskInput] to create task inputs | |
| 38 * for each value in a list of values. | |
| 39 */ | |
| 40 typedef TaskInput<E> UnaryFunction<B, E>(B object); | |
| 41 | |
| 42 /** | |
| 43 * An [AnalysisTarget] wrapper for an [AnalysisContext]. | |
| 44 */ | |
| 45 class AnalysisContextTarget implements AnalysisTarget { | |
| 46 static final AnalysisContextTarget request = new AnalysisContextTarget(null); | |
| 47 | |
| 48 final AnalysisContext context; | |
| 49 | |
| 50 AnalysisContextTarget(this.context); | |
| 51 | |
| 52 @override | |
| 53 Source get source => null; | |
| 54 } | |
| 55 | |
| 56 /** | |
| 57 * An object with which an analysis result can be associated. | |
| 58 * | |
| 59 * Clients are allowed to subtype this class when creating new kinds of targets. | |
| 60 * Instances of this type are used in hashed data structures, so subtypes are | |
| 61 * required to correctly implement [==] and [hashCode]. | |
| 62 */ | |
| 63 abstract class AnalysisTarget { | |
| 64 /** | |
| 65 * Return the source associated with this target, or `null` if this target is | |
| 66 * not associated with a source. | |
| 67 */ | |
| 68 Source get source; | |
| 69 } | |
| 70 | |
| 71 /** | |
| 72 * An object used to compute one or more analysis results associated with a | |
| 73 * single target. | |
| 74 * | |
| 75 * Clients are expected to extend this class when creating new tasks. | |
| 76 */ | |
| 77 abstract class AnalysisTask { | |
| 78 /** | |
| 79 * A table mapping the types of analysis tasks to the number of times each | |
| 80 * kind of task has been performed. | |
| 81 */ | |
| 82 static final Map<Type, int> countMap = new HashMap<Type, int>(); | |
| 83 | |
| 84 /** | |
| 85 * A table mapping the types of analysis tasks to stopwatches used to compute | |
| 86 * how much time was spent executing each kind of task. | |
| 87 */ | |
| 88 static final Map<Type, Stopwatch> stopwatchMap = | |
| 89 new HashMap<Type, Stopwatch>(); | |
| 90 | |
| 91 /** | |
| 92 * The context in which the task is to be performed. | |
| 93 */ | |
| 94 final AnalysisContext context; | |
| 95 | |
| 96 /** | |
| 97 * The target for which result values are being produced. | |
| 98 */ | |
| 99 final AnalysisTarget target; | |
| 100 | |
| 101 /** | |
| 102 * A table mapping input names to input values. | |
| 103 */ | |
| 104 Map<String, dynamic> inputs; | |
| 105 | |
| 106 /** | |
| 107 * A table mapping result descriptors whose values are produced by this task | |
| 108 * to the values that were produced. | |
| 109 */ | |
| 110 Map<ResultDescriptor, dynamic> outputs = | |
| 111 new HashMap<ResultDescriptor, dynamic>(); | |
| 112 | |
| 113 /** | |
| 114 * The exception that was thrown while performing this task, or `null` if the | |
| 115 * task completed successfully. | |
| 116 */ | |
| 117 CaughtException caughtException; | |
| 118 | |
| 119 /** | |
| 120 * If a dependency cycle was found while computing the inputs for the task, | |
| 121 * the set of [WorkItem]s contained in the cycle (if there are overlapping | |
| 122 * cycles, this is the set of all [WorkItem]s in the entire strongly | |
| 123 * connected component). Otherwise, `null`. | |
| 124 */ | |
| 125 List<WorkItem> dependencyCycle; | |
| 126 | |
| 127 /** | |
| 128 * Initialize a newly created task to perform analysis within the given | |
| 129 * [context] in order to produce results for the given [target]. | |
| 130 */ | |
| 131 AnalysisTask(this.context, this.target); | |
| 132 | |
| 133 /** | |
| 134 * Return a textual description of this task. | |
| 135 */ | |
| 136 String get description; | |
| 137 | |
| 138 /** | |
| 139 * Return the descriptor that describes this task. | |
| 140 */ | |
| 141 TaskDescriptor get descriptor; | |
| 142 | |
| 143 /** | |
| 144 * Indicates whether the task is capable of handling dependency cycles. A | |
| 145 * task that overrides this getter to return `true` must be prepared for the | |
| 146 * possibility that it will be invoked with a non-`null` value of | |
| 147 * [dependencyCycle], and with not all of its inputs computed. | |
| 148 */ | |
| 149 bool get handlesDependencyCycles => false; | |
| 150 | |
| 151 /** | |
| 152 * Return the value of the input with the given [name]. Throw an exception if | |
| 153 * the input value is not defined. | |
| 154 */ | |
| 155 Object getRequiredInput(String name) { | |
| 156 if (inputs == null || !inputs.containsKey(name)) { | |
| 157 throw new AnalysisException("Could not $description: missing $name"); | |
| 158 } | |
| 159 return inputs[name]; | |
| 160 } | |
| 161 | |
| 162 /** | |
| 163 * Return the source associated with the target. Throw an exception if | |
| 164 * the target is not associated with a source. | |
| 165 */ | |
| 166 Source getRequiredSource() { | |
| 167 Source source = target.source; | |
| 168 if (source == null) { | |
| 169 throw new AnalysisException("Could not $description: missing source"); | |
| 170 } | |
| 171 return source; | |
| 172 } | |
| 173 | |
| 174 /** | |
| 175 * Perform this analysis task, protected by an exception handler. | |
| 176 * | |
| 177 * This method should throw an [AnalysisException] if an exception occurs | |
| 178 * while performing the task. If other kinds of exceptions are thrown they | |
| 179 * will be wrapped in an [AnalysisException]. | |
| 180 * | |
| 181 * If no exception is thrown, this method must fully populate the [outputs] | |
| 182 * map (have a key/value pair for each result that this task is expected to | |
| 183 * produce). | |
| 184 */ | |
| 185 void internalPerform(); | |
| 186 | |
| 187 /** | |
| 188 * Perform this analysis task. When this method returns, either the [outputs] | |
| 189 * map should be fully populated (have a key/value pair for each result that | |
| 190 * this task is expected to produce) or the [caughtException] should be set. | |
| 191 * | |
| 192 * Clients should not override this method. | |
| 193 */ | |
| 194 void perform() { | |
| 195 try { | |
| 196 _safelyPerform(); | |
| 197 } on AnalysisException catch (exception, stackTrace) { | |
| 198 caughtException = new CaughtException(exception, stackTrace); | |
| 199 AnalysisEngine.instance.logger.logInformation( | |
| 200 "Task failed: ${description}", caughtException); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 @override | |
| 205 String toString() => description; | |
| 206 | |
| 207 /** | |
| 208 * Perform this analysis task, ensuring that all exceptions are wrapped in an | |
| 209 * [AnalysisException]. | |
| 210 * | |
| 211 * Clients should not override this method. | |
| 212 */ | |
| 213 void _safelyPerform() { | |
| 214 try { | |
| 215 // | |
| 216 // Report that this task is being performed. | |
| 217 // | |
| 218 String contextName = context.name; | |
| 219 if (contextName == null) { | |
| 220 contextName = 'unnamed'; | |
| 221 } | |
| 222 AnalysisEngine.instance.instrumentationService.logAnalysisTask( | |
| 223 contextName, this); | |
| 224 // | |
| 225 // Gather statistics on the performance of the task. | |
| 226 // | |
| 227 int count = countMap[runtimeType]; | |
| 228 countMap[runtimeType] = count == null ? 1 : count + 1; | |
| 229 Stopwatch stopwatch = stopwatchMap[runtimeType]; | |
| 230 if (stopwatch == null) { | |
| 231 stopwatch = new Stopwatch(); | |
| 232 stopwatchMap[runtimeType] = stopwatch; | |
| 233 } | |
| 234 stopwatch.start(); | |
| 235 // | |
| 236 // Actually perform the task. | |
| 237 // | |
| 238 try { | |
| 239 if (dependencyCycle != null && !handlesDependencyCycles) { | |
| 240 throw new InfiniteTaskLoopException(this, dependencyCycle); | |
| 241 } | |
| 242 internalPerform(); | |
| 243 } finally { | |
| 244 stopwatch.stop(); | |
| 245 } | |
| 246 } on AnalysisException { | |
| 247 rethrow; | |
| 248 } catch (exception, stackTrace) { | |
| 249 throw new AnalysisException( | |
| 250 'Unexpected exception while performing $description', | |
| 251 new CaughtException(exception, stackTrace)); | |
| 252 } | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 /** | |
| 257 * A description of a [List]-based analysis result that can be computed by an | |
| 258 * [AnalysisTask]. | |
| 259 * | |
| 260 * Clients are not expected to subtype this class. | |
| 261 */ | |
| 262 abstract class ListResultDescriptor<E> implements ResultDescriptor<List<E>> { | |
| 263 /** | |
| 264 * Initialize a newly created analysis result to have the given [name] and | |
| 265 * [defaultValue]. If a [cachingPolicy] is provided, it will control how long | |
| 266 * values associated with this result will remain in the cache. | |
| 267 */ | |
| 268 factory ListResultDescriptor(String name, List<E> defaultValue, | |
| 269 {ResultCachingPolicy<List<E>> cachingPolicy}) = ListResultDescriptorImpl<E
>; | |
| 270 | |
| 271 @override | |
| 272 ListTaskInput<E> of(AnalysisTarget target); | |
| 273 } | |
| 274 | |
| 275 /** | |
| 276 * A description of an input to an [AnalysisTask] that can be used to compute | |
| 277 * that input. | |
| 278 * | |
| 279 * Clients are not expected to subtype this class. | |
| 280 */ | |
| 281 abstract class ListTaskInput<E> extends TaskInput<List<E>> { | |
| 282 /** | |
| 283 * Return a task input that can be used to compute a list whose elements are | |
| 284 * the result of passing the elements of this input to the [mapper] function. | |
| 285 */ | |
| 286 ListTaskInput /*<V>*/ toList(UnaryFunction<E, dynamic /*<V>*/ > mapper); | |
| 287 | |
| 288 /** | |
| 289 * Return a task input that can be used to compute a list whose elements are | |
| 290 * [valueResult]'s associated with those elements. | |
| 291 */ | |
| 292 ListTaskInput /*<V>*/ toListOf(ResultDescriptor /*<V>*/ valueResult); | |
| 293 | |
| 294 /** | |
| 295 * Return a task input that can be used to compute a map whose keys are the | |
| 296 * elements of this input and whose values are the result of passing the | |
| 297 * corresponding key to the [mapper] function. | |
| 298 */ | |
| 299 MapTaskInput<E, dynamic /*V*/ > toMap( | |
| 300 UnaryFunction<E, dynamic /*<V>*/ > mapper); | |
| 301 | |
| 302 /** | |
| 303 * Return a task input that can be used to compute a map whose keys are the | |
| 304 * elements of this input and whose values are the [valueResult]'s associated | |
| 305 * with those elements. | |
| 306 */ | |
| 307 MapTaskInput<AnalysisTarget, dynamic /*V*/ > toMapOf( | |
| 308 ResultDescriptor /*<V>*/ valueResult); | |
| 309 } | |
| 310 | |
| 311 /** | |
| 312 * A description of an input with a [Map] based values. | |
| 313 * | |
| 314 * Clients are not expected to subtype this class. | |
| 315 */ | |
| 316 abstract class MapTaskInput<K, V> extends TaskInput<Map<K, V>> { | |
| 317 /** | |
| 318 * [V] must be a [List]. | |
| 319 * Return a task input that can be used to compute a list whose elements are | |
| 320 * the result of passing keys [K] and the corresponding elements of [V] to | |
| 321 * the [mapper] function. | |
| 322 */ | |
| 323 TaskInput<List /*<E>*/ > toFlattenList( | |
| 324 BinaryFunction<K, dynamic /*element of V*/, dynamic /*<E>*/ > mapper); | |
| 325 } | |
| 326 | |
| 327 /** | |
| 328 * A policy object that can compute sizes of results and provide the maximum | |
| 329 * active and idle sizes that can be kept in the cache. | |
| 330 * | |
| 331 * All the [ResultDescriptor]s with the same [ResultCachingPolicy] instance | |
| 332 * share the same total size in a cache. | |
| 333 */ | |
| 334 abstract class ResultCachingPolicy<T> { | |
| 335 /** | |
| 336 * Return the maximum total size of results that can be kept in the cache | |
| 337 * while analysis is in progress. | |
| 338 */ | |
| 339 int get maxActiveSize; | |
| 340 | |
| 341 /** | |
| 342 * Return the maximum total size of results that can be kept in the cache | |
| 343 * while analysis is idle. | |
| 344 */ | |
| 345 int get maxIdleSize; | |
| 346 | |
| 347 /** | |
| 348 * Return the size of the given [object]. | |
| 349 */ | |
| 350 int measure(T object); | |
| 351 } | |
| 352 | |
| 353 /** | |
| 354 * A description of an analysis result that can be computed by an [AnalysisTask]
. | |
| 355 * | |
| 356 * Clients are not expected to subtype this class. | |
| 357 */ | |
| 358 abstract class ResultDescriptor<V> { | |
| 359 /** | |
| 360 * Initialize a newly created analysis result to have the given [name] and | |
| 361 * [defaultValue]. | |
| 362 * | |
| 363 * The given [cachingPolicy] is used to limit the total size of results | |
| 364 * described by this descriptor. If no policy is specified, the results are | |
| 365 * never evicted from the cache, and removed only when they are invalidated. | |
| 366 */ | |
| 367 factory ResultDescriptor(String name, V defaultValue, | |
| 368 {ResultCachingPolicy<V> cachingPolicy}) = ResultDescriptorImpl; | |
| 369 | |
| 370 /** | |
| 371 * Return the caching policy for results described by this descriptor. | |
| 372 */ | |
| 373 ResultCachingPolicy<V> get cachingPolicy; | |
| 374 | |
| 375 /** | |
| 376 * Return the default value for results described by this descriptor. | |
| 377 */ | |
| 378 V get defaultValue; | |
| 379 | |
| 380 /** | |
| 381 * Return the name of this descriptor. | |
| 382 */ | |
| 383 String get name; | |
| 384 | |
| 385 /** | |
| 386 * Return a task input that can be used to compute this result for the given | |
| 387 * [target]. | |
| 388 */ | |
| 389 TaskInput<V> of(AnalysisTarget target); | |
| 390 } | |
| 391 | |
| 392 /** | |
| 393 * A description of an [AnalysisTask]. | |
| 394 */ | |
| 395 abstract class TaskDescriptor { | |
| 396 /** | |
| 397 * Initialize a newly created task descriptor to have the given [name] and to | |
| 398 * describe a task that takes the inputs built using the given [inputBuilder], | |
| 399 * and produces the given [results]. The [buildTask] will be used to create | |
| 400 * the instance of [AnalysisTask] thusly described. | |
| 401 */ | |
| 402 factory TaskDescriptor(String name, BuildTask buildTask, | |
| 403 CreateTaskInputs inputBuilder, | |
| 404 List<ResultDescriptor> results) = TaskDescriptorImpl; | |
| 405 | |
| 406 /** | |
| 407 * Return the builder used to build the inputs to the task. | |
| 408 */ | |
| 409 CreateTaskInputs get createTaskInputs; | |
| 410 | |
| 411 /** | |
| 412 * Return the name of the task being described. | |
| 413 */ | |
| 414 String get name; | |
| 415 | |
| 416 /** | |
| 417 * Return a list of the analysis results that will be computed by this task. | |
| 418 */ | |
| 419 List<ResultDescriptor> get results; | |
| 420 | |
| 421 /** | |
| 422 * Create and return a task that is described by this descriptor that can be | |
| 423 * used to compute results based on the given [inputs]. | |
| 424 */ | |
| 425 AnalysisTask createTask(AnalysisContext context, AnalysisTarget target, | |
| 426 Map<String, dynamic> inputs); | |
| 427 } | |
| 428 | |
| 429 /** | |
| 430 * A description of an input to an [AnalysisTask] that can be used to compute | |
| 431 * that input. | |
| 432 * | |
| 433 * Clients are not expected to subtype this class. | |
| 434 */ | |
| 435 abstract class TaskInput<V> { | |
| 436 /** | |
| 437 * Create and return a builder that can be used to build this task input. | |
| 438 */ | |
| 439 TaskInputBuilder<V> createBuilder(); | |
| 440 | |
| 441 /** | |
| 442 * Return a task input that can be used to compute a list whose elements are | |
| 443 * the result of passing the result of this input to the [mapper] function. | |
| 444 */ | |
| 445 ListTaskInput /*<E>*/ mappedToList(List /*<E>*/ mapper(V value)); | |
| 446 } | |
| 447 | |
| 448 /** | |
| 449 * An object used to build the value associated with a single [TaskInput]. | |
| 450 * | |
| 451 * All builders work by requesting one or more results (each result being | |
| 452 * associated with a target). The interaction pattern is modeled after the class | |
| 453 * [Iterator], in which the method [moveNext] is invoked to move from one result | |
| 454 * request to the next. The getters [currentResult] and [currentTarget] are used | |
| 455 * to get the result and target of the current request. The value of the result | |
| 456 * must be supplied using the [currentValue] setter before [moveNext] can be | |
| 457 * invoked to move to the next request. When [moveNext] returns `false`, | |
| 458 * indicating that there are no more requests, the method [inputValue] can be | |
| 459 * used to access the value of the input that was built. | |
| 460 * | |
| 461 * Clients are not expected to subtype this class. | |
| 462 */ | |
| 463 abstract class TaskInputBuilder<V> { | |
| 464 /** | |
| 465 * Return the result that needs to be computed, or `null` if [moveNext] has | |
| 466 * not been invoked or if the last invocation of [moveNext] returned `false`. | |
| 467 */ | |
| 468 ResultDescriptor get currentResult; | |
| 469 | |
| 470 /** | |
| 471 * Return the target for which the result needs to be computed, or `null` if | |
| 472 * [moveNext] has not been invoked or if the last invocation of [moveNext] | |
| 473 * returned `false`. | |
| 474 */ | |
| 475 AnalysisTarget get currentTarget; | |
| 476 | |
| 477 /** | |
| 478 * Set the [value] that was computed for the current result. | |
| 479 * | |
| 480 * Throws a [StateError] if [moveNext] has not been invoked or if the last | |
| 481 * invocation of [moveNext] returned `false`. | |
| 482 */ | |
| 483 void set currentValue(Object value); | |
| 484 | |
| 485 /** | |
| 486 * Return the [value] that was computed by this builder. | |
| 487 * | |
| 488 * Throws a [StateError] if [moveNext] has not been invoked or if the last | |
| 489 * invocation of [moveNext] returned `true`. | |
| 490 * | |
| 491 * Returns `null` if no value could be computed due to a circular dependency. | |
| 492 */ | |
| 493 V get inputValue; | |
| 494 | |
| 495 /** | |
| 496 * Record that no value is available for the current result, due to a | |
| 497 * circular dependency. | |
| 498 * | |
| 499 * Throws a [StateError] if [moveNext] has not been invoked or if the last | |
| 500 * invocation of [moveNext] returned `false`. | |
| 501 */ | |
| 502 void currentValueNotAvailable(); | |
| 503 | |
| 504 /** | |
| 505 * Move to the next result that needs to be computed in order to build the | |
| 506 * inputs for a task. Return `true` if there is another result that needs to | |
| 507 * be computed, or `false` if the inputs have been computed. | |
| 508 * | |
| 509 * It is safe to invoke [moveNext] after it has returned `false`. In this case | |
| 510 * [moveNext] has no effect and will again return `false`. | |
| 511 * | |
| 512 * Throws a [StateError] if the value of the current result has not been | |
| 513 * provided using [currentValue]. | |
| 514 */ | |
| 515 bool moveNext(); | |
| 516 } | |
| OLD | NEW |