| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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 engine.resolver; | 5 library engine.resolver; |
| 6 | 6 |
| 7 import "dart:math" as math; | 7 import "dart:math" as math; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 | 9 |
| 10 import 'package:analyzer/src/generated/utilities_collection.dart'; | 10 import 'package:analyzer/src/generated/utilities_collection.dart'; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 * callback indicates that [computation] may be used to compute implicit | 32 * callback indicates that [computation] may be used to compute implicit |
| 33 * constructors for [classElement], but that the computation may not be invoked | 33 * constructors for [classElement], but that the computation may not be invoked |
| 34 * until after implicit constructors have been built for [superclassElement]. | 34 * until after implicit constructors have been built for [superclassElement]. |
| 35 */ | 35 */ |
| 36 typedef void ImplicitConstructorBuilderCallback(ClassElement classElement, | 36 typedef void ImplicitConstructorBuilderCallback(ClassElement classElement, |
| 37 ClassElement superclassElement, void computation()); | 37 ClassElement superclassElement, void computation()); |
| 38 | 38 |
| 39 typedef void VoidFunction(); | 39 typedef void VoidFunction(); |
| 40 | 40 |
| 41 /** | 41 /** |
| 42 * Instances of the class `AngularCompilationUnitBuilder` build an Angular speci
fic element | |
| 43 * model for a single compilation unit. | |
| 44 */ | |
| 45 class AngularCompilationUnitBuilder { | |
| 46 static String _NG_COMPONENT = "Component"; | |
| 47 | |
| 48 static String _NG_CONTROLLER = "Controller"; | |
| 49 | |
| 50 static String _NG_DECORATOR = "Decorator"; | |
| 51 | |
| 52 static String _NG_FORMATTER = "Formatter"; | |
| 53 | |
| 54 static String _NAME = "name"; | |
| 55 | |
| 56 static String _SELECTOR = "selector"; | |
| 57 | |
| 58 static String _PUBLISH_AS = "publishAs"; | |
| 59 | |
| 60 static String _TEMPLATE_URL = "templateUrl"; | |
| 61 | |
| 62 static String _CSS_URL = "cssUrl"; | |
| 63 | |
| 64 static String _NG_ATTR = "NgAttr"; | |
| 65 | |
| 66 static String _NG_CALLBACK = "NgCallback"; | |
| 67 | |
| 68 static String _NG_ONE_WAY = "NgOneWay"; | |
| 69 | |
| 70 static String _NG_ONE_WAY_ONE_TIME = "NgOneWayOneTime"; | |
| 71 | |
| 72 static String _NG_TWO_WAY = "NgTwoWay"; | |
| 73 | |
| 74 /** | |
| 75 * The listener to which errors will be reported. | |
| 76 */ | |
| 77 final AnalysisErrorListener _errorListener; | |
| 78 | |
| 79 /** | |
| 80 * The source containing the unit that will be analyzed. | |
| 81 */ | |
| 82 final Source _source; | |
| 83 | |
| 84 /** | |
| 85 * The compilation unit with built Dart element models. | |
| 86 */ | |
| 87 final CompilationUnit _unit; | |
| 88 | |
| 89 /** | |
| 90 * The [ClassDeclaration] that is currently being analyzed. | |
| 91 */ | |
| 92 ClassDeclaration _classDeclaration; | |
| 93 | |
| 94 /** | |
| 95 * The [ClassElementImpl] that is currently being analyzed. | |
| 96 */ | |
| 97 ClassElementImpl _classElement; | |
| 98 | |
| 99 /** | |
| 100 * The [Annotation] that is currently being analyzed. | |
| 101 */ | |
| 102 Annotation _annotation; | |
| 103 | |
| 104 /** | |
| 105 * Initialize a newly created compilation unit element builder. | |
| 106 * | |
| 107 * @param errorListener the listener to which errors will be reported. | |
| 108 * @param source the source containing the unit that will be analyzed | |
| 109 * @param unit the compilation unit with built Dart element models | |
| 110 */ | |
| 111 AngularCompilationUnitBuilder(this._errorListener, this._source, this._unit); | |
| 112 | |
| 113 /** | |
| 114 * Builds Angular specific element models and adds them to the existing Dart e
lements. | |
| 115 */ | |
| 116 void build() { | |
| 117 _parseViews(); | |
| 118 // process classes | |
| 119 for (CompilationUnitMember unitMember in _unit.declarations) { | |
| 120 if (unitMember is ClassDeclaration) { | |
| 121 this._classDeclaration = unitMember; | |
| 122 this._classElement = _classDeclaration.element as ClassElementImpl; | |
| 123 // process annotations | |
| 124 NodeList<Annotation> annotations = _classDeclaration.metadata; | |
| 125 for (Annotation annotation in annotations) { | |
| 126 // verify annotation | |
| 127 if (annotation.arguments == null) { | |
| 128 continue; | |
| 129 } | |
| 130 this._annotation = annotation; | |
| 131 // @Formatter | |
| 132 if (_isAngularAnnotation(annotation, _NG_FORMATTER)) { | |
| 133 _parseFormatter(); | |
| 134 continue; | |
| 135 } | |
| 136 // @Component | |
| 137 if (_isAngularAnnotation(annotation, _NG_COMPONENT)) { | |
| 138 _parseComponent(); | |
| 139 continue; | |
| 140 } | |
| 141 // @Controller | |
| 142 if (_isAngularAnnotation(annotation, _NG_CONTROLLER)) { | |
| 143 _parseController(); | |
| 144 continue; | |
| 145 } | |
| 146 // @Decorator | |
| 147 if (_isAngularAnnotation(annotation, _NG_DECORATOR)) { | |
| 148 _parseDecorator(); | |
| 149 continue; | |
| 150 } | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 /** | |
| 157 * @return the argument [Expression] with given name form [annotation], may be | |
| 158 * `null` if not found. | |
| 159 */ | |
| 160 Expression _getArgument(String name) { | |
| 161 List<Expression> arguments = _annotation.arguments.arguments; | |
| 162 for (Expression argument in arguments) { | |
| 163 if (argument is NamedExpression) { | |
| 164 NamedExpression namedExpression = argument; | |
| 165 String argumentName = namedExpression.name.label.name; | |
| 166 if (name == argumentName) { | |
| 167 return namedExpression.expression; | |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 return null; | |
| 172 } | |
| 173 | |
| 174 /** | |
| 175 * @return the [String] value of the named argument. | |
| 176 */ | |
| 177 String _getStringArgument(String name) => _getStringLiteral(name).value; | |
| 178 | |
| 179 /** | |
| 180 * @return the offset of the value of the named argument. | |
| 181 */ | |
| 182 int _getStringArgumentOffset(String name) { | |
| 183 Expression argument = _getArgument(name); | |
| 184 return (argument as SimpleStringLiteral).contentsOffset; | |
| 185 } | |
| 186 | |
| 187 /** | |
| 188 * @return the [SimpleStringLiteral] of the named argument. | |
| 189 */ | |
| 190 SimpleStringLiteral _getStringLiteral(String name) { | |
| 191 Expression argument = _getArgument(name); | |
| 192 return argument as SimpleStringLiteral; | |
| 193 } | |
| 194 | |
| 195 /** | |
| 196 * Checks if [namedArguments] has string value for the argument with the given
name. | |
| 197 */ | |
| 198 bool _hasStringArgument(String name) { | |
| 199 Expression argument = _getArgument(name); | |
| 200 return argument is SimpleStringLiteral; | |
| 201 } | |
| 202 | |
| 203 /** | |
| 204 * Checks if given [Annotation] is an annotation with required name. | |
| 205 */ | |
| 206 bool _isAngularAnnotation(Annotation annotation, String name) { | |
| 207 Element element = annotation.element; | |
| 208 if (element is ConstructorElement) { | |
| 209 ConstructorElement constructorElement = element; | |
| 210 if (constructorElement.returnType.displayName != name) { | |
| 211 return false; | |
| 212 } | |
| 213 return _isAngularLibraryElement(constructorElement); | |
| 214 } | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * Checks if the given [Element] is a part of the Angular library. | |
| 220 */ | |
| 221 bool _isAngularLibraryElement(Element element) { | |
| 222 LibraryElement library = element.library; | |
| 223 return library != null && | |
| 224 library.name != null && | |
| 225 library.name.startsWith("angular"); | |
| 226 } | |
| 227 | |
| 228 void _parseComponent() { | |
| 229 bool isValid = true; | |
| 230 // publishAs | |
| 231 String name = null; | |
| 232 int nameOffset = -1; | |
| 233 if (_hasStringArgument(_PUBLISH_AS)) { | |
| 234 name = _getStringArgument(_PUBLISH_AS); | |
| 235 nameOffset = _getStringArgumentOffset(_PUBLISH_AS); | |
| 236 } | |
| 237 // selector | |
| 238 AngularSelectorElement selector = null; | |
| 239 if (!_hasStringArgument(_SELECTOR)) { | |
| 240 _reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); | |
| 241 isValid = false; | |
| 242 } else { | |
| 243 SimpleStringLiteral selectorLiteral = _getStringLiteral(_SELECTOR); | |
| 244 selector = _parseSelectorFromString(selectorLiteral); | |
| 245 if (selector == null) { | |
| 246 _reportErrorForArgument( | |
| 247 _SELECTOR, | |
| 248 AngularCode.CANNOT_PARSE_SELECTOR, | |
| 249 [selectorLiteral]); | |
| 250 isValid = false; | |
| 251 } | |
| 252 } | |
| 253 // templateUrl | |
| 254 String templateUri = null; | |
| 255 int templateUriOffset = -1; | |
| 256 if (_hasStringArgument(_TEMPLATE_URL)) { | |
| 257 templateUri = _getStringArgument(_TEMPLATE_URL); | |
| 258 templateUriOffset = _getStringArgumentOffset(_TEMPLATE_URL); | |
| 259 } | |
| 260 // cssUrl | |
| 261 String styleUri = null; | |
| 262 int styleUriOffset = -1; | |
| 263 if (_hasStringArgument(_CSS_URL)) { | |
| 264 styleUri = _getStringArgument(_CSS_URL); | |
| 265 styleUriOffset = _getStringArgumentOffset(_CSS_URL); | |
| 266 } | |
| 267 // create | |
| 268 if (isValid) { | |
| 269 AngularComponentElementImpl element = | |
| 270 new AngularComponentElementImpl(name, nameOffset, _annotation.offset); | |
| 271 element.selector = selector; | |
| 272 element.templateUri = templateUri; | |
| 273 element.templateUriOffset = templateUriOffset; | |
| 274 element.styleUri = styleUri; | |
| 275 element.styleUriOffset = styleUriOffset; | |
| 276 element.properties = _parseComponentProperties(); | |
| 277 element.scopeProperties = _parseScopeProperties(); | |
| 278 _classElement.addToolkitObjects(element); | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 /** | |
| 283 * Parses [AngularPropertyElement]s from [annotation] and [classDeclaration]. | |
| 284 */ | |
| 285 List<AngularPropertyElement> _parseComponentProperties() { | |
| 286 List<AngularPropertyElement> properties = <AngularPropertyElement>[]; | |
| 287 _parseComponentProperties_fromMap(properties); | |
| 288 _parseComponentProperties_fromFields(properties); | |
| 289 return properties; | |
| 290 } | |
| 291 | |
| 292 /** | |
| 293 * Parses [AngularPropertyElement]s from [annotation]. | |
| 294 */ | |
| 295 void | |
| 296 _parseComponentProperties_fromFields(List<AngularPropertyElement> properti
es) { | |
| 297 NodeList<ClassMember> members = _classDeclaration.members; | |
| 298 for (ClassMember member in members) { | |
| 299 if (member is FieldDeclaration) { | |
| 300 FieldDeclaration fieldDeclaration = member; | |
| 301 for (Annotation annotation in fieldDeclaration.metadata) { | |
| 302 // prepare property kind (if property annotation at all) | |
| 303 AngularPropertyKind kind = null; | |
| 304 if (_isAngularAnnotation(annotation, _NG_ATTR)) { | |
| 305 kind = AngularPropertyKind.ATTR; | |
| 306 } else if (_isAngularAnnotation(annotation, _NG_CALLBACK)) { | |
| 307 kind = AngularPropertyKind.CALLBACK; | |
| 308 } else if (_isAngularAnnotation(annotation, _NG_ONE_WAY)) { | |
| 309 kind = AngularPropertyKind.ONE_WAY; | |
| 310 } else if (_isAngularAnnotation(annotation, _NG_ONE_WAY_ONE_TIME)) { | |
| 311 kind = AngularPropertyKind.ONE_WAY_ONE_TIME; | |
| 312 } else if (_isAngularAnnotation(annotation, _NG_TWO_WAY)) { | |
| 313 kind = AngularPropertyKind.TWO_WAY; | |
| 314 } | |
| 315 // add property | |
| 316 if (kind != null) { | |
| 317 SimpleStringLiteral nameLiteral = | |
| 318 _getOnlySimpleStringLiteralArgument(annotation); | |
| 319 FieldElement field = _getOnlyFieldElement(fieldDeclaration); | |
| 320 if (nameLiteral != null && field != null) { | |
| 321 AngularPropertyElementImpl property = | |
| 322 new AngularPropertyElementImpl(nameLiteral.value, nameLiteral.
contentsOffset); | |
| 323 property.field = field; | |
| 324 property.propertyKind = kind; | |
| 325 properties.add(property); | |
| 326 } | |
| 327 } | |
| 328 } | |
| 329 } | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * Parses [AngularPropertyElement]s from [annotation]. | |
| 335 */ | |
| 336 void | |
| 337 _parseComponentProperties_fromMap(List<AngularPropertyElement> properties)
{ | |
| 338 Expression mapExpression = _getArgument("map"); | |
| 339 // may be not properties | |
| 340 if (mapExpression == null) { | |
| 341 return; | |
| 342 } | |
| 343 // prepare map literal | |
| 344 if (mapExpression is! MapLiteral) { | |
| 345 _reportErrorForNode(AngularCode.INVALID_PROPERTY_MAP, mapExpression); | |
| 346 return; | |
| 347 } | |
| 348 MapLiteral mapLiteral = mapExpression as MapLiteral; | |
| 349 // analyze map entries | |
| 350 for (MapLiteralEntry entry in mapLiteral.entries) { | |
| 351 // prepare property name | |
| 352 Expression nameExpression = entry.key; | |
| 353 if (nameExpression is! SimpleStringLiteral) { | |
| 354 _reportErrorForNode(AngularCode.INVALID_PROPERTY_NAME, nameExpression); | |
| 355 continue; | |
| 356 } | |
| 357 SimpleStringLiteral nameLiteral = nameExpression as SimpleStringLiteral; | |
| 358 String name = nameLiteral.value; | |
| 359 int nameOffset = nameLiteral.contentsOffset; | |
| 360 // prepare field specification | |
| 361 Expression specExpression = entry.value; | |
| 362 if (specExpression is! SimpleStringLiteral) { | |
| 363 _reportErrorForNode(AngularCode.INVALID_PROPERTY_SPEC, specExpression); | |
| 364 continue; | |
| 365 } | |
| 366 SimpleStringLiteral specLiteral = specExpression as SimpleStringLiteral; | |
| 367 String spec = specLiteral.value; | |
| 368 // parse binding kind and field name | |
| 369 AngularPropertyKind kind; | |
| 370 int fieldNameOffset; | |
| 371 if (StringUtilities.startsWithChar(spec, 0x40)) { | |
| 372 kind = AngularPropertyKind.ATTR; | |
| 373 fieldNameOffset = 1; | |
| 374 } else if (StringUtilities.startsWithChar(spec, 0x26)) { | |
| 375 kind = AngularPropertyKind.CALLBACK; | |
| 376 fieldNameOffset = 1; | |
| 377 } else if (StringUtilities.startsWith3(spec, 0, 0x3D, 0x3E, 0x21)) { | |
| 378 kind = AngularPropertyKind.ONE_WAY_ONE_TIME; | |
| 379 fieldNameOffset = 3; | |
| 380 } else if (StringUtilities.startsWith2(spec, 0, 0x3D, 0x3E)) { | |
| 381 kind = AngularPropertyKind.ONE_WAY; | |
| 382 fieldNameOffset = 2; | |
| 383 } else if (StringUtilities.startsWith3(spec, 0, 0x3C, 0x3D, 0x3E)) { | |
| 384 kind = AngularPropertyKind.TWO_WAY; | |
| 385 fieldNameOffset = 3; | |
| 386 } else { | |
| 387 _reportErrorForNode( | |
| 388 AngularCode.INVALID_PROPERTY_KIND, | |
| 389 specLiteral, | |
| 390 [spec]); | |
| 391 continue; | |
| 392 } | |
| 393 String fieldName = spec.substring(fieldNameOffset); | |
| 394 fieldNameOffset += specLiteral.contentsOffset; | |
| 395 // prepare field | |
| 396 PropertyAccessorElement setter = | |
| 397 _classElement.type.lookUpSetter(fieldName, _classElement.library); | |
| 398 if (setter == null) { | |
| 399 _reportErrorForOffset( | |
| 400 AngularCode.INVALID_PROPERTY_FIELD, | |
| 401 fieldNameOffset, | |
| 402 fieldName.length, | |
| 403 [fieldName]); | |
| 404 continue; | |
| 405 } | |
| 406 FieldElement field = setter.variable as FieldElement; | |
| 407 // add property | |
| 408 AngularPropertyElementImpl property = | |
| 409 new AngularPropertyElementImpl(name, nameOffset); | |
| 410 property.field = field; | |
| 411 property.propertyKind = kind; | |
| 412 property.fieldNameOffset = fieldNameOffset; | |
| 413 properties.add(property); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 void _parseController() { | |
| 418 bool isValid = true; | |
| 419 // publishAs | |
| 420 if (!_hasStringArgument(_PUBLISH_AS)) { | |
| 421 _reportErrorForAnnotation(AngularCode.MISSING_PUBLISH_AS); | |
| 422 isValid = false; | |
| 423 } | |
| 424 // selector | |
| 425 AngularSelectorElement selector = null; | |
| 426 if (!_hasStringArgument(_SELECTOR)) { | |
| 427 _reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); | |
| 428 isValid = false; | |
| 429 } else { | |
| 430 SimpleStringLiteral selectorLiteral = _getStringLiteral(_SELECTOR); | |
| 431 selector = _parseSelectorFromString(selectorLiteral); | |
| 432 if (selector == null) { | |
| 433 _reportErrorForArgument( | |
| 434 _SELECTOR, | |
| 435 AngularCode.CANNOT_PARSE_SELECTOR, | |
| 436 [selectorLiteral]); | |
| 437 isValid = false; | |
| 438 } | |
| 439 } | |
| 440 // create | |
| 441 if (isValid) { | |
| 442 String name = _getStringArgument(_PUBLISH_AS); | |
| 443 int nameOffset = _getStringArgumentOffset(_PUBLISH_AS); | |
| 444 AngularControllerElementImpl element = | |
| 445 new AngularControllerElementImpl(name, nameOffset); | |
| 446 element.selector = selector; | |
| 447 _classElement.addToolkitObjects(element); | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 void _parseDecorator() { | |
| 452 bool isValid = true; | |
| 453 // selector | |
| 454 AngularSelectorElement selector = null; | |
| 455 if (!_hasStringArgument(_SELECTOR)) { | |
| 456 _reportErrorForAnnotation(AngularCode.MISSING_SELECTOR); | |
| 457 isValid = false; | |
| 458 } else { | |
| 459 SimpleStringLiteral selectorLiteral = _getStringLiteral(_SELECTOR); | |
| 460 selector = _parseSelectorFromString(selectorLiteral); | |
| 461 if (selector == null) { | |
| 462 _reportErrorForArgument( | |
| 463 _SELECTOR, | |
| 464 AngularCode.CANNOT_PARSE_SELECTOR, | |
| 465 [selectorLiteral]); | |
| 466 isValid = false; | |
| 467 } | |
| 468 } | |
| 469 // create | |
| 470 if (isValid) { | |
| 471 int offset = _annotation.offset; | |
| 472 AngularDecoratorElementImpl element = | |
| 473 new AngularDecoratorElementImpl(offset); | |
| 474 element.selector = selector; | |
| 475 element.properties = _parseComponentProperties(); | |
| 476 _classElement.addToolkitObjects(element); | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 void _parseFormatter() { | |
| 481 bool isValid = true; | |
| 482 // name | |
| 483 if (!_hasStringArgument(_NAME)) { | |
| 484 _reportErrorForAnnotation(AngularCode.MISSING_NAME); | |
| 485 isValid = false; | |
| 486 } | |
| 487 // create | |
| 488 if (isValid) { | |
| 489 String name = _getStringArgument(_NAME); | |
| 490 int nameOffset = _getStringArgumentOffset(_NAME); | |
| 491 _classElement.addToolkitObjects( | |
| 492 new AngularFormatterElementImpl(name, nameOffset)); | |
| 493 } | |
| 494 } | |
| 495 | |
| 496 List<AngularScopePropertyElement> _parseScopeProperties() { | |
| 497 List<AngularScopePropertyElement> properties = | |
| 498 <AngularScopePropertyElement>[ | |
| 499 ]; | |
| 500 _classDeclaration.accept( | |
| 501 new _AngularCompilationUnitBuilder_parseScopeProperties(properties)); | |
| 502 return properties; | |
| 503 } | |
| 504 | |
| 505 /** | |
| 506 * Create [AngularViewElement] for each valid <code>view('template.html')</cod
e> invocation, | |
| 507 * where <code>view</code> is <code>ViewFactory</code>. | |
| 508 */ | |
| 509 void _parseViews() { | |
| 510 List<AngularViewElement> views = <AngularViewElement>[]; | |
| 511 _unit.accept( | |
| 512 new RecursiveAstVisitor_AngularCompilationUnitBuilder_parseViews(views))
; | |
| 513 if (!views.isEmpty) { | |
| 514 List<AngularViewElement> viewArray = views; | |
| 515 (_unit.element as CompilationUnitElementImpl).angularViews = viewArray; | |
| 516 } | |
| 517 } | |
| 518 | |
| 519 void _reportErrorForAnnotation(ErrorCode errorCode, | |
| 520 [List<Object> arguments]) { | |
| 521 _reportErrorForNode(errorCode, _annotation, arguments); | |
| 522 } | |
| 523 | |
| 524 void _reportErrorForArgument(String argumentName, ErrorCode errorCode, | |
| 525 [List<Object> arguments]) { | |
| 526 Expression argument = _getArgument(argumentName); | |
| 527 _reportErrorForNode(errorCode, argument, arguments); | |
| 528 } | |
| 529 | |
| 530 void _reportErrorForNode(ErrorCode errorCode, AstNode node, | |
| 531 [List<Object> arguments]) { | |
| 532 int offset = node.offset; | |
| 533 int length = node.length; | |
| 534 _reportErrorForOffset(errorCode, offset, length, arguments); | |
| 535 } | |
| 536 | |
| 537 void _reportErrorForOffset(ErrorCode errorCode, int offset, int length, | |
| 538 [List<Object> arguments]) { | |
| 539 _errorListener.onError( | |
| 540 new AnalysisError.con2(_source, offset, length, errorCode, arguments)); | |
| 541 } | |
| 542 | |
| 543 static Element getElement(AstNode node, int offset) { | |
| 544 // maybe node is not SimpleStringLiteral | |
| 545 if (node is! SimpleStringLiteral) { | |
| 546 return null; | |
| 547 } | |
| 548 SimpleStringLiteral literal = node as SimpleStringLiteral; | |
| 549 // maybe has AngularElement | |
| 550 { | |
| 551 Element element = literal.toolkitElement; | |
| 552 if (element is AngularElement) { | |
| 553 return element; | |
| 554 } | |
| 555 } | |
| 556 // prepare enclosing ClassDeclaration | |
| 557 ClassDeclaration classDeclaration = | |
| 558 node.getAncestor((node) => node is ClassDeclaration); | |
| 559 if (classDeclaration == null) { | |
| 560 return null; | |
| 561 } | |
| 562 // prepare ClassElement | |
| 563 ClassElement classElement = classDeclaration.element; | |
| 564 if (classElement == null) { | |
| 565 return null; | |
| 566 } | |
| 567 // check toolkit objects | |
| 568 for (ToolkitObjectElement toolkitObject in classElement.toolkitObjects) { | |
| 569 List<AngularPropertyElement> properties = | |
| 570 AngularPropertyElement.EMPTY_ARRAY; | |
| 571 // maybe name | |
| 572 if (toolkitObject is AngularElement) { | |
| 573 if (_isNameCoveredByLiteral(toolkitObject, node)) { | |
| 574 return toolkitObject; | |
| 575 } | |
| 576 } | |
| 577 // try selector | |
| 578 if (toolkitObject is AngularHasSelectorElement) { | |
| 579 AngularHasSelectorElement hasSelector = toolkitObject; | |
| 580 AngularSelectorElement selector = hasSelector.selector; | |
| 581 if (_isNameCoveredByLiteral(selector, node)) { | |
| 582 return selector; | |
| 583 } | |
| 584 } | |
| 585 // try properties of AngularComponentElement | |
| 586 if (toolkitObject is AngularComponentElement) { | |
| 587 AngularComponentElement component = toolkitObject; | |
| 588 properties = component.properties; | |
| 589 } | |
| 590 // try properties of AngularDirectiveElement | |
| 591 if (toolkitObject is AngularDecoratorElement) { | |
| 592 AngularDecoratorElement directive = toolkitObject; | |
| 593 properties = directive.properties; | |
| 594 } | |
| 595 // check properties | |
| 596 for (AngularPropertyElement property in properties) { | |
| 597 // property name (use complete node range) | |
| 598 if (_isNameCoveredByLiteral(property, node)) { | |
| 599 return property; | |
| 600 } | |
| 601 // field name (use complete node range, including @, => and <=>) | |
| 602 FieldElement field = property.field; | |
| 603 if (field != null) { | |
| 604 int fieldOffset = property.fieldNameOffset; | |
| 605 int fieldEnd = fieldOffset + field.name.length; | |
| 606 if (node.offset <= fieldOffset && fieldEnd < node.end) { | |
| 607 return field; | |
| 608 } | |
| 609 } | |
| 610 } | |
| 611 } | |
| 612 // no Element | |
| 613 return null; | |
| 614 } | |
| 615 | |
| 616 /** | |
| 617 * Parses given selector text and returns [AngularSelectorElement]. May be `nu
ll` if | |
| 618 * cannot parse. | |
| 619 */ | |
| 620 static AngularSelectorElement parseSelector(int offset, String text) { | |
| 621 // [attribute] | |
| 622 if (StringUtilities.startsWithChar(text, 0x5B) && | |
| 623 StringUtilities.endsWithChar(text, 0x5D)) { | |
| 624 int nameOffset = offset + 1; | |
| 625 String attributeName = text.substring(1, text.length - 1); | |
| 626 // TODO(scheglov) report warning if there are spaces between | |
| 627 // [ and identifier | |
| 628 return new HasAttributeSelectorElementImpl(attributeName, nameOffset); | |
| 629 } | |
| 630 // .class | |
| 631 if (StringUtilities.startsWithChar(text, 0x2E)) { | |
| 632 int nameOffset = offset + 1; | |
| 633 String className = text.substring(1, text.length); | |
| 634 return new AngularHasClassSelectorElementImpl(className, nameOffset); | |
| 635 } | |
| 636 // tag[attribute] | |
| 637 if (StringUtilities.endsWithChar(text, 0x5D)) { | |
| 638 int index = StringUtilities.indexOf1(text, 0, 0x5B); | |
| 639 if (index != -1) { | |
| 640 String tagName = text.substring(0, index); | |
| 641 String attributeName = text.substring(index + 1, text.length - 1); | |
| 642 if (StringUtilities.isTagName(tagName)) { | |
| 643 return new IsTagHasAttributeSelectorElementImpl( | |
| 644 tagName, | |
| 645 attributeName); | |
| 646 } | |
| 647 } | |
| 648 } | |
| 649 // tag | |
| 650 if (StringUtilities.isTagName(text)) { | |
| 651 return new AngularTagSelectorElementImpl(text, offset); | |
| 652 } | |
| 653 return null; | |
| 654 } | |
| 655 | |
| 656 /** | |
| 657 * Returns the [FieldElement] of the first field in the given [FieldDeclaratio
n]. | |
| 658 */ | |
| 659 static FieldElement _getOnlyFieldElement(FieldDeclaration fieldDeclaration) { | |
| 660 NodeList<VariableDeclaration> fields = fieldDeclaration.fields.variables; | |
| 661 return fields[0].element as FieldElement; | |
| 662 } | |
| 663 | |
| 664 /** | |
| 665 * If given [Annotation] has one argument and it is [SimpleStringLiteral], ret
urns it, | |
| 666 * otherwise returns `null`. | |
| 667 */ | |
| 668 static SimpleStringLiteral | |
| 669 _getOnlySimpleStringLiteralArgument(Annotation annotation) { | |
| 670 SimpleStringLiteral nameLiteral = null; | |
| 671 ArgumentList argsNode = annotation.arguments; | |
| 672 if (argsNode != null) { | |
| 673 NodeList<Expression> args = argsNode.arguments; | |
| 674 if (args.length == 1) { | |
| 675 Expression arg = args[0]; | |
| 676 if (arg is SimpleStringLiteral) { | |
| 677 nameLiteral = arg; | |
| 678 } | |
| 679 } | |
| 680 } | |
| 681 return nameLiteral; | |
| 682 } | |
| 683 | |
| 684 /** | |
| 685 * Checks if the name range of the given [Element] is completely covered by th
e given | |
| 686 * [SimpleStringLiteral]. | |
| 687 */ | |
| 688 static bool _isNameCoveredByLiteral(Element element, AstNode node) { | |
| 689 if (element != null) { | |
| 690 String name = element.name; | |
| 691 if (name != null) { | |
| 692 int nameOffset = element.nameOffset; | |
| 693 int nameEnd = nameOffset + name.length; | |
| 694 return node.offset <= nameOffset && nameEnd < node.end; | |
| 695 } | |
| 696 } | |
| 697 return false; | |
| 698 } | |
| 699 | |
| 700 /** | |
| 701 * Parses given [SimpleStringLiteral] using [parseSelector]. | |
| 702 */ | |
| 703 static AngularSelectorElement | |
| 704 _parseSelectorFromString(SimpleStringLiteral literal) { | |
| 705 int offset = literal.contentsOffset; | |
| 706 String text = literal.stringValue; | |
| 707 return parseSelector(offset, text); | |
| 708 } | |
| 709 } | |
| 710 | |
| 711 /** | |
| 712 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for | 42 * Instances of the class `BestPracticesVerifier` traverse an AST structure look
ing for |
| 713 * violations of Dart best practices. | 43 * violations of Dart best practices. |
| 714 */ | 44 */ |
| 715 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { | 45 class BestPracticesVerifier extends RecursiveAstVisitor<Object> { |
| 716 // static String _HASHCODE_GETTER_NAME = "hashCode"; | 46 // static String _HASHCODE_GETTER_NAME = "hashCode"; |
| 717 | 47 |
| 718 static String _NULL_TYPE_NAME = "Null"; | 48 static String _NULL_TYPE_NAME = "Null"; |
| 719 | 49 |
| 720 static String _TO_INT_METHOD_NAME = "toInt"; | 50 static String _TO_INT_METHOD_NAME = "toInt"; |
| 721 | 51 |
| (...skipping 8451 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9173 CompilationUnit ast = library.getAST(source); | 8503 CompilationUnit ast = library.getAST(source); |
| 9174 ast.accept( | 8504 ast.accept( |
| 9175 new VariableResolverVisitor.con1(library, source, _typeProvider)); | 8505 new VariableResolverVisitor.con1(library, source, _typeProvider)); |
| 9176 ResolverVisitor visitor = | 8506 ResolverVisitor visitor = |
| 9177 new ResolverVisitor.con1(library, source, _typeProvider); | 8507 new ResolverVisitor.con1(library, source, _typeProvider); |
| 9178 ast.accept(visitor); | 8508 ast.accept(visitor); |
| 9179 } | 8509 } |
| 9180 } finally { | 8510 } finally { |
| 9181 timeCounter.stop(); | 8511 timeCounter.stop(); |
| 9182 } | 8512 } |
| 9183 // Angular | |
| 9184 timeCounter = PerformanceStatistics.angular.start(); | |
| 9185 try { | |
| 9186 for (Source source in library.compilationUnitSources) { | |
| 9187 CompilationUnit ast = library.getAST(source); | |
| 9188 new AngularCompilationUnitBuilder(_errorListener, source, ast).build(); | |
| 9189 } | |
| 9190 } finally { | |
| 9191 timeCounter.stop(); | |
| 9192 } | |
| 9193 // Polymer | 8513 // Polymer |
| 9194 timeCounter = PerformanceStatistics.polymer.start(); | 8514 timeCounter = PerformanceStatistics.polymer.start(); |
| 9195 try { | 8515 try { |
| 9196 for (Source source in library.compilationUnitSources) { | 8516 for (Source source in library.compilationUnitSources) { |
| 9197 CompilationUnit ast = library.getAST(source); | 8517 CompilationUnit ast = library.getAST(source); |
| 9198 new PolymerCompilationUnitBuilder(ast).build(); | 8518 new PolymerCompilationUnitBuilder(ast).build(); |
| 9199 } | 8519 } |
| 9200 } finally { | 8520 } finally { |
| 9201 timeCounter.stop(); | 8521 timeCounter.stop(); |
| 9202 } | 8522 } |
| (...skipping 543 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 9746 CompilationUnit ast = unit.compilationUnit; | 9066 CompilationUnit ast = unit.compilationUnit; |
| 9747 ast.accept( | 9067 ast.accept( |
| 9748 new VariableResolverVisitor.con3(library, source, _typeProvider)); | 9068 new VariableResolverVisitor.con3(library, source, _typeProvider)); |
| 9749 ResolverVisitor visitor = | 9069 ResolverVisitor visitor = |
| 9750 new ResolverVisitor.con4(library, source, _typeProvider); | 9070 new ResolverVisitor.con4(library, source, _typeProvider); |
| 9751 ast.accept(visitor); | 9071 ast.accept(visitor); |
| 9752 } | 9072 } |
| 9753 } finally { | 9073 } finally { |
| 9754 timeCounter.stop(); | 9074 timeCounter.stop(); |
| 9755 } | 9075 } |
| 9756 // Angular | |
| 9757 timeCounter = PerformanceStatistics.angular.start(); | |
| 9758 try { | |
| 9759 for (ResolvableCompilationUnit unit in library.resolvableCompilationUnits) | |
| 9760 { | |
| 9761 Source source = unit.source; | |
| 9762 CompilationUnit ast = unit.compilationUnit; | |
| 9763 new AngularCompilationUnitBuilder(_errorListener, source, ast).build(); | |
| 9764 } | |
| 9765 } finally { | |
| 9766 timeCounter.stop(); | |
| 9767 } | |
| 9768 // Polymer | 9076 // Polymer |
| 9769 timeCounter = PerformanceStatistics.polymer.start(); | 9077 timeCounter = PerformanceStatistics.polymer.start(); |
| 9770 try { | 9078 try { |
| 9771 for (Source source in library.compilationUnitSources) { | 9079 for (Source source in library.compilationUnitSources) { |
| 9772 CompilationUnit ast = library.getAST(source); | 9080 CompilationUnit ast = library.getAST(source); |
| 9773 new PolymerCompilationUnitBuilder(ast).build(); | 9081 new PolymerCompilationUnitBuilder(ast).build(); |
| 9774 } | 9082 } |
| 9775 } finally { | 9083 } finally { |
| 9776 timeCounter.stop(); | 9084 timeCounter.stop(); |
| 9777 } | 9085 } |
| (...skipping 968 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 10746 // if (source != null) { | 10054 // if (source != null) { |
| 10747 // String fullName = source.fullName; | 10055 // String fullName = source.fullName; |
| 10748 // if (fullName != null) { | 10056 // if (fullName != null) { |
| 10749 // return fullName.replaceAll(r'\', '/'); | 10057 // return fullName.replaceAll(r'\', '/'); |
| 10750 // } | 10058 // } |
| 10751 // } | 10059 // } |
| 10752 // return null; | 10060 // return null; |
| 10753 // } | 10061 // } |
| 10754 } | 10062 } |
| 10755 | 10063 |
| 10756 class RecursiveAstVisitor_AngularCompilationUnitBuilder_parseViews extends | |
| 10757 RecursiveAstVisitor<Object> { | |
| 10758 List<AngularViewElement> views; | |
| 10759 | |
| 10760 RecursiveAstVisitor_AngularCompilationUnitBuilder_parseViews(this.views) | |
| 10761 : super(); | |
| 10762 | |
| 10763 @override | |
| 10764 Object visitMethodInvocation(MethodInvocation node) { | |
| 10765 _addView(node); | |
| 10766 return super.visitMethodInvocation(node); | |
| 10767 } | |
| 10768 | |
| 10769 void _addView(MethodInvocation node) { | |
| 10770 // only one argument | |
| 10771 List<Expression> arguments = node.argumentList.arguments; | |
| 10772 if (arguments.length != 1) { | |
| 10773 return; | |
| 10774 } | |
| 10775 // String literal | |
| 10776 Expression argument = arguments[0]; | |
| 10777 if (argument is! SimpleStringLiteral) { | |
| 10778 return; | |
| 10779 } | |
| 10780 SimpleStringLiteral literal = argument as SimpleStringLiteral; | |
| 10781 // just view('template') | |
| 10782 if (node.realTarget != null) { | |
| 10783 return; | |
| 10784 } | |
| 10785 // should be ViewFactory | |
| 10786 if (!_isViewFactory(node.methodName)) { | |
| 10787 return; | |
| 10788 } | |
| 10789 // add AngularViewElement | |
| 10790 String templateUri = literal.stringValue; | |
| 10791 int templateUriOffset = literal.contentsOffset; | |
| 10792 views.add(new AngularViewElementImpl(templateUri, templateUriOffset)); | |
| 10793 } | |
| 10794 | |
| 10795 bool _isViewFactory(Expression target) { | |
| 10796 if (target is SimpleIdentifier) { | |
| 10797 SimpleIdentifier identifier = target; | |
| 10798 Element element = identifier.staticElement; | |
| 10799 if (element is VariableElement) { | |
| 10800 VariableElement variable = element; | |
| 10801 DartType type = variable.type; | |
| 10802 if (type is InterfaceType) { | |
| 10803 InterfaceType interfaceType = type; | |
| 10804 return interfaceType.name == "ViewFactory"; | |
| 10805 } | |
| 10806 } | |
| 10807 } | |
| 10808 return false; | |
| 10809 } | |
| 10810 } | |
| 10811 | |
| 10812 /** | 10064 /** |
| 10813 * Kind of the redirecting constructor. | 10065 * Kind of the redirecting constructor. |
| 10814 */ | 10066 */ |
| 10815 class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> { | 10067 class RedirectingConstructorKind extends Enum<RedirectingConstructorKind> { |
| 10816 static const RedirectingConstructorKind CONST = | 10068 static const RedirectingConstructorKind CONST = |
| 10817 const RedirectingConstructorKind('CONST', 0); | 10069 const RedirectingConstructorKind('CONST', 0); |
| 10818 | 10070 |
| 10819 static const RedirectingConstructorKind NORMAL = | 10071 static const RedirectingConstructorKind NORMAL = |
| 10820 const RedirectingConstructorKind('NORMAL', 1); | 10072 const RedirectingConstructorKind('NORMAL', 1); |
| 10821 | 10073 |
| (...skipping 5009 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 15831 if (_enclosingFunction != null && | 15083 if (_enclosingFunction != null && |
| 15832 (element.enclosingElement != _enclosingFunction)) { | 15084 (element.enclosingElement != _enclosingFunction)) { |
| 15833 parameterImpl.markPotentiallyMutatedInClosure(); | 15085 parameterImpl.markPotentiallyMutatedInClosure(); |
| 15834 } | 15086 } |
| 15835 } | 15087 } |
| 15836 } | 15088 } |
| 15837 return null; | 15089 return null; |
| 15838 } | 15090 } |
| 15839 } | 15091 } |
| 15840 | 15092 |
| 15841 class _AngularCompilationUnitBuilder_parseScopeProperties extends | |
| 15842 RecursiveAstVisitor<Object> { | |
| 15843 List<AngularScopePropertyElement> properties; | |
| 15844 | |
| 15845 _AngularCompilationUnitBuilder_parseScopeProperties(this.properties) | |
| 15846 : super(); | |
| 15847 | |
| 15848 @override | |
| 15849 Object visitAssignmentExpression(AssignmentExpression node) { | |
| 15850 _addProperty(node); | |
| 15851 return super.visitAssignmentExpression(node); | |
| 15852 } | |
| 15853 | |
| 15854 void _addProperty(AssignmentExpression node) { | |
| 15855 // try to find "name" in scope[name] | |
| 15856 SimpleStringLiteral nameNode = _getNameNode(node.leftHandSide); | |
| 15857 if (nameNode == null) { | |
| 15858 return; | |
| 15859 } | |
| 15860 // prepare unique | |
| 15861 String name = nameNode.stringValue; | |
| 15862 if (_hasPropertyWithName(name)) { | |
| 15863 return; | |
| 15864 } | |
| 15865 // do add property | |
| 15866 int nameOffset = nameNode.contentsOffset; | |
| 15867 AngularScopePropertyElement property = new AngularScopePropertyElementImpl( | |
| 15868 name, | |
| 15869 nameOffset, | |
| 15870 node.rightHandSide.bestType); | |
| 15871 nameNode.toolkitElement = property; | |
| 15872 properties.add(property); | |
| 15873 } | |
| 15874 | |
| 15875 SimpleStringLiteral _getNameNode(Expression node) { | |
| 15876 if (node is IndexExpression) { | |
| 15877 IndexExpression indexExpression = node; | |
| 15878 Expression target = indexExpression.target; | |
| 15879 Expression index = indexExpression.index; | |
| 15880 if (index is SimpleStringLiteral && _isContext(target)) { | |
| 15881 return index; | |
| 15882 } | |
| 15883 } | |
| 15884 return null; | |
| 15885 } | |
| 15886 | |
| 15887 bool _hasPropertyWithName(String name) { | |
| 15888 for (AngularScopePropertyElement property in properties) { | |
| 15889 if (property.name == name) { | |
| 15890 return true; | |
| 15891 } | |
| 15892 } | |
| 15893 return false; | |
| 15894 } | |
| 15895 | |
| 15896 bool _isContext(Expression target) { | |
| 15897 if (target is PrefixedIdentifier) { | |
| 15898 PrefixedIdentifier prefixed = target; | |
| 15899 SimpleIdentifier prefix = prefixed.prefix; | |
| 15900 SimpleIdentifier identifier = prefixed.identifier; | |
| 15901 return (identifier.name == "context") && _isScope(prefix); | |
| 15902 } | |
| 15903 return false; | |
| 15904 } | |
| 15905 | |
| 15906 bool _isScope(Expression target) { | |
| 15907 if (target != null) { | |
| 15908 DartType type = target.bestType; | |
| 15909 if (type is InterfaceType) { | |
| 15910 InterfaceType interfaceType = type; | |
| 15911 return interfaceType.name == "Scope"; | |
| 15912 } | |
| 15913 } | |
| 15914 return false; | |
| 15915 } | |
| 15916 } | |
| 15917 | |
| 15918 class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor { | 15093 class _ConstantVerifier_validateInitializerExpression extends ConstantVisitor { |
| 15919 final ConstantVerifier verifier; | 15094 final ConstantVerifier verifier; |
| 15920 | 15095 |
| 15921 List<ParameterElement> parameterElements; | 15096 List<ParameterElement> parameterElements; |
| 15922 | 15097 |
| 15923 _ConstantVerifier_validateInitializerExpression(TypeProvider arg0, | 15098 _ConstantVerifier_validateInitializerExpression(TypeProvider arg0, |
| 15924 ErrorReporter arg1, this.verifier, this.parameterElements) | 15099 ErrorReporter arg1, this.verifier, this.parameterElements) |
| 15925 : super.con1(arg0, arg1); | 15100 : super.con1(arg0, arg1); |
| 15926 | 15101 |
| 15927 @override | 15102 @override |
| (...skipping 481 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 16409 * library. | 15584 * library. |
| 16410 */ | 15585 */ |
| 16411 final HashSet<String> members = new HashSet<String>(); | 15586 final HashSet<String> members = new HashSet<String>(); |
| 16412 | 15587 |
| 16413 /** | 15588 /** |
| 16414 * Names of resolved or unresolved class members that are read in the | 15589 * Names of resolved or unresolved class members that are read in the |
| 16415 * library. | 15590 * library. |
| 16416 */ | 15591 */ |
| 16417 final HashSet<String> readMembers = new HashSet<String>(); | 15592 final HashSet<String> readMembers = new HashSet<String>(); |
| 16418 } | 15593 } |
| OLD | NEW |