Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, 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 /** | |
| 6 * This library is capable of producing linked summaries from unlinked | |
| 7 * ones (or prelinked ones). It functions by building a miniature | |
| 8 * element model to represent the contents of the summaries, and then | |
| 9 * scanning the element model to gather linked information and adding | |
| 10 * it to the summary data structures. | |
| 11 * | |
| 12 * The reason we use a miniature element model to do the linking | |
| 13 * (rather than resynthesizing the full element model from the | |
| 14 * summaries) is that it is expected that we will only need to | |
| 15 * traverse a small subset of the element properties in order to link. | |
| 16 * Resynthesizing only those properties that we need should save | |
| 17 * substantial CPU time. | |
| 18 * | |
| 19 * The element model implements the same interfaces as the full | |
| 20 * element model, so we can re-use code elsewhere in the analysis | |
| 21 * engine to do the linking. However, only a small subset of the | |
| 22 * methods and getters defined in the full element model are | |
| 23 * implemented here. To avoid static warnings, each element model | |
| 24 * class contains an implementation of `noSuchMethod`. | |
| 25 * | |
| 26 * The miniature element model follows the following design | |
| 27 * principles: | |
| 28 * | |
| 29 * - With few exceptions, resynthesis is done incrementally on demand, | |
| 30 * so that we don't pay the cost of resynthesizing elements (or | |
| 31 * properties of elements) that aren't referenced from a part of the | |
| 32 * element model that is relevant to linking. | |
| 33 * | |
| 34 * - Computation of values in the miniature element model is similar | |
| 35 * to the task model, but much lighter weight. Instead of declaring | |
| 36 * tasks and their relationships using classes, each task is simply | |
| 37 * a method (frequently a getter) that computes a value. Instead of | |
| 38 * using a general purpose cache, values are cached by the methods | |
| 39 * themselves in private fields (with `null` typically representing | |
| 40 * "not yet cached"). | |
| 41 * | |
| 42 * - No attempt is made to detect cyclic dependencies due to bugs in | |
| 43 * the analyzer. This saves time because dependency evaluation | |
| 44 * doesn't have to be a separate step from evaluating a value; we | |
| 45 * can simply call the getter. | |
| 46 * | |
| 47 * - However, for cases where cyclic dependencies may occur in the | |
| 48 * absence of analyzer bugs (e.g. because of errors in the code | |
| 49 * being analyzed, or cycles between top level and static variables | |
| 50 * undergoing type inference), we do precompute dependencies, and we | |
| 51 * use Tarjan's strongly connected components algorithm to detect | |
| 52 * cycles. | |
| 53 * | |
| 54 * - As much as possible, bookkeeping data is pointed to directly by | |
| 55 * the element objects, rather than being stored in maps. | |
| 56 * | |
| 57 * - Where possible, we favor method dispatch instead of "is" and "as" | |
| 58 * checks. E.g. see [ReferenceableElementForLink.asConstructor]. | |
| 59 */ | |
| 60 | |
| 61 import 'package:analyzer/dart/element/element.dart'; | |
| 62 import 'package:analyzer/dart/element/type.dart'; | |
| 63 import 'package:analyzer/src/generated/utilities_dart.dart'; | |
| 64 import 'package:analyzer/src/summary/format.dart'; | |
| 65 import 'package:analyzer/src/summary/idl.dart'; | |
| 66 import 'package:analyzer/src/summary/prelink.dart'; | |
| 67 | |
| 68 /** | |
| 69 * Link together the build unit consisting of [libraryUris], using | |
| 70 * [getDependency] to fetch the [LinkedLibrary] objects from other | |
| 71 * build units, and [getUnit] to fetch the [UnlinkedUnit] objects from | |
| 72 * both this build unit and other build units. | |
| 73 * | |
| 74 * A map is returned whose keys are the URIs of the libraries in this | |
| 75 * build unit, and whose values are the corresponding | |
| 76 * [LinkedLibraryBuilder]s. | |
| 77 */ | |
| 78 Map<String, LinkedLibraryBuilder> link(Set<String> libraryUris, | |
| 79 GetDependencyCallback getDependency, GetUnitCallback getUnit) { | |
| 80 Map<String, LinkedLibraryBuilder> linkedLibraries = | |
| 81 <String, LinkedLibraryBuilder>{}; | |
| 82 for (String absoluteUri in libraryUris) { | |
| 83 Uri uri = Uri.parse(absoluteUri); | |
| 84 UnlinkedUnit getRelativeUnit(String relativeUri) => | |
| 85 getUnit(resolveRelativeUri(uri, Uri.parse(relativeUri)).toString()); | |
| 86 linkedLibraries[absoluteUri] = prelink( | |
| 87 getUnit(absoluteUri), | |
| 88 getRelativeUnit, | |
| 89 (String relativeUri) => getRelativeUnit(relativeUri)?.publicNamespace); | |
| 90 } | |
| 91 relink(linkedLibraries, getDependency, getUnit); | |
| 92 return linkedLibraries; | |
| 93 } | |
| 94 | |
| 95 /** | |
| 96 * Given [libraries] (a map from URI to [LinkedLibraryBuilder] | |
| 97 * containing correct prelinked information), rebuild linked | |
| 98 * information, using [getDependency] to fetch the [LinkedLibrary] | |
| 99 * objects from other build units, and [getUnit] to fetch the | |
| 100 * [UnlinkedUnit] objects from both this build unit and other build | |
| 101 * units. | |
| 102 */ | |
| 103 void relink(Map<String, LinkedLibraryBuilder> libraries, | |
| 104 GetDependencyCallback getDependency, GetUnitCallback getUnit) { | |
| 105 new _Linker(libraries, getDependency, getUnit).link(); | |
| 106 } | |
| 107 | |
| 108 /** | |
| 109 * Type of the callback used by [link] and [relink] to request | |
| 110 * [LinkedLibrary] objects from other build units. | |
| 111 */ | |
| 112 typedef LinkedLibrary GetDependencyCallback(String absoluteUri); | |
| 113 | |
| 114 /** | |
| 115 * Type of the callback used by [link[ and [relin] to request | |
|
scheglov
2016/03/25 18:30:27
mistypes
Paul Berry
2016/03/28 16:02:10
Done.
| |
| 116 * [UnlinkedUnit] objects. | |
| 117 */ | |
| 118 typedef UnlinkedUnit GetUnitCallback(String absoluteUri); | |
| 119 | |
| 120 /** | |
| 121 * Element representing a class or enum resynthesized from a summary | |
| 122 * during linking. | |
| 123 */ | |
| 124 abstract class ClassElementForLink | |
| 125 implements ClassElement, ReferenceableElementForLink { | |
| 126 @override | |
| 127 ConstructorElementForLink get asConstructor => unnamedConstructor; | |
| 128 | |
| 129 /** | |
| 130 * Indicates whether this is the core class `Object`. | |
| 131 */ | |
| 132 bool get isObject; | |
| 133 | |
| 134 @override | |
| 135 String get name; | |
| 136 | |
| 137 @override | |
| 138 ConstructorElementForLink get unnamedConstructor; | |
| 139 | |
| 140 /** | |
| 141 * Perform type inference and cycle detection on this class and | |
| 142 * store the resulting information in the enclosing elements. | |
| 143 */ | |
| 144 void link(LinkedUnitBuilder linkedUnit); | |
| 145 | |
| 146 @override | |
| 147 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Element representing a class resynthesized from a summary during | |
| 152 * linking. | |
| 153 */ | |
| 154 class ClassElementForLink_Class extends ClassElementForLink { | |
| 155 /** | |
| 156 * The unlinked representation of the class in the summary. | |
| 157 */ | |
| 158 final UnlinkedClass _unlinkedClass; | |
| 159 | |
| 160 @override | |
| 161 final CompilationUnitElementForLink enclosingElement; | |
| 162 | |
| 163 List<ConstructorElementForLink> _constructors; | |
| 164 ConstructorElementForLink _unnamedConstructor; | |
| 165 bool _unnamedConstructorComputed = false; | |
| 166 List<FieldElementForLink> _fields; | |
| 167 InterfaceTypeForLink _supertype; | |
| 168 InterfaceTypeForLink _type; | |
| 169 | |
| 170 ClassElementForLink_Class(this._unlinkedClass, this.enclosingElement); | |
|
scheglov
2016/03/25 18:30:27
Maybe worth to keep the order of constructor param
Paul Berry
2016/03/28 16:02:10
Done.
| |
| 171 | |
| 172 @override | |
| 173 List<ConstructorElementForLink> get constructors { | |
| 174 if (_constructors == null) { | |
| 175 _constructors = <ConstructorElementForLink>[]; | |
| 176 for (UnlinkedExecutable unlinkedExecutable | |
| 177 in _unlinkedClass.executables) { | |
| 178 if (unlinkedExecutable.kind == UnlinkedExecutableKind.constructor) { | |
| 179 _constructors | |
| 180 .add(new ConstructorElementForLink(unlinkedExecutable, this)); | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 return _constructors; | |
| 185 } | |
| 186 | |
| 187 @override | |
| 188 List<FieldElementForLink> get fields { | |
| 189 if (_fields == null) { | |
| 190 _fields = <FieldElementForLink>[]; | |
| 191 for (UnlinkedVariable field in _unlinkedClass.fields) { | |
| 192 _fields.add(new FieldElementForLink(field, this)); | |
| 193 } | |
| 194 } | |
| 195 return _fields; | |
| 196 } | |
| 197 | |
| 198 @override | |
| 199 bool get isObject => _unlinkedClass.hasNoSupertype; | |
| 200 | |
| 201 @override | |
| 202 String get name => _unlinkedClass.name; | |
| 203 | |
| 204 @override | |
| 205 InterfaceTypeForLink get supertype { | |
| 206 if (isObject) { | |
| 207 return null; | |
| 208 } | |
| 209 return _supertype ??= _unlinkedClass.supertype == null | |
| 210 ? enclosingElement.enclosingElement._linker.objectType | |
| 211 : enclosingElement._resolveTypeRef(_unlinkedClass.supertype); | |
| 212 } | |
| 213 | |
| 214 @override | |
| 215 ConstructorElementForLink get unnamedConstructor { | |
| 216 if (!_unnamedConstructorComputed) { | |
| 217 for (ConstructorElementForLink constructor in constructors) { | |
| 218 if (constructor.name.isEmpty) { | |
| 219 _unnamedConstructor = constructor; | |
| 220 break; | |
| 221 } | |
| 222 } | |
| 223 _unnamedConstructorComputed = true; | |
| 224 } | |
| 225 return _unnamedConstructor; | |
| 226 } | |
| 227 | |
| 228 @override | |
| 229 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i), | |
| 230 List<int> implicitFunctionTypeIndices) { | |
| 231 if (_unlinkedClass.typeParameters.length != 0) { | |
| 232 // TODO(paulberry): implement. | |
| 233 throw new UnimplementedError(); | |
| 234 } else { | |
| 235 return _type ??= new InterfaceTypeForLink(this); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 @override | |
| 240 ReferenceableElementForLink getContainedName(name) { | |
| 241 // TODO(paulberry): implement. | |
| 242 throw new UnimplementedError(); | |
| 243 } | |
| 244 | |
| 245 @override | |
| 246 void link(LinkedUnitBuilder linkedUnit) { | |
| 247 for (ConstructorElementForLink constructorElement in constructors) { | |
| 248 constructorElement.link(linkedUnit); | |
| 249 } | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 /** | |
| 254 * Element representing an enum resynthesized from a summary during | |
| 255 * linking. | |
| 256 */ | |
| 257 class ClassElementForLink_Enum extends ClassElementForLink { | |
| 258 /** | |
| 259 * The unlinked representation of the enum in the summary. | |
| 260 */ | |
| 261 final UnlinkedEnum _unlinkedEnum; | |
| 262 | |
| 263 ClassElementForLink_Enum(this._unlinkedEnum); | |
|
scheglov
2016/03/25 18:30:27
Do we need enclosingElement too?
Paul Berry
2016/03/28 16:02:10
I'm not sure. I'm working on a follow up CL, and
| |
| 264 | |
| 265 @override | |
| 266 bool get isObject => false; | |
| 267 | |
| 268 @override | |
| 269 String get name => _unlinkedEnum.name; | |
| 270 | |
| 271 @override | |
| 272 ConstructorElementForLink get unnamedConstructor => null; | |
| 273 | |
| 274 @override | |
| 275 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i), | |
| 276 List<int> implicitFunctionTypeIndices) { | |
| 277 // TODO(paulberry): implement. | |
| 278 throw new UnimplementedError(); | |
| 279 } | |
| 280 | |
| 281 @override | |
| 282 ReferenceableElementForLink getContainedName(name) { | |
| 283 // TODO(paulberry): implement. | |
| 284 throw new UnimplementedError(); | |
| 285 } | |
| 286 | |
| 287 @override | |
| 288 void link(LinkedUnitBuilder linkedUnit) {} | |
| 289 } | |
| 290 | |
| 291 /** | |
| 292 * Element representing a compilation unit resynthesized from a | |
| 293 * summary during linking. | |
| 294 */ | |
| 295 abstract class CompilationUnitElementForLink implements CompilationUnitElement { | |
| 296 /** | |
| 297 * The unlinked representation of the compilation unit in the | |
| 298 * summary. | |
| 299 */ | |
| 300 final UnlinkedUnit _unlinkedUnit; | |
| 301 | |
| 302 /** | |
| 303 * For each entry in [UnlinkedUnit.references], the element referred | |
| 304 * to by the reference, or `null` if it hasn't been located yet. | |
| 305 */ | |
| 306 final List<ReferenceableElementForLink> _references; | |
| 307 | |
| 308 List<ClassElementForLink> _types; | |
| 309 Map<String, ReferenceableElementForLink> _containedNames; | |
| 310 | |
| 311 @override | |
| 312 final LibraryElementForLink enclosingElement; | |
| 313 | |
| 314 CompilationUnitElementForLink( | |
| 315 this.enclosingElement, UnlinkedUnit unlinkedUnit) | |
| 316 : _references = new List<ReferenceableElementForLink>( | |
| 317 unlinkedUnit.references.length), | |
| 318 _unlinkedUnit = unlinkedUnit; | |
| 319 | |
| 320 @override | |
| 321 bool get isInBuildUnit; | |
| 322 | |
| 323 @override | |
| 324 List<ClassElementForLink> get types { | |
| 325 if (_types == null) { | |
| 326 _types = <ClassElementForLink>[]; | |
|
scheglov
2016/03/25 18:30:27
Do we want to create a fixed length list here too?
Paul Berry
2016/03/28 16:02:10
I'm not sure it's worth it--it's a fair amount of
| |
| 327 for (UnlinkedClass unlinkedClass in _unlinkedUnit.classes) { | |
| 328 _types.add(new ClassElementForLink_Class(unlinkedClass, this)); | |
| 329 } | |
| 330 for (UnlinkedEnum unlinkedEnum in _unlinkedUnit.enums) { | |
| 331 _types.add(new ClassElementForLink_Enum(unlinkedEnum)); | |
| 332 } | |
| 333 } | |
| 334 return _types; | |
| 335 } | |
| 336 | |
| 337 /** | |
| 338 * The linked representation of the compilation unit in the summary. | |
| 339 */ | |
| 340 LinkedUnit get _linkedUnit; | |
| 341 | |
| 342 /** | |
| 343 * Search the unit for a top level element with the given [name]. | |
| 344 * If no name is found, return the singleton instance of | |
| 345 * [UndefinedElementForLink]. | |
| 346 */ | |
| 347 ReferenceableElementForLink getContainedName(name) { | |
| 348 if (_containedNames == null) { | |
| 349 _containedNames = <String, ReferenceableElementForLink>{}; | |
| 350 for (ClassElementForLink type in types) { | |
| 351 // TODO(paulberry): what's the correct way to handle name conflicts? | |
| 352 _containedNames[type.name] = type; | |
| 353 } | |
| 354 // TODO(paulberry): fill in other top level entities. | |
| 355 } | |
| 356 return _containedNames.putIfAbsent( | |
| 357 name, () => UndefinedElementForLink.instance); | |
| 358 } | |
| 359 | |
| 360 @override | |
| 361 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 362 | |
| 363 /** | |
| 364 * Return the element referred to by the given [index] in | |
| 365 * [UnlinkedUnit.references]. If the reference is unresolved, | |
| 366 * return [UndefinedElementForLink.instance]. | |
| 367 */ | |
| 368 ReferenceableElementForLink _resolveRef(int index) { | |
| 369 if (_references[index] == null) { | |
| 370 UnlinkedReference unlinkedReference = _unlinkedUnit.references[index]; | |
| 371 LinkedReference linkedReference = _linkedUnit.references[index]; | |
| 372 String name = unlinkedReference.name; | |
| 373 int containingReference = unlinkedReference.prefixReference; | |
| 374 if (containingReference != 0) { | |
| 375 _references[index] = | |
| 376 _resolveRef(containingReference).getContainedName(name); | |
| 377 } else if (linkedReference.dependency == 0) { | |
| 378 _references[index] = enclosingElement.getContainedName(name); | |
| 379 } else { | |
| 380 // TODO(paulberry): implement. | |
| 381 throw new UnimplementedError(); | |
| 382 } | |
| 383 } | |
| 384 return _references[index]; | |
| 385 } | |
| 386 | |
| 387 /** | |
| 388 * Resolve an [EntityRef] into a type. If the reference is | |
| 389 * unresolved, return [DynamicTypeImpl.instance]. | |
| 390 * | |
| 391 * TODO(paulberry): or should we have a class representing an | |
| 392 * unresolved type, for consistency with the full element model? | |
| 393 */ | |
| 394 DartTypeForLink _resolveTypeRef(EntityRef type, {bool defaultVoid: false}) { | |
| 395 if (type == null) { | |
| 396 if (defaultVoid) { | |
| 397 return VoidTypeForLink.instance; | |
| 398 } else { | |
| 399 return DynamicTypeForLink.instance; | |
| 400 } | |
| 401 } | |
| 402 if (type.paramReference != 0) { | |
| 403 // TODO(paulberry): implement. | |
| 404 throw new UnimplementedError(); | |
| 405 } else if (type.syntheticReturnType != null) { | |
| 406 // TODO(paulberry): implement. | |
| 407 throw new UnimplementedError(); | |
| 408 } else { | |
| 409 DartTypeForLink getTypeArgument(int i) { | |
| 410 if (i < type.typeArguments.length) { | |
| 411 return _resolveTypeRef(type.typeArguments[i]); | |
| 412 } else { | |
| 413 return DynamicTypeForLink.instance; | |
| 414 } | |
| 415 } | |
| 416 ReferenceableElementForLink element = _resolveRef(type.reference); | |
| 417 return element.buildType( | |
| 418 getTypeArgument, type.implicitFunctionTypeIndices); | |
| 419 } | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 /** | |
| 424 * Element representing a compilation unit which is part of the build | |
| 425 * unit being linked. | |
| 426 */ | |
| 427 class CompilationUnitElementInBuildUnit extends CompilationUnitElementForLink { | |
| 428 @override | |
| 429 final LinkedUnitBuilder _linkedUnit; | |
| 430 | |
| 431 CompilationUnitElementInBuildUnit(LibraryElementInBuildUnit libraryElement, | |
| 432 UnlinkedUnit unlinkedUnit, this._linkedUnit) | |
| 433 : super(libraryElement, unlinkedUnit); | |
| 434 | |
| 435 @override | |
| 436 bool get isInBuildUnit => true; | |
| 437 | |
| 438 /** | |
| 439 * Perform type inference and const cycle detection on this | |
| 440 * compilation unit. | |
| 441 */ | |
| 442 void link() { | |
| 443 for (ClassElementForLink classElement in types) { | |
| 444 classElement.link(_linkedUnit); | |
| 445 } | |
| 446 } | |
| 447 | |
| 448 /** | |
| 449 * Throw away any information produced by a previous call to [link]. | |
| 450 */ | |
| 451 void unlink() { | |
| 452 _linkedUnit.constCycles.clear(); | |
| 453 _linkedUnit.references.length = _unlinkedUnit.references.length; | |
| 454 _linkedUnit.types.clear(); | |
| 455 } | |
| 456 } | |
| 457 | |
| 458 /** | |
| 459 * Element representing a compilation unit which is depended upon | |
| 460 * (either directly or indirectly) by the build unit being linked. | |
| 461 */ | |
| 462 class CompilationUnitElementInDependency extends CompilationUnitElementForLink { | |
| 463 @override | |
| 464 final LinkedUnit _linkedUnit; | |
| 465 | |
| 466 CompilationUnitElementInDependency(LibraryElementInDependency libraryElement, | |
| 467 UnlinkedUnit unlinkedUnit, this._linkedUnit) | |
| 468 : super(libraryElement, unlinkedUnit); | |
| 469 | |
| 470 @override | |
| 471 bool get isInBuildUnit => false; | |
| 472 } | |
| 473 | |
| 474 /** | |
| 475 * Instance of [ConstNode] representing a constant constructor. | |
| 476 */ | |
| 477 class ConstConstructorNode extends ConstNode { | |
| 478 /** | |
| 479 * The [ConstructorElement] to which this node refers. | |
| 480 */ | |
| 481 final ConstructorElementForLink constructorElement; | |
| 482 | |
| 483 /** | |
| 484 * Once this node has been evaluated, indicates whether the | |
| 485 * constructor is free of constant evaluation cycles. | |
| 486 */ | |
| 487 bool isCycleFree = false; | |
| 488 | |
| 489 ConstConstructorNode(this.constructorElement); | |
| 490 | |
| 491 @override | |
| 492 List<ConstNode> computeDependencies() { | |
| 493 List<ConstNode> dependencies = <ConstNode>[]; | |
| 494 void safeAddDependency(ConstNode target) { | |
| 495 if (target != null) { | |
| 496 dependencies.add(target); | |
| 497 } | |
| 498 } | |
| 499 UnlinkedExecutable unlinkedExecutable = | |
| 500 constructorElement._unlinkedExecutable; | |
| 501 ClassElementForLink_Class enclosingClass = | |
| 502 constructorElement.enclosingElement; | |
| 503 ConstructorElementForLink redirectedConstructor = | |
| 504 _getConstRedirectedConstructor(); | |
| 505 if (redirectedConstructor != null) { | |
| 506 if (redirectedConstructor._constNode != null) { | |
| 507 safeAddDependency(redirectedConstructor._constNode); | |
| 508 } | |
| 509 } else if (unlinkedExecutable.isFactory) { | |
| 510 // Factory constructor, but getConstRedirectedConstructor returned | |
| 511 // null. This can happen if we're visiting one of the special external | |
| 512 // const factory constructors in the SDK, or if the code contains | |
| 513 // errors (such as delegating to a non-const constructor, or delegating | |
| 514 // to a constructor that can't be resolved). In any of these cases, | |
| 515 // we'll evaluate calls to this constructor without having to refer to | |
| 516 // any other constants. So we don't need to report any dependencies. | |
| 517 } else { | |
| 518 bool superInvocationFound = false; | |
| 519 for (UnlinkedConstructorInitializer constructorInitializer | |
| 520 in constructorElement._unlinkedExecutable.constantInitializers) { | |
| 521 if (constructorInitializer.kind == | |
| 522 UnlinkedConstructorInitializerKind.superInvocation) { | |
| 523 superInvocationFound = true; | |
| 524 } | |
| 525 CompilationUnitElementForLink compilationUnit = | |
| 526 constructorElement.enclosingElement.enclosingElement; | |
| 527 collectDependencies( | |
| 528 dependencies, constructorInitializer.expression, compilationUnit); | |
| 529 constructorInitializer.arguments.map((UnlinkedConst unlinkedConst) => | |
| 530 collectDependencies(dependencies, unlinkedConst, compilationUnit)); | |
| 531 } | |
| 532 | |
| 533 if (!superInvocationFound) { | |
| 534 // No explicit superconstructor invocation found, so we need to | |
| 535 // manually insert a reference to the implicit superconstructor. | |
| 536 ClassElementForLink superClass = enclosingClass.supertype?.element; | |
| 537 if (superClass != null && !superClass.isObject) { | |
| 538 ConstructorElementForLink unnamedConstructor = | |
| 539 superClass.unnamedConstructor; | |
| 540 safeAddDependency(unnamedConstructor?._constNode); | |
| 541 } | |
| 542 } | |
| 543 for (FieldElementForLink field in enclosingClass.fields) { | |
| 544 // Note: non-static const isn't allowed but we handle it anyway so | |
| 545 // that we won't be confused by incorrect code. | |
| 546 if ((field.isFinal || field.isConst) && !field.isStatic) { | |
| 547 safeAddDependency(field._constNode); | |
| 548 } | |
| 549 } | |
| 550 for (ParameterElementForLink parameterElement | |
| 551 in constructorElement.parameters) { | |
| 552 safeAddDependency(parameterElement._constNode); | |
| 553 } | |
| 554 } | |
| 555 return dependencies; | |
| 556 } | |
| 557 | |
| 558 /** | |
| 559 * If [constructorElement] redirects to another constructor, return | |
| 560 * the constructor it redirects to. | |
| 561 */ | |
| 562 ConstructorElementForLink _getConstRedirectedConstructor() { | |
| 563 // TODO(paulberry): implement | |
| 564 return null; | |
| 565 } | |
| 566 } | |
| 567 | |
| 568 /** | |
| 569 * Specialization of [DependencyWalker] for detecting constant | |
| 570 * evaluation cycles. | |
| 571 */ | |
| 572 class ConstDependencyWalker extends DependencyWalker<ConstNode> { | |
| 573 @override | |
| 574 void evaluate(ConstNode v) { | |
| 575 if (v is ConstConstructorNode) { | |
| 576 v.isCycleFree = true; | |
| 577 } | |
| 578 v.isEvaluated = true; | |
| 579 } | |
| 580 | |
| 581 @override | |
| 582 void evaluateScc(List<ConstNode> scc) { | |
| 583 for (ConstNode v in scc) { | |
| 584 if (v is ConstConstructorNode) { | |
| 585 v.isCycleFree = false; | |
| 586 } | |
| 587 v.isEvaluated = true; | |
| 588 } | |
| 589 } | |
| 590 } | |
| 591 | |
| 592 /** | |
| 593 * Specialization of [Node] used to construct the constant evaluation | |
| 594 * dependency graph. | |
| 595 */ | |
| 596 abstract class ConstNode extends Node<ConstNode> { | |
| 597 @override | |
| 598 bool isEvaluated = false; | |
| 599 | |
| 600 /** | |
| 601 * Collect the dependencies in [unlinkedConst] (which should be | |
| 602 * interpreted relative to [compilationUnit]) and store them in | |
| 603 * [dependencies]. | |
| 604 */ | |
| 605 void collectDependencies( | |
| 606 List<ConstNode> dependencies, | |
| 607 UnlinkedConst unlinkedConst, | |
| 608 CompilationUnitElementForLink compilationUnit) { | |
| 609 if (unlinkedConst == null) { | |
| 610 return; | |
| 611 } | |
| 612 int refPtr = 0; | |
| 613 for (UnlinkedConstOperation operation in unlinkedConst.operations) { | |
| 614 switch (operation) { | |
| 615 case UnlinkedConstOperation.pushReference: | |
| 616 // TODO(paulberry): implement. | |
| 617 throw new UnimplementedError(); | |
| 618 case UnlinkedConstOperation.makeTypedList: | |
| 619 refPtr++; | |
| 620 break; | |
| 621 case UnlinkedConstOperation.makeTypedMap: | |
| 622 refPtr += 2; | |
| 623 break; | |
| 624 case UnlinkedConstOperation.invokeConstructor: | |
| 625 EntityRef ref = unlinkedConst.references[refPtr++]; | |
| 626 ConstructorElementForLink element = | |
| 627 compilationUnit._resolveRef(ref.reference).asConstructor; | |
| 628 if (element?._constNode != null) { | |
| 629 dependencies.add(element._constNode); | |
| 630 } | |
| 631 break; | |
| 632 default: | |
| 633 break; | |
| 634 } | |
| 635 } | |
| 636 assert(refPtr == unlinkedConst.references.length); | |
| 637 } | |
| 638 } | |
| 639 | |
| 640 /** | |
| 641 * Instance of [ConstNode] representing a parameter with a default | |
| 642 * value. | |
| 643 */ | |
| 644 class ConstParameterNode extends ConstNode { | |
| 645 /** | |
| 646 * The [ParameterElement] to which this node refers. | |
| 647 */ | |
| 648 final ParameterElementForLink parameterElement; | |
| 649 | |
| 650 ConstParameterNode(this.parameterElement); | |
| 651 | |
| 652 @override | |
| 653 List<ConstNode> computeDependencies() { | |
| 654 List<ConstNode> dependencies = <ConstNode>[]; | |
| 655 collectDependencies( | |
| 656 dependencies, | |
| 657 parameterElement._unlinkedParam.defaultValue, | |
| 658 parameterElement.compilationUnit); | |
| 659 return dependencies; | |
| 660 } | |
| 661 } | |
| 662 | |
| 663 /** | |
| 664 * Element representing a constructor resynthesized from a summary | |
| 665 * during linking. | |
| 666 */ | |
| 667 class ConstructorElementForLink implements ConstructorElement { | |
| 668 /** | |
| 669 * The unlinked representation of the constructor in the summary. | |
| 670 */ | |
| 671 final UnlinkedExecutable _unlinkedExecutable; | |
| 672 | |
| 673 /** | |
| 674 * If this is a `const` constructor and the enclosing library is | |
| 675 * part of the build unit being linked, the constructor's node in | |
| 676 * the constant evaluation dependency graph. Otherwise `null`. | |
| 677 */ | |
| 678 ConstConstructorNode _constNode; | |
| 679 | |
| 680 @override | |
| 681 final ClassElementForLink_Class enclosingElement; | |
| 682 | |
| 683 List<ParameterElementForLink> _parameters; | |
| 684 | |
| 685 ConstructorElementForLink(this._unlinkedExecutable, this.enclosingElement) { | |
| 686 if (enclosingElement.enclosingElement.isInBuildUnit && | |
| 687 _unlinkedExecutable.constCycleSlot != 0) { | |
| 688 _constNode = new ConstConstructorNode(this); | |
| 689 } | |
| 690 } | |
| 691 | |
| 692 @override | |
| 693 bool get isCycleFree { | |
| 694 if (!_constNode.isEvaluated) { | |
| 695 new ConstDependencyWalker().walk(_constNode); | |
| 696 } | |
| 697 return _constNode.isCycleFree; | |
| 698 } | |
| 699 | |
| 700 @override | |
| 701 String get name => _unlinkedExecutable.name; | |
| 702 | |
| 703 @override | |
| 704 List<ParameterElementForLink> get parameters { | |
| 705 if (_parameters == null) { | |
| 706 _parameters = <ParameterElementForLink>[]; | |
| 707 for (UnlinkedParam unlinkedParam in _unlinkedExecutable.parameters) { | |
| 708 _parameters.add(new ParameterElementForLink( | |
| 709 unlinkedParam, enclosingElement.enclosingElement)); | |
| 710 } | |
| 711 } | |
| 712 return _parameters; | |
| 713 } | |
| 714 | |
| 715 /** | |
| 716 * Perform const cycle detection on this constructor. | |
| 717 */ | |
| 718 void link(LinkedUnitBuilder linkedUnit) { | |
| 719 if (_constNode != null && !isCycleFree) { | |
| 720 linkedUnit.constCycles.add(_unlinkedExecutable.constCycleSlot); | |
| 721 } | |
| 722 } | |
| 723 | |
| 724 @override | |
| 725 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 726 } | |
| 727 | |
| 728 /** | |
| 729 * Instance of [ConstNode] representing a constant field or constant | |
| 730 * top level variable. | |
| 731 */ | |
| 732 class ConstVariableNode extends ConstNode { | |
| 733 /** | |
| 734 * The [FieldElement] or [TopLevelVariableElement] to which this | |
| 735 * node refers. | |
| 736 */ | |
| 737 final VariableElementForLink variableElement; | |
| 738 | |
| 739 ConstVariableNode(this.variableElement); | |
| 740 | |
| 741 @override | |
| 742 List<ConstNode> computeDependencies() { | |
| 743 List<ConstNode> dependencies = <ConstNode>[]; | |
| 744 collectDependencies( | |
| 745 dependencies, | |
| 746 variableElement.unlinkedVariable.constExpr, | |
| 747 variableElement.compilationUnit); | |
| 748 return dependencies; | |
| 749 } | |
| 750 } | |
| 751 | |
| 752 /** | |
| 753 * Representation of a type resynthesized from a summary during linking. | |
| 754 */ | |
| 755 class DartTypeForLink implements DartType { | |
| 756 const DartTypeForLink(); | |
| 757 | |
| 758 @override | |
| 759 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 760 } | |
| 761 | |
| 762 /** | |
| 763 * An instance of [DependencyWalker] contains the core algorithms for | |
| 764 * walking a dependency graph and evaluating nodes in a safe order. | |
| 765 */ | |
| 766 abstract class DependencyWalker<NodeType extends Node<NodeType>> { | |
| 767 /** | |
| 768 * Called by [walk] to evaluate a single non-cyclical node, after | |
| 769 * all that node's dependencies have been evaluated. | |
| 770 */ | |
| 771 void evaluate(NodeType v); | |
| 772 | |
| 773 /** | |
| 774 * Called by [walk] to evaluate a strongly connected component | |
| 775 * containing one or more nodes. All dependencies of the strongly | |
| 776 * connected component have been evaluated. | |
| 777 */ | |
| 778 void evaluateScc(List<NodeType> scc); | |
| 779 | |
| 780 /** | |
| 781 * Walk the dependency graph starting at [startingPoint], finding | |
| 782 * strongly connected components and evaluating them in a safe order | |
| 783 * by calling [evaluate] and [evaluateScc]. | |
| 784 * | |
| 785 * This is an implementation of Tarjan's strongly connected | |
| 786 * components algorithm | |
| 787 * (https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_alg orithm). | |
| 788 */ | |
| 789 void walk(NodeType startingPoint) { | |
| 790 // TODO(paulberry): consider rewriting in a non-recursive way so | |
| 791 // that long dependency chains don't cause stack overflow. | |
| 792 | |
| 793 // TODO(paulberry): in the event that an exception occurs during | |
| 794 // the walk, restore the state of the [Node] data structures so | |
| 795 // that further evaluation will be safe. | |
| 796 | |
| 797 // The index which will be assigned to the next node that is | |
| 798 // freshly visited. | |
| 799 int index = 1; | |
| 800 | |
| 801 // Stack of nodes which have been seen so far and whose strongly | |
| 802 // connected component is still being determined. Nodes are only | |
| 803 // popped off the stack when they are evaluated, so sometimes the | |
| 804 // stack contains nodes that were visited after the current node. | |
| 805 List<NodeType> stack = <NodeType>[]; | |
| 806 | |
| 807 void strongConnect(NodeType node) { | |
| 808 // Assign the current node an index and add it to the stack. We | |
| 809 // haven't seen any of its dependencies yet, so set its lowLink | |
| 810 // to its index, indicating that so far it is the only node in | |
| 811 // its strongly connected component. | |
| 812 node.index = node.lowLink = index++; | |
| 813 stack.add(node); | |
| 814 | |
| 815 // Consider the node's dependencies one at a time. | |
| 816 for (NodeType dependency in node.dependencies) { | |
| 817 // If the dependency has already been evaluated, it can't be | |
| 818 // part of this node's strongly connected component, so we can | |
| 819 // skip it. | |
| 820 if (dependency.isEvaluated) { | |
| 821 continue; | |
| 822 } | |
| 823 if (dependency.index == 0) { | |
| 824 // The dependency hasn't been seen yet, so recurse on it. | |
| 825 strongConnect(dependency); | |
| 826 // If the dependency's lowLink refers to a node that was | |
| 827 // visited before the current node, that means that the | |
| 828 // current node, the dependency, and the node referred to by | |
| 829 // the dependency's lowLink are all part of the same | |
| 830 // strongly connected component, so we need to update the | |
| 831 // current node's lowLink accordingly. | |
| 832 if (dependency.lowLink < node.lowLink) { | |
| 833 node.lowLink = dependency.lowLink; | |
| 834 } | |
| 835 } else { | |
| 836 // The dependency has already been seen, so it is part of | |
| 837 // the current node's strongly connected component. If it | |
| 838 // was visited earlier than the current node's lowLink, then | |
| 839 // it is a new addition to the current node's strongly | |
| 840 // connected component, so we need to update the current | |
| 841 // node's lowLink accordingly. | |
| 842 if (dependency.index < node.lowLink) { | |
| 843 node.lowLink = dependency.index; | |
| 844 } | |
| 845 } | |
| 846 } | |
| 847 | |
| 848 // If the current node's lowLink is the same as its index, then | |
| 849 // we have finished visiting a strongly connected component, so | |
| 850 // pop the stack and evaluate it before moving on. | |
| 851 if (node.lowLink == node.index) { | |
| 852 // In the case where the strongly connected component has only | |
| 853 // one node, determine whether there is a trivial cycle or | |
| 854 // not. | |
| 855 // | |
| 856 // TODO(paulberry): could we figure this out in the for-loop | |
| 857 // above and save some effort? | |
| 858 if (identical(stack.last, node)) { | |
| 859 stack.removeLast(); | |
| 860 if (_hasTrivialScc(node)) { | |
| 861 evaluateScc(<NodeType>[node]); | |
| 862 } else { | |
| 863 evaluate(node); | |
| 864 } | |
| 865 } else { | |
| 866 // There are multiple nodes in the strongly connected | |
| 867 // component. | |
| 868 List<NodeType> scc = <NodeType>[]; | |
| 869 while (true) { | |
| 870 NodeType otherNode = stack.removeLast(); | |
| 871 scc.add(otherNode); | |
| 872 if (identical(otherNode, node)) { | |
| 873 break; | |
| 874 } | |
| 875 } | |
| 876 evaluateScc(scc); | |
| 877 } | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 // Kick off the algorithm starting with the starting point. | |
| 882 strongConnect(startingPoint); | |
| 883 } | |
| 884 | |
| 885 /** | |
| 886 * The given [node] is in a strongly connected component of size 1. | |
| 887 * Determine if it contains a trivial cycle (i.e. depends on | |
| 888 * itself). | |
| 889 */ | |
| 890 bool _hasTrivialScc(NodeType node) { | |
| 891 for (NodeType dependency in node.dependencies) { | |
| 892 if (identical(dependency, node)) { | |
| 893 return true; | |
| 894 } | |
| 895 } | |
| 896 return false; | |
| 897 } | |
| 898 } | |
| 899 | |
| 900 /** | |
| 901 * Representation of the dynamic type during linking. | |
| 902 */ | |
| 903 class DynamicTypeForLink extends DartTypeForLink { | |
| 904 /** | |
| 905 * Singleton instance of the dynamic type. | |
| 906 */ | |
| 907 static const DynamicTypeForLink instance = const DynamicTypeForLink._(); | |
| 908 | |
| 909 const DynamicTypeForLink._(); | |
| 910 } | |
| 911 | |
| 912 /** | |
| 913 * Element representing a field resynthesized from a summary during linking. | |
| 914 */ | |
| 915 class FieldElementForLink extends VariableElementForLink | |
| 916 implements FieldElement { | |
| 917 /** | |
| 918 * The unlinked representation of the field in the summary. | |
| 919 */ | |
| 920 final ClassElementForLink_Class enclosingElement; | |
| 921 | |
| 922 FieldElementForLink(UnlinkedVariable unlinkedVariable, | |
| 923 ClassElementForLink_Class enclosingElement) | |
| 924 : enclosingElement = enclosingElement, | |
| 925 super(unlinkedVariable, enclosingElement.enclosingElement); | |
| 926 | |
| 927 @override | |
| 928 bool get isConst => unlinkedVariable.isConst; | |
| 929 | |
| 930 @override | |
| 931 bool get isFinal => unlinkedVariable.isFinal; | |
| 932 | |
| 933 @override | |
| 934 bool get isStatic => unlinkedVariable.isStatic; | |
| 935 | |
| 936 @override | |
| 937 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 938 } | |
| 939 | |
| 940 /** | |
| 941 * Representation of an interface type during linking. | |
| 942 */ | |
| 943 class InterfaceTypeForLink extends DartTypeForLink implements InterfaceType { | |
| 944 @override | |
| 945 final ClassElementForLink element; | |
| 946 | |
| 947 InterfaceTypeForLink(this.element); | |
| 948 } | |
| 949 | |
| 950 /** | |
| 951 * Element representing a library resynthesied from a summary during | |
| 952 * linking. The type parameter, [UnitElement], represents the type | |
| 953 * that will be used for the compilation unit elements. | |
| 954 */ | |
| 955 abstract class LibraryElementForLink< | |
| 956 UnitElement extends CompilationUnitElementForLink> | |
| 957 implements LibraryElement { | |
| 958 /** | |
| 959 * Pointer back to the linker. | |
| 960 */ | |
| 961 final _Linker _linker; | |
| 962 | |
| 963 /** | |
| 964 * The absolute URI of this library. | |
| 965 */ | |
| 966 final Uri _absoluteUri; | |
| 967 | |
| 968 List<UnitElement> _units; | |
| 969 final Map<String, ReferenceableElementForLink> _containedNames = | |
| 970 <String, ReferenceableElementForLink>{}; | |
| 971 | |
| 972 LibraryElementForLink(this._linker, this._absoluteUri); | |
| 973 | |
| 974 @override | |
| 975 List<UnitElement> get units { | |
| 976 if (_units == null) { | |
| 977 UnlinkedUnit definingUnit = _linker.getUnit(_absoluteUri.toString()); | |
| 978 _units = <UnitElement>[_makeUnitElement(definingUnit, 0)]; | |
| 979 int numParts = definingUnit.parts.length; | |
| 980 for (int i = 0; i < numParts; i++) { | |
| 981 // TODO(paulberry): make sure we handle the case where Uri.parse fails. | |
| 982 // TODO(paulberry): make sure we handle the case where | |
| 983 // resolveRelativeUri fails. | |
| 984 UnlinkedUnit partUnit = _linker.getUnit(resolveRelativeUri( | |
| 985 _absoluteUri, Uri.parse(definingUnit.publicNamespace.parts[i])) | |
| 986 .toString()); | |
| 987 _units.add( | |
| 988 _makeUnitElement(partUnit ?? new UnlinkedUnitBuilder(), i + 1)); | |
| 989 } | |
| 990 } | |
| 991 return _units; | |
| 992 } | |
| 993 | |
| 994 /** | |
| 995 * The linked representation of the library in the summary. | |
| 996 */ | |
| 997 LinkedLibrary get _linkedLibrary; | |
| 998 | |
| 999 /** | |
| 1000 * Search all the units for a top level element with the given | |
| 1001 * [name]. If no name is found, return the singleton instance of | |
| 1002 * [UndefinedElementForLink]. | |
| 1003 */ | |
| 1004 ReferenceableElementForLink getContainedName(name) => | |
| 1005 _containedNames.putIfAbsent(name, () { | |
| 1006 for (UnitElement unit in units) { | |
| 1007 ReferenceableElementForLink element = unit.getContainedName(name); | |
| 1008 if (!identical(element, UndefinedElementForLink.instance)) { | |
| 1009 return element; | |
| 1010 } | |
| 1011 } | |
| 1012 return UndefinedElementForLink.instance; | |
| 1013 }); | |
| 1014 | |
| 1015 @override | |
| 1016 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 1017 | |
| 1018 /** | |
| 1019 * Create a [UnitElement] for one of the library's compilation | |
| 1020 * units. | |
| 1021 */ | |
| 1022 UnitElement _makeUnitElement(UnlinkedUnit unlinkedUnit, int i); | |
| 1023 } | |
| 1024 | |
| 1025 /** | |
| 1026 * Element representing a library which is part of the build unit | |
| 1027 * being linked. | |
| 1028 */ | |
| 1029 class LibraryElementInBuildUnit | |
| 1030 extends LibraryElementForLink<CompilationUnitElementInBuildUnit> { | |
| 1031 @override | |
| 1032 final LinkedLibraryBuilder _linkedLibrary; | |
| 1033 | |
| 1034 LibraryElementInBuildUnit( | |
| 1035 _Linker linker, Uri absoluteUri, this._linkedLibrary) | |
| 1036 : super(linker, absoluteUri); | |
| 1037 | |
| 1038 /** | |
| 1039 * Perform type inference and const cycle detection on this library. | |
| 1040 */ | |
| 1041 void link() { | |
| 1042 for (CompilationUnitElementInBuildUnit unit in units) { | |
| 1043 unit.link(); | |
| 1044 } | |
| 1045 } | |
| 1046 | |
| 1047 /** | |
| 1048 * Throw away any information produced by a previous call to [link]. | |
| 1049 */ | |
| 1050 void unlink() { | |
| 1051 _linkedLibrary.dependencies.length = | |
| 1052 _linkedLibrary.numPrelinkedDependencies; | |
| 1053 for (CompilationUnitElementInBuildUnit unit in units) { | |
| 1054 unit.link(); | |
| 1055 } | |
| 1056 } | |
| 1057 | |
| 1058 @override | |
| 1059 CompilationUnitElementInBuildUnit _makeUnitElement( | |
| 1060 UnlinkedUnit unlinkedUnit, int i) => | |
| 1061 new CompilationUnitElementInBuildUnit( | |
| 1062 this, unlinkedUnit, _linkedLibrary.units[i]); | |
| 1063 } | |
| 1064 | |
| 1065 /** | |
| 1066 * Element representing a library which is depended upon (either | |
| 1067 * directly or indirectly) by the build unit being linked. | |
| 1068 */ | |
| 1069 class LibraryElementInDependency | |
| 1070 extends LibraryElementForLink<CompilationUnitElementInDependency> { | |
| 1071 @override | |
| 1072 final LinkedLibrary _linkedLibrary; | |
| 1073 | |
| 1074 LibraryElementInDependency( | |
| 1075 _Linker linker, Uri absoluteUri, this._linkedLibrary) | |
| 1076 : super(linker, absoluteUri); | |
| 1077 | |
| 1078 @override | |
| 1079 CompilationUnitElementInDependency _makeUnitElement( | |
| 1080 UnlinkedUnit unlinkedUnit, int i) => | |
| 1081 new CompilationUnitElementInDependency( | |
| 1082 this, unlinkedUnit, _linkedLibrary.units[i]); | |
| 1083 } | |
| 1084 | |
| 1085 /** | |
| 1086 * Instances of [Node] represent nodes in a dependency graph. The | |
| 1087 * type parameter, [NodeType], is the derived type (this affords some | |
| 1088 * extra type safety by making it difficult to accidentally construct | |
| 1089 * bridges between unrelated dependency graphs). | |
| 1090 */ | |
| 1091 abstract class Node<NodeType> { | |
| 1092 /** | |
| 1093 * Index used by Tarjan's strongly connected components algorithm. | |
| 1094 * Zero means the node has not been visited yet; a nonzero value | |
| 1095 * counts the order in which the node was visited. | |
| 1096 */ | |
| 1097 int index = 0; | |
| 1098 | |
| 1099 /** | |
| 1100 * Low link used by Tarjan's strongly connected components | |
| 1101 * algorithm. This represents the smallest [index] of all the nodes | |
| 1102 * in the strongly connected component to which this node belongs. | |
| 1103 */ | |
| 1104 int lowLink = 0; | |
| 1105 | |
| 1106 List<NodeType> _dependencies; | |
| 1107 | |
| 1108 /** | |
| 1109 * Retrieve the dependencies of this node. | |
| 1110 */ | |
| 1111 List<NodeType> get dependencies => _dependencies ??= computeDependencies(); | |
| 1112 | |
| 1113 /** | |
| 1114 * Indicates whether this node has been evaluated yet. | |
| 1115 */ | |
| 1116 bool get isEvaluated; | |
| 1117 | |
| 1118 /** | |
| 1119 * Compute the dependencies of this node. | |
| 1120 */ | |
| 1121 List<NodeType> computeDependencies(); | |
| 1122 } | |
| 1123 | |
| 1124 /** | |
| 1125 * Element representing a function or method parameter resynthesized | |
| 1126 * from a summary during linking. | |
| 1127 */ | |
| 1128 class ParameterElementForLink implements ParameterElement { | |
| 1129 /** | |
| 1130 * The unlinked representation of the parameter in the summary. | |
| 1131 */ | |
| 1132 final UnlinkedParam _unlinkedParam; | |
| 1133 | |
| 1134 /** | |
| 1135 * If this parameter has a default value and the enclosing library | |
| 1136 * is part of the build unit being linked, the parameter's node in | |
| 1137 * the constant evaluation dependency graph. Otherwise `null`. | |
| 1138 */ | |
| 1139 ConstNode _constNode; | |
| 1140 | |
| 1141 /** | |
| 1142 * The compilation unit in which this parameter appears. | |
| 1143 */ | |
| 1144 final CompilationUnitElementForLink compilationUnit; | |
| 1145 | |
| 1146 ParameterElementForLink(this._unlinkedParam, this.compilationUnit) { | |
| 1147 if (_unlinkedParam.defaultValue != null) { | |
| 1148 _constNode = new ConstParameterNode(this); | |
| 1149 } | |
| 1150 } | |
| 1151 | |
| 1152 @override | |
| 1153 noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); | |
| 1154 } | |
| 1155 | |
| 1156 /** | |
| 1157 * Abstract base class representing an element which can be the target | |
| 1158 * of a reference. | |
| 1159 */ | |
| 1160 abstract class ReferenceableElementForLink { | |
| 1161 /** | |
| 1162 * If this element can be used in a constructor invocation context, | |
| 1163 * return the associated constructor (which may be `this` or some | |
| 1164 * other element). Otherwise return `null`. | |
| 1165 */ | |
| 1166 ConstructorElementForLink get asConstructor; | |
| 1167 | |
| 1168 /** | |
| 1169 * Return the type indicated by this element when it is used in a | |
| 1170 * type instantiation context. If this element can't legally be | |
| 1171 * instantiated as a type, return the dynamic type. | |
| 1172 */ | |
| 1173 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i), | |
| 1174 List<int> implicitFunctionTypeIndices); | |
| 1175 | |
| 1176 /** | |
| 1177 * If this element contains other named elements, return the | |
| 1178 * contained element having the given [name]. If this element can't | |
| 1179 * contain other named elements, or it doesn't contain an element | |
| 1180 * with the given name, return the singleton of | |
| 1181 * [UndefinedElementForLink]. | |
| 1182 */ | |
| 1183 ReferenceableElementForLink getContainedName(name); | |
| 1184 } | |
| 1185 | |
| 1186 /** | |
| 1187 * Singleton element used for unresolved references. | |
| 1188 */ | |
| 1189 class UndefinedElementForLink implements ReferenceableElementForLink { | |
| 1190 static const UndefinedElementForLink instance = | |
| 1191 const UndefinedElementForLink._(); | |
| 1192 | |
| 1193 const UndefinedElementForLink._(); | |
| 1194 | |
| 1195 @override | |
| 1196 ConstructorElementForLink get asConstructor => null; | |
| 1197 | |
| 1198 @override | |
| 1199 DartTypeForLink buildType(DartTypeForLink getTypeArgument(int i), | |
| 1200 List<int> implicitFunctionTypeIndices) => | |
| 1201 DynamicTypeForLink.instance; | |
| 1202 | |
| 1203 @override | |
| 1204 ReferenceableElementForLink getContainedName(name) => this; | |
| 1205 } | |
| 1206 | |
| 1207 /** | |
| 1208 * Element representing a top level variable resynthesized from a | |
| 1209 * summary during linking. | |
| 1210 */ | |
| 1211 class VariableElementForLink { | |
| 1212 /** | |
| 1213 * The unlinked representation of the variable in the summary. | |
| 1214 */ | |
| 1215 final UnlinkedVariable unlinkedVariable; | |
| 1216 | |
| 1217 /** | |
| 1218 * If this variable is declared `const` and the enclosing library is | |
| 1219 * part of the build unit being linked, the variable's node in the | |
| 1220 * constant evaluation dependency graph. Otherwise `null`. | |
| 1221 */ | |
| 1222 ConstNode _constNode; | |
| 1223 | |
| 1224 /** | |
| 1225 * The compilation unit in which this variable appears. | |
| 1226 */ | |
| 1227 final CompilationUnitElementForLink compilationUnit; | |
| 1228 | |
| 1229 VariableElementForLink(this.unlinkedVariable, this.compilationUnit) { | |
| 1230 if (compilationUnit.isInBuildUnit && unlinkedVariable.constExpr != null) { | |
| 1231 _constNode = new ConstVariableNode(this); | |
| 1232 } | |
| 1233 } | |
| 1234 } | |
| 1235 | |
| 1236 /** | |
| 1237 * Representation of the void type during linking. | |
| 1238 */ | |
| 1239 class VoidTypeForLink extends DartTypeForLink { | |
| 1240 static const VoidTypeForLink instance = const VoidTypeForLink._(); | |
| 1241 const VoidTypeForLink._(); | |
| 1242 } | |
| 1243 | |
| 1244 /** | |
| 1245 * Instances of [_Linker] contain the necessary information to link | |
| 1246 * together a single build unit. | |
| 1247 */ | |
| 1248 class _Linker { | |
| 1249 /** | |
| 1250 * Callback to ask the client for a [LinkedLibrary] for a | |
| 1251 * dependency. | |
| 1252 */ | |
| 1253 final GetDependencyCallback getDependency; | |
| 1254 | |
| 1255 /** | |
| 1256 * Callback to ask the client for an [UnlinkedUnit]. | |
| 1257 */ | |
| 1258 final GetUnitCallback getUnit; | |
| 1259 | |
| 1260 /** | |
| 1261 * Map containing all library elements accessed during linking, | |
| 1262 * whether they are part of the build unit being linked or whether | |
| 1263 * they are dependencies. | |
| 1264 */ | |
| 1265 final Map<Uri, LibraryElementForLink> _libraries = | |
| 1266 <Uri, LibraryElementForLink>{}; | |
| 1267 | |
| 1268 /** | |
| 1269 * List of library elements for the libraries in the build unit | |
| 1270 * being linked. | |
| 1271 */ | |
| 1272 final List<LibraryElementInBuildUnit> _librariesInBuildUnit = | |
| 1273 <LibraryElementInBuildUnit>[]; | |
| 1274 | |
| 1275 InterfaceTypeForLink _objectType; | |
| 1276 LibraryElementForLink _coreLibrary; | |
| 1277 | |
| 1278 _Linker(Map<String, LinkedLibraryBuilder> linkedLibraries, this.getDependency, | |
| 1279 this.getUnit) { | |
| 1280 // Create elements for the libraries to be linked. The rest of | |
| 1281 // the element model will be created on demand. | |
| 1282 linkedLibraries | |
| 1283 .forEach((String absoluteUri, LinkedLibraryBuilder linkedLibrary) { | |
| 1284 Uri uri = Uri.parse(absoluteUri); | |
| 1285 _librariesInBuildUnit.add(_libraries[uri] = | |
| 1286 new LibraryElementInBuildUnit(this, uri, linkedLibrary)); | |
| 1287 }); | |
| 1288 } | |
| 1289 | |
| 1290 /** | |
| 1291 * Get the library element for `dart:core`. | |
| 1292 */ | |
| 1293 LibraryElementForLink get coreLibrary => | |
| 1294 _coreLibrary ??= getLibrary(Uri.parse('dart:core')); | |
| 1295 | |
| 1296 /** | |
| 1297 * Get the `InterfaceType` for the type `Object`. | |
| 1298 */ | |
| 1299 InterfaceTypeForLink get objectType => _objectType ??= coreLibrary | |
| 1300 .getContainedName('Object') | |
| 1301 .buildType((int i) => DynamicTypeForLink.instance, const []); | |
| 1302 | |
| 1303 /** | |
| 1304 * Get the library element for the library having the given [uri]. | |
| 1305 */ | |
| 1306 LibraryElementForLink getLibrary(Uri uri) => _libraries.putIfAbsent( | |
| 1307 uri, | |
| 1308 () => new LibraryElementInDependency( | |
| 1309 this, uri, getDependency(uri.toString()))); | |
| 1310 | |
| 1311 /** | |
| 1312 * Perform type inference and const cycle detection on all libraries | |
| 1313 * in the build unit being linked. | |
| 1314 */ | |
| 1315 void link() { | |
| 1316 for (LibraryElementInBuildUnit library in _librariesInBuildUnit) { | |
| 1317 library.link(); | |
| 1318 } | |
| 1319 // TODO(paulberry): set dependencies. | |
| 1320 } | |
| 1321 | |
| 1322 /** | |
| 1323 * Throw away any information produced by a previous call to [link]. | |
| 1324 */ | |
| 1325 void unlink() { | |
| 1326 for (LibraryElementInBuildUnit library in _librariesInBuildUnit) { | |
| 1327 library.unlink(); | |
| 1328 } | |
| 1329 } | |
| 1330 } | |
| OLD | NEW |