| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.resolution; | 5 library dart2js.resolution; |
| 6 | 6 |
| 7 import 'dart:collection' show Queue; | 7 import 'dart:collection' show Queue; |
| 8 | 8 |
| 9 import '../common.dart'; | 9 import '../common.dart'; |
| 10 import '../common/names.dart' show Identifiers; | 10 import '../common/names.dart' show Identifiers; |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 CommonElements get commonElements => resolution.commonElements; | 75 CommonElements get commonElements => resolution.commonElements; |
| 76 ParsingContext get parsingContext => resolution.parsingContext; | 76 ParsingContext get parsingContext => resolution.parsingContext; |
| 77 CompilerOptions get options => resolution.options; | 77 CompilerOptions get options => resolution.options; |
| 78 ResolutionEnqueuer get enqueuer => resolution.enqueuer; | 78 ResolutionEnqueuer get enqueuer => resolution.enqueuer; |
| 79 OpenWorld get world => enqueuer.worldBuilder; | 79 OpenWorld get world => enqueuer.worldBuilder; |
| 80 | 80 |
| 81 ResolutionImpact resolve(Element element) { | 81 ResolutionImpact resolve(Element element) { |
| 82 return measure(() { | 82 return measure(() { |
| 83 if (Elements.isMalformed(element)) { | 83 if (Elements.isMalformed(element)) { |
| 84 // TODO(johnniwinther): Add a predicate for this. | 84 // TODO(johnniwinther): Add a predicate for this. |
| 85 assert(invariant(element, element is! ErroneousElement, | 85 assert( |
| 86 message: "Element $element expected to have parse errors.")); | 86 element is! ErroneousElement, |
| 87 failedAt( |
| 88 element, "Element $element expected to have parse errors.")); |
| 87 _ensureTreeElements(element); | 89 _ensureTreeElements(element); |
| 88 return const ResolutionImpact(); | 90 return const ResolutionImpact(); |
| 89 } | 91 } |
| 90 | 92 |
| 91 WorldImpact processMetadata([WorldImpact result]) { | 93 WorldImpact processMetadata([WorldImpact result]) { |
| 92 for (MetadataAnnotation metadata in element.implementation.metadata) { | 94 for (MetadataAnnotation metadata in element.implementation.metadata) { |
| 93 metadata.ensureResolved(resolution); | 95 metadata.ensureResolved(resolution); |
| 94 } | 96 } |
| 95 return result; | 97 return result; |
| 96 } | 98 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 113 TypedefElement typdef = element; | 115 TypedefElement typdef = element; |
| 114 return processMetadata(resolveTypedef(typdef)); | 116 return processMetadata(resolveTypedef(typdef)); |
| 115 } | 117 } |
| 116 | 118 |
| 117 reporter.internalError(element, "resolve($element) not implemented."); | 119 reporter.internalError(element, "resolve($element) not implemented."); |
| 118 }); | 120 }); |
| 119 } | 121 } |
| 120 | 122 |
| 121 void resolveRedirectingConstructor(InitializerResolver resolver, Node node, | 123 void resolveRedirectingConstructor(InitializerResolver resolver, Node node, |
| 122 FunctionElement constructor, FunctionElement redirection) { | 124 FunctionElement constructor, FunctionElement redirection) { |
| 123 assert(invariant(node, constructor.isImplementation, | 125 assert( |
| 124 message: 'Redirecting constructors must be resolved on implementation ' | 126 constructor.isImplementation, |
| 127 failedAt( |
| 128 node, |
| 129 'Redirecting constructors must be resolved on implementation ' |
| 125 'elements.')); | 130 'elements.')); |
| 126 Setlet<FunctionElement> seen = new Setlet<FunctionElement>(); | 131 Setlet<FunctionElement> seen = new Setlet<FunctionElement>(); |
| 127 seen.add(constructor); | 132 seen.add(constructor); |
| 128 while (redirection != null) { | 133 while (redirection != null) { |
| 129 // Ensure that we follow redirections through implementation elements. | 134 // Ensure that we follow redirections through implementation elements. |
| 130 redirection = redirection.implementation; | 135 redirection = redirection.implementation; |
| 131 if (redirection.isError) { | 136 if (redirection.isError) { |
| 132 break; | 137 break; |
| 133 } | 138 } |
| 134 if (seen.contains(redirection)) { | 139 if (seen.contains(redirection)) { |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 } | 291 } |
| 287 | 292 |
| 288 resolution.target.resolveNativeMember(element, registry.impactBuilder); | 293 resolution.target.resolveNativeMember(element, registry.impactBuilder); |
| 289 | 294 |
| 290 return registry.impactBuilder; | 295 return registry.impactBuilder; |
| 291 }); | 296 }); |
| 292 } | 297 } |
| 293 | 298 |
| 294 /// Returns `true` if [element] has been processed by the resolution enqueuer. | 299 /// Returns `true` if [element] has been processed by the resolution enqueuer. |
| 295 bool _hasBeenProcessed(MemberElement element) { | 300 bool _hasBeenProcessed(MemberElement element) { |
| 296 assert(invariant(element, element == element.analyzableElement.declaration, | 301 assert(element == element.analyzableElement.declaration, |
| 297 message: "Unexpected element $element")); | 302 failedAt(element, "Unexpected element $element")); |
| 298 return enqueuer.processedEntities.contains(element); | 303 return enqueuer.processedEntities.contains(element); |
| 299 } | 304 } |
| 300 | 305 |
| 301 WorldImpact resolveMethodElement(FunctionElementX element) { | 306 WorldImpact resolveMethodElement(FunctionElementX element) { |
| 302 assert(invariant(element, element.isDeclaration)); | 307 assert(element.isDeclaration, failedAt(element)); |
| 303 return reporter.withCurrentElement(element, () { | 308 return reporter.withCurrentElement(element, () { |
| 304 if (_hasBeenProcessed(element)) { | 309 if (_hasBeenProcessed(element)) { |
| 305 // TODO(karlklose): Remove the check for [isConstructor]. [elememts] | 310 // TODO(karlklose): Remove the check for [isConstructor]. [elememts] |
| 306 // should never be non-null, not even for constructors. | 311 // should never be non-null, not even for constructors. |
| 307 assert(invariant(element, element.isConstructor, | 312 assert( |
| 308 message: 'Non-constructor element $element ' | 313 element.isConstructor, |
| 309 'has already been analyzed.')); | 314 failedAt(element, |
| 315 'Non-constructor element $element has already been analyzed.')); |
| 310 return const ResolutionImpact(); | 316 return const ResolutionImpact(); |
| 311 } | 317 } |
| 312 if (element.isSynthesized) { | 318 if (element.isSynthesized) { |
| 313 if (element.isGenerativeConstructor) { | 319 if (element.isGenerativeConstructor) { |
| 314 ResolutionRegistry registry = | 320 ResolutionRegistry registry = |
| 315 new ResolutionRegistry(this.target, _ensureTreeElements(element)); | 321 new ResolutionRegistry(this.target, _ensureTreeElements(element)); |
| 316 ConstructorElement constructor = element.asFunctionElement(); | 322 ConstructorElement constructor = element.asFunctionElement(); |
| 317 ConstructorElement target = constructor.definingConstructor; | 323 ConstructorElement target = constructor.definingConstructor; |
| 318 // Ensure the signature of the synthesized element is | 324 // Ensure the signature of the synthesized element is |
| 319 // resolved. This is the only place where the resolver is | 325 // resolved. This is the only place where the resolver is |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 374 } | 380 } |
| 375 ResolverVisitor visitor = visitorFor(element); | 381 ResolverVisitor visitor = visitorFor(element); |
| 376 ResolutionRegistry registry = visitor.registry; | 382 ResolutionRegistry registry = visitor.registry; |
| 377 // TODO(johnniwinther): Maybe remove this when placeholderCollector | 383 // TODO(johnniwinther): Maybe remove this when placeholderCollector |
| 378 // migrates to the backend ast. | 384 // migrates to the backend ast. |
| 379 registry.defineElement(element.definition, element); | 385 registry.defineElement(element.definition, element); |
| 380 // TODO(johnniwinther): Share the resolved type between all variables | 386 // TODO(johnniwinther): Share the resolved type between all variables |
| 381 // declared in the same declaration. | 387 // declared in the same declaration. |
| 382 if (tree.type != null) { | 388 if (tree.type != null) { |
| 383 ResolutionDartType type = visitor.resolveTypeAnnotation(tree.type); | 389 ResolutionDartType type = visitor.resolveTypeAnnotation(tree.type); |
| 384 assert(invariant( | 390 assert( |
| 385 element, | |
| 386 element.variables.type == null || | 391 element.variables.type == null || |
| 387 // Crude check but we have no equivalence relation that | 392 // Crude check but we have no equivalence relation that |
| 388 // equates malformed types, like matching creations of type | 393 // equates malformed types, like matching creations of type |
| 389 // `Foo<Unresolved>`. | 394 // `Foo<Unresolved>`. |
| 390 element.variables.type.toString() == type.toString(), | 395 element.variables.type.toString() == type.toString(), |
| 391 message: "Unexpected type computed for $element. " | 396 failedAt( |
| 397 element, |
| 398 "Unexpected type computed for $element. " |
| 392 "Was ${element.variables.type}, computed $type.")); | 399 "Was ${element.variables.type}, computed $type.")); |
| 393 element.variables.type = type; | 400 element.variables.type = type; |
| 394 } else if (element.variables.type == null) { | 401 } else if (element.variables.type == null) { |
| 395 // Only assign the dynamic type if the element has no known type. This | 402 // Only assign the dynamic type if the element has no known type. This |
| 396 // happens for enum fields where the type is known but is not in the | 403 // happens for enum fields where the type is known but is not in the |
| 397 // synthesized AST. | 404 // synthesized AST. |
| 398 element.variables.type = const ResolutionDynamicType(); | 405 element.variables.type = const ResolutionDynamicType(); |
| 399 } else { | 406 } else { |
| 400 registry.registerCheckedModeCheck(element.variables.type); | 407 registry.registerCheckedModeCheck(element.variables.type); |
| 401 } | 408 } |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 reporter.reportErrorMessage(annotation, MessageKind.VOID_NOT_ALLOWED); | 460 reporter.reportErrorMessage(annotation, MessageKind.VOID_NOT_ALLOWED); |
| 454 } | 461 } |
| 455 return type; | 462 return type; |
| 456 } | 463 } |
| 457 | 464 |
| 458 ResolutionDartType _resolveReturnType( | 465 ResolutionDartType _resolveReturnType( |
| 459 Element element, TypeAnnotation annotation) { | 466 Element element, TypeAnnotation annotation) { |
| 460 if (annotation == null) return const ResolutionDynamicType(); | 467 if (annotation == null) return const ResolutionDynamicType(); |
| 461 ResolutionDartType result = | 468 ResolutionDartType result = |
| 462 visitorFor(element).resolveTypeAnnotation(annotation); | 469 visitorFor(element).resolveTypeAnnotation(annotation); |
| 463 assert(invariant(annotation, result != null, | 470 assert(result != null, |
| 464 message: "No type computed for $annotation.")); | 471 failedAt(annotation, "No type computed for $annotation.")); |
| 465 if (result == null) { | 472 if (result == null) { |
| 466 // TODO(karklose): warning. | 473 // TODO(karklose): warning. |
| 467 return const ResolutionDynamicType(); | 474 return const ResolutionDynamicType(); |
| 468 } | 475 } |
| 469 return result; | 476 return result; |
| 470 } | 477 } |
| 471 | 478 |
| 472 void resolveRedirectionChain(ConstructorElement constructor, Spannable node) { | 479 void resolveRedirectionChain(ConstructorElement constructor, Spannable node) { |
| 473 ConstructorElement target = constructor; | 480 ConstructorElement target = constructor; |
| 474 ResolutionDartType targetType; | 481 ResolutionDartType targetType; |
| 475 List<ConstructorElement> seen = new List<ConstructorElement>(); | 482 List<ConstructorElement> seen = new List<ConstructorElement>(); |
| 476 bool isMalformed = false; | 483 bool isMalformed = false; |
| 477 // Follow the chain of redirections and check for cycles. | 484 // Follow the chain of redirections and check for cycles. |
| 478 while (target.isRedirectingFactory) { | 485 while (target.isRedirectingFactory) { |
| 479 if (target.hasEffectiveTarget) { | 486 if (target.hasEffectiveTarget) { |
| 480 // We found a constructor that already has been processed. | 487 // We found a constructor that already has been processed. |
| 481 // TODO(johnniwinther): Should `effectiveTargetType` be part of the | 488 // TODO(johnniwinther): Should `effectiveTargetType` be part of the |
| 482 // interface? | 489 // interface? |
| 483 targetType = | 490 targetType = |
| 484 target.computeEffectiveTargetType(target.enclosingClass.thisType); | 491 target.computeEffectiveTargetType(target.enclosingClass.thisType); |
| 485 assert(invariant(target, targetType != null, | 492 assert( |
| 486 message: 'Redirection target type has not been computed for ' | 493 targetType != null, |
| 487 '$target')); | 494 failedAt(target, |
| 495 'Redirection target type has not been computed for $target')); |
| 488 target = target.effectiveTarget; | 496 target = target.effectiveTarget; |
| 489 break; | 497 break; |
| 490 } | 498 } |
| 491 | 499 |
| 492 Element nextTarget = target.immediateRedirectionTarget; | 500 Element nextTarget = target.immediateRedirectionTarget; |
| 493 | 501 |
| 494 if (seen.contains(nextTarget)) { | 502 if (seen.contains(nextTarget)) { |
| 495 reporter.reportErrorMessage( | 503 reporter.reportErrorMessage( |
| 496 node, MessageKind.CYCLIC_REDIRECTING_FACTORY); | 504 node, MessageKind.CYCLIC_REDIRECTING_FACTORY); |
| 497 targetType = target.enclosingClass.thisType; | 505 targetType = target.enclosingClass.thisType; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 515 } | 523 } |
| 516 | 524 |
| 517 // [target] is now the actual target of the redirections. Run through | 525 // [target] is now the actual target of the redirections. Run through |
| 518 // the constructors again and set their [redirectionTarget], so that we | 526 // the constructors again and set their [redirectionTarget], so that we |
| 519 // do not have to run the loop for these constructors again. Furthermore, | 527 // do not have to run the loop for these constructors again. Furthermore, |
| 520 // compute [redirectionTargetType] for each factory by computing the | 528 // compute [redirectionTargetType] for each factory by computing the |
| 521 // substitution of the target type with respect to the factory type. | 529 // substitution of the target type with respect to the factory type. |
| 522 while (!seen.isEmpty) { | 530 while (!seen.isEmpty) { |
| 523 ConstructorElementX factory = seen.removeLast(); | 531 ConstructorElementX factory = seen.removeLast(); |
| 524 ResolvedAst resolvedAst = factory.resolvedAst; | 532 ResolvedAst resolvedAst = factory.resolvedAst; |
| 525 assert(invariant(node, resolvedAst != null, | 533 assert( |
| 526 message: 'No ResolvedAst for $factory.')); | 534 resolvedAst != null, failedAt(node, 'No ResolvedAst for $factory.')); |
| 527 RedirectingFactoryBody redirectionNode = resolvedAst.body; | 535 RedirectingFactoryBody redirectionNode = resolvedAst.body; |
| 528 ResolutionDartType factoryType = | 536 ResolutionDartType factoryType = |
| 529 resolvedAst.elements.getType(redirectionNode); | 537 resolvedAst.elements.getType(redirectionNode); |
| 530 if (!factoryType.isDynamic) { | 538 if (!factoryType.isDynamic) { |
| 531 targetType = targetType.substByContext(factoryType); | 539 targetType = targetType.substByContext(factoryType); |
| 532 } | 540 } |
| 533 factory.setEffectiveTarget(target, targetType, isMalformed: isMalformed); | 541 factory.setEffectiveTarget(target, targetType, isMalformed: isMalformed); |
| 534 } | 542 } |
| 535 } | 543 } |
| 536 | 544 |
| 537 /** | 545 /** |
| 538 * Load and resolve the supertypes of [cls]. | 546 * Load and resolve the supertypes of [cls]. |
| 539 * | 547 * |
| 540 * Warning: do not call this method directly. It should only be | 548 * Warning: do not call this method directly. It should only be |
| 541 * called by [resolveClass] and [ClassSupertypeResolver]. | 549 * called by [resolveClass] and [ClassSupertypeResolver]. |
| 542 */ | 550 */ |
| 543 void loadSupertypes(BaseClassElementX cls, Spannable from) { | 551 void loadSupertypes(BaseClassElementX cls, Spannable from) { |
| 544 measure(() { | 552 measure(() { |
| 545 if (cls.supertypeLoadState == STATE_DONE) return; | 553 if (cls.supertypeLoadState == STATE_DONE) return; |
| 546 if (cls.supertypeLoadState == STATE_STARTED) { | 554 if (cls.supertypeLoadState == STATE_STARTED) { |
| 547 reporter.reportErrorMessage( | 555 reporter.reportErrorMessage( |
| 548 from, MessageKind.CYCLIC_CLASS_HIERARCHY, {'className': cls.name}); | 556 from, MessageKind.CYCLIC_CLASS_HIERARCHY, {'className': cls.name}); |
| 549 cls.supertypeLoadState = STATE_DONE; | 557 cls.supertypeLoadState = STATE_DONE; |
| 550 cls.hasIncompleteHierarchy = true; | 558 cls.hasIncompleteHierarchy = true; |
| 551 ClassElement objectClass = commonElements.objectClass; | 559 ClassElement objectClass = commonElements.objectClass; |
| 552 cls.allSupertypesAndSelf = objectClass.allSupertypesAndSelf | 560 cls.allSupertypesAndSelf = objectClass.allSupertypesAndSelf |
| 553 .extendClass(cls.computeType(resolution)); | 561 .extendClass(cls.computeType(resolution)); |
| 554 cls.supertype = cls.allSupertypes.head; | 562 cls.supertype = cls.allSupertypes.head; |
| 555 assert(invariant(from, cls.supertype != null, | 563 assert(cls.supertype != null, |
| 556 message: 'Missing supertype on cyclic class $cls.')); | 564 failedAt(from, 'Missing supertype on cyclic class $cls.')); |
| 557 cls.interfaces = const Link<ResolutionDartType>(); | 565 cls.interfaces = const Link<ResolutionDartType>(); |
| 558 return; | 566 return; |
| 559 } | 567 } |
| 560 cls.supertypeLoadState = STATE_STARTED; | 568 cls.supertypeLoadState = STATE_STARTED; |
| 561 reporter.withCurrentElement(cls, () { | 569 reporter.withCurrentElement(cls, () { |
| 562 // TODO(ahe): Cache the node in cls. | 570 // TODO(ahe): Cache the node in cls. |
| 563 cls | 571 cls |
| 564 .parseNode(parsingContext) | 572 .parseNode(parsingContext) |
| 565 .accept(new ClassSupertypeResolver(resolution, cls)); | 573 .accept(new ClassSupertypeResolver(resolution, cls)); |
| 566 if (cls.supertypeLoadState != STATE_DONE) { | 574 if (cls.supertypeLoadState != STATE_DONE) { |
| (...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 796 // Show the user the problematic uses of 'super' in the mixin. | 804 // Show the user the problematic uses of 'super' in the mixin. |
| 797 List<DiagnosticMessage> infos = <DiagnosticMessage>[]; | 805 List<DiagnosticMessage> infos = <DiagnosticMessage>[]; |
| 798 for (SourceSpan use in superUses) { | 806 for (SourceSpan use in superUses) { |
| 799 infos.add( | 807 infos.add( |
| 800 reporter.createMessage(use, MessageKind.ILLEGAL_MIXIN_SUPER_USE)); | 808 reporter.createMessage(use, MessageKind.ILLEGAL_MIXIN_SUPER_USE)); |
| 801 } | 809 } |
| 802 reporter.reportError(error, infos); | 810 reporter.reportError(error, infos); |
| 803 } | 811 } |
| 804 | 812 |
| 805 void checkClassMembers(ClassElement cls) { | 813 void checkClassMembers(ClassElement cls) { |
| 806 assert(invariant(cls, cls.isDeclaration)); | 814 assert(cls.isDeclaration, failedAt(cls)); |
| 807 if (cls.isObject) return; | 815 if (cls.isObject) return; |
| 808 // TODO(johnniwinther): Should this be done on the implementation element as | 816 // TODO(johnniwinther): Should this be done on the implementation element as |
| 809 // well? | 817 // well? |
| 810 List<Element> constConstructors = <Element>[]; | 818 List<Element> constConstructors = <Element>[]; |
| 811 List<Element> nonFinalInstanceFields = <Element>[]; | 819 List<Element> nonFinalInstanceFields = <Element>[]; |
| 812 cls.forEachMember((holder, dynamic member) { | 820 cls.forEachMember((holder, dynamic member) { |
| 813 reporter.withCurrentElement(member, () { | 821 reporter.withCurrentElement(member, () { |
| 814 // Perform various checks as side effect of "computing" the type. | 822 // Perform various checks as side effect of "computing" the type. |
| 815 member.computeType(resolution); | 823 member.computeType(resolution); |
| 816 | 824 |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1072 annotation.resolutionState = STATE_STARTED; | 1080 annotation.resolutionState = STATE_STARTED; |
| 1073 | 1081 |
| 1074 Node node = annotation.parseNode(parsingContext); | 1082 Node node = annotation.parseNode(parsingContext); |
| 1075 Element annotatedElement = annotation.annotatedElement; | 1083 Element annotatedElement = annotation.annotatedElement; |
| 1076 AnalyzableElement context = annotatedElement.analyzableElement; | 1084 AnalyzableElement context = annotatedElement.analyzableElement; |
| 1077 ClassElement classElement = annotatedElement.enclosingClass; | 1085 ClassElement classElement = annotatedElement.enclosingClass; |
| 1078 if (classElement != null) { | 1086 if (classElement != null) { |
| 1079 // The annotation is resolved in the scope of [classElement]. | 1087 // The annotation is resolved in the scope of [classElement]. |
| 1080 classElement.ensureResolved(resolution); | 1088 classElement.ensureResolved(resolution); |
| 1081 } | 1089 } |
| 1082 assert(invariant(node, context != null, | 1090 assert( |
| 1083 message: "No context found for metadata annotation " | 1091 context != null, |
| 1092 failedAt( |
| 1093 node, |
| 1094 "No context found for metadata annotation " |
| 1084 "on $annotatedElement.")); | 1095 "on $annotatedElement.")); |
| 1085 ResolverVisitor visitor = | 1096 ResolverVisitor visitor = |
| 1086 visitorFor(context, useEnclosingScope: true); | 1097 visitorFor(context, useEnclosingScope: true); |
| 1087 ResolutionRegistry registry = visitor.registry; | 1098 ResolutionRegistry registry = visitor.registry; |
| 1088 node.accept(visitor); | 1099 node.accept(visitor); |
| 1089 // TODO(johnniwinther): Avoid passing the [TreeElements] to | 1100 // TODO(johnniwinther): Avoid passing the [TreeElements] to |
| 1090 // [compileMetadata]. | 1101 // [compileMetadata]. |
| 1091 ConstantExpression constant = constantCompiler.compileMetadata( | 1102 ConstantExpression constant = constantCompiler.compileMetadata( |
| 1092 annotation, node, registry.mapping); | 1103 annotation, node, registry.mapping); |
| 1093 switch (constant.kind) { | 1104 switch (constant.kind) { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1141 } | 1152 } |
| 1142 return element._treeElements; | 1153 return element._treeElements; |
| 1143 } | 1154 } |
| 1144 | 1155 |
| 1145 abstract class AnalyzableElementX implements AnalyzableElement { | 1156 abstract class AnalyzableElementX implements AnalyzableElement { |
| 1146 TreeElements _treeElements; | 1157 TreeElements _treeElements; |
| 1147 | 1158 |
| 1148 bool get hasTreeElements => _treeElements != null; | 1159 bool get hasTreeElements => _treeElements != null; |
| 1149 | 1160 |
| 1150 TreeElements get treeElements { | 1161 TreeElements get treeElements { |
| 1151 assert(invariant(this, _treeElements != null, | 1162 assert(_treeElements != null, |
| 1152 message: "TreeElements have not been computed for $this.")); | 1163 failedAt(this, "TreeElements have not been computed for $this.")); |
| 1153 return _treeElements; | 1164 return _treeElements; |
| 1154 } | 1165 } |
| 1155 | 1166 |
| 1156 void reuseElement() { | 1167 void reuseElement() { |
| 1157 _treeElements = null; | 1168 _treeElements = null; |
| 1158 } | 1169 } |
| 1159 } | 1170 } |
| OLD | NEW |