| 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 library kernel.analyzer.ast_from_analyzer; | |
| 5 | |
| 6 import '../ast.dart' as ast; | |
| 7 import '../frontend/accessors.dart'; | |
| 8 import '../frontend/super_initializers.dart'; | |
| 9 import '../log.dart'; | |
| 10 import '../type_algebra.dart'; | |
| 11 import '../transformations/flags.dart'; | |
| 12 import 'analyzer.dart'; | |
| 13 import 'loader.dart'; | |
| 14 import 'package:analyzer/analyzer.dart'; | |
| 15 import 'package:analyzer/dart/ast/standard_resolution_map.dart'; | |
| 16 import 'package:analyzer/src/generated/parser.dart'; | |
| 17 import 'package:analyzer/src/dart/element/member.dart'; | |
| 18 import 'package:analyzer/src/error/codes.dart'; | |
| 19 | |
| 20 /// Provides reference-level access to libraries, classes, and members. | |
| 21 /// | |
| 22 /// "Reference level" objects are incomplete nodes that have no children but | |
| 23 /// can be used for linking until the loader promotes the node to a higher | |
| 24 /// loading level. | |
| 25 /// | |
| 26 /// The [ReferenceScope] is the most restrictive scope in a hierarchy of scopes | |
| 27 /// that provide increasing amounts of contextual information. [TypeScope] is | |
| 28 /// used when type parameters might be in scope, and [MemberScope] is used when | |
| 29 /// building the body of a [ast.Member]. | |
| 30 class ReferenceScope { | |
| 31 final ReferenceLevelLoader loader; | |
| 32 | |
| 33 ReferenceScope(this.loader); | |
| 34 | |
| 35 bool get strongMode => loader.strongMode; | |
| 36 | |
| 37 ast.Library getLibraryReference(LibraryElement element) { | |
| 38 if (element == null) return null; | |
| 39 return loader.getLibraryReference(getBaseElement(element)); | |
| 40 } | |
| 41 | |
| 42 ast.Class getRootClassReference() { | |
| 43 return loader.getRootClassReference(); | |
| 44 } | |
| 45 | |
| 46 ast.Class getClassReference(ClassElement element) { | |
| 47 return loader.getClassReference(getBaseElement(element)); | |
| 48 } | |
| 49 | |
| 50 ast.Member getMemberReference(Element element) { | |
| 51 return loader.getMemberReference(getBaseElement(element)); | |
| 52 } | |
| 53 | |
| 54 static Element getBaseElement(Element element) { | |
| 55 while (element is Member) { | |
| 56 element = (element as Member).baseElement; | |
| 57 } | |
| 58 return element; | |
| 59 } | |
| 60 | |
| 61 static bool supportsConcreteGet(Element element) { | |
| 62 return (element is PropertyAccessorElement && | |
| 63 element.isGetter && | |
| 64 !element.isAbstract) || | |
| 65 element is FieldElement || | |
| 66 element is TopLevelVariableElement || | |
| 67 element is MethodElement && !element.isAbstract || | |
| 68 isTopLevelFunction(element); | |
| 69 } | |
| 70 | |
| 71 static bool supportsInterfaceGet(Element element) { | |
| 72 return (element is PropertyAccessorElement && element.isGetter) || | |
| 73 element is FieldElement || | |
| 74 element is MethodElement; | |
| 75 } | |
| 76 | |
| 77 static bool supportsConcreteSet(Element element) { | |
| 78 return (element is PropertyAccessorElement && | |
| 79 element.isSetter && | |
| 80 !element.isAbstract) || | |
| 81 element is FieldElement && !element.isFinal && !element.isConst || | |
| 82 element is TopLevelVariableElement && | |
| 83 !element.isFinal && | |
| 84 !element.isConst; | |
| 85 } | |
| 86 | |
| 87 static bool supportsInterfaceSet(Element element) { | |
| 88 return (element is PropertyAccessorElement && element.isSetter) || | |
| 89 element is FieldElement && !element.isFinal && !element.isConst; | |
| 90 } | |
| 91 | |
| 92 static bool supportsConcreteIndexGet(Element element) { | |
| 93 return element is MethodElement && | |
| 94 element.name == '[]' && | |
| 95 !element.isAbstract; | |
| 96 } | |
| 97 | |
| 98 static bool supportsInterfaceIndexGet(Element element) { | |
| 99 return element is MethodElement && element.name == '[]'; | |
| 100 } | |
| 101 | |
| 102 static bool supportsConcreteIndexSet(Element element) { | |
| 103 return element is MethodElement && | |
| 104 element.name == '[]=' && | |
| 105 !element.isAbstract; | |
| 106 } | |
| 107 | |
| 108 static bool supportsInterfaceIndexSet(Element element) { | |
| 109 return element is MethodElement && element.name == '[]='; | |
| 110 } | |
| 111 | |
| 112 static bool supportsConcreteMethodCall(Element element) { | |
| 113 // Note that local functions are not valid targets for method calls because | |
| 114 // they are not "methods" or even "procedures" in our AST. | |
| 115 return element is MethodElement && !element.isAbstract || | |
| 116 isTopLevelFunction(element) || | |
| 117 element is ConstructorElement && element.isFactory; | |
| 118 } | |
| 119 | |
| 120 static bool supportsInterfaceMethodCall(Element element) { | |
| 121 return element is MethodElement; | |
| 122 } | |
| 123 | |
| 124 static bool supportsConstructorCall(Element element) { | |
| 125 return element is ConstructorElement && !element.isFactory; | |
| 126 } | |
| 127 | |
| 128 ast.Member _resolveGet( | |
| 129 Element element, Element auxiliary, bool predicate(Element element)) { | |
| 130 element = desynthesizeGetter(element); | |
| 131 if (predicate(element)) return getMemberReference(element); | |
| 132 if (element is PropertyAccessorElement && element.isSetter) { | |
| 133 // The getter is sometimes stored as the 'corresponding getter' instead | |
| 134 // of the 'auxiliary' element. | |
| 135 auxiliary ??= element.correspondingGetter; | |
| 136 } | |
| 137 auxiliary = desynthesizeGetter(auxiliary); | |
| 138 if (predicate(auxiliary)) return getMemberReference(auxiliary); | |
| 139 return null; | |
| 140 } | |
| 141 | |
| 142 ast.Member resolveConcreteGet(Element element, Element auxiliary) { | |
| 143 return _resolveGet(element, auxiliary, supportsConcreteGet); | |
| 144 } | |
| 145 | |
| 146 ast.Member resolveInterfaceGet(Element element, Element auxiliary) { | |
| 147 if (!strongMode) return null; | |
| 148 return _resolveGet(element, auxiliary, supportsInterfaceGet); | |
| 149 } | |
| 150 | |
| 151 DartType getterTypeOfElement(Element element) { | |
| 152 if (element is VariableElement) { | |
| 153 return element.type; | |
| 154 } else if (element is PropertyAccessorElement && element.isGetter) { | |
| 155 return element.returnType; | |
| 156 } else { | |
| 157 return null; | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 /// Returns the interface target of a `call` dispatch to the given member. | |
| 162 /// | |
| 163 /// For example, if the member is a field of type C, the target will be the | |
| 164 /// `call` method of class C, if it has such a method. | |
| 165 /// | |
| 166 /// If the class C has a getter or field named `call`, this method returns | |
| 167 /// `null` - the static type system does support typed calls with indirection. | |
| 168 ast.Member resolveInterfaceFunctionCall(Element element) { | |
| 169 if (!strongMode || element == null) return null; | |
| 170 return resolveInterfaceFunctionCallOnType(getterTypeOfElement(element)); | |
| 171 } | |
| 172 | |
| 173 /// Returns the `call` method of [callee], if it has one, otherwise `null`. | |
| 174 ast.Member resolveInterfaceFunctionCallOnType(DartType callee) { | |
| 175 return callee is InterfaceType | |
| 176 ? resolveInterfaceMethod(callee.getMethod('call')) | |
| 177 : null; | |
| 178 } | |
| 179 | |
| 180 ast.Member _resolveSet( | |
| 181 Element element, Element auxiliary, bool predicate(Element element)) { | |
| 182 element = desynthesizeSetter(element); | |
| 183 if (predicate(element)) { | |
| 184 return getMemberReference(element); | |
| 185 } | |
| 186 if (element is PropertyAccessorElement && element.isSetter) { | |
| 187 // The setter is sometimes stored as the 'corresponding setter' instead | |
| 188 // of the 'auxiliary' element. | |
| 189 auxiliary ??= element.correspondingGetter; | |
| 190 } | |
| 191 auxiliary = desynthesizeSetter(auxiliary); | |
| 192 if (predicate(auxiliary)) { | |
| 193 return getMemberReference(auxiliary); | |
| 194 } | |
| 195 return null; | |
| 196 } | |
| 197 | |
| 198 ast.Member resolveConcreteSet(Element element, Element auxiliary) { | |
| 199 return _resolveSet(element, auxiliary, supportsConcreteSet); | |
| 200 } | |
| 201 | |
| 202 ast.Member resolveInterfaceSet(Element element, Element auxiliary) { | |
| 203 if (!strongMode) return null; | |
| 204 return _resolveSet(element, auxiliary, supportsInterfaceSet); | |
| 205 } | |
| 206 | |
| 207 ast.Member resolveConcreteIndexGet(Element element, Element auxiliary) { | |
| 208 if (supportsConcreteIndexGet(element)) { | |
| 209 return getMemberReference(element); | |
| 210 } | |
| 211 if (supportsConcreteIndexGet(auxiliary)) { | |
| 212 return getMemberReference(auxiliary); | |
| 213 } | |
| 214 return null; | |
| 215 } | |
| 216 | |
| 217 ast.Member resolveInterfaceIndexGet(Element element, Element auxiliary) { | |
| 218 if (!strongMode) return null; | |
| 219 if (supportsInterfaceIndexGet(element)) { | |
| 220 return getMemberReference(element); | |
| 221 } | |
| 222 if (supportsInterfaceIndexGet(auxiliary)) { | |
| 223 return getMemberReference(auxiliary); | |
| 224 } | |
| 225 return null; | |
| 226 } | |
| 227 | |
| 228 ast.Member resolveConcreteIndexSet(Element element, Element auxiliary) { | |
| 229 if (supportsConcreteIndexSet(element)) { | |
| 230 return getMemberReference(element); | |
| 231 } | |
| 232 if (supportsConcreteIndexSet(auxiliary)) { | |
| 233 return getMemberReference(auxiliary); | |
| 234 } | |
| 235 return null; | |
| 236 } | |
| 237 | |
| 238 ast.Member resolveInterfaceIndexSet(Element element, Element auxiliary) { | |
| 239 if (!strongMode) return null; | |
| 240 if (supportsInterfaceIndexSet(element)) { | |
| 241 return getMemberReference(element); | |
| 242 } | |
| 243 if (supportsInterfaceIndexSet(auxiliary)) { | |
| 244 return getMemberReference(auxiliary); | |
| 245 } | |
| 246 return null; | |
| 247 } | |
| 248 | |
| 249 ast.Member resolveConcreteMethod(Element element) { | |
| 250 if (supportsConcreteMethodCall(element)) { | |
| 251 return getMemberReference(element); | |
| 252 } | |
| 253 return null; | |
| 254 } | |
| 255 | |
| 256 ast.Member resolveInterfaceMethod(Element element) { | |
| 257 if (!strongMode) return null; | |
| 258 if (supportsInterfaceMethodCall(element)) { | |
| 259 return getMemberReference(element); | |
| 260 } | |
| 261 return null; | |
| 262 } | |
| 263 | |
| 264 ast.Constructor resolveConstructor(Element element) { | |
| 265 if (supportsConstructorCall(element)) { | |
| 266 return getMemberReference(element); | |
| 267 } | |
| 268 return null; | |
| 269 } | |
| 270 | |
| 271 ast.Field resolveField(Element element) { | |
| 272 if (element is FieldElement && !element.isSynthetic) { | |
| 273 return getMemberReference(element); | |
| 274 } | |
| 275 return null; | |
| 276 } | |
| 277 | |
| 278 /// A static accessor that generates a 'throw NoSuchMethodError' when a | |
| 279 /// read or write access could not be resolved. | |
| 280 Accessor staticAccess(String name, Element element, [Element auxiliary]) { | |
| 281 return new _StaticAccessor( | |
| 282 this, | |
| 283 name, | |
| 284 resolveConcreteGet(element, auxiliary), | |
| 285 resolveConcreteSet(element, auxiliary)); | |
| 286 } | |
| 287 | |
| 288 /// An accessor that generates a 'throw NoSuchMethodError' on both read | |
| 289 /// and write access. | |
| 290 Accessor unresolvedAccess(String name) { | |
| 291 return new _StaticAccessor(this, name, null, null); | |
| 292 } | |
| 293 } | |
| 294 | |
| 295 class TypeScope extends ReferenceScope { | |
| 296 final Map<TypeParameterElement, ast.TypeParameter> localTypeParameters = | |
| 297 <TypeParameterElement, ast.TypeParameter>{}; | |
| 298 TypeAnnotationBuilder _typeBuilder; | |
| 299 | |
| 300 TypeScope(ReferenceLevelLoader loader) : super(loader) { | |
| 301 _typeBuilder = new TypeAnnotationBuilder(this); | |
| 302 } | |
| 303 | |
| 304 String get location => '?'; | |
| 305 | |
| 306 bool get allowClassTypeParameters => false; | |
| 307 | |
| 308 ast.DartType get defaultTypeParameterBound => getRootClassReference().rawType; | |
| 309 | |
| 310 ast.TypeParameter tryGetTypeParameterReference(TypeParameterElement element) { | |
| 311 return localTypeParameters[element] ?? | |
| 312 loader.tryGetClassTypeParameter(element); | |
| 313 } | |
| 314 | |
| 315 ast.TypeParameter getTypeParameterReference(TypeParameterElement element) { | |
| 316 return localTypeParameters[element] ?? | |
| 317 loader.tryGetClassTypeParameter(element) ?? | |
| 318 (localTypeParameters[element] = new ast.TypeParameter(element.name)); | |
| 319 } | |
| 320 | |
| 321 ast.TypeParameter makeTypeParameter(TypeParameterElement element, | |
| 322 {ast.DartType bound}) { | |
| 323 var typeParameter = getTypeParameterReference(element); | |
| 324 assert(bound != null); | |
| 325 typeParameter.bound = bound; | |
| 326 return typeParameter; | |
| 327 } | |
| 328 | |
| 329 ast.DartType buildType(DartType type) { | |
| 330 return _typeBuilder.buildFromDartType(type); | |
| 331 } | |
| 332 | |
| 333 ast.Supertype buildSupertype(DartType type) { | |
| 334 if (type is InterfaceType) { | |
| 335 var classElement = type.element; | |
| 336 if (classElement == null) return getRootClassReference().asRawSupertype; | |
| 337 var classNode = getClassReference(classElement); | |
| 338 if (classNode.typeParameters.isEmpty || | |
| 339 classNode.typeParameters.length != type.typeArguments.length) { | |
| 340 return classNode.asRawSupertype; | |
| 341 } else { | |
| 342 return new ast.Supertype(classNode, | |
| 343 type.typeArguments.map(buildType).toList(growable: false)); | |
| 344 } | |
| 345 } | |
| 346 return getRootClassReference().asRawSupertype; | |
| 347 } | |
| 348 | |
| 349 ast.DartType buildTypeAnnotation(AstNode node) { | |
| 350 return _typeBuilder.build(node); | |
| 351 } | |
| 352 | |
| 353 ast.DartType buildOptionalTypeAnnotation(AstNode node) { | |
| 354 return node == null ? null : _typeBuilder.build(node); | |
| 355 } | |
| 356 | |
| 357 ast.DartType getInferredType(Expression node) { | |
| 358 if (!strongMode) return const ast.DynamicType(); | |
| 359 // TODO: Is this official way to get the strong-mode inferred type? | |
| 360 return buildType(node.staticType); | |
| 361 } | |
| 362 | |
| 363 ast.DartType getInferredTypeArgument(Expression node, int index) { | |
| 364 var type = getInferredType(node); | |
| 365 return type is ast.InterfaceType && index < type.typeArguments.length | |
| 366 ? type.typeArguments[index] | |
| 367 : const ast.DynamicType(); | |
| 368 } | |
| 369 | |
| 370 ast.DartType getInferredReturnType(Expression node) { | |
| 371 var type = getInferredType(node); | |
| 372 return type is ast.FunctionType ? type.returnType : const ast.DynamicType(); | |
| 373 } | |
| 374 | |
| 375 List<ast.DartType> getInferredInvocationTypeArguments( | |
| 376 InvocationExpression node) { | |
| 377 if (!strongMode) return <ast.DartType>[]; | |
| 378 ast.DartType inferredFunctionType = buildType(node.staticInvokeType); | |
| 379 ast.DartType genericFunctionType = buildType(node.function.staticType); | |
| 380 if (genericFunctionType is ast.FunctionType) { | |
| 381 if (genericFunctionType.typeParameters.isEmpty) return <ast.DartType>[]; | |
| 382 // Attempt to unify the two types to obtain a substitution of the type | |
| 383 // variables. If successful, use the substituted types in the order | |
| 384 // they occur in the type parameter list. | |
| 385 var substitution = unifyTypes(genericFunctionType.withoutTypeParameters, | |
| 386 inferredFunctionType, genericFunctionType.typeParameters.toSet()); | |
| 387 if (substitution != null) { | |
| 388 return genericFunctionType.typeParameters | |
| 389 .map((p) => substitution[p] ?? const ast.DynamicType()) | |
| 390 .toList(); | |
| 391 } | |
| 392 return new List<ast.DartType>.filled( | |
| 393 genericFunctionType.typeParameters.length, const ast.DynamicType(), | |
| 394 growable: true); | |
| 395 } else { | |
| 396 return <ast.DartType>[]; | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 List<ast.DartType> buildOptionalTypeArgumentList(TypeArgumentList node) { | |
| 401 if (node == null) return null; | |
| 402 return _typeBuilder.buildList(node.arguments); | |
| 403 } | |
| 404 | |
| 405 List<ast.DartType> buildTypeArgumentList(TypeArgumentList node) { | |
| 406 return _typeBuilder.buildList(node.arguments); | |
| 407 } | |
| 408 | |
| 409 List<ast.TypeParameter> buildOptionalTypeParameterList(TypeParameterList node, | |
| 410 {bool strongModeOnly: false}) { | |
| 411 if (node == null) return <ast.TypeParameter>[]; | |
| 412 if (strongModeOnly && !strongMode) return <ast.TypeParameter>[]; | |
| 413 return node.typeParameters.map(buildTypeParameter).toList(); | |
| 414 } | |
| 415 | |
| 416 ast.TypeParameter buildTypeParameter(TypeParameter node) { | |
| 417 return makeTypeParameter(node.element, | |
| 418 bound: buildOptionalTypeAnnotation(node.bound) ?? | |
| 419 defaultTypeParameterBound); | |
| 420 } | |
| 421 | |
| 422 ConstructorElement findDefaultConstructor(ClassElement class_) { | |
| 423 for (var constructor in class_.constructors) { | |
| 424 // Note: isDefaultConstructor checks if the constructor is suitable for | |
| 425 // being invoked without arguments. It does not imply that it is | |
| 426 // synthetic. | |
| 427 if (constructor.isDefaultConstructor && !constructor.isFactory) { | |
| 428 return constructor; | |
| 429 } | |
| 430 } | |
| 431 return null; | |
| 432 } | |
| 433 | |
| 434 ast.FunctionNode buildFunctionInterface(FunctionTypedElement element) { | |
| 435 var positional = <ast.VariableDeclaration>[]; | |
| 436 var named = <ast.VariableDeclaration>[]; | |
| 437 int requiredParameterCount = 0; | |
| 438 // Initialize type parameters in two passes: put them into scope, | |
| 439 // and compute the bounds afterwards while they are all in scope. | |
| 440 var typeParameters = <ast.TypeParameter>[]; | |
| 441 var typeParameterElements = | |
| 442 element is ConstructorElement && element.isFactory | |
| 443 ? element.enclosingElement.typeParameters | |
| 444 : element.typeParameters; | |
| 445 if (strongMode || element is ConstructorElement) { | |
| 446 for (var parameter in typeParameterElements) { | |
| 447 var parameterNode = new ast.TypeParameter(parameter.name); | |
| 448 typeParameters.add(parameterNode); | |
| 449 localTypeParameters[parameter] = parameterNode; | |
| 450 } | |
| 451 } | |
| 452 for (int i = 0; i < typeParameters.length; ++i) { | |
| 453 var parameter = typeParameterElements[i]; | |
| 454 var parameterNode = typeParameters[i]; | |
| 455 parameterNode.bound = parameter.bound == null | |
| 456 ? defaultTypeParameterBound | |
| 457 : buildType(parameter.bound); | |
| 458 } | |
| 459 for (var parameter in element.parameters) { | |
| 460 var parameterNode = new ast.VariableDeclaration(parameter.name, | |
| 461 type: buildType(parameter.type)); | |
| 462 switch (parameter.parameterKind) { | |
| 463 case ParameterKind.REQUIRED: | |
| 464 positional.add(parameterNode); | |
| 465 ++requiredParameterCount; | |
| 466 break; | |
| 467 | |
| 468 case ParameterKind.POSITIONAL: | |
| 469 positional.add(parameterNode); | |
| 470 break; | |
| 471 | |
| 472 case ParameterKind.NAMED: | |
| 473 named.add(parameterNode); | |
| 474 break; | |
| 475 } | |
| 476 } | |
| 477 var returnType = element is ConstructorElement | |
| 478 ? const ast.VoidType() | |
| 479 : buildType(element.returnType); | |
| 480 return new ast.FunctionNode(null, | |
| 481 typeParameters: typeParameters, | |
| 482 positionalParameters: positional, | |
| 483 namedParameters: named, | |
| 484 requiredParameterCount: requiredParameterCount, | |
| 485 returnType: returnType)..fileOffset = element.nameOffset; | |
| 486 } | |
| 487 } | |
| 488 | |
| 489 class ExpressionScope extends TypeScope { | |
| 490 ast.Library currentLibrary; | |
| 491 final Map<LocalElement, ast.VariableDeclaration> localVariables = | |
| 492 <LocalElement, ast.VariableDeclaration>{}; | |
| 493 | |
| 494 ExpressionBuilder _expressionBuilder; | |
| 495 StatementBuilder _statementBuilder; | |
| 496 | |
| 497 ExpressionScope(ReferenceLevelLoader loader, this.currentLibrary) | |
| 498 : super(loader) { | |
| 499 assert(currentLibrary != null); | |
| 500 _expressionBuilder = new ExpressionBuilder(this); | |
| 501 _statementBuilder = new StatementBuilder(this); | |
| 502 } | |
| 503 | |
| 504 bool get allowThis => false; // Overridden by MemberScope. | |
| 505 | |
| 506 ast.Name buildName(SimpleIdentifier node) { | |
| 507 return new ast.Name(node.name, currentLibrary); | |
| 508 } | |
| 509 | |
| 510 ast.Statement buildStatement(Statement node) { | |
| 511 return _statementBuilder.build(node); | |
| 512 } | |
| 513 | |
| 514 ast.Statement buildOptionalFunctionBody(FunctionBody body) { | |
| 515 if (body == null || | |
| 516 body is EmptyFunctionBody || | |
| 517 body is NativeFunctionBody) { | |
| 518 return null; | |
| 519 } | |
| 520 return buildMandatoryFunctionBody(body); | |
| 521 } | |
| 522 | |
| 523 ast.Statement buildMandatoryFunctionBody(FunctionBody body) { | |
| 524 try { | |
| 525 if (body is BlockFunctionBody) { | |
| 526 return buildStatement(body.block); | |
| 527 } else if (body is ExpressionFunctionBody) { | |
| 528 if (bodyHasVoidReturn(body)) { | |
| 529 return new ast.ExpressionStatement(buildExpression(body.expression)); | |
| 530 } else { | |
| 531 return new ast.ReturnStatement(buildExpression(body.expression)) | |
| 532 ..fileOffset = body.expression.offset; | |
| 533 } | |
| 534 } else { | |
| 535 return internalError('Missing function body'); | |
| 536 } | |
| 537 } on _CompilationError catch (e) { | |
| 538 return new ast.ExpressionStatement(buildThrowCompileTimeError(e.message)); | |
| 539 } | |
| 540 } | |
| 541 | |
| 542 ast.AsyncMarker getAsyncMarker({bool isAsync: false, bool isStar: false}) { | |
| 543 return ast.AsyncMarker.values[(isAsync ? 2 : 0) + (isStar ? 1 : 0)]; | |
| 544 } | |
| 545 | |
| 546 ast.FunctionNode buildFunctionNode( | |
| 547 FormalParameterList formalParameters, FunctionBody body, | |
| 548 {TypeName returnType, | |
| 549 List<ast.TypeParameter> typeParameters, | |
| 550 ast.DartType inferredReturnType}) { | |
| 551 // TODO(asgerf): This will in many cases rebuild the interface built by | |
| 552 // TypeScope.buildFunctionInterface. | |
| 553 var positional = <ast.VariableDeclaration>[]; | |
| 554 var named = <ast.VariableDeclaration>[]; | |
| 555 int requiredParameterCount = 0; | |
| 556 var formals = formalParameters?.parameters ?? const <FormalParameter>[]; | |
| 557 for (var parameter in formals) { | |
| 558 var declaration = makeVariableDeclaration(parameter.element, | |
| 559 initializer: parameter is DefaultFormalParameter | |
| 560 ? buildOptionalTopLevelExpression(parameter.defaultValue) | |
| 561 : null, | |
| 562 type: buildType( | |
| 563 resolutionMap.elementDeclaredByFormalParameter(parameter).type)); | |
| 564 switch (parameter.kind) { | |
| 565 case ParameterKind.REQUIRED: | |
| 566 positional.add(declaration); | |
| 567 ++requiredParameterCount; | |
| 568 declaration.initializer = null; | |
| 569 break; | |
| 570 | |
| 571 case ParameterKind.POSITIONAL: | |
| 572 positional.add(declaration); | |
| 573 break; | |
| 574 | |
| 575 case ParameterKind.NAMED: | |
| 576 named.add(declaration); | |
| 577 break; | |
| 578 } | |
| 579 } | |
| 580 int offset = formalParameters?.offset ?? body.offset; | |
| 581 int endOffset = body.endToken.offset; | |
| 582 ast.AsyncMarker asyncMarker = | |
| 583 getAsyncMarker(isAsync: body.isAsynchronous, isStar: body.isGenerator); | |
| 584 return new ast.FunctionNode(buildOptionalFunctionBody(body), | |
| 585 typeParameters: typeParameters, | |
| 586 positionalParameters: positional, | |
| 587 namedParameters: named, | |
| 588 requiredParameterCount: requiredParameterCount, | |
| 589 returnType: buildOptionalTypeAnnotation(returnType) ?? | |
| 590 inferredReturnType ?? | |
| 591 const ast.DynamicType(), | |
| 592 asyncMarker: asyncMarker, | |
| 593 dartAsyncMarker: asyncMarker) | |
| 594 ..fileOffset = offset | |
| 595 ..fileEndOffset = endOffset; | |
| 596 } | |
| 597 | |
| 598 ast.Expression buildOptionalTopLevelExpression(Expression node) { | |
| 599 return node == null ? null : buildTopLevelExpression(node); | |
| 600 } | |
| 601 | |
| 602 ast.Expression buildTopLevelExpression(Expression node) { | |
| 603 try { | |
| 604 return _expressionBuilder.build(node); | |
| 605 } on _CompilationError catch (e) { | |
| 606 return buildThrowCompileTimeError(e.message); | |
| 607 } | |
| 608 } | |
| 609 | |
| 610 ast.Expression buildExpression(Expression node) { | |
| 611 return _expressionBuilder.build(node); | |
| 612 } | |
| 613 | |
| 614 ast.Expression buildOptionalExpression(Expression node) { | |
| 615 return node == null ? null : _expressionBuilder.build(node); | |
| 616 } | |
| 617 | |
| 618 Accessor buildLeftHandValue(Expression node) { | |
| 619 return _expressionBuilder.buildLeftHandValue(node); | |
| 620 } | |
| 621 | |
| 622 ast.Expression buildStringLiteral(Expression node) { | |
| 623 List<ast.Expression> parts = <ast.Expression>[]; | |
| 624 new StringLiteralPartBuilder(this, parts).build(node); | |
| 625 return parts.length == 1 && parts[0] is ast.StringLiteral | |
| 626 ? parts[0] | |
| 627 : new ast.StringConcatenation(parts); | |
| 628 } | |
| 629 | |
| 630 ast.Expression buildThis() { | |
| 631 return allowThis | |
| 632 ? new ast.ThisExpression() | |
| 633 : emitCompileTimeError(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS); | |
| 634 } | |
| 635 | |
| 636 ast.Initializer buildInitializer(ConstructorInitializer node) { | |
| 637 try { | |
| 638 return new InitializerBuilder(this).build(node); | |
| 639 } on _CompilationError catch (_) { | |
| 640 return new ast.InvalidInitializer(); | |
| 641 } | |
| 642 } | |
| 643 | |
| 644 bool isFinal(Element element) { | |
| 645 return element is VariableElement && element.isFinal || | |
| 646 element is FunctionElement; | |
| 647 } | |
| 648 | |
| 649 bool isConst(Element element) { | |
| 650 return element is VariableElement && element.isConst; | |
| 651 } | |
| 652 | |
| 653 ast.VariableDeclaration getVariableReference(LocalElement element) { | |
| 654 return localVariables.putIfAbsent(element, () { | |
| 655 return new ast.VariableDeclaration(element.name, | |
| 656 isFinal: isFinal(element), | |
| 657 isConst: isConst(element))..fileOffset = element.nameOffset; | |
| 658 }); | |
| 659 } | |
| 660 | |
| 661 ast.DartType getInferredVariableType(Element element) { | |
| 662 if (!strongMode) return const ast.DynamicType(); | |
| 663 if (element is FunctionTypedElement) { | |
| 664 return buildType(element.type); | |
| 665 } else if (element is VariableElement) { | |
| 666 return buildType(element.type); | |
| 667 } else { | |
| 668 log.severe('Unexpected variable element: $element'); | |
| 669 return const ast.DynamicType(); | |
| 670 } | |
| 671 } | |
| 672 | |
| 673 ast.VariableDeclaration makeVariableDeclaration(LocalElement element, | |
| 674 {ast.DartType type, ast.Expression initializer, int equalsOffset}) { | |
| 675 var declaration = getVariableReference(element); | |
| 676 if (equalsOffset != null) declaration.fileEqualsOffset = equalsOffset; | |
| 677 declaration.type = type ?? getInferredVariableType(element); | |
| 678 if (initializer != null) { | |
| 679 declaration.initializer = initializer..parent = declaration; | |
| 680 } | |
| 681 return declaration; | |
| 682 } | |
| 683 | |
| 684 /// Returns true if [arguments] can be accepted by [target] | |
| 685 /// (not taking type checks into account). | |
| 686 bool areArgumentsCompatible( | |
| 687 FunctionTypedElement target, ast.Arguments arguments) { | |
| 688 var positionals = arguments.positional; | |
| 689 var parameters = target.parameters; | |
| 690 const required = ParameterKind.REQUIRED; // For avoiding long lines. | |
| 691 const named = ParameterKind.NAMED; | |
| 692 // If the first unprovided parameter is required, there are too few | |
| 693 // positional arguments. | |
| 694 if (positionals.length < parameters.length && | |
| 695 parameters[positionals.length].parameterKind == required) { | |
| 696 return false; | |
| 697 } | |
| 698 // If there are more positional arguments than parameters, or if the last | |
| 699 // positional argument corresponds to a named parameter, there are too many | |
| 700 // positional arguments. | |
| 701 if (positionals.length > parameters.length) return false; | |
| 702 if (positionals.isNotEmpty && | |
| 703 parameters[positionals.length - 1].parameterKind == named) { | |
| 704 return false; // Too many positional arguments. | |
| 705 } | |
| 706 if (arguments.named.isEmpty) return true; | |
| 707 int firstNamedParameter = positionals.length; | |
| 708 while (firstNamedParameter < parameters.length && | |
| 709 parameters[firstNamedParameter].parameterKind != ParameterKind.NAMED) { | |
| 710 ++firstNamedParameter; | |
| 711 } | |
| 712 namedLoop: | |
| 713 for (int i = 0; i < arguments.named.length; ++i) { | |
| 714 String name = arguments.named[i].name; | |
| 715 for (int j = firstNamedParameter; j < parameters.length; ++j) { | |
| 716 if (parameters[j].parameterKind == ParameterKind.NAMED && | |
| 717 parameters[j].name == name) { | |
| 718 continue namedLoop; | |
| 719 } | |
| 720 } | |
| 721 return false; | |
| 722 } | |
| 723 return true; | |
| 724 } | |
| 725 | |
| 726 /// Throws a NoSuchMethodError corresponding to a call to [memberName] on | |
| 727 /// [receiver] with the given [arguments]. | |
| 728 /// | |
| 729 /// If provided, [candiateTarget] provides the expected arity and argument | |
| 730 /// names for the best candidate target. | |
| 731 ast.Expression buildThrowNoSuchMethodError( | |
| 732 ast.Expression receiver, String memberName, ast.Arguments arguments, | |
| 733 {Element candidateTarget}) { | |
| 734 // TODO(asgerf): When we have better integration with patch files, use | |
| 735 // the internal constructor that provides a more detailed error message. | |
| 736 ast.Expression candidateArgumentNames; | |
| 737 if (candidateTarget is FunctionTypedElement) { | |
| 738 candidateArgumentNames = new ast.ListLiteral(candidateTarget.parameters | |
| 739 .map((p) => new ast.StringLiteral(p.name)) | |
| 740 .toList()); | |
| 741 } else { | |
| 742 candidateArgumentNames = new ast.NullLiteral(); | |
| 743 } | |
| 744 return new ast.Throw(new ast.ConstructorInvocation( | |
| 745 loader.getCoreClassConstructorReference('NoSuchMethodError'), | |
| 746 new ast.Arguments(<ast.Expression>[ | |
| 747 receiver, | |
| 748 new ast.SymbolLiteral(memberName), | |
| 749 new ast.ListLiteral(arguments.positional), | |
| 750 new ast.MapLiteral(arguments.named.map((arg) { | |
| 751 return new ast.MapEntry(new ast.SymbolLiteral(arg.name), arg.value); | |
| 752 }).toList()), | |
| 753 candidateArgumentNames | |
| 754 ]))); | |
| 755 } | |
| 756 | |
| 757 ast.Expression buildThrowCompileTimeError(String message) { | |
| 758 // The spec does not mandate a specific behavior in face of a compile-time | |
| 759 // error. We just throw a string. The VM throws an uncatchable exception | |
| 760 // for this case. | |
| 761 // TOOD(asgerf): Should we add uncatchable exceptions to kernel? | |
| 762 return new ast.Throw(new ast.StringLiteral(message)); | |
| 763 } | |
| 764 | |
| 765 ast.Expression buildThrowCompileTimeErrorFromCode(ErrorCode code, | |
| 766 [List arguments]) { | |
| 767 return buildThrowCompileTimeError(makeErrorMessage(code, arguments)); | |
| 768 } | |
| 769 | |
| 770 static final RegExp _errorMessagePattern = new RegExp(r'\{(\d+)\}'); | |
| 771 | |
| 772 String makeErrorMessage(ErrorCode error, [List arguments]) { | |
| 773 String message = error.message; | |
| 774 if (arguments != null) { | |
| 775 message = message.replaceAllMapped(_errorMessagePattern, (m) { | |
| 776 String numberString = m.group(1); | |
| 777 int index = int.parse(numberString); | |
| 778 return arguments[index]; | |
| 779 }); | |
| 780 } | |
| 781 return message; | |
| 782 } | |
| 783 | |
| 784 /// Throws an exception that will be caught at the function level, to replace | |
| 785 /// the entire function with a throw. | |
| 786 emitCompileTimeError(ErrorCode error, [List arguments]) { | |
| 787 throw new _CompilationError(makeErrorMessage(error, arguments)); | |
| 788 } | |
| 789 | |
| 790 ast.Expression buildThrowAbstractClassInstantiationError(String name) { | |
| 791 return new ast.Throw(new ast.ConstructorInvocation( | |
| 792 loader.getCoreClassConstructorReference( | |
| 793 'AbstractClassInstantiationError'), | |
| 794 new ast.Arguments(<ast.Expression>[new ast.StringLiteral(name)]))); | |
| 795 } | |
| 796 | |
| 797 ast.Expression buildThrowFallThroughError() { | |
| 798 return new ast.Throw(new ast.ConstructorInvocation( | |
| 799 loader.getCoreClassConstructorReference('FallThroughError'), | |
| 800 new ast.Arguments.empty())); | |
| 801 } | |
| 802 | |
| 803 emitInvalidConstant([ErrorCode error]) { | |
| 804 error ??= CompileTimeErrorCode.INVALID_CONSTANT; | |
| 805 return emitCompileTimeError(error); | |
| 806 } | |
| 807 | |
| 808 internalError(String message) { | |
| 809 throw 'Internal error when compiling $location: $message'; | |
| 810 } | |
| 811 | |
| 812 unsupportedFeature(String feature) { | |
| 813 throw new _CompilationError('$feature is not supported'); | |
| 814 } | |
| 815 | |
| 816 ast.Expression buildAnnotation(Annotation annotation) { | |
| 817 Element element = annotation.element; | |
| 818 if (annotation.arguments == null) { | |
| 819 var target = resolveConcreteGet(element, null); | |
| 820 return target == null | |
| 821 ? new ast.InvalidExpression() | |
| 822 : new ast.StaticGet(target); | |
| 823 } else if (element is ConstructorElement && element.isConst) { | |
| 824 var target = resolveConstructor(element); | |
| 825 return target == null | |
| 826 ? new ast.InvalidExpression() | |
| 827 : new ast.ConstructorInvocation( | |
| 828 target, _expressionBuilder.buildArguments(annotation.arguments), | |
| 829 isConst: true); | |
| 830 } else { | |
| 831 return new ast.InvalidExpression(); | |
| 832 } | |
| 833 } | |
| 834 | |
| 835 void addTransformerFlag(int flags) { | |
| 836 // Overridden by MemberScope. | |
| 837 } | |
| 838 | |
| 839 /// True if the body of the given method must return nothing. | |
| 840 bool hasVoidReturn(ExecutableElement element) { | |
| 841 return (strongMode && element.returnType.isVoid) || | |
| 842 (element is PropertyAccessorElement && element.isSetter) || | |
| 843 element.name == '[]='; | |
| 844 } | |
| 845 | |
| 846 bool bodyHasVoidReturn(FunctionBody body) { | |
| 847 AstNode parent = body.parent; | |
| 848 return parent is MethodDeclaration && hasVoidReturn(parent.element) || | |
| 849 parent is FunctionDeclaration && hasVoidReturn(parent.element); | |
| 850 } | |
| 851 } | |
| 852 | |
| 853 /// A scope in which class type parameters are in scope, while not in scope | |
| 854 /// of a specific member. | |
| 855 class ClassScope extends ExpressionScope { | |
| 856 @override | |
| 857 bool get allowClassTypeParameters => true; | |
| 858 | |
| 859 ClassScope(ReferenceLevelLoader loader, ast.Library library) | |
| 860 : super(loader, library); | |
| 861 } | |
| 862 | |
| 863 /// Translates expressions, statements, and other constructs into [ast] nodes. | |
| 864 /// | |
| 865 /// Naming convention: | |
| 866 /// - `buildX` may not be given null as argument (it may crash the compiler). | |
| 867 /// - `buildOptionalX` returns null or an empty list if given null | |
| 868 /// - `buildMandatoryX` returns an invalid node if given null. | |
| 869 class MemberScope extends ExpressionScope { | |
| 870 /// A reference to the member currently being upgraded to body level. | |
| 871 final ast.Member currentMember; | |
| 872 | |
| 873 MemberScope(ReferenceLevelLoader loader, ast.Member currentMember) | |
| 874 : currentMember = currentMember, | |
| 875 super(loader, currentMember.enclosingLibrary) { | |
| 876 assert(currentMember != null); | |
| 877 } | |
| 878 | |
| 879 ast.Class get currentClass => currentMember.enclosingClass; | |
| 880 | |
| 881 bool get allowThis => _memberHasThis(currentMember); | |
| 882 | |
| 883 @override | |
| 884 bool get allowClassTypeParameters { | |
| 885 return currentMember.isInstanceMember || currentMember is ast.Constructor; | |
| 886 } | |
| 887 | |
| 888 /// Returns a string for debugging use, indicating the location of the member | |
| 889 /// being built. | |
| 890 String get location { | |
| 891 var library = currentMember.enclosingLibrary?.importUri ?? '<No Library>'; | |
| 892 var className = currentMember.enclosingClass == null | |
| 893 ? null | |
| 894 : (currentMember.enclosingClass?.name ?? '<Anonymous Class>'); | |
| 895 var member = | |
| 896 currentMember.name?.name ?? '<Anonymous ${currentMember.runtimeType}>'; | |
| 897 return [library, className, member].join('::'); | |
| 898 } | |
| 899 | |
| 900 bool _memberHasThis(ast.Member member) { | |
| 901 return member is ast.Procedure && !member.isStatic || | |
| 902 member is ast.Constructor; | |
| 903 } | |
| 904 | |
| 905 void addTransformerFlag(int flags) { | |
| 906 currentMember.transformerFlags |= flags; | |
| 907 } | |
| 908 } | |
| 909 | |
| 910 class LabelStack { | |
| 911 final List<String> labels; // Contains null for unlabeled targets. | |
| 912 final LabelStack next; | |
| 913 final List<ast.Statement> jumps = <ast.Statement>[]; | |
| 914 bool isSwitchTarget = false; | |
| 915 | |
| 916 LabelStack(String label, this.next) : labels = <String>[label]; | |
| 917 LabelStack.unlabeled(this.next) : labels = <String>[null]; | |
| 918 LabelStack.switchCase(String label, this.next) | |
| 919 : isSwitchTarget = true, | |
| 920 labels = <String>[label]; | |
| 921 LabelStack.many(this.labels, this.next); | |
| 922 } | |
| 923 | |
| 924 class StatementBuilder extends GeneralizingAstVisitor<ast.Statement> { | |
| 925 final ExpressionScope scope; | |
| 926 LabelStack breakStack, continueStack; | |
| 927 | |
| 928 StatementBuilder(this.scope, [this.breakStack, this.continueStack]); | |
| 929 | |
| 930 ast.Statement build(Statement node) { | |
| 931 ast.Statement result = node.accept(this); | |
| 932 result.fileOffset = _getOffset(node); | |
| 933 return result; | |
| 934 } | |
| 935 | |
| 936 ast.Statement buildOptional(Statement node) { | |
| 937 ast.Statement result = node?.accept(this); | |
| 938 result?.fileOffset = _getOffset(node); | |
| 939 return result; | |
| 940 } | |
| 941 | |
| 942 int _getOffset(AstNode node) { | |
| 943 return node.offset; | |
| 944 } | |
| 945 | |
| 946 ast.Statement buildInScope( | |
| 947 Statement node, LabelStack breakNode, LabelStack continueNode) { | |
| 948 var oldBreak = this.breakStack; | |
| 949 var oldContinue = this.continueStack; | |
| 950 breakStack = breakNode; | |
| 951 continueStack = continueNode; | |
| 952 var result = build(node); | |
| 953 this.breakStack = oldBreak; | |
| 954 this.continueStack = oldContinue; | |
| 955 return result; | |
| 956 } | |
| 957 | |
| 958 void buildBlockMember(Statement node, List<ast.Statement> output) { | |
| 959 if (node is LabeledStatement && | |
| 960 node.statement is VariableDeclarationStatement) { | |
| 961 // If a variable is labeled, its scope is part of the enclosing block. | |
| 962 LabeledStatement labeled = node; | |
| 963 node = labeled.statement; | |
| 964 } | |
| 965 if (node is VariableDeclarationStatement) { | |
| 966 VariableDeclarationList list = node.variables; | |
| 967 ast.DartType type = scope.buildOptionalTypeAnnotation(list.type); | |
| 968 for (VariableDeclaration decl in list.variables) { | |
| 969 LocalElement local = decl.element as dynamic; // Cross cast. | |
| 970 output.add(scope.makeVariableDeclaration(local, | |
| 971 type: type, | |
| 972 initializer: scope.buildOptionalExpression(decl.initializer), | |
| 973 equalsOffset: decl.equals?.offset)); | |
| 974 } | |
| 975 } else { | |
| 976 output.add(build(node)); | |
| 977 } | |
| 978 } | |
| 979 | |
| 980 ast.Statement makeBreakTarget(ast.Statement node, LabelStack stackNode) { | |
| 981 if (stackNode.jumps.isEmpty) return node; | |
| 982 var labeled = new ast.LabeledStatement(node); | |
| 983 for (var jump in stackNode.jumps) { | |
| 984 (jump as ast.BreakStatement).target = labeled; | |
| 985 } | |
| 986 return labeled; | |
| 987 } | |
| 988 | |
| 989 LabelStack findLabelTarget(String label, LabelStack stack) { | |
| 990 while (stack != null) { | |
| 991 if (stack.labels.contains(label)) return stack; | |
| 992 stack = stack.next; | |
| 993 } | |
| 994 return null; | |
| 995 } | |
| 996 | |
| 997 ast.Statement visitAssertStatement(AssertStatement node) { | |
| 998 return new ast.AssertStatement(scope.buildExpression(node.condition), | |
| 999 scope.buildOptionalExpression(node.message)); | |
| 1000 } | |
| 1001 | |
| 1002 ast.Statement visitBlock(Block node) { | |
| 1003 List<ast.Statement> statements = <ast.Statement>[]; | |
| 1004 for (Statement statement in node.statements) { | |
| 1005 buildBlockMember(statement, statements); | |
| 1006 } | |
| 1007 return new ast.Block(statements); | |
| 1008 } | |
| 1009 | |
| 1010 ast.Statement visitBreakStatement(BreakStatement node) { | |
| 1011 var stackNode = findLabelTarget(node.label?.name, breakStack); | |
| 1012 if (stackNode == null) { | |
| 1013 return node.label == null | |
| 1014 ? scope.emitCompileTimeError(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP) | |
| 1015 : scope.emitCompileTimeError( | |
| 1016 CompileTimeErrorCode.LABEL_UNDEFINED, [node.label.name]); | |
| 1017 } | |
| 1018 var result = new ast.BreakStatement(null); | |
| 1019 stackNode.jumps.add(result); | |
| 1020 return result; | |
| 1021 } | |
| 1022 | |
| 1023 ast.Statement visitContinueStatement(ContinueStatement node) { | |
| 1024 var stackNode = findLabelTarget(node.label?.name, continueStack); | |
| 1025 if (stackNode == null) { | |
| 1026 return node.label == null | |
| 1027 ? scope.emitCompileTimeError(ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP) | |
| 1028 : scope.emitCompileTimeError( | |
| 1029 CompileTimeErrorCode.LABEL_UNDEFINED, [node.label.name]); | |
| 1030 } | |
| 1031 var result = stackNode.isSwitchTarget | |
| 1032 ? new ast.ContinueSwitchStatement(null) | |
| 1033 : new ast.BreakStatement(null); | |
| 1034 stackNode.jumps.add(result); | |
| 1035 return result; | |
| 1036 } | |
| 1037 | |
| 1038 void addLoopLabels(Statement loop, LabelStack continueNode) { | |
| 1039 AstNode parent = loop.parent; | |
| 1040 if (parent is LabeledStatement) { | |
| 1041 for (var label in parent.labels) { | |
| 1042 continueNode.labels.add(label.label.name); | |
| 1043 } | |
| 1044 } | |
| 1045 } | |
| 1046 | |
| 1047 ast.Statement visitDoStatement(DoStatement node) { | |
| 1048 LabelStack breakNode = new LabelStack.unlabeled(breakStack); | |
| 1049 LabelStack continueNode = new LabelStack.unlabeled(continueStack); | |
| 1050 addLoopLabels(node, continueNode); | |
| 1051 var body = buildInScope(node.body, breakNode, continueNode); | |
| 1052 var loop = new ast.DoStatement(makeBreakTarget(body, continueNode), | |
| 1053 scope.buildExpression(node.condition)); | |
| 1054 return makeBreakTarget(loop, breakNode); | |
| 1055 } | |
| 1056 | |
| 1057 ast.Statement visitWhileStatement(WhileStatement node) { | |
| 1058 LabelStack breakNode = new LabelStack.unlabeled(breakStack); | |
| 1059 LabelStack continueNode = new LabelStack.unlabeled(continueStack); | |
| 1060 addLoopLabels(node, continueNode); | |
| 1061 var body = buildInScope(node.body, breakNode, continueNode); | |
| 1062 var loop = new ast.WhileStatement(scope.buildExpression(node.condition), | |
| 1063 makeBreakTarget(body, continueNode)); | |
| 1064 return makeBreakTarget(loop, breakNode); | |
| 1065 } | |
| 1066 | |
| 1067 ast.Statement visitEmptyStatement(EmptyStatement node) { | |
| 1068 return new ast.EmptyStatement(); | |
| 1069 } | |
| 1070 | |
| 1071 ast.Statement visitExpressionStatement(ExpressionStatement node) { | |
| 1072 return new ast.ExpressionStatement(scope.buildExpression(node.expression)); | |
| 1073 } | |
| 1074 | |
| 1075 static String _getLabelName(Label label) { | |
| 1076 return label.label.name; | |
| 1077 } | |
| 1078 | |
| 1079 ast.Statement visitLabeledStatement(LabeledStatement node) { | |
| 1080 // Only set up breaks here. Loops handle labeling on their own. | |
| 1081 var breakNode = new LabelStack.many( | |
| 1082 node.labels.map(_getLabelName).toList(), breakStack); | |
| 1083 var body = buildInScope(node.statement, breakNode, continueStack); | |
| 1084 return makeBreakTarget(body, breakNode); | |
| 1085 } | |
| 1086 | |
| 1087 static bool isBreakingExpression(ast.Expression node) { | |
| 1088 return node is ast.Throw || node is ast.Rethrow; | |
| 1089 } | |
| 1090 | |
| 1091 static bool isBreakingStatement(ast.Statement node) { | |
| 1092 return node is ast.BreakStatement || | |
| 1093 node is ast.ContinueSwitchStatement || | |
| 1094 node is ast.ReturnStatement || | |
| 1095 node is ast.ExpressionStatement && | |
| 1096 isBreakingExpression(node.expression); | |
| 1097 } | |
| 1098 | |
| 1099 ast.Statement visitSwitchStatement(SwitchStatement node) { | |
| 1100 // Group all cases into case blocks. Use parallel lists to collect the | |
| 1101 // intermediate terms until we are ready to create the AST nodes. | |
| 1102 LabelStack breakNode = new LabelStack.unlabeled(breakStack); | |
| 1103 LabelStack continueNode = continueStack; | |
| 1104 var cases = <ast.SwitchCase>[]; | |
| 1105 var bodies = <List<Statement>>[]; | |
| 1106 var labelToNode = <String, ast.SwitchCase>{}; | |
| 1107 ast.SwitchCase currentCase = null; | |
| 1108 for (var member in node.members) { | |
| 1109 if (currentCase != null && currentCase.isDefault) { | |
| 1110 var error = member is SwitchCase | |
| 1111 ? ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE | |
| 1112 : ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES; | |
| 1113 return scope.emitCompileTimeError(error); | |
| 1114 } | |
| 1115 if (currentCase == null) { | |
| 1116 currentCase = new ast.SwitchCase(<ast.Expression>[], null); | |
| 1117 cases.add(currentCase); | |
| 1118 } | |
| 1119 if (member is SwitchCase) { | |
| 1120 var expression = scope.buildExpression(member.expression); | |
| 1121 currentCase.expressions.add(expression..parent = currentCase); | |
| 1122 } else { | |
| 1123 currentCase.isDefault = true; | |
| 1124 } | |
| 1125 for (Label label in member.labels) { | |
| 1126 continueNode = | |
| 1127 new LabelStack.switchCase(label.label.name, continueNode); | |
| 1128 labelToNode[label.label.name] = currentCase; | |
| 1129 } | |
| 1130 if (member.statements?.isNotEmpty ?? false) { | |
| 1131 bodies.add(member.statements); | |
| 1132 currentCase = null; | |
| 1133 } | |
| 1134 } | |
| 1135 if (currentCase != null) { | |
| 1136 // Close off a trailing block. | |
| 1137 bodies.add(const <Statement>[]); | |
| 1138 currentCase = null; | |
| 1139 } | |
| 1140 // Now that the label environment is set up, build the bodies. | |
| 1141 var oldBreak = this.breakStack; | |
| 1142 var oldContinue = this.continueStack; | |
| 1143 this.breakStack = breakNode; | |
| 1144 this.continueStack = continueNode; | |
| 1145 for (int i = 0; i < cases.length; ++i) { | |
| 1146 var blockNodes = <ast.Statement>[]; | |
| 1147 for (var statement in bodies[i]) { | |
| 1148 buildBlockMember(statement, blockNodes); | |
| 1149 } | |
| 1150 if (blockNodes.isEmpty || !isBreakingStatement(blockNodes.last)) { | |
| 1151 if (i < cases.length - 1) { | |
| 1152 blockNodes.add( | |
| 1153 new ast.ExpressionStatement(scope.buildThrowFallThroughError())); | |
| 1154 } else { | |
| 1155 var jump = new ast.BreakStatement(null); | |
| 1156 blockNodes.add(jump); | |
| 1157 breakNode.jumps.add(jump); | |
| 1158 } | |
| 1159 } | |
| 1160 cases[i].body = new ast.Block(blockNodes)..parent = cases[i]; | |
| 1161 } | |
| 1162 // Unwind the stack of case labels and bind their jumps to the case target. | |
| 1163 while (continueNode != oldContinue) { | |
| 1164 for (var jump in continueNode.jumps) { | |
| 1165 (jump as ast.ContinueSwitchStatement).target = | |
| 1166 labelToNode[continueNode.labels.first]; | |
| 1167 } | |
| 1168 continueNode = continueNode.next; | |
| 1169 } | |
| 1170 var expression = scope.buildExpression(node.expression); | |
| 1171 var result = new ast.SwitchStatement(expression, cases); | |
| 1172 this.breakStack = oldBreak; | |
| 1173 this.continueStack = oldContinue; | |
| 1174 return makeBreakTarget(result, breakNode); | |
| 1175 } | |
| 1176 | |
| 1177 ast.Statement visitForStatement(ForStatement node) { | |
| 1178 List<ast.VariableDeclaration> variables = <ast.VariableDeclaration>[]; | |
| 1179 ast.Expression initialExpression; | |
| 1180 if (node.variables != null) { | |
| 1181 VariableDeclarationList list = node.variables; | |
| 1182 var type = scope.buildOptionalTypeAnnotation(list.type); | |
| 1183 for (var variable in list.variables) { | |
| 1184 LocalElement local = variable.element as dynamic; // Cross cast. | |
| 1185 variables.add(scope.makeVariableDeclaration(local, | |
| 1186 initializer: scope.buildOptionalExpression(variable.initializer), | |
| 1187 type: type, | |
| 1188 equalsOffset: variable.equals?.offset)); | |
| 1189 } | |
| 1190 } else if (node.initialization != null) { | |
| 1191 initialExpression = scope.buildExpression(node.initialization); | |
| 1192 } | |
| 1193 var breakNode = new LabelStack.unlabeled(breakStack); | |
| 1194 var continueNode = new LabelStack.unlabeled(continueStack); | |
| 1195 addLoopLabels(node, continueNode); | |
| 1196 var body = buildInScope(node.body, breakNode, continueNode); | |
| 1197 var loop = new ast.ForStatement( | |
| 1198 variables, | |
| 1199 scope.buildOptionalExpression(node.condition), | |
| 1200 node.updaters.map(scope.buildExpression).toList(), | |
| 1201 makeBreakTarget(body, continueNode)); | |
| 1202 loop = makeBreakTarget(loop, breakNode); | |
| 1203 if (initialExpression != null) { | |
| 1204 return new ast.Block(<ast.Statement>[ | |
| 1205 new ast.ExpressionStatement(initialExpression), | |
| 1206 loop | |
| 1207 ]); | |
| 1208 } | |
| 1209 return loop; | |
| 1210 } | |
| 1211 | |
| 1212 DartType iterableElementType(DartType iterable) { | |
| 1213 if (iterable is InterfaceType) { | |
| 1214 var iterator = iterable.lookUpInheritedGetter('iterator')?.returnType; | |
| 1215 if (iterator is InterfaceType) { | |
| 1216 return iterator.lookUpInheritedGetter('current')?.returnType; | |
| 1217 } | |
| 1218 } | |
| 1219 return null; | |
| 1220 } | |
| 1221 | |
| 1222 DartType streamElementType(DartType stream) { | |
| 1223 if (stream is InterfaceType) { | |
| 1224 var class_ = stream.element; | |
| 1225 if (class_.library.isDartAsync && | |
| 1226 class_.name == 'Stream' && | |
| 1227 stream.typeArguments.length == 1) { | |
| 1228 return stream.typeArguments[0]; | |
| 1229 } | |
| 1230 } | |
| 1231 return null; | |
| 1232 } | |
| 1233 | |
| 1234 ast.Statement visitForEachStatement(ForEachStatement node) { | |
| 1235 ast.VariableDeclaration variable; | |
| 1236 Accessor leftHand; | |
| 1237 if (node.loopVariable != null) { | |
| 1238 DeclaredIdentifier loopVariable = node.loopVariable; | |
| 1239 variable = scope.makeVariableDeclaration(loopVariable.element, | |
| 1240 type: scope.buildOptionalTypeAnnotation(loopVariable.type)); | |
| 1241 } else if (node.identifier != null) { | |
| 1242 leftHand = scope.buildLeftHandValue(node.identifier); | |
| 1243 variable = new ast.VariableDeclaration(null, isFinal: true); | |
| 1244 if (scope.strongMode) { | |
| 1245 var containerType = node.iterable.staticType; | |
| 1246 DartType elementType = node.awaitKeyword != null | |
| 1247 ? streamElementType(containerType) | |
| 1248 : iterableElementType(containerType); | |
| 1249 if (elementType != null) { | |
| 1250 variable.type = scope.buildType(elementType); | |
| 1251 } | |
| 1252 } | |
| 1253 } | |
| 1254 var breakNode = new LabelStack.unlabeled(breakStack); | |
| 1255 var continueNode = new LabelStack.unlabeled(continueStack); | |
| 1256 addLoopLabels(node, continueNode); | |
| 1257 var body = buildInScope(node.body, breakNode, continueNode); | |
| 1258 if (leftHand != null) { | |
| 1259 // Desugar | |
| 1260 // | |
| 1261 // for (x in e) BODY | |
| 1262 // | |
| 1263 // to | |
| 1264 // | |
| 1265 // for (var tmp in e) { | |
| 1266 // x = tmp; | |
| 1267 // BODY | |
| 1268 // } | |
| 1269 body = new ast.Block(<ast.Statement>[ | |
| 1270 new ast.ExpressionStatement(leftHand | |
| 1271 .buildAssignment(new ast.VariableGet(variable), voidContext: true)), | |
| 1272 body | |
| 1273 ]); | |
| 1274 } | |
| 1275 var loop = new ast.ForInStatement( | |
| 1276 variable, | |
| 1277 scope.buildExpression(node.iterable), | |
| 1278 makeBreakTarget(body, continueNode), | |
| 1279 isAsync: node.awaitKeyword != null)..fileOffset = node.offset; | |
| 1280 return makeBreakTarget(loop, breakNode); | |
| 1281 } | |
| 1282 | |
| 1283 ast.Statement visitIfStatement(IfStatement node) { | |
| 1284 return new ast.IfStatement(scope.buildExpression(node.condition), | |
| 1285 build(node.thenStatement), buildOptional(node.elseStatement)); | |
| 1286 } | |
| 1287 | |
| 1288 ast.Statement visitReturnStatement(ReturnStatement node) { | |
| 1289 return new ast.ReturnStatement( | |
| 1290 scope.buildOptionalExpression(node.expression)); | |
| 1291 } | |
| 1292 | |
| 1293 ast.Catch buildCatchClause(CatchClause node) { | |
| 1294 var exceptionVariable = node.exceptionParameter == null | |
| 1295 ? null | |
| 1296 : scope.makeVariableDeclaration(node.exceptionParameter.staticElement); | |
| 1297 var stackTraceVariable = node.stackTraceParameter == null | |
| 1298 ? null | |
| 1299 : scope.makeVariableDeclaration(node.stackTraceParameter.staticElement); | |
| 1300 return new ast.Catch(exceptionVariable, build(node.body), | |
| 1301 stackTrace: stackTraceVariable, | |
| 1302 guard: scope.buildOptionalTypeAnnotation(node.exceptionType) ?? | |
| 1303 const ast.DynamicType()); | |
| 1304 } | |
| 1305 | |
| 1306 ast.Statement visitTryStatement(TryStatement node) { | |
| 1307 ast.Statement statement = build(node.body); | |
| 1308 if (node.catchClauses.isNotEmpty) { | |
| 1309 statement = new ast.TryCatch( | |
| 1310 statement, node.catchClauses.map(buildCatchClause).toList()); | |
| 1311 } | |
| 1312 if (node.finallyBlock != null) { | |
| 1313 statement = new ast.TryFinally(statement, build(node.finallyBlock)); | |
| 1314 } | |
| 1315 return statement; | |
| 1316 } | |
| 1317 | |
| 1318 ast.Statement visitVariableDeclarationStatement( | |
| 1319 VariableDeclarationStatement node) { | |
| 1320 // This is only reached when a variable is declared in non-block level, | |
| 1321 // because visitBlock intercepts visits to its children. | |
| 1322 // An example where we hit this case is: | |
| 1323 // | |
| 1324 // if (foo) var x = 5, y = x + 1; | |
| 1325 // | |
| 1326 // We wrap these in a block: | |
| 1327 // | |
| 1328 // if (foo) { | |
| 1329 // var x = 5; | |
| 1330 // var y = x + 1; | |
| 1331 // } | |
| 1332 // | |
| 1333 // Note that the use of a block here is required by the kernel language, | |
| 1334 // even if there is only one variable declaration. | |
| 1335 List<ast.Statement> statements = <ast.Statement>[]; | |
| 1336 buildBlockMember(node, statements); | |
| 1337 return new ast.Block(statements); | |
| 1338 } | |
| 1339 | |
| 1340 ast.Statement visitYieldStatement(YieldStatement node) { | |
| 1341 return new ast.YieldStatement(scope.buildExpression(node.expression), | |
| 1342 isYieldStar: node.star != null); | |
| 1343 } | |
| 1344 | |
| 1345 ast.Statement visitFunctionDeclarationStatement( | |
| 1346 FunctionDeclarationStatement node) { | |
| 1347 var declaration = node.functionDeclaration; | |
| 1348 var expression = declaration.functionExpression; | |
| 1349 LocalElement element = declaration.element as dynamic; // Cross cast. | |
| 1350 return new ast.FunctionDeclaration( | |
| 1351 scope.makeVariableDeclaration(element, | |
| 1352 type: scope.buildType(declaration.element.type)), | |
| 1353 scope.buildFunctionNode(expression.parameters, expression.body, | |
| 1354 typeParameters: scope.buildOptionalTypeParameterList( | |
| 1355 expression.typeParameters, | |
| 1356 strongModeOnly: true), | |
| 1357 returnType: declaration.returnType))..fileOffset = node.offset; | |
| 1358 } | |
| 1359 | |
| 1360 @override | |
| 1361 visitStatement(Statement node) { | |
| 1362 return scope.internalError('Unhandled statement ${node.runtimeType}'); | |
| 1363 } | |
| 1364 } | |
| 1365 | |
| 1366 class ExpressionBuilder | |
| 1367 extends GeneralizingAstVisitor /* <ast.Expression | Accessor> */ { | |
| 1368 final ExpressionScope scope; | |
| 1369 ast.VariableDeclaration cascadeReceiver; | |
| 1370 ExpressionBuilder(this.scope); | |
| 1371 | |
| 1372 ast.Expression build(Expression node) { | |
| 1373 var result = node.accept(this); | |
| 1374 if (result is Accessor) { | |
| 1375 result = result.buildSimpleRead(); | |
| 1376 } | |
| 1377 // For some method invocations we have already set a file offset to | |
| 1378 // override the default behavior of _getOffset. | |
| 1379 if (node is! MethodInvocation || result.fileOffset < 0) { | |
| 1380 result.fileOffset = _getOffset(node); | |
| 1381 } | |
| 1382 return result; | |
| 1383 } | |
| 1384 | |
| 1385 int _getOffset(AstNode node) { | |
| 1386 if (node is MethodInvocation) { | |
| 1387 return node.methodName.offset; | |
| 1388 } else if (node is InstanceCreationExpression) { | |
| 1389 return node.constructorName.offset; | |
| 1390 } else if (node is BinaryExpression) { | |
| 1391 return node.operator.offset; | |
| 1392 } else if (node is PrefixedIdentifier) { | |
| 1393 return node.identifier.offset; | |
| 1394 } else if (node is AssignmentExpression) { | |
| 1395 return _getOffset(node.leftHandSide); | |
| 1396 } else if (node is PropertyAccess) { | |
| 1397 return node.propertyName.offset; | |
| 1398 } else if (node is IsExpression) { | |
| 1399 return node.isOperator.offset; | |
| 1400 } else if (node is AsExpression) { | |
| 1401 return node.asOperator.offset; | |
| 1402 } else if (node is StringLiteral) { | |
| 1403 // Use a catch-all for StringInterpolation and AdjacentStrings: | |
| 1404 // the debugger stops at the end. | |
| 1405 return node.end; | |
| 1406 } else if (node is IndexExpression) { | |
| 1407 return node.leftBracket.offset; | |
| 1408 } | |
| 1409 return node.offset; | |
| 1410 } | |
| 1411 | |
| 1412 Accessor buildLeftHandValue(Expression node) { | |
| 1413 var result = node.accept(this); | |
| 1414 if (result is Accessor) { | |
| 1415 return result; | |
| 1416 } else { | |
| 1417 return new ReadOnlyAccessor(result); | |
| 1418 } | |
| 1419 } | |
| 1420 | |
| 1421 ast.Expression visitAsExpression(AsExpression node) { | |
| 1422 return new ast.AsExpression( | |
| 1423 build(node.expression), scope.buildTypeAnnotation(node.type)); | |
| 1424 } | |
| 1425 | |
| 1426 ast.Expression visitAssignmentExpression(AssignmentExpression node) { | |
| 1427 bool voidContext = isInVoidContext(node); | |
| 1428 String operator = node.operator.value(); | |
| 1429 var leftHand = buildLeftHandValue(node.leftHandSide); | |
| 1430 var rightHand = build(node.rightHandSide); | |
| 1431 if (operator == '=') { | |
| 1432 return leftHand.buildAssignment(rightHand, voidContext: voidContext); | |
| 1433 } else if (operator == '??=') { | |
| 1434 return leftHand.buildNullAwareAssignment( | |
| 1435 rightHand, scope.buildType(node.staticType), | |
| 1436 voidContext: voidContext); | |
| 1437 } else { | |
| 1438 // Cut off the trailing '='. | |
| 1439 var name = new ast.Name(operator.substring(0, operator.length - 1)); | |
| 1440 return leftHand.buildCompoundAssignment(name, rightHand, | |
| 1441 offset: node.offset, | |
| 1442 voidContext: voidContext, | |
| 1443 interfaceTarget: scope.resolveInterfaceMethod(node.staticElement)); | |
| 1444 } | |
| 1445 } | |
| 1446 | |
| 1447 ast.Expression visitAwaitExpression(AwaitExpression node) { | |
| 1448 return new ast.AwaitExpression(build(node.expression)); | |
| 1449 } | |
| 1450 | |
| 1451 ast.Arguments buildSingleArgument(Expression node) { | |
| 1452 return new ast.Arguments(<ast.Expression>[build(node)]); | |
| 1453 } | |
| 1454 | |
| 1455 ast.Expression visitBinaryExpression(BinaryExpression node) { | |
| 1456 String operator = node.operator.value(); | |
| 1457 if (operator == '&&' || operator == '||') { | |
| 1458 return new ast.LogicalExpression( | |
| 1459 build(node.leftOperand), operator, build(node.rightOperand)); | |
| 1460 } | |
| 1461 if (operator == '??') { | |
| 1462 ast.Expression leftOperand = build(node.leftOperand); | |
| 1463 if (leftOperand is ast.VariableGet) { | |
| 1464 return new ast.ConditionalExpression( | |
| 1465 buildIsNull(leftOperand, offset: node.leftOperand.offset), | |
| 1466 build(node.rightOperand), | |
| 1467 new ast.VariableGet(leftOperand.variable), | |
| 1468 scope.getInferredType(node)); | |
| 1469 } else { | |
| 1470 var variable = new ast.VariableDeclaration.forValue(leftOperand); | |
| 1471 return new ast.Let( | |
| 1472 variable, | |
| 1473 new ast.ConditionalExpression( | |
| 1474 buildIsNull(new ast.VariableGet(variable), | |
| 1475 offset: leftOperand.fileOffset), | |
| 1476 build(node.rightOperand), | |
| 1477 new ast.VariableGet(variable), | |
| 1478 scope.getInferredType(node))); | |
| 1479 } | |
| 1480 } | |
| 1481 bool isNegated = false; | |
| 1482 if (operator == '!=') { | |
| 1483 isNegated = true; | |
| 1484 operator = '=='; | |
| 1485 } | |
| 1486 ast.Expression expression; | |
| 1487 if (node.leftOperand is SuperExpression) { | |
| 1488 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 1489 expression = new ast.SuperMethodInvocation( | |
| 1490 new ast.Name(operator), | |
| 1491 buildSingleArgument(node.rightOperand), | |
| 1492 scope.resolveConcreteMethod(node.staticElement)); | |
| 1493 } else { | |
| 1494 expression = new ast.MethodInvocation( | |
| 1495 build(node.leftOperand), | |
| 1496 new ast.Name(operator), | |
| 1497 buildSingleArgument(node.rightOperand), | |
| 1498 scope.resolveInterfaceMethod(node.staticElement)); | |
| 1499 } | |
| 1500 return isNegated ? new ast.Not(expression) : expression; | |
| 1501 } | |
| 1502 | |
| 1503 ast.Expression visitBooleanLiteral(BooleanLiteral node) { | |
| 1504 return new ast.BoolLiteral(node.value); | |
| 1505 } | |
| 1506 | |
| 1507 ast.Expression visitDoubleLiteral(DoubleLiteral node) { | |
| 1508 return new ast.DoubleLiteral(node.value); | |
| 1509 } | |
| 1510 | |
| 1511 ast.Expression visitIntegerLiteral(IntegerLiteral node) { | |
| 1512 return new ast.IntLiteral(node.value); | |
| 1513 } | |
| 1514 | |
| 1515 ast.Expression visitNullLiteral(NullLiteral node) { | |
| 1516 return new ast.NullLiteral(); | |
| 1517 } | |
| 1518 | |
| 1519 ast.Expression visitSimpleStringLiteral(SimpleStringLiteral node) { | |
| 1520 return new ast.StringLiteral(node.value); | |
| 1521 } | |
| 1522 | |
| 1523 ast.Expression visitStringLiteral(StringLiteral node) { | |
| 1524 return scope.buildStringLiteral(node); | |
| 1525 } | |
| 1526 | |
| 1527 static Object _getTokenValue(Token token) { | |
| 1528 return token.value(); | |
| 1529 } | |
| 1530 | |
| 1531 ast.Expression visitSymbolLiteral(SymbolLiteral node) { | |
| 1532 String value = node.components.map(_getTokenValue).join('.'); | |
| 1533 return new ast.SymbolLiteral(value); | |
| 1534 } | |
| 1535 | |
| 1536 ast.Expression visitCascadeExpression(CascadeExpression node) { | |
| 1537 var receiver = build(node.target); | |
| 1538 // If receiver is a variable it would be tempting to reuse it, but it | |
| 1539 // might be reassigned in one of the cascade sections. | |
| 1540 var receiverVariable = new ast.VariableDeclaration.forValue(receiver, | |
| 1541 type: scope.getInferredType(node.target)); | |
| 1542 var oldReceiver = this.cascadeReceiver; | |
| 1543 cascadeReceiver = receiverVariable; | |
| 1544 ast.Expression result = new ast.VariableGet(receiverVariable); | |
| 1545 for (var section in node.cascadeSections.reversed) { | |
| 1546 var dummy = new ast.VariableDeclaration.forValue(build(section)); | |
| 1547 result = new ast.Let(dummy, result); | |
| 1548 } | |
| 1549 cascadeReceiver = oldReceiver; | |
| 1550 return new ast.Let(receiverVariable, result); | |
| 1551 } | |
| 1552 | |
| 1553 ast.Expression makeCascadeReceiver() { | |
| 1554 assert(cascadeReceiver != null); | |
| 1555 return new ast.VariableGet(cascadeReceiver); | |
| 1556 } | |
| 1557 | |
| 1558 ast.Expression visitConditionalExpression(ConditionalExpression node) { | |
| 1559 return new ast.ConditionalExpression( | |
| 1560 build(node.condition), | |
| 1561 build(node.thenExpression), | |
| 1562 build(node.elseExpression), | |
| 1563 scope.getInferredType(node)); | |
| 1564 } | |
| 1565 | |
| 1566 ast.Expression visitFunctionExpression(FunctionExpression node) { | |
| 1567 return new ast.FunctionExpression(scope.buildFunctionNode( | |
| 1568 node.parameters, node.body, | |
| 1569 typeParameters: scope.buildOptionalTypeParameterList( | |
| 1570 node.typeParameters, | |
| 1571 strongModeOnly: true), | |
| 1572 inferredReturnType: scope.getInferredReturnType(node))); | |
| 1573 } | |
| 1574 | |
| 1575 ast.Arguments buildArguments(ArgumentList valueArguments, | |
| 1576 {TypeArgumentList explicitTypeArguments, | |
| 1577 List<ast.DartType> inferTypeArguments()}) { | |
| 1578 var positional = <ast.Expression>[]; | |
| 1579 var named = <ast.NamedExpression>[]; | |
| 1580 for (var argument in valueArguments.arguments) { | |
| 1581 if (argument is NamedExpression) { | |
| 1582 named.add(new ast.NamedExpression( | |
| 1583 argument.name.label.name, build(argument.expression))); | |
| 1584 } else if (named.isNotEmpty) { | |
| 1585 return scope.emitCompileTimeError( | |
| 1586 ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT); | |
| 1587 } else { | |
| 1588 positional.add(build(argument)); | |
| 1589 } | |
| 1590 } | |
| 1591 List<ast.DartType> typeArguments; | |
| 1592 if (explicitTypeArguments != null) { | |
| 1593 typeArguments = scope.buildTypeArgumentList(explicitTypeArguments); | |
| 1594 } else if (inferTypeArguments != null) { | |
| 1595 typeArguments = inferTypeArguments(); | |
| 1596 } | |
| 1597 return new ast.Arguments(positional, named: named, types: typeArguments); | |
| 1598 } | |
| 1599 | |
| 1600 ast.Arguments buildArgumentsForInvocation(InvocationExpression node) { | |
| 1601 if (scope.strongMode) { | |
| 1602 return buildArguments(node.argumentList, | |
| 1603 explicitTypeArguments: node.typeArguments, | |
| 1604 inferTypeArguments: () => | |
| 1605 scope.getInferredInvocationTypeArguments(node)); | |
| 1606 } else { | |
| 1607 return buildArguments(node.argumentList); | |
| 1608 } | |
| 1609 } | |
| 1610 | |
| 1611 static final ast.Name callName = new ast.Name('call'); | |
| 1612 | |
| 1613 ast.Expression visitFunctionExpressionInvocation( | |
| 1614 FunctionExpressionInvocation node) { | |
| 1615 return new ast.MethodInvocation( | |
| 1616 build(node.function), | |
| 1617 callName, | |
| 1618 buildArgumentsForInvocation(node), | |
| 1619 scope.resolveInterfaceFunctionCallOnType(node.function.staticType)); | |
| 1620 } | |
| 1621 | |
| 1622 visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 1623 switch (ElementKind.of(node.prefix.staticElement)) { | |
| 1624 case ElementKind.CLASS: | |
| 1625 case ElementKind.LIBRARY: | |
| 1626 case ElementKind.PREFIX: | |
| 1627 case ElementKind.IMPORT: | |
| 1628 if (node.identifier.staticElement != null) { | |
| 1629 // Should be resolved to a static access. | |
| 1630 // Do not invoke 'build', because the identifier should be seen as a | |
| 1631 // left-hand value or an expression depending on the context. | |
| 1632 return visitSimpleIdentifier(node.identifier); | |
| 1633 } | |
| 1634 // Unresolved access on a class or library. | |
| 1635 return scope.unresolvedAccess(node.identifier.name); | |
| 1636 | |
| 1637 case ElementKind.DYNAMIC: | |
| 1638 case ElementKind.FUNCTION_TYPE_ALIAS: | |
| 1639 case ElementKind.TYPE_PARAMETER: | |
| 1640 // TODO: Check with the spec to see exactly when a type literal can be | |
| 1641 // used in a property access without surrounding parentheses. | |
| 1642 // For now, just fall through to the property access case. | |
| 1643 | |
| 1644 case ElementKind.FIELD: | |
| 1645 case ElementKind.TOP_LEVEL_VARIABLE: | |
| 1646 case ElementKind.FUNCTION: | |
| 1647 case ElementKind.METHOD: | |
| 1648 case ElementKind.GETTER: | |
| 1649 case ElementKind.SETTER: | |
| 1650 case ElementKind.LOCAL_VARIABLE: | |
| 1651 case ElementKind.PARAMETER: | |
| 1652 case ElementKind.ERROR: | |
| 1653 Element element = node.identifier.staticElement; | |
| 1654 Element auxiliary = node.identifier.auxiliaryElements?.staticElement; | |
| 1655 return PropertyAccessor.make( | |
| 1656 build(node.prefix), | |
| 1657 scope.buildName(node.identifier), | |
| 1658 scope.resolveInterfaceGet(element, auxiliary), | |
| 1659 scope.resolveInterfaceSet(element, auxiliary)); | |
| 1660 | |
| 1661 case ElementKind.UNIVERSE: | |
| 1662 case ElementKind.NAME: | |
| 1663 case ElementKind.CONSTRUCTOR: | |
| 1664 case ElementKind.EXPORT: | |
| 1665 case ElementKind.LABEL: | |
| 1666 default: | |
| 1667 return scope.internalError( | |
| 1668 'Unexpected element kind: ${node.prefix.staticElement}'); | |
| 1669 } | |
| 1670 } | |
| 1671 | |
| 1672 bool isStatic(Element element) { | |
| 1673 if (element is ClassMemberElement) { | |
| 1674 return element.isStatic || element.enclosingElement == null; | |
| 1675 } | |
| 1676 if (element is PropertyAccessorElement) { | |
| 1677 return element.isStatic || element.enclosingElement == null; | |
| 1678 } | |
| 1679 if (element is FunctionElement) { | |
| 1680 return element.isStatic; | |
| 1681 } | |
| 1682 return false; | |
| 1683 } | |
| 1684 | |
| 1685 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 1686 Element element = node.staticElement; | |
| 1687 switch (ElementKind.of(element)) { | |
| 1688 case ElementKind.CLASS: | |
| 1689 case ElementKind.DYNAMIC: | |
| 1690 case ElementKind.FUNCTION_TYPE_ALIAS: | |
| 1691 case ElementKind.TYPE_PARAMETER: | |
| 1692 return new ast.TypeLiteral(scope.buildTypeAnnotation(node)); | |
| 1693 | |
| 1694 case ElementKind.ERROR: // This covers the case where nothing was found. | |
| 1695 if (!scope.allowThis) { | |
| 1696 return scope.unresolvedAccess(node.name); | |
| 1697 } | |
| 1698 return PropertyAccessor.make( | |
| 1699 scope.buildThis(), scope.buildName(node), null, null); | |
| 1700 | |
| 1701 case ElementKind.FIELD: | |
| 1702 case ElementKind.TOP_LEVEL_VARIABLE: | |
| 1703 case ElementKind.GETTER: | |
| 1704 case ElementKind.SETTER: | |
| 1705 case ElementKind.METHOD: | |
| 1706 Element auxiliary = node.auxiliaryElements?.staticElement; | |
| 1707 if (isStatic(element)) { | |
| 1708 return scope.staticAccess(node.name, element, auxiliary); | |
| 1709 } | |
| 1710 if (!scope.allowThis) { | |
| 1711 return scope.unresolvedAccess(node.name); | |
| 1712 } | |
| 1713 return PropertyAccessor.make( | |
| 1714 scope.buildThis(), | |
| 1715 scope.buildName(node), | |
| 1716 scope.resolveInterfaceGet(element, auxiliary), | |
| 1717 scope.resolveInterfaceSet(element, auxiliary)); | |
| 1718 | |
| 1719 case ElementKind.FUNCTION: | |
| 1720 FunctionElement function = element; | |
| 1721 if (isTopLevelFunction(function)) { | |
| 1722 return scope.staticAccess(node.name, function); | |
| 1723 } | |
| 1724 if (function == function.library.loadLibraryFunction) { | |
| 1725 return scope.unsupportedFeature('Deferred loading'); | |
| 1726 } | |
| 1727 return new VariableAccessor(scope.getVariableReference(function)); | |
| 1728 | |
| 1729 case ElementKind.LOCAL_VARIABLE: | |
| 1730 case ElementKind.PARAMETER: | |
| 1731 VariableElement variable = element; | |
| 1732 var type = identical(node.staticType, variable.type) | |
| 1733 ? null | |
| 1734 : scope.buildType(node.staticType); | |
| 1735 return new VariableAccessor(scope.getVariableReference(element), type); | |
| 1736 | |
| 1737 case ElementKind.IMPORT: | |
| 1738 case ElementKind.LIBRARY: | |
| 1739 case ElementKind.PREFIX: | |
| 1740 return scope.emitCompileTimeError( | |
| 1741 CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, | |
| 1742 [node.name]); | |
| 1743 | |
| 1744 case ElementKind.COMPILATION_UNIT: | |
| 1745 case ElementKind.CONSTRUCTOR: | |
| 1746 case ElementKind.EXPORT: | |
| 1747 case ElementKind.LABEL: | |
| 1748 case ElementKind.UNIVERSE: | |
| 1749 case ElementKind.NAME: | |
| 1750 default: | |
| 1751 return scope.internalError('Unexpected element kind: $element'); | |
| 1752 } | |
| 1753 } | |
| 1754 | |
| 1755 visitIndexExpression(IndexExpression node) { | |
| 1756 Element element = node.staticElement; | |
| 1757 Element auxiliary = node.auxiliaryElements?.staticElement; | |
| 1758 if (node.isCascaded) { | |
| 1759 return IndexAccessor.make( | |
| 1760 makeCascadeReceiver(), | |
| 1761 build(node.index), | |
| 1762 scope.resolveInterfaceIndexGet(element, auxiliary), | |
| 1763 scope.resolveInterfaceIndexSet(element, auxiliary)); | |
| 1764 } else if (node.target is SuperExpression) { | |
| 1765 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 1766 return new SuperIndexAccessor( | |
| 1767 build(node.index), | |
| 1768 scope.resolveConcreteIndexGet(element, auxiliary), | |
| 1769 scope.resolveConcreteIndexSet(element, auxiliary)); | |
| 1770 } else { | |
| 1771 return IndexAccessor.make( | |
| 1772 build(node.target), | |
| 1773 build(node.index), | |
| 1774 scope.resolveInterfaceIndexGet(element, auxiliary), | |
| 1775 scope.resolveInterfaceIndexSet(element, auxiliary)); | |
| 1776 } | |
| 1777 } | |
| 1778 | |
| 1779 /// Follows any number of redirecting factories, returning the effective | |
| 1780 /// target or `null` if a cycle is found. | |
| 1781 /// | |
| 1782 /// The returned element is a [Member] if the type arguments to the effective | |
| 1783 /// target are different from the original arguments. | |
| 1784 ConstructorElement getEffectiveFactoryTarget(ConstructorElement element) { | |
| 1785 ConstructorElement anchor = null; | |
| 1786 int n = 1; | |
| 1787 while (element.isFactory && element.redirectedConstructor != null) { | |
| 1788 element = element.redirectedConstructor; | |
| 1789 var base = ReferenceScope.getBaseElement(element); | |
| 1790 if (base == anchor) return null; // Cyclic redirection. | |
| 1791 if (n & ++n == 0) { | |
| 1792 anchor = base; | |
| 1793 } | |
| 1794 } | |
| 1795 return element; | |
| 1796 } | |
| 1797 | |
| 1798 /// Forces the list of type arguments to have the specified length. If the | |
| 1799 /// length was changed, all type arguments are changed to `dynamic`. | |
| 1800 void _coerceTypeArgumentArity(List<ast.DartType> typeArguments, int arity) { | |
| 1801 if (typeArguments.length != arity) { | |
| 1802 typeArguments.length = arity; | |
| 1803 typeArguments.fillRange(0, arity, const ast.DynamicType()); | |
| 1804 } | |
| 1805 } | |
| 1806 | |
| 1807 ast.Expression visitInstanceCreationExpression( | |
| 1808 InstanceCreationExpression node) { | |
| 1809 ConstructorElement element = node.staticElement; | |
| 1810 ClassElement classElement = element?.enclosingElement; | |
| 1811 List<ast.DartType> inferTypeArguments() { | |
| 1812 var inferredType = scope.getInferredType(node); | |
| 1813 if (inferredType is ast.InterfaceType) { | |
| 1814 return inferredType.typeArguments.toList(); | |
| 1815 } | |
| 1816 int numberOfTypeArguments = | |
| 1817 classElement == null ? 0 : classElement.typeParameters.length; | |
| 1818 return new List<ast.DartType>.filled( | |
| 1819 numberOfTypeArguments, const ast.DynamicType(), | |
| 1820 growable: true); | |
| 1821 } | |
| 1822 | |
| 1823 var arguments = buildArguments(node.argumentList, | |
| 1824 explicitTypeArguments: node.constructorName.type.typeArguments, | |
| 1825 inferTypeArguments: inferTypeArguments); | |
| 1826 ast.Expression noSuchMethodError() { | |
| 1827 return node.isConst | |
| 1828 ? scope.emitInvalidConstant() | |
| 1829 : scope.buildThrowNoSuchMethodError( | |
| 1830 new ast.NullLiteral(), '${node.constructorName}', arguments, | |
| 1831 candidateTarget: element); | |
| 1832 } | |
| 1833 | |
| 1834 if (element == null) { | |
| 1835 return noSuchMethodError(); | |
| 1836 } | |
| 1837 assert(classElement != null); | |
| 1838 var redirect = getEffectiveFactoryTarget(element); | |
| 1839 if (redirect == null) { | |
| 1840 return scope.buildThrowCompileTimeError( | |
| 1841 CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT.message); | |
| 1842 } | |
| 1843 if (redirect != element) { | |
| 1844 ast.InterfaceType returnType = scope.buildType(redirect.returnType); | |
| 1845 arguments.types | |
| 1846 ..clear() | |
| 1847 ..addAll(returnType.typeArguments); | |
| 1848 element = redirect; | |
| 1849 classElement = element.enclosingElement; | |
| 1850 } | |
| 1851 element = ReferenceScope.getBaseElement(element); | |
| 1852 if (node.isConst && !element.isConst) { | |
| 1853 return scope | |
| 1854 .emitInvalidConstant(CompileTimeErrorCode.CONST_WITH_NON_CONST); | |
| 1855 } | |
| 1856 if (classElement.isEnum) { | |
| 1857 return scope.emitCompileTimeError(CompileTimeErrorCode.INSTANTIATE_ENUM); | |
| 1858 } | |
| 1859 _coerceTypeArgumentArity( | |
| 1860 arguments.types, classElement.typeParameters.length); | |
| 1861 if (element.isFactory) { | |
| 1862 ast.Member target = scope.resolveConcreteMethod(element); | |
| 1863 if (target is ast.Procedure && | |
| 1864 scope.areArgumentsCompatible(element, arguments)) { | |
| 1865 return new ast.StaticInvocation(target, arguments, | |
| 1866 isConst: node.isConst); | |
| 1867 } else { | |
| 1868 return noSuchMethodError(); | |
| 1869 } | |
| 1870 } | |
| 1871 if (classElement.isAbstract) { | |
| 1872 return node.isConst | |
| 1873 ? scope.emitInvalidConstant() | |
| 1874 : scope.buildThrowAbstractClassInstantiationError(classElement.name); | |
| 1875 } | |
| 1876 ast.Constructor constructor = scope.resolveConstructor(element); | |
| 1877 if (constructor != null && | |
| 1878 scope.areArgumentsCompatible(element, arguments)) { | |
| 1879 return new ast.ConstructorInvocation(constructor, arguments, | |
| 1880 isConst: node.isConst); | |
| 1881 } else { | |
| 1882 return noSuchMethodError(); | |
| 1883 } | |
| 1884 } | |
| 1885 | |
| 1886 ast.Expression visitIsExpression(IsExpression node) { | |
| 1887 if (node.notOperator != null) { | |
| 1888 // Put offset on the IsExpression for "is!" cases: | |
| 1889 // As it is wrapped in a not, it won't get an offset otherwise. | |
| 1890 return new ast.Not(new ast.IsExpression( | |
| 1891 build(node.expression), scope.buildTypeAnnotation(node.type)) | |
| 1892 ..fileOffset = _getOffset(node)); | |
| 1893 } else { | |
| 1894 return new ast.IsExpression( | |
| 1895 build(node.expression), scope.buildTypeAnnotation(node.type)); | |
| 1896 } | |
| 1897 } | |
| 1898 | |
| 1899 /// Emit a method invocation, either as a direct call `o.f(x)` or decomposed | |
| 1900 /// into a getter and function invocation `o.f.call(x)`. | |
| 1901 ast.Expression buildDecomposableMethodInvocation(ast.Expression receiver, | |
| 1902 ast.Name name, ast.Arguments arguments, Element targetElement) { | |
| 1903 // Try to emit a typed call to an interface method. | |
| 1904 ast.Procedure targetMethod = scope.resolveInterfaceMethod(targetElement); | |
| 1905 if (targetMethod != null) { | |
| 1906 return new ast.MethodInvocation(receiver, name, arguments, targetMethod); | |
| 1907 } | |
| 1908 // Try to emit a typed call to getter or field and call the returned | |
| 1909 // function. | |
| 1910 ast.Member targetGetter = scope.resolveInterfaceGet(targetElement, null); | |
| 1911 if (targetGetter != null) { | |
| 1912 return new ast.MethodInvocation( | |
| 1913 new ast.PropertyGet(receiver, name, targetGetter), | |
| 1914 callName, | |
| 1915 arguments, | |
| 1916 scope.resolveInterfaceFunctionCall(targetElement)); | |
| 1917 } | |
| 1918 // Emit a dynamic call. | |
| 1919 return new ast.MethodInvocation(receiver, name, arguments); | |
| 1920 } | |
| 1921 | |
| 1922 ast.Expression visitMethodInvocation(MethodInvocation node) { | |
| 1923 Element element = node.methodName.staticElement; | |
| 1924 if (element != null && element == element.library?.loadLibraryFunction) { | |
| 1925 return scope.unsupportedFeature('Deferred loading'); | |
| 1926 } | |
| 1927 var target = node.target; | |
| 1928 if (node.isCascaded) { | |
| 1929 return buildDecomposableMethodInvocation( | |
| 1930 makeCascadeReceiver(), | |
| 1931 scope.buildName(node.methodName), | |
| 1932 buildArgumentsForInvocation(node), | |
| 1933 element); | |
| 1934 } else if (target is SuperExpression) { | |
| 1935 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 1936 return new ast.SuperMethodInvocation( | |
| 1937 scope.buildName(node.methodName), | |
| 1938 buildArgumentsForInvocation(node), | |
| 1939 scope.resolveConcreteMethod(element)); | |
| 1940 } else if (isLocal(element)) { | |
| 1941 // Set the offset directly: Normally the offset is at the start of the | |
| 1942 // method, but in this case, because we insert a '.call', we want it at | |
| 1943 // the end instead. | |
| 1944 return new ast.MethodInvocation( | |
| 1945 new ast.VariableGet(scope.getVariableReference(element)), | |
| 1946 callName, | |
| 1947 buildArgumentsForInvocation(node), | |
| 1948 scope.resolveInterfaceFunctionCall(element)) | |
| 1949 ..fileOffset = node.methodName.end; | |
| 1950 } else if (isStaticMethod(element)) { | |
| 1951 var method = scope.resolveConcreteMethod(element); | |
| 1952 var arguments = buildArgumentsForInvocation(node); | |
| 1953 if (method == null || !scope.areArgumentsCompatible(element, arguments)) { | |
| 1954 return scope.buildThrowNoSuchMethodError( | |
| 1955 new ast.NullLiteral(), node.methodName.name, arguments, | |
| 1956 candidateTarget: element); | |
| 1957 } | |
| 1958 return new ast.StaticInvocation(method, arguments); | |
| 1959 } else if (isStaticVariableOrGetter(element)) { | |
| 1960 var method = scope.resolveConcreteGet(element, null); | |
| 1961 if (method == null) { | |
| 1962 return scope.buildThrowNoSuchMethodError( | |
| 1963 new ast.NullLiteral(), node.methodName.name, new ast.Arguments([]), | |
| 1964 candidateTarget: element); | |
| 1965 } | |
| 1966 // Set the offset directly: Normally the offset is at the start of the | |
| 1967 // method, but in this case, because we insert a '.call', we want it at | |
| 1968 // the end instead. | |
| 1969 return new ast.MethodInvocation( | |
| 1970 new ast.StaticGet(method), | |
| 1971 callName, | |
| 1972 buildArgumentsForInvocation(node), | |
| 1973 scope.resolveInterfaceFunctionCall(element)) | |
| 1974 ..fileOffset = node.methodName.end; | |
| 1975 } else if (target == null && !scope.allowThis || | |
| 1976 target is Identifier && target.staticElement is ClassElement || | |
| 1977 target is Identifier && target.staticElement is PrefixElement) { | |
| 1978 return scope.buildThrowNoSuchMethodError(new ast.NullLiteral(), | |
| 1979 node.methodName.name, buildArgumentsForInvocation(node), | |
| 1980 candidateTarget: element); | |
| 1981 } else if (target == null) { | |
| 1982 return buildDecomposableMethodInvocation( | |
| 1983 scope.buildThis(), | |
| 1984 scope.buildName(node.methodName), | |
| 1985 buildArgumentsForInvocation(node), | |
| 1986 element); | |
| 1987 } else if (node.operator.value() == '?.') { | |
| 1988 var receiver = makeOrReuseVariable(build(target)); | |
| 1989 return makeLet( | |
| 1990 receiver, | |
| 1991 new ast.ConditionalExpression( | |
| 1992 buildIsNull(new ast.VariableGet(receiver)), | |
| 1993 new ast.NullLiteral(), | |
| 1994 buildDecomposableMethodInvocation( | |
| 1995 new ast.VariableGet(receiver), | |
| 1996 scope.buildName(node.methodName), | |
| 1997 buildArgumentsForInvocation(node), | |
| 1998 element)..fileOffset = node.methodName.offset, | |
| 1999 scope.buildType(node.staticType))); | |
| 2000 } else { | |
| 2001 return buildDecomposableMethodInvocation( | |
| 2002 build(node.target), | |
| 2003 scope.buildName(node.methodName), | |
| 2004 buildArgumentsForInvocation(node), | |
| 2005 element); | |
| 2006 } | |
| 2007 } | |
| 2008 | |
| 2009 ast.Expression visitNamedExpression(NamedExpression node) { | |
| 2010 return scope.internalError('Unexpected named expression'); | |
| 2011 } | |
| 2012 | |
| 2013 ast.Expression visitParenthesizedExpression(ParenthesizedExpression node) { | |
| 2014 return build(node.expression); | |
| 2015 } | |
| 2016 | |
| 2017 bool isInVoidContext(Expression node) { | |
| 2018 AstNode parent = node.parent; | |
| 2019 return parent is ForStatement && | |
| 2020 (parent.updaters.contains(node) || parent.initialization == node) || | |
| 2021 parent is ExpressionStatement || | |
| 2022 parent is ExpressionFunctionBody && scope.bodyHasVoidReturn(parent); | |
| 2023 } | |
| 2024 | |
| 2025 ast.Expression visitPostfixExpression(PostfixExpression node) { | |
| 2026 String operator = node.operator.value(); | |
| 2027 switch (operator) { | |
| 2028 case '++': | |
| 2029 case '--': | |
| 2030 var leftHand = buildLeftHandValue(node.operand); | |
| 2031 var binaryOperator = new ast.Name(operator[0]); | |
| 2032 return leftHand.buildPostfixIncrement(binaryOperator, | |
| 2033 offset: node.operator.offset, | |
| 2034 voidContext: isInVoidContext(node), | |
| 2035 interfaceTarget: scope.resolveInterfaceMethod(node.staticElement)); | |
| 2036 | |
| 2037 default: | |
| 2038 return scope.internalError('Invalid postfix operator $operator'); | |
| 2039 } | |
| 2040 } | |
| 2041 | |
| 2042 ast.Expression visitPrefixExpression(PrefixExpression node) { | |
| 2043 String operator = node.operator.value(); | |
| 2044 switch (operator) { | |
| 2045 case '-': | |
| 2046 case '~': | |
| 2047 var name = new ast.Name(operator == '-' ? 'unary-' : '~'); | |
| 2048 if (node.operand is SuperExpression) { | |
| 2049 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 2050 return new ast.SuperMethodInvocation(name, new ast.Arguments.empty(), | |
| 2051 scope.resolveConcreteMethod(node.staticElement)); | |
| 2052 } | |
| 2053 return new ast.MethodInvocation( | |
| 2054 build(node.operand), | |
| 2055 name, | |
| 2056 new ast.Arguments.empty(), | |
| 2057 scope.resolveInterfaceMethod(node.staticElement)); | |
| 2058 | |
| 2059 case '!': | |
| 2060 return new ast.Not(build(node.operand)); | |
| 2061 | |
| 2062 case '++': | |
| 2063 case '--': | |
| 2064 var leftHand = buildLeftHandValue(node.operand); | |
| 2065 var binaryOperator = new ast.Name(operator[0]); | |
| 2066 return leftHand.buildPrefixIncrement(binaryOperator, | |
| 2067 offset: node.offset, | |
| 2068 interfaceTarget: scope.resolveInterfaceMethod(node.staticElement)); | |
| 2069 | |
| 2070 default: | |
| 2071 return scope.internalError('Invalid prefix operator $operator'); | |
| 2072 } | |
| 2073 } | |
| 2074 | |
| 2075 visitPropertyAccess(PropertyAccess node) { | |
| 2076 Element element = node.propertyName.staticElement; | |
| 2077 Element auxiliary = node.propertyName.auxiliaryElements?.staticElement; | |
| 2078 var getter = scope.resolveInterfaceGet(element, auxiliary); | |
| 2079 var setter = scope.resolveInterfaceSet(element, auxiliary); | |
| 2080 Expression target = node.target; | |
| 2081 if (node.isCascaded) { | |
| 2082 return PropertyAccessor.make(makeCascadeReceiver(), | |
| 2083 scope.buildName(node.propertyName), getter, setter); | |
| 2084 } else if (node.target is SuperExpression) { | |
| 2085 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 2086 return new SuperPropertyAccessor( | |
| 2087 scope.buildName(node.propertyName), | |
| 2088 scope.resolveConcreteGet(element, auxiliary), | |
| 2089 scope.resolveConcreteSet(element, auxiliary)); | |
| 2090 } else if (target is Identifier && target.staticElement is ClassElement) { | |
| 2091 // Note that this case also covers null-aware static access on a class, | |
| 2092 // which is equivalent to a regular static access. | |
| 2093 return scope.staticAccess(node.propertyName.name, element, auxiliary); | |
| 2094 } else if (node.operator.value() == '?.') { | |
| 2095 return new NullAwarePropertyAccessor( | |
| 2096 build(target), | |
| 2097 scope.buildName(node.propertyName), | |
| 2098 getter, | |
| 2099 setter, | |
| 2100 scope.buildType(node.staticType)); | |
| 2101 } else { | |
| 2102 return PropertyAccessor.make( | |
| 2103 build(target), scope.buildName(node.propertyName), getter, setter); | |
| 2104 } | |
| 2105 } | |
| 2106 | |
| 2107 ast.Expression visitRethrowExpression(RethrowExpression node) { | |
| 2108 return new ast.Rethrow(); | |
| 2109 } | |
| 2110 | |
| 2111 ast.Expression visitSuperExpression(SuperExpression node) { | |
| 2112 return scope | |
| 2113 .emitCompileTimeError(CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT); | |
| 2114 } | |
| 2115 | |
| 2116 ast.Expression visitThisExpression(ThisExpression node) { | |
| 2117 return scope.buildThis(); | |
| 2118 } | |
| 2119 | |
| 2120 ast.Expression visitThrowExpression(ThrowExpression node) { | |
| 2121 return new ast.Throw(build(node.expression)); | |
| 2122 } | |
| 2123 | |
| 2124 ast.Expression visitListLiteral(ListLiteral node) { | |
| 2125 ast.DartType type = node.typeArguments?.arguments?.isNotEmpty ?? false | |
| 2126 ? scope.buildTypeAnnotation(node.typeArguments.arguments[0]) | |
| 2127 : scope.getInferredTypeArgument(node, 0); | |
| 2128 return new ast.ListLiteral(node.elements.map(build).toList(), | |
| 2129 typeArgument: type, isConst: node.constKeyword != null); | |
| 2130 } | |
| 2131 | |
| 2132 ast.Expression visitMapLiteral(MapLiteral node) { | |
| 2133 ast.DartType key, value; | |
| 2134 if (node.typeArguments != null && node.typeArguments.arguments.length > 1) { | |
| 2135 key = scope.buildTypeAnnotation(node.typeArguments.arguments[0]); | |
| 2136 value = scope.buildTypeAnnotation(node.typeArguments.arguments[1]); | |
| 2137 } else { | |
| 2138 key = scope.getInferredTypeArgument(node, 0); | |
| 2139 value = scope.getInferredTypeArgument(node, 1); | |
| 2140 } | |
| 2141 return new ast.MapLiteral(node.entries.map(buildMapEntry).toList(), | |
| 2142 keyType: key, valueType: value, isConst: node.constKeyword != null); | |
| 2143 } | |
| 2144 | |
| 2145 ast.MapEntry buildMapEntry(MapLiteralEntry node) { | |
| 2146 return new ast.MapEntry(build(node.key), build(node.value)); | |
| 2147 } | |
| 2148 | |
| 2149 ast.Expression visitExpression(Expression node) { | |
| 2150 return scope.internalError('Unhandled expression ${node.runtimeType}'); | |
| 2151 } | |
| 2152 } | |
| 2153 | |
| 2154 class StringLiteralPartBuilder extends GeneralizingAstVisitor<Null> { | |
| 2155 final ExpressionScope scope; | |
| 2156 final List<ast.Expression> output; | |
| 2157 StringLiteralPartBuilder(this.scope, this.output); | |
| 2158 | |
| 2159 void build(Expression node) { | |
| 2160 node.accept(this); | |
| 2161 } | |
| 2162 | |
| 2163 void buildInterpolationElement(InterpolationElement node) { | |
| 2164 node.accept(this); | |
| 2165 } | |
| 2166 | |
| 2167 visitSimpleStringLiteral(SimpleStringLiteral node) { | |
| 2168 output.add(new ast.StringLiteral(node.value)); | |
| 2169 } | |
| 2170 | |
| 2171 visitAdjacentStrings(AdjacentStrings node) { | |
| 2172 node.strings.forEach(build); | |
| 2173 } | |
| 2174 | |
| 2175 visitStringInterpolation(StringInterpolation node) { | |
| 2176 node.elements.forEach(buildInterpolationElement); | |
| 2177 } | |
| 2178 | |
| 2179 visitInterpolationString(InterpolationString node) { | |
| 2180 output.add(new ast.StringLiteral(node.value)); | |
| 2181 } | |
| 2182 | |
| 2183 visitInterpolationExpression(InterpolationExpression node) { | |
| 2184 output.add(scope.buildExpression(node.expression)); | |
| 2185 } | |
| 2186 } | |
| 2187 | |
| 2188 class TypeAnnotationBuilder extends GeneralizingAstVisitor<ast.DartType> { | |
| 2189 final TypeScope scope; | |
| 2190 | |
| 2191 TypeAnnotationBuilder(this.scope); | |
| 2192 | |
| 2193 ast.DartType build(AstNode node) { | |
| 2194 return node.accept(this); | |
| 2195 } | |
| 2196 | |
| 2197 List<ast.DartType> buildList(Iterable<AstNode> node) { | |
| 2198 return node.map(build).toList(); | |
| 2199 } | |
| 2200 | |
| 2201 /// Replace unbound type variables in [type] with 'dynamic' and convert | |
| 2202 /// to an [ast.DartType]. | |
| 2203 ast.DartType buildClosedTypeFromDartType(DartType type) { | |
| 2204 return convertType(type, <TypeParameterElement>[]); | |
| 2205 } | |
| 2206 | |
| 2207 /// Convert to an [ast.DartType] and keep type variables. | |
| 2208 ast.DartType buildFromDartType(DartType type) { | |
| 2209 return convertType(type, null); | |
| 2210 } | |
| 2211 | |
| 2212 /// True if [parameter] should not be reified, because spec mode does not | |
| 2213 /// currently reify generic method type parameters. | |
| 2214 bool isUnreifiedTypeParameter(TypeParameterElement parameter) { | |
| 2215 return !scope.strongMode && parameter.enclosingElement is! ClassElement; | |
| 2216 } | |
| 2217 | |
| 2218 /// Converts [type] to an [ast.DartType], while replacing unbound type | |
| 2219 /// variables with 'dynamic'. | |
| 2220 /// | |
| 2221 /// If [boundVariables] is null, no type variables are replaced, otherwise all | |
| 2222 /// type variables except those in [boundVariables] are replaced. In other | |
| 2223 /// words, it represents the bound variables, or "all variables" if omitted. | |
| 2224 ast.DartType convertType( | |
| 2225 DartType type, List<TypeParameterElement> boundVariables) { | |
| 2226 if (type is TypeParameterType) { | |
| 2227 if (isUnreifiedTypeParameter(type.element)) { | |
| 2228 return const ast.DynamicType(); | |
| 2229 } | |
| 2230 if (boundVariables == null || boundVariables.contains(type)) { | |
| 2231 var typeParameter = scope.tryGetTypeParameterReference(type.element); | |
| 2232 if (typeParameter == null) { | |
| 2233 // The analyzer sometimes gives us a type parameter that was not | |
| 2234 // bound anywhere. Make sure we do not emit a dangling reference. | |
| 2235 if (type.element.bound != null) { | |
| 2236 return convertType(type.element.bound, []); | |
| 2237 } | |
| 2238 return const ast.DynamicType(); | |
| 2239 } | |
| 2240 if (!scope.allowClassTypeParameters && | |
| 2241 typeParameter.parent is ast.Class) { | |
| 2242 return const ast.InvalidType(); | |
| 2243 } | |
| 2244 return new ast.TypeParameterType(typeParameter); | |
| 2245 } else { | |
| 2246 return const ast.DynamicType(); | |
| 2247 } | |
| 2248 } else if (type is InterfaceType) { | |
| 2249 var classNode = scope.getClassReference(type.element); | |
| 2250 if (type.typeArguments.length == 0) { | |
| 2251 return classNode.rawType; | |
| 2252 } | |
| 2253 if (type.typeArguments.length != classNode.typeParameters.length) { | |
| 2254 log.warning('Type parameter arity error in $type'); | |
| 2255 return const ast.InvalidType(); | |
| 2256 } | |
| 2257 return new ast.InterfaceType( | |
| 2258 classNode, convertTypeList(type.typeArguments, boundVariables)); | |
| 2259 } else if (type is FunctionType) { | |
| 2260 // TODO: Avoid infinite recursion in case of illegal circular typedef. | |
| 2261 boundVariables?.addAll(type.typeParameters); | |
| 2262 var positionals = | |
| 2263 concatenate(type.normalParameterTypes, type.optionalParameterTypes); | |
| 2264 var result = new ast.FunctionType( | |
| 2265 convertTypeList(positionals, boundVariables), | |
| 2266 convertType(type.returnType, boundVariables), | |
| 2267 typeParameters: | |
| 2268 convertTypeParameterList(type.typeFormals, boundVariables), | |
| 2269 namedParameters: | |
| 2270 convertTypeMap(type.namedParameterTypes, boundVariables), | |
| 2271 requiredParameterCount: type.normalParameterTypes.length); | |
| 2272 boundVariables?.removeRange( | |
| 2273 boundVariables.length - type.typeParameters.length, | |
| 2274 boundVariables.length); | |
| 2275 return result; | |
| 2276 } else if (type.isUndefined) { | |
| 2277 log.warning('Unresolved type found in ${scope.location}'); | |
| 2278 return const ast.InvalidType(); | |
| 2279 } else if (type.isVoid) { | |
| 2280 return const ast.VoidType(); | |
| 2281 } else if (type.isDynamic) { | |
| 2282 return const ast.DynamicType(); | |
| 2283 } else { | |
| 2284 log.severe('Unexpected DartType: $type'); | |
| 2285 return const ast.InvalidType(); | |
| 2286 } | |
| 2287 } | |
| 2288 | |
| 2289 static Iterable/*<E>*/ concatenate/*<E>*/( | |
| 2290 Iterable/*<E>*/ x, Iterable/*<E>*/ y) => | |
| 2291 <Iterable<dynamic/*=E*/ >>[x, y].expand((z) => z); | |
| 2292 | |
| 2293 ast.TypeParameter convertTypeParameter(TypeParameterElement typeParameter, | |
| 2294 List<TypeParameterElement> boundVariables) { | |
| 2295 return scope.makeTypeParameter(typeParameter, | |
| 2296 bound: typeParameter.bound == null | |
| 2297 ? scope.defaultTypeParameterBound | |
| 2298 : convertType(typeParameter.bound, boundVariables)); | |
| 2299 } | |
| 2300 | |
| 2301 List<ast.TypeParameter> convertTypeParameterList( | |
| 2302 Iterable<TypeParameterElement> typeParameters, | |
| 2303 List<TypeParameterElement> boundVariables) { | |
| 2304 if (typeParameters.isEmpty) return const <ast.TypeParameter>[]; | |
| 2305 return typeParameters | |
| 2306 .map((tp) => convertTypeParameter(tp, boundVariables)) | |
| 2307 .toList(); | |
| 2308 } | |
| 2309 | |
| 2310 List<ast.DartType> convertTypeList( | |
| 2311 Iterable<DartType> types, List<TypeParameterElement> boundVariables) { | |
| 2312 if (types.isEmpty) return const <ast.DartType>[]; | |
| 2313 return types.map((t) => convertType(t, boundVariables)).toList(); | |
| 2314 } | |
| 2315 | |
| 2316 List<ast.NamedType> convertTypeMap( | |
| 2317 Map<String, DartType> types, List<TypeParameterElement> boundVariables) { | |
| 2318 if (types.isEmpty) return const <ast.NamedType>[]; | |
| 2319 List<ast.NamedType> result = <ast.NamedType>[]; | |
| 2320 types.forEach((name, type) { | |
| 2321 result.add(new ast.NamedType(name, convertType(type, boundVariables))); | |
| 2322 }); | |
| 2323 sortAndRemoveDuplicates(result); | |
| 2324 return result; | |
| 2325 } | |
| 2326 | |
| 2327 ast.DartType visitSimpleIdentifier(SimpleIdentifier node) { | |
| 2328 Element element = node.staticElement; | |
| 2329 switch (ElementKind.of(element)) { | |
| 2330 case ElementKind.CLASS: | |
| 2331 return scope.getClassReference(element).rawType; | |
| 2332 | |
| 2333 case ElementKind.DYNAMIC: | |
| 2334 return const ast.DynamicType(); | |
| 2335 | |
| 2336 case ElementKind.FUNCTION_TYPE_ALIAS: | |
| 2337 FunctionTypeAliasElement functionType = element; | |
| 2338 return buildClosedTypeFromDartType(functionType.type); | |
| 2339 | |
| 2340 case ElementKind.TYPE_PARAMETER: | |
| 2341 var typeParameter = scope.getTypeParameterReference(element); | |
| 2342 if (!scope.allowClassTypeParameters && | |
| 2343 typeParameter.parent is ast.Class) { | |
| 2344 return const ast.InvalidType(); | |
| 2345 } | |
| 2346 if (isUnreifiedTypeParameter(element)) { | |
| 2347 return const ast.DynamicType(); | |
| 2348 } | |
| 2349 return new ast.TypeParameterType(typeParameter); | |
| 2350 | |
| 2351 case ElementKind.COMPILATION_UNIT: | |
| 2352 case ElementKind.CONSTRUCTOR: | |
| 2353 case ElementKind.EXPORT: | |
| 2354 case ElementKind.IMPORT: | |
| 2355 case ElementKind.LABEL: | |
| 2356 case ElementKind.LIBRARY: | |
| 2357 case ElementKind.PREFIX: | |
| 2358 case ElementKind.UNIVERSE: | |
| 2359 case ElementKind.ERROR: // This covers the case where nothing was found. | |
| 2360 case ElementKind.FIELD: | |
| 2361 case ElementKind.TOP_LEVEL_VARIABLE: | |
| 2362 case ElementKind.GETTER: | |
| 2363 case ElementKind.SETTER: | |
| 2364 case ElementKind.METHOD: | |
| 2365 case ElementKind.LOCAL_VARIABLE: | |
| 2366 case ElementKind.PARAMETER: | |
| 2367 case ElementKind.FUNCTION: | |
| 2368 case ElementKind.NAME: | |
| 2369 default: | |
| 2370 log.severe('Invalid type annotation: $element'); | |
| 2371 return const ast.InvalidType(); | |
| 2372 } | |
| 2373 } | |
| 2374 | |
| 2375 visitPrefixedIdentifier(PrefixedIdentifier node) { | |
| 2376 return build(node.identifier); | |
| 2377 } | |
| 2378 | |
| 2379 visitTypeName(TypeName node) { | |
| 2380 return buildFromDartType(node.type); | |
| 2381 } | |
| 2382 | |
| 2383 visitNode(AstNode node) { | |
| 2384 log.severe('Unexpected type annotation: $node'); | |
| 2385 return new ast.InvalidType(); | |
| 2386 } | |
| 2387 } | |
| 2388 | |
| 2389 class InitializerBuilder extends GeneralizingAstVisitor<ast.Initializer> { | |
| 2390 final MemberScope scope; | |
| 2391 | |
| 2392 InitializerBuilder(this.scope); | |
| 2393 | |
| 2394 ast.Initializer build(ConstructorInitializer node) { | |
| 2395 return node.accept(this); | |
| 2396 } | |
| 2397 | |
| 2398 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | |
| 2399 var target = scope.resolveField(node.fieldName.staticElement); | |
| 2400 if (target == null) { | |
| 2401 return new ast.InvalidInitializer(); | |
| 2402 } | |
| 2403 return new ast.FieldInitializer( | |
| 2404 target, scope.buildExpression(node.expression)); | |
| 2405 } | |
| 2406 | |
| 2407 visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
| 2408 var target = scope.resolveConstructor(node.staticElement); | |
| 2409 if (target == null) { | |
| 2410 return new ast.InvalidInitializer(); | |
| 2411 } | |
| 2412 scope.addTransformerFlag(TransformerFlag.superCalls); | |
| 2413 return new ast.SuperInitializer( | |
| 2414 target, scope._expressionBuilder.buildArguments(node.argumentList)); | |
| 2415 } | |
| 2416 | |
| 2417 visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) { | |
| 2418 var target = scope.resolveConstructor(node.staticElement); | |
| 2419 if (target == null) { | |
| 2420 return new ast.InvalidInitializer(); | |
| 2421 } | |
| 2422 return new ast.RedirectingInitializer( | |
| 2423 target, scope._expressionBuilder.buildArguments(node.argumentList)); | |
| 2424 } | |
| 2425 | |
| 2426 visitNode(AstNode node) { | |
| 2427 log.severe('Unexpected constructor initializer: ${node.runtimeType}'); | |
| 2428 return new ast.InvalidInitializer(); | |
| 2429 } | |
| 2430 } | |
| 2431 | |
| 2432 /// Brings a class from hierarchy level to body level. | |
| 2433 // | |
| 2434 // TODO(asgerf): Error recovery during class construction is currently handled | |
| 2435 // locally, but this can in theory break global invariants in the kernel IR. | |
| 2436 // To safely compile code with compile-time errors, we may need a recovery | |
| 2437 // pass to enforce all kernel invariants before it is given to the backend. | |
| 2438 class ClassBodyBuilder extends GeneralizingAstVisitor<Null> { | |
| 2439 final ClassScope scope; | |
| 2440 final ExpressionScope annotationScope; | |
| 2441 final ast.Class currentClass; | |
| 2442 final ClassElement element; | |
| 2443 ast.Library get currentLibrary => currentClass.enclosingLibrary; | |
| 2444 | |
| 2445 ClassBodyBuilder( | |
| 2446 ReferenceLevelLoader loader, ast.Class currentClass, this.element) | |
| 2447 : this.currentClass = currentClass, | |
| 2448 scope = new ClassScope(loader, currentClass.enclosingLibrary), | |
| 2449 annotationScope = | |
| 2450 new ExpressionScope(loader, currentClass.enclosingLibrary); | |
| 2451 | |
| 2452 void build(CompilationUnitMember node) { | |
| 2453 if (node == null) { | |
| 2454 buildBrokenClass(); | |
| 2455 return; | |
| 2456 } | |
| 2457 node.accept(this); | |
| 2458 } | |
| 2459 | |
| 2460 /// Builds an empty class for broken classes that have no AST. | |
| 2461 /// | |
| 2462 /// This should only be used to recover from a compile-time error. | |
| 2463 void buildBrokenClass() { | |
| 2464 currentClass.name = element.name; | |
| 2465 currentClass.supertype = scope.getRootClassReference().asRawSupertype; | |
| 2466 currentClass.constructors.add( | |
| 2467 new ast.Constructor(new ast.FunctionNode(new ast.InvalidStatement())) | |
| 2468 ..fileOffset = element.nameOffset); | |
| 2469 } | |
| 2470 | |
| 2471 void addAnnotations(List<Annotation> annotations) { | |
| 2472 // Class type parameters are not in scope in the annotation list. | |
| 2473 for (var annotation in annotations) { | |
| 2474 currentClass.addAnnotation(annotationScope.buildAnnotation(annotation)); | |
| 2475 } | |
| 2476 } | |
| 2477 | |
| 2478 void _buildMemberBody(ast.Member member, Element element, AstNode node) { | |
| 2479 new MemberBodyBuilder(scope.loader, member, element).build(node); | |
| 2480 } | |
| 2481 | |
| 2482 /// True if the given class member should not be emitted, and does not | |
| 2483 /// correspond to any Kernel member. | |
| 2484 /// | |
| 2485 /// This is true for redirecting factories with a resolved target. These are | |
| 2486 /// always bypassed at the call site. | |
| 2487 bool _isIgnoredMember(ClassMember node) { | |
| 2488 if (node is ConstructorDeclaration && node.factoryKeyword != null) { | |
| 2489 var element = resolutionMap.elementDeclaredByConstructorDeclaration(node); | |
| 2490 return element.redirectedConstructor != null && | |
| 2491 (element.isSynthetic || scope.loader.ignoreRedirectingFactories); | |
| 2492 } else { | |
| 2493 return false; | |
| 2494 } | |
| 2495 } | |
| 2496 | |
| 2497 visitClassDeclaration(ClassDeclaration node) { | |
| 2498 addAnnotations(node.metadata); | |
| 2499 ast.Class classNode = currentClass; | |
| 2500 assert(classNode.members.isEmpty); // All members will be added here. | |
| 2501 | |
| 2502 bool foundConstructor = false; | |
| 2503 for (var member in node.members) { | |
| 2504 if (_isIgnoredMember(member)) continue; | |
| 2505 if (member is FieldDeclaration) { | |
| 2506 for (var variable in member.fields.variables) { | |
| 2507 // Ignore fields inserted through error recovery. | |
| 2508 if (variable.isSynthetic || variable.length == 0) continue; | |
| 2509 var field = scope.getMemberReference(variable.element); | |
| 2510 classNode.addMember(field); | |
| 2511 _buildMemberBody(field, variable.element, variable); | |
| 2512 } | |
| 2513 } else { | |
| 2514 var memberNode = scope.getMemberReference(member.element); | |
| 2515 classNode.addMember(memberNode); | |
| 2516 _buildMemberBody(memberNode, member.element, member); | |
| 2517 if (member is ConstructorDeclaration) { | |
| 2518 foundConstructor = true; | |
| 2519 } | |
| 2520 } | |
| 2521 } | |
| 2522 | |
| 2523 if (!foundConstructor) { | |
| 2524 var defaultConstructor = scope.findDefaultConstructor(node.element); | |
| 2525 if (defaultConstructor != null) { | |
| 2526 assert(defaultConstructor.enclosingElement == node.element); | |
| 2527 if (!defaultConstructor.isSynthetic) { | |
| 2528 throw 'Non-synthetic default constructor not in list of members. ' | |
| 2529 '${node} $element $defaultConstructor'; | |
| 2530 } | |
| 2531 var memberNode = scope.getMemberReference(defaultConstructor); | |
| 2532 classNode.addMember(memberNode); | |
| 2533 buildDefaultConstructor(memberNode, defaultConstructor); | |
| 2534 } | |
| 2535 } | |
| 2536 | |
| 2537 addDefaultInstanceFieldInitializers(classNode); | |
| 2538 } | |
| 2539 | |
| 2540 void buildDefaultConstructor( | |
| 2541 ast.Constructor constructor, ConstructorElement element) { | |
| 2542 var function = constructor.function; | |
| 2543 function.body = new ast.EmptyStatement()..parent = function; | |
| 2544 var class_ = element.enclosingElement; | |
| 2545 if (class_.supertype != null) { | |
| 2546 // DESIGN TODO: If the super class is a mixin application, we will link to | |
| 2547 // a constructor not in the immediate super class. This is a problem due | |
| 2548 // to the fact that mixed-in fields come with initializers which need to | |
| 2549 // be executed by a constructor. The mixin transformer takes care of | |
| 2550 // this by making forwarding constructors and the super initializers will | |
| 2551 // be rewritten to use them (see `transformations/mixin_full_resolution`). | |
| 2552 var superConstructor = | |
| 2553 scope.findDefaultConstructor(class_.supertype.element); | |
| 2554 var target = scope.resolveConstructor(superConstructor); | |
| 2555 if (target == null) { | |
| 2556 constructor.initializers | |
| 2557 .add(new ast.InvalidInitializer()..parent = constructor); | |
| 2558 } else { | |
| 2559 var arguments = new ast.Arguments.empty(); | |
| 2560 constructor.initializers.add( | |
| 2561 new ast.SuperInitializer(target, arguments)..parent = constructor); | |
| 2562 } | |
| 2563 } | |
| 2564 } | |
| 2565 | |
| 2566 /// Adds initializers to instance fields that are have no initializer and are | |
| 2567 /// not initialized by all constructors in the class. | |
| 2568 void addDefaultInstanceFieldInitializers(ast.Class node) { | |
| 2569 List<ast.Field> uninitializedFields = new List<ast.Field>(); | |
| 2570 for (var field in node.fields) { | |
| 2571 if (field.initializer != null || field.isStatic) continue; | |
| 2572 uninitializedFields.add(field); | |
| 2573 } | |
| 2574 if (uninitializedFields.isEmpty) return; | |
| 2575 constructorLoop: | |
| 2576 for (var constructor in node.constructors) { | |
| 2577 var remainingFields = uninitializedFields.toSet(); | |
| 2578 for (var initializer in constructor.initializers) { | |
| 2579 if (initializer is ast.FieldInitializer) { | |
| 2580 remainingFields.remove(initializer.field); | |
| 2581 } else if (initializer is ast.RedirectingInitializer) { | |
| 2582 // The target constructor will be checked in another iteration. | |
| 2583 continue constructorLoop; | |
| 2584 } | |
| 2585 } | |
| 2586 for (var field in remainingFields) { | |
| 2587 if (field.initializer == null) { | |
| 2588 field.initializer = new ast.NullLiteral()..parent = field; | |
| 2589 } | |
| 2590 } | |
| 2591 } | |
| 2592 } | |
| 2593 | |
| 2594 /// True for the `values` field of an `enum` class. | |
| 2595 static bool _isValuesField(FieldElement field) => field.name == 'values'; | |
| 2596 | |
| 2597 /// True for the `index` field of an `enum` class. | |
| 2598 static bool _isIndexField(FieldElement field) => field.name == 'index'; | |
| 2599 | |
| 2600 visitEnumDeclaration(EnumDeclaration node) { | |
| 2601 addAnnotations(node.metadata); | |
| 2602 ast.Class classNode = currentClass; | |
| 2603 | |
| 2604 var intType = scope.loader.getCoreClassReference('int').rawType; | |
| 2605 var indexFieldElement = element.fields.firstWhere(_isIndexField); | |
| 2606 ast.Field indexField = scope.getMemberReference(indexFieldElement); | |
| 2607 indexField.type = intType; | |
| 2608 classNode.addMember(indexField); | |
| 2609 | |
| 2610 var stringType = scope.loader.getCoreClassReference('String').rawType; | |
| 2611 ast.Field nameField = new ast.Field( | |
| 2612 new ast.Name('_name', scope.currentLibrary), | |
| 2613 type: stringType, | |
| 2614 isFinal: true, | |
| 2615 fileUri: classNode.fileUri); | |
| 2616 classNode.addMember(nameField); | |
| 2617 | |
| 2618 var indexParameter = new ast.VariableDeclaration('index', type: intType); | |
| 2619 var nameParameter = new ast.VariableDeclaration('name', type: stringType); | |
| 2620 var function = new ast.FunctionNode(new ast.EmptyStatement(), | |
| 2621 positionalParameters: [indexParameter, nameParameter]); | |
| 2622 var superConstructor = scope.loader.getRootClassConstructorReference(); | |
| 2623 var constructor = new ast.Constructor(function, | |
| 2624 name: new ast.Name(''), | |
| 2625 isConst: true, | |
| 2626 initializers: [ | |
| 2627 new ast.FieldInitializer( | |
| 2628 indexField, new ast.VariableGet(indexParameter)), | |
| 2629 new ast.FieldInitializer( | |
| 2630 nameField, new ast.VariableGet(nameParameter)), | |
| 2631 new ast.SuperInitializer(superConstructor, new ast.Arguments.empty()) | |
| 2632 ])..fileOffset = element.nameOffset; | |
| 2633 classNode.addMember(constructor); | |
| 2634 | |
| 2635 int index = 0; | |
| 2636 var enumConstantFields = <ast.Field>[]; | |
| 2637 for (var constant in node.constants) { | |
| 2638 ast.Field field = scope.getMemberReference(constant.element); | |
| 2639 field.initializer = new ast.ConstructorInvocation( | |
| 2640 constructor, | |
| 2641 new ast.Arguments([ | |
| 2642 new ast.IntLiteral(index), | |
| 2643 new ast.StringLiteral('${classNode.name}.${field.name.name}') | |
| 2644 ]), | |
| 2645 isConst: true)..parent = field; | |
| 2646 field.type = classNode.rawType; | |
| 2647 classNode.addMember(field); | |
| 2648 ++index; | |
| 2649 enumConstantFields.add(field); | |
| 2650 } | |
| 2651 | |
| 2652 // Add the 'values' field. | |
| 2653 var valuesFieldElement = element.fields.firstWhere(_isValuesField); | |
| 2654 ast.Field valuesField = scope.getMemberReference(valuesFieldElement); | |
| 2655 var enumType = classNode.rawType; | |
| 2656 valuesField.type = new ast.InterfaceType( | |
| 2657 scope.loader.getCoreClassReference('List'), <ast.DartType>[enumType]); | |
| 2658 valuesField.initializer = new ast.ListLiteral( | |
| 2659 enumConstantFields.map(_makeStaticGet).toList(), | |
| 2660 isConst: true, | |
| 2661 typeArgument: enumType)..parent = valuesField; | |
| 2662 classNode.addMember(valuesField); | |
| 2663 | |
| 2664 // Add the 'toString()' method. | |
| 2665 var body = new ast.ReturnStatement( | |
| 2666 new ast.DirectPropertyGet(new ast.ThisExpression(), nameField)); | |
| 2667 var toStringFunction = new ast.FunctionNode(body, returnType: stringType); | |
| 2668 var toStringMethod = new ast.Procedure( | |
| 2669 new ast.Name('toString'), ast.ProcedureKind.Method, toStringFunction, | |
| 2670 fileUri: classNode.fileUri); | |
| 2671 classNode.addMember(toStringMethod); | |
| 2672 } | |
| 2673 | |
| 2674 visitClassTypeAlias(ClassTypeAlias node) { | |
| 2675 addAnnotations(node.metadata); | |
| 2676 assert(node.withClause != null && node.withClause.mixinTypes.isNotEmpty); | |
| 2677 ast.Class classNode = currentClass; | |
| 2678 for (var constructor in element.constructors) { | |
| 2679 var constructorNode = scope.getMemberReference(constructor); | |
| 2680 classNode.addMember(constructorNode); | |
| 2681 buildMixinConstructor(constructorNode, constructor); | |
| 2682 } | |
| 2683 } | |
| 2684 | |
| 2685 void buildMixinConstructor( | |
| 2686 ast.Constructor constructor, ConstructorElement element) { | |
| 2687 var function = constructor.function; | |
| 2688 function.body = new ast.EmptyStatement()..parent = function; | |
| 2689 // Call the corresponding constructor in super class. | |
| 2690 ClassElement classElement = element.enclosingElement; | |
| 2691 var targetConstructor = classElement.supertype.element.constructors | |
| 2692 .firstWhere((c) => c.name == element.name); | |
| 2693 var positionalArguments = constructor.function.positionalParameters | |
| 2694 .map(_makeVariableGet) | |
| 2695 .toList(); | |
| 2696 var namedArguments = constructor.function.namedParameters | |
| 2697 .map(_makeNamedExpressionFrom) | |
| 2698 .toList(); | |
| 2699 constructor.initializers.add(new ast.SuperInitializer( | |
| 2700 scope.getMemberReference(targetConstructor), | |
| 2701 new ast.Arguments(positionalArguments, named: namedArguments)) | |
| 2702 ..parent = constructor); | |
| 2703 } | |
| 2704 | |
| 2705 visitNode(AstNode node) { | |
| 2706 throw 'Unsupported class declaration: ${node.runtimeType}'; | |
| 2707 } | |
| 2708 } | |
| 2709 | |
| 2710 /// Brings a member from reference level to body level. | |
| 2711 class MemberBodyBuilder extends GeneralizingAstVisitor<Null> { | |
| 2712 final MemberScope scope; | |
| 2713 final Element element; | |
| 2714 ast.Member get currentMember => scope.currentMember; | |
| 2715 | |
| 2716 MemberBodyBuilder( | |
| 2717 ReferenceLevelLoader loader, ast.Member member, this.element) | |
| 2718 : scope = new MemberScope(loader, member); | |
| 2719 | |
| 2720 void build(AstNode node) { | |
| 2721 if (node != null) { | |
| 2722 currentMember.fileEndOffset = node.endToken.offset; | |
| 2723 node.accept(this); | |
| 2724 } else { | |
| 2725 buildBrokenMember(); | |
| 2726 } | |
| 2727 } | |
| 2728 | |
| 2729 /// Builds an empty member. | |
| 2730 /// | |
| 2731 /// This should only be used to recover from a compile-time error. | |
| 2732 void buildBrokenMember() { | |
| 2733 var member = currentMember; | |
| 2734 member.name = new ast.Name(element.name, scope.currentLibrary); | |
| 2735 if (member is ast.Procedure) { | |
| 2736 member.function = new ast.FunctionNode(new ast.InvalidStatement()) | |
| 2737 ..parent = member; | |
| 2738 } else if (member is ast.Constructor) { | |
| 2739 member.function = new ast.FunctionNode(new ast.InvalidStatement()) | |
| 2740 ..parent = member; | |
| 2741 } | |
| 2742 } | |
| 2743 | |
| 2744 void addAnnotations(List<Annotation> annotations) { | |
| 2745 for (var annotation in annotations) { | |
| 2746 currentMember.addAnnotation(scope.buildAnnotation(annotation)); | |
| 2747 } | |
| 2748 } | |
| 2749 | |
| 2750 void handleNativeBody(FunctionBody body) { | |
| 2751 if (body is NativeFunctionBody) { | |
| 2752 currentMember.isExternal = true; | |
| 2753 currentMember.addAnnotation(new ast.ConstructorInvocation( | |
| 2754 scope.loader.getCoreClassConstructorReference('ExternalName', | |
| 2755 library: 'dart:_internal'), | |
| 2756 new ast.Arguments(<ast.Expression>[ | |
| 2757 new ast.StringLiteral(body.stringLiteral.stringValue) | |
| 2758 ]), | |
| 2759 isConst: true)); | |
| 2760 } | |
| 2761 } | |
| 2762 | |
| 2763 visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 2764 if (node.factoryKeyword != null) { | |
| 2765 buildFactoryConstructor(node); | |
| 2766 } else { | |
| 2767 buildGenerativeConstructor(node); | |
| 2768 } | |
| 2769 } | |
| 2770 | |
| 2771 void buildGenerativeConstructor(ConstructorDeclaration node) { | |
| 2772 if (currentMember is! ast.Constructor) { | |
| 2773 buildBrokenMember(); | |
| 2774 return; | |
| 2775 } | |
| 2776 addAnnotations(node.metadata); | |
| 2777 ast.Constructor constructor = currentMember; | |
| 2778 constructor.function = scope.buildFunctionNode(node.parameters, node.body, | |
| 2779 inferredReturnType: const ast.VoidType())..parent = constructor; | |
| 2780 handleNativeBody(node.body); | |
| 2781 if (node.body is EmptyFunctionBody && !constructor.isExternal) { | |
| 2782 var function = constructor.function; | |
| 2783 function.body = new ast.EmptyStatement()..parent = function; | |
| 2784 } | |
| 2785 for (var parameter in node.parameters.parameterElements) { | |
| 2786 if (parameter is FieldFormalParameterElement) { | |
| 2787 ast.Initializer initializer; | |
| 2788 if (parameter.field == null) { | |
| 2789 initializer = new ast.LocalInitializer( | |
| 2790 new ast.VariableDeclaration.forValue(scope | |
| 2791 .buildThrowCompileTimeErrorFromCode( | |
| 2792 CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD, | |
| 2793 [parameter.name]))); | |
| 2794 } else { | |
| 2795 initializer = new ast.FieldInitializer( | |
| 2796 scope.getMemberReference(parameter.field), | |
| 2797 new ast.VariableGet(scope.getVariableReference(parameter))); | |
| 2798 } | |
| 2799 constructor.initializers.add(initializer..parent = constructor); | |
| 2800 } | |
| 2801 } | |
| 2802 bool hasExplicitConstructorCall = false; | |
| 2803 for (var initializer in node.initializers) { | |
| 2804 var node = scope.buildInitializer(initializer); | |
| 2805 constructor.initializers.add(node..parent = constructor); | |
| 2806 if (node is ast.SuperInitializer || node is ast.RedirectingInitializer) { | |
| 2807 hasExplicitConstructorCall = true; | |
| 2808 } | |
| 2809 } | |
| 2810 ClassElement classElement = resolutionMap | |
| 2811 .elementDeclaredByConstructorDeclaration(node) | |
| 2812 .enclosingElement; | |
| 2813 if (classElement.supertype != null && !hasExplicitConstructorCall) { | |
| 2814 ConstructorElement targetElement = | |
| 2815 scope.findDefaultConstructor(classElement.supertype.element); | |
| 2816 ast.Constructor target = scope.resolveConstructor(targetElement); | |
| 2817 ast.Initializer initializer = target == null | |
| 2818 ? new ast.InvalidInitializer() | |
| 2819 : new ast.SuperInitializer( | |
| 2820 target, new ast.Arguments(<ast.Expression>[])); | |
| 2821 constructor.initializers.add(initializer..parent = constructor); | |
| 2822 } else { | |
| 2823 moveSuperInitializerLast(constructor); | |
| 2824 } | |
| 2825 } | |
| 2826 | |
| 2827 void buildFactoryConstructor(ConstructorDeclaration node) { | |
| 2828 if (currentMember is! ast.Procedure) { | |
| 2829 buildBrokenMember(); | |
| 2830 return; | |
| 2831 } | |
| 2832 addAnnotations(node.metadata); | |
| 2833 ast.Procedure procedure = currentMember; | |
| 2834 ClassElement classElement = resolutionMap | |
| 2835 .elementDeclaredByConstructorDeclaration(node) | |
| 2836 .enclosingElement; | |
| 2837 ast.Class classNode = procedure.enclosingClass; | |
| 2838 var types = getFreshTypeParameters(classNode.typeParameters); | |
| 2839 for (int i = 0; i < classElement.typeParameters.length; ++i) { | |
| 2840 scope.localTypeParameters[classElement.typeParameters[i]] = | |
| 2841 types.freshTypeParameters[i]; | |
| 2842 } | |
| 2843 var inferredReturnType = types.freshTypeParameters.isEmpty | |
| 2844 ? classNode.rawType | |
| 2845 : new ast.InterfaceType( | |
| 2846 classNode, | |
| 2847 types.freshTypeParameters | |
| 2848 .map(makeTypeParameterType) | |
| 2849 .toList(growable: false)); | |
| 2850 var function = scope.buildFunctionNode(node.parameters, node.body, | |
| 2851 typeParameters: types.freshTypeParameters, | |
| 2852 inferredReturnType: inferredReturnType); | |
| 2853 procedure.function = function..parent = procedure; | |
| 2854 handleNativeBody(node.body); | |
| 2855 if (node.redirectedConstructor != null) { | |
| 2856 // Add a new synthetic field to [classNode] for representing factory | |
| 2857 // constructors. This is used by the new frontend engine to support | |
| 2858 // resolving source code. | |
| 2859 // | |
| 2860 // The synthetic field looks like this: | |
| 2861 // | |
| 2862 // final _redirecting# = [c1, ..., cn]; | |
| 2863 // | |
| 2864 // Where each c1 ... cn are an instance of [StaticGet] whose target is | |
| 2865 // the redirecting factory created above. The new frontend engine reads | |
| 2866 // this field and rewrites them. | |
| 2867 // | |
| 2868 // TODO(ahe): Generate the correct factory body instead. This requires | |
| 2869 // access to default values from other files, we'll probably never do | |
| 2870 // that in this file, and instead rely on the new compiler for this. | |
| 2871 var element = resolutionMap.elementDeclaredByConstructorDeclaration(node); | |
| 2872 assert(!element.isSynthetic); | |
| 2873 var expression; | |
| 2874 if (node.element.redirectedConstructor != null) { | |
| 2875 assert(!scope.loader.ignoreRedirectingFactories); | |
| 2876 ConstructorElement element = node.element.redirectedConstructor; | |
| 2877 while (element.isFactory && element.redirectedConstructor != null) { | |
| 2878 element = element.redirectedConstructor; | |
| 2879 } | |
| 2880 ast.Member target = scope.getMemberReference(element); | |
| 2881 assert(target != null); | |
| 2882 expression = new ast.Let( | |
| 2883 new ast.VariableDeclaration.forValue(new ast.StaticGet(target)), | |
| 2884 new ast.InvalidExpression()); | |
| 2885 ast.Name constructors = | |
| 2886 new ast.Name("_redirecting#", scope.currentLibrary); | |
| 2887 ast.Field constructorsField; | |
| 2888 for (ast.Field field in classNode.fields) { | |
| 2889 if (field.name == constructors) { | |
| 2890 constructorsField = field; | |
| 2891 break; | |
| 2892 } | |
| 2893 } | |
| 2894 if (constructorsField == null) { | |
| 2895 ast.ListLiteral literal = new ast.ListLiteral(<ast.Expression>[]); | |
| 2896 constructorsField = new ast.Field(constructors, | |
| 2897 isStatic: true, | |
| 2898 initializer: literal, | |
| 2899 fileUri: classNode.fileUri)..fileOffset = classNode.fileOffset; | |
| 2900 classNode.addMember(constructorsField); | |
| 2901 } | |
| 2902 ast.ListLiteral literal = constructorsField.initializer; | |
| 2903 literal.expressions.add(new ast.StaticGet(procedure)..parent = literal); | |
| 2904 } else { | |
| 2905 var name = node.redirectedConstructor.type.name.name; | |
| 2906 if (node.redirectedConstructor.name != null) { | |
| 2907 name += '.' + node.redirectedConstructor.name.name; | |
| 2908 } | |
| 2909 // TODO(asgerf): Sometimes a TypeError should be thrown. | |
| 2910 expression = scope.buildThrowNoSuchMethodError( | |
| 2911 new ast.NullLiteral(), name, new ast.Arguments.empty()); | |
| 2912 } | |
| 2913 var function = procedure.function; | |
| 2914 function.body = new ast.ExpressionStatement(expression) | |
| 2915 ..parent = function; | |
| 2916 } | |
| 2917 } | |
| 2918 | |
| 2919 visitMethodDeclaration(MethodDeclaration node) { | |
| 2920 addAnnotations(node.metadata); | |
| 2921 ast.Procedure procedure = currentMember; | |
| 2922 procedure.function = scope.buildFunctionNode(node.parameters, node.body, | |
| 2923 returnType: node.returnType, | |
| 2924 inferredReturnType: scope.buildType( | |
| 2925 resolutionMap.elementDeclaredByMethodDeclaration(node).returnType), | |
| 2926 typeParameters: scope.buildOptionalTypeParameterList( | |
| 2927 node.typeParameters, | |
| 2928 strongModeOnly: true))..parent = procedure; | |
| 2929 handleNativeBody(node.body); | |
| 2930 } | |
| 2931 | |
| 2932 visitVariableDeclaration(VariableDeclaration node) { | |
| 2933 addAnnotations(node.metadata); | |
| 2934 ast.Field field = currentMember; | |
| 2935 field.type = scope.buildType( | |
| 2936 resolutionMap.elementDeclaredByVariableDeclaration(node).type); | |
| 2937 if (node.initializer != null) { | |
| 2938 field.initializer = scope.buildTopLevelExpression(node.initializer) | |
| 2939 ..parent = field; | |
| 2940 } else if (field.isStatic) { | |
| 2941 // Add null initializer to static fields without an initializer. | |
| 2942 // For instance fields, this is handled when building the class. | |
| 2943 field.initializer = new ast.NullLiteral()..parent = field; | |
| 2944 } | |
| 2945 } | |
| 2946 | |
| 2947 visitFunctionDeclaration(FunctionDeclaration node) { | |
| 2948 addAnnotations(node.metadata); | |
| 2949 var function = node.functionExpression; | |
| 2950 ast.Procedure procedure = currentMember; | |
| 2951 procedure.function = scope.buildFunctionNode( | |
| 2952 function.parameters, function.body, | |
| 2953 returnType: node.returnType, | |
| 2954 typeParameters: scope.buildOptionalTypeParameterList( | |
| 2955 function.typeParameters, | |
| 2956 strongModeOnly: true))..parent = procedure; | |
| 2957 handleNativeBody(function.body); | |
| 2958 } | |
| 2959 | |
| 2960 visitNode(AstNode node) { | |
| 2961 log.severe('Unexpected class or library member: $node'); | |
| 2962 } | |
| 2963 } | |
| 2964 | |
| 2965 /// Internal exception thrown from the expression or statement builder when a | |
| 2966 /// compilation error is found. | |
| 2967 /// | |
| 2968 /// This is then caught at the function level to replace the entire function | |
| 2969 /// body (or field initializer) with a throw. | |
| 2970 class _CompilationError { | |
| 2971 String message; | |
| 2972 | |
| 2973 _CompilationError(this.message); | |
| 2974 } | |
| 2975 | |
| 2976 /// Constructor alias for [ast.TypeParameterType], use instead of a closure. | |
| 2977 ast.DartType makeTypeParameterType(ast.TypeParameter parameter) { | |
| 2978 return new ast.TypeParameterType(parameter); | |
| 2979 } | |
| 2980 | |
| 2981 /// Constructor alias for [ast.VariableGet], use instead of a closure. | |
| 2982 ast.VariableGet _makeVariableGet(ast.VariableDeclaration variable) { | |
| 2983 return new ast.VariableGet(variable); | |
| 2984 } | |
| 2985 | |
| 2986 /// Constructor alias for [ast.StaticGet], use instead of a closure. | |
| 2987 ast.StaticGet _makeStaticGet(ast.Field field) { | |
| 2988 return new ast.StaticGet(field); | |
| 2989 } | |
| 2990 | |
| 2991 /// Create a named expression with the name and value of the given variable. | |
| 2992 ast.NamedExpression _makeNamedExpressionFrom(ast.VariableDeclaration variable) { | |
| 2993 return new ast.NamedExpression(variable.name, new ast.VariableGet(variable)); | |
| 2994 } | |
| 2995 | |
| 2996 /// A [StaticAccessor] that throws a NoSuchMethodError when a suitable target | |
| 2997 /// could not be resolved. | |
| 2998 class _StaticAccessor extends StaticAccessor { | |
| 2999 final ExpressionScope scope; | |
| 3000 final String name; | |
| 3001 | |
| 3002 _StaticAccessor( | |
| 3003 this.scope, this.name, ast.Member readTarget, ast.Member writeTarget) | |
| 3004 : super(readTarget, writeTarget); | |
| 3005 | |
| 3006 @override | |
| 3007 makeInvalidRead() { | |
| 3008 return scope.buildThrowNoSuchMethodError( | |
| 3009 new ast.NullLiteral(), name, new ast.Arguments([])); | |
| 3010 } | |
| 3011 | |
| 3012 @override | |
| 3013 makeInvalidWrite(ast.Expression value) { | |
| 3014 return scope.buildThrowNoSuchMethodError( | |
| 3015 new ast.NullLiteral(), name, new ast.Arguments([value])); | |
| 3016 } | |
| 3017 } | |
| 3018 | |
| 3019 bool isTopLevelFunction(Element element) { | |
| 3020 return element is FunctionElement && | |
| 3021 element.enclosingElement is CompilationUnitElement; | |
| 3022 } | |
| 3023 | |
| 3024 bool isLocalFunction(Element element) { | |
| 3025 return element is FunctionElement && | |
| 3026 element.enclosingElement is! CompilationUnitElement && | |
| 3027 element.enclosingElement is! LibraryElement; | |
| 3028 } | |
| 3029 | |
| 3030 bool isLocal(Element element) { | |
| 3031 return isLocalFunction(element) || | |
| 3032 element is LocalVariableElement || | |
| 3033 element is ParameterElement; | |
| 3034 } | |
| 3035 | |
| 3036 bool isInstanceMethod(Element element) { | |
| 3037 return element is MethodElement && !element.isStatic; | |
| 3038 } | |
| 3039 | |
| 3040 bool isStaticMethod(Element element) { | |
| 3041 return element is MethodElement && element.isStatic || | |
| 3042 isTopLevelFunction(element); | |
| 3043 } | |
| 3044 | |
| 3045 bool isStaticVariableOrGetter(Element element) { | |
| 3046 element = desynthesizeGetter(element); | |
| 3047 return element is FieldElement && element.isStatic || | |
| 3048 element is TopLevelVariableElement; | |
| 3049 } | |
| 3050 | |
| 3051 Element desynthesizeGetter(Element element) { | |
| 3052 if (element == null || !element.isSynthetic) return element; | |
| 3053 if (element is PropertyAccessorElement) return element.variable; | |
| 3054 if (element is FieldElement) return element.getter; | |
| 3055 return element; | |
| 3056 } | |
| 3057 | |
| 3058 Element desynthesizeSetter(Element element) { | |
| 3059 if (element == null || !element.isSynthetic) return element; | |
| 3060 if (element is PropertyAccessorElement) return element.variable; | |
| 3061 if (element is FieldElement) return element.setter; | |
| 3062 return element; | |
| 3063 } | |
| 3064 | |
| 3065 void sortAndRemoveDuplicates/*<T extends Comparable<T>>*/(List/*<T>*/ list) { | |
| 3066 list.sort(); | |
| 3067 int deleted = 0; | |
| 3068 for (int i = 1; i < list.length; ++i) { | |
| 3069 var item = list[i]; | |
| 3070 if (list[i - 1].compareTo(item) == 0) { | |
| 3071 ++deleted; | |
| 3072 } else if (deleted > 0) { | |
| 3073 list[i - deleted] = item; | |
| 3074 } | |
| 3075 } | |
| 3076 if (deleted > 0) { | |
| 3077 list.length -= deleted; | |
| 3078 } | |
| 3079 } | |
| OLD | NEW |