OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library fasta.body_builder; |
| 6 |
| 7 import 'package:dart_parser/src/parser.dart' show |
| 8 FormalParameterType, |
| 9 optional; |
| 10 |
| 11 import 'package:dart_parser/src/error_kind.dart' show |
| 12 ErrorKind; |
| 13 |
| 14 import 'package:kernel/ast.dart'; |
| 15 |
| 16 import 'package:kernel/clone.dart' show |
| 17 CloneVisitor; |
| 18 |
| 19 import 'package:kernel/transformations/flags.dart' show |
| 20 TransformerFlag; |
| 21 |
| 22 import 'package:kernel/class_hierarchy.dart' show |
| 23 ClassHierarchy; |
| 24 |
| 25 import 'package:kernel/core_types.dart' show |
| 26 CoreTypes; |
| 27 |
| 28 import 'package:dart_scanner/src/token.dart' show |
| 29 BeginGroupToken, |
| 30 ErrorToken, |
| 31 Token, |
| 32 isBinaryOperator, |
| 33 isMinusOperator; |
| 34 |
| 35 import '../errors.dart' show |
| 36 InputError, |
| 37 internalError; |
| 38 |
| 39 import '../errors.dart' as errors show |
| 40 inputError; |
| 41 |
| 42 import '../source/scope_listener.dart' show |
| 43 JumpTargetKind, |
| 44 NullValue, |
| 45 ScopeListener; |
| 46 |
| 47 import '../builder/scope.dart' show |
| 48 AccessErrorBuilder, |
| 49 AmbiguousBuilder, |
| 50 Scope; |
| 51 |
| 52 import '../source/outline_builder.dart' show |
| 53 asyncMarkerFromTokens; |
| 54 |
| 55 import 'builder_accessors.dart'; |
| 56 |
| 57 import 'frontend_accessors.dart' show |
| 58 buildIsNull, |
| 59 makeBinary, |
| 60 makeLet; |
| 61 |
| 62 import 'builder_accessors.dart' as builder_accessors show |
| 63 throwNoSuchMethodError; |
| 64 |
| 65 import '../quote.dart' show |
| 66 Quote, |
| 67 analyzeQuote, |
| 68 unescape, |
| 69 unescapeFirstStringPart, |
| 70 unescapeLastStringPart, |
| 71 unescapeString; |
| 72 |
| 73 import '../modifier.dart' show |
| 74 Modifier, |
| 75 constMask, |
| 76 finalMask; |
| 77 |
| 78 import 'redirecting_factory_body.dart' show |
| 79 getRedirectionTarget; |
| 80 |
| 81 import 'kernel_builder.dart'; |
| 82 |
| 83 const bool showNits = false; |
| 84 |
| 85 final Name callName = new Name("call"); |
| 86 |
| 87 final Name plusName = new Name("+"); |
| 88 |
| 89 final Name minusName = new Name("-"); |
| 90 |
| 91 final Name multiplyName = new Name("*"); |
| 92 |
| 93 final Name divisionName = new Name("/"); |
| 94 |
| 95 final Name percentName = new Name("%"); |
| 96 |
| 97 final Name ampersandName = new Name("&"); |
| 98 |
| 99 final Name leftShiftName = new Name("<<"); |
| 100 |
| 101 final Name rightShiftName = new Name(">>"); |
| 102 |
| 103 final Name caretName = new Name("^"); |
| 104 |
| 105 final Name barName = new Name("|"); |
| 106 |
| 107 final Name mustacheName = new Name("~/"); |
| 108 |
| 109 class BodyBuilder extends ScopeListener<JumpTarget> implements BuilderHelper { |
| 110 final KernelLibraryBuilder library; |
| 111 |
| 112 final MemberBuilder member; |
| 113 |
| 114 final KernelClassBuilder classBuilder; |
| 115 |
| 116 final ClassHierarchy hierarchy; |
| 117 |
| 118 final CoreTypes coreTypes; |
| 119 |
| 120 final bool isInstanceMember; |
| 121 |
| 122 final Map<String, FieldInitializer> fieldInitializers = |
| 123 <String, FieldInitializer>{}; |
| 124 |
| 125 final Scope enclosingScope; |
| 126 |
| 127 Scope formalParameterScope; |
| 128 |
| 129 bool isFirstIdentifier = false; |
| 130 |
| 131 bool hasParserError = false; |
| 132 |
| 133 bool inInitializer = false; |
| 134 |
| 135 bool inCatchClause = false; |
| 136 |
| 137 int functionNestingLevel = 0; |
| 138 |
| 139 Statement compileTimeErrorInTry; |
| 140 |
| 141 Statement compileTimeErrorInLoopOrSwitch; |
| 142 |
| 143 Scope switchScope; |
| 144 |
| 145 CloneVisitor cloner; |
| 146 |
| 147 BodyBuilder(this.library, this.member, Scope scope, this.formalParameterScope, |
| 148 this.hierarchy, this.coreTypes, this.classBuilder, this.isInstanceMember) |
| 149 : enclosingScope = scope, |
| 150 super(scope); |
| 151 |
| 152 bool get inConstructor { |
| 153 return functionNestingLevel == 0 && member is KernelConstructorBuilder; |
| 154 } |
| 155 |
| 156 bool get isInstanceContext { |
| 157 return isInstanceMember || member is KernelConstructorBuilder; |
| 158 } |
| 159 |
| 160 void push(Object node) { |
| 161 isFirstIdentifier = false; |
| 162 inInitializer = false; |
| 163 super.push(node); |
| 164 } |
| 165 |
| 166 Expression popForValue() => toValue(pop()); |
| 167 |
| 168 Expression popForEffect() => toEffect(pop()); |
| 169 |
| 170 Expression toValue(Object node) { |
| 171 if (node is UnresolvedIdentifier) { |
| 172 return throwNoSuchMethodError(node.name.name, new Arguments.empty(), |
| 173 node.fileOffset, isGetter: true); |
| 174 } else if (node is BuilderAccessor) { |
| 175 return node.buildSimpleRead(); |
| 176 } else if (node is TypeVariableBuilder) { |
| 177 TypeParameterType type = node.buildTypesWithBuiltArguments(null); |
| 178 if (!isInstanceContext && type.parameter.parent is Class) { |
| 179 return buildCompileTimeError( |
| 180 "Type variables can only be used in instance methods."); |
| 181 } else { |
| 182 return new TypeLiteral(type); |
| 183 } |
| 184 } else if (node is TypeDeclarationBuilder) { |
| 185 return new TypeLiteral(node.buildTypesWithBuiltArguments(null)); |
| 186 } else if (node is KernelTypeBuilder) { |
| 187 return new TypeLiteral(node.build()); |
| 188 } else if (node is Expression) { |
| 189 return node; |
| 190 } else if (node is PrefixBuilder) { |
| 191 return buildCompileTimeError("A library can't be used as an expression."); |
| 192 } else { |
| 193 return internalError("Unhandled: ${node.runtimeType}"); |
| 194 } |
| 195 } |
| 196 |
| 197 Expression toEffect(Object node) { |
| 198 if (node is BuilderAccessor) return node.buildForEffect(); |
| 199 return toValue(node); |
| 200 } |
| 201 |
| 202 List<Expression> popListForValue(int n) { |
| 203 List<Expression> list = |
| 204 new List<Expression>.filled(n, null, growable: true); |
| 205 for (int i = n - 1; i >= 0; i--) { |
| 206 list[i] = popForValue(); |
| 207 } |
| 208 return list; |
| 209 } |
| 210 |
| 211 List<Expression> popListForEffect(int n) { |
| 212 List<Expression> list = |
| 213 new List<Expression>.filled(n, null, growable: true); |
| 214 for (int i = n - 1; i >= 0; i--) { |
| 215 list[i] = popForEffect(); |
| 216 } |
| 217 return list; |
| 218 } |
| 219 |
| 220 Block popBlock(int count) { |
| 221 List<Statement> statements = popList(count) ?? <Statement>[]; |
| 222 List<Statement> copy; |
| 223 for (int i = 0; i < statements.length; i++) { |
| 224 var statement = statements[i]; |
| 225 if (statement is List) { |
| 226 copy ??= new List<Statement>.from(statements.getRange(0, i)); |
| 227 copy.addAll(statement); |
| 228 } else if (copy != null) { |
| 229 copy.add(statement); |
| 230 } |
| 231 } |
| 232 return new Block(copy ?? statements); |
| 233 } |
| 234 |
| 235 Statement popStatementIfNotNull(Object value) { |
| 236 return value == null ? null : popStatement(); |
| 237 } |
| 238 |
| 239 Statement popStatement() { |
| 240 var statement = pop(); |
| 241 if (statement is List) { |
| 242 return new Block(new List<Statement>.from(statement)); |
| 243 } else { |
| 244 return statement; |
| 245 } |
| 246 } |
| 247 |
| 248 void ignore(Unhandled value) { |
| 249 pop(); |
| 250 } |
| 251 |
| 252 void enterSwitchScope() { |
| 253 push(switchScope ?? NullValue.SwitchScope); |
| 254 switchScope = scope; |
| 255 } |
| 256 |
| 257 void exitSwitchScope() { |
| 258 switchScope = pop(); |
| 259 } |
| 260 |
| 261 Uri get uri => library.fileUri ?? library.uri; |
| 262 |
| 263 JumpTarget createJumpTarget(JumpTargetKind kind) => new JumpTarget(kind); |
| 264 |
| 265 void endMetadata(Token beginToken, Token periodBeforeName, Token endToken) { |
| 266 debugEvent("Metadata"); |
| 267 pop(); // Arguments. |
| 268 popIfNotNull(periodBeforeName); // Postfix. |
| 269 pop(); // Type arguments. |
| 270 pop(); // Expression or type name (depends on arguments). |
| 271 // TODO(ahe): Implement metadata on local declarations. |
| 272 } |
| 273 |
| 274 void endMetadataStar(int count, bool forParameter) { |
| 275 debugEvent("MetadataStar"); |
| 276 push(NullValue.Metadata); |
| 277 } |
| 278 |
| 279 void endTopLevelFields(int count, Token beginToken, Token endToken) { |
| 280 debugEvent("TopLevelFields"); |
| 281 doFields(count); |
| 282 // There's no metadata here because of a slight assymetry between |
| 283 // [parseTopLevelMember] and [parseMember]. This assymetry leads to |
| 284 // DietListener discarding top-level member metadata. |
| 285 } |
| 286 |
| 287 void endFields(int count, Token beginToken, Token endToken) { |
| 288 debugEvent("Fields"); |
| 289 doFields(count); |
| 290 pop(); // Metadata. |
| 291 } |
| 292 |
| 293 void doFields(int count) { |
| 294 List nodes = popList(count); |
| 295 pop(); // Type. |
| 296 pop(); // Modifiers. |
| 297 for (var node in nodes) { |
| 298 if (node is Identifier) { |
| 299 // Ignore, there's no initializer. |
| 300 } else if (node is VariableDeclaration) { |
| 301 FieldBuilder field; |
| 302 if (classBuilder != null) { |
| 303 field = classBuilder.members[node.name]; |
| 304 } else { |
| 305 field = library.members[node.name]; |
| 306 } |
| 307 if (field.next != null) { |
| 308 // TODO(ahe): This can happen, for example, if a final field is |
| 309 // combined with a setter. |
| 310 internalError( |
| 311 "Unhandled: '${field.name}' has more than one declaration."); |
| 312 } |
| 313 field.initializer = node.initializer; |
| 314 } else { |
| 315 internalError("Unhandled: ${node.runtimeType}"); |
| 316 } |
| 317 } |
| 318 } |
| 319 |
| 320 void endMember() { |
| 321 debugEvent("Member"); |
| 322 checkEmpty(); |
| 323 } |
| 324 |
| 325 void endFunctionBody(int count, Token beginToken, Token endToken) { |
| 326 debugEvent("FunctionBody"); |
| 327 if (beginToken == null) { |
| 328 assert(count == 0); |
| 329 push(NullValue.Block); |
| 330 } else { |
| 331 Block block = popBlock(count); |
| 332 exitLocalScope(); |
| 333 push(block); |
| 334 } |
| 335 } |
| 336 |
| 337 void prepareInitializers() { |
| 338 scope = formalParameterScope; |
| 339 assert(fieldInitializers.isEmpty); |
| 340 final member = this.member; |
| 341 if (member is KernelConstructorBuilder) { |
| 342 Constructor constructor = member.constructor; |
| 343 classBuilder.members.forEach((String name, Builder builder) { |
| 344 if (builder is KernelFieldBuilder && builder.isInstanceMember) { |
| 345 // TODO(ahe): Compute initializers (as in `field = initializer`). |
| 346 fieldInitializers[name] = new FieldInitializer(builder.field, null) |
| 347 ..parent = constructor; |
| 348 } |
| 349 }); |
| 350 if (member.formals != null) { |
| 351 for (KernelFormalParameterBuilder formal in member.formals) { |
| 352 if (formal.hasThis) { |
| 353 FieldInitializer initializer = fieldInitializers[formal.name]; |
| 354 if (initializer != null) { |
| 355 fieldInitializers.remove(formal.name); |
| 356 initializer.value = new VariableGet(formal.declaration) |
| 357 ..parent = initializer; |
| 358 member.addInitializer(initializer); |
| 359 } |
| 360 } |
| 361 } |
| 362 } |
| 363 } |
| 364 } |
| 365 |
| 366 void beginConstructorInitializer(Token token) { |
| 367 debugEvent("beginConstructorInitializer"); |
| 368 inInitializer = true; |
| 369 } |
| 370 |
| 371 void endConstructorInitializer(Token token) { |
| 372 debugEvent("endConstructorInitializer"); |
| 373 assert(!inInitializer); |
| 374 final member = this.member; |
| 375 var node = pop(); |
| 376 Initializer initializer; |
| 377 if (node is Initializer) { |
| 378 initializer = node; |
| 379 } else if (node is BuilderAccessor) { |
| 380 initializer = node.buildFieldInitializer(fieldInitializers); |
| 381 } else if (node is ConstructorInvocation) { |
| 382 initializer = new SuperInitializer(node.target, node.arguments); |
| 383 } else { |
| 384 if (node is !Throw) { |
| 385 node = wrapInvalid(node); |
| 386 } |
| 387 initializer = |
| 388 new LocalInitializer(new VariableDeclaration.forValue(node)); |
| 389 } |
| 390 if (member is KernelConstructorBuilder) { |
| 391 member.addInitializer(initializer); |
| 392 } else { |
| 393 inputError("Can't have initializers: ${member.name}", token.charOffset); |
| 394 } |
| 395 } |
| 396 |
| 397 void handleNoInitializers() { |
| 398 debugEvent("NoInitializers"); |
| 399 } |
| 400 |
| 401 void endInitializers(int count, Token beginToken, Token endToken) { |
| 402 debugEvent("Initializers"); |
| 403 } |
| 404 |
| 405 void finishFunction(FormalParameters formals, |
| 406 AsyncMarker asyncModifier, Statement body) { |
| 407 debugEvent("finishFunction"); |
| 408 KernelFunctionBuilder builder = member; |
| 409 if (builder is KernelConstructorBuilder) { |
| 410 if (asyncModifier != AsyncMarker.Sync) { |
| 411 // TODO(ahe): Change this to a null check. |
| 412 inputError("Can't be marked as ${asyncModifier}: ${builder.name}", |
| 413 body?.fileOffset); |
| 414 } |
| 415 } else if (builder is KernelProcedureBuilder) { |
| 416 builder.asyncModifier = asyncModifier; |
| 417 } else { |
| 418 internalError("Unhandled: ${builder.runtimeType}"); |
| 419 } |
| 420 builder.body = body; |
| 421 if (formals?.optional != null) { |
| 422 Iterator<FormalParameterBuilder> formalBuilders = |
| 423 builder.formals.skip(formals.required.length).iterator; |
| 424 for (VariableDeclaration parameter in formals.optional.formals) { |
| 425 bool hasMore = formalBuilders.moveNext(); |
| 426 assert(hasMore); |
| 427 VariableDeclaration realParameter = formalBuilders.current.target; |
| 428 Expression initializer = parameter.initializer ?? new NullLiteral(); |
| 429 realParameter.initializer = initializer |
| 430 ..parent = realParameter; |
| 431 } |
| 432 } |
| 433 } |
| 434 |
| 435 void endExpressionStatement(Token token) { |
| 436 debugEvent("ExpressionStatement"); |
| 437 push(new ExpressionStatement(popForEffect())); |
| 438 } |
| 439 |
| 440 void endArguments(int count, Token beginToken, Token endToken) { |
| 441 debugEvent("Arguments"); |
| 442 List arguments = popList(count) ?? <Expression>[]; |
| 443 int firstNamedArgument = arguments.length; |
| 444 for (int i = 0; i < arguments.length; i++) { |
| 445 var node = arguments[i]; |
| 446 if (node is NamedExpression) { |
| 447 firstNamedArgument = i < firstNamedArgument ? i : firstNamedArgument; |
| 448 } else { |
| 449 arguments[i] = node = toValue(node); |
| 450 if (i > firstNamedArgument) { |
| 451 internalError("Expected named argument: $node"); |
| 452 } |
| 453 } |
| 454 } |
| 455 if (firstNamedArgument < arguments.length) { |
| 456 List<Expression> positional = new List<Expression>.from( |
| 457 arguments.getRange(0, firstNamedArgument)); |
| 458 List<NamedExpression> named = new List<NamedExpression>.from( |
| 459 arguments.getRange(firstNamedArgument,arguments.length)); |
| 460 push(new Arguments(positional, named: named)); |
| 461 } else { |
| 462 push(new Arguments(arguments)); |
| 463 } |
| 464 } |
| 465 |
| 466 void handleParenthesizedExpression(BeginGroupToken token) { |
| 467 debugEvent("ParenthesizedExpression"); |
| 468 push(popForValue()); |
| 469 } |
| 470 |
| 471 void endSend(Token token) { |
| 472 debugEvent("Send"); |
| 473 Arguments arguments = pop(); |
| 474 List typeArguments = pop(); |
| 475 Object receiver = pop(); |
| 476 if (arguments != null && typeArguments != null) { |
| 477 arguments.types.addAll(typeArguments); |
| 478 } else { |
| 479 assert(typeArguments == null); |
| 480 } |
| 481 if (receiver is Identifier) { |
| 482 Name name = new Name(receiver.name, library.library); |
| 483 if (arguments == null) { |
| 484 push(new IncompletePropertyAccessor(this, token.charOffset, name)); |
| 485 } else { |
| 486 push(new SendAccessor(this, token.charOffset, name, arguments)); |
| 487 } |
| 488 } else if (arguments == null) { |
| 489 push(receiver); |
| 490 } else { |
| 491 push(finishSend(receiver, arguments, token.charOffset)); |
| 492 } |
| 493 } |
| 494 |
| 495 finishSend(Object receiver, Arguments arguments, int charOffset) { |
| 496 if (receiver is BuilderAccessor) { |
| 497 return receiver.doInvocation(charOffset, arguments); |
| 498 } else if (receiver is UnresolvedIdentifier) { |
| 499 return throwNoSuchMethodError(receiver.name.name, arguments, |
| 500 receiver.fileOffset); |
| 501 } else { |
| 502 return buildMethodInvocation(toValue(receiver), callName, |
| 503 arguments, charOffset); |
| 504 } |
| 505 } |
| 506 |
| 507 void beginCascade(Token token) { |
| 508 debugEvent("beginCascade"); |
| 509 Expression expression = popForValue(); |
| 510 if (expression is CascadeReceiver) { |
| 511 push(expression); |
| 512 push(new VariableAccessor( |
| 513 this, expression.fileOffset, expression.variable)); |
| 514 expression.extend(); |
| 515 } else { |
| 516 VariableDeclaration variable = |
| 517 new VariableDeclaration.forValue(expression); |
| 518 push(new CascadeReceiver(variable)); |
| 519 push(new VariableAccessor(this, expression.fileOffset, variable)); |
| 520 } |
| 521 } |
| 522 |
| 523 void endCascade() { |
| 524 debugEvent("endCascade"); |
| 525 Expression expression = popForEffect(); |
| 526 CascadeReceiver cascadeReceiver = pop(); |
| 527 cascadeReceiver.finalize(expression); |
| 528 push(cascadeReceiver); |
| 529 } |
| 530 |
| 531 void handleBinaryExpression(Token token) { |
| 532 debugEvent("BinaryExpression"); |
| 533 if (optional(".", token) || optional("..", token)) { |
| 534 return doDotOrCascadeExpression(token); |
| 535 } |
| 536 if (optional("&&", token) || optional("||", token)) { |
| 537 return doLogicalExpression(token); |
| 538 } |
| 539 if (optional("??", token)) return doIfNull(token); |
| 540 if (optional("?.", token)) return doIfNotNull(token); |
| 541 Expression argument = popForValue(); |
| 542 var receiver = pop(); |
| 543 bool isSuper = false; |
| 544 if (receiver is ThisAccessor && receiver.isSuper) { |
| 545 isSuper = true; |
| 546 receiver = new ThisExpression(); |
| 547 } |
| 548 push(buildBinaryOperator(toValue(receiver), token, argument, isSuper)); |
| 549 } |
| 550 |
| 551 Expression buildBinaryOperator(Expression a, Token token, Expression b, |
| 552 bool isSuper) { |
| 553 bool negate = false; |
| 554 String operator = token.stringValue; |
| 555 if (identical("!=", operator)) { |
| 556 operator = "=="; |
| 557 negate = true; |
| 558 } |
| 559 if (!isBinaryOperator(operator) && !isMinusOperator(operator)) { |
| 560 return buildCompileTimeError("Not an operator: '$operator'.", |
| 561 token.charOffset); |
| 562 } else { |
| 563 Expression result = makeBinary(a, new Name(operator), null, b); |
| 564 if (isSuper) { |
| 565 result = toSuperMethodInvocation(result); |
| 566 } |
| 567 return negate ? new Not(result) : result; |
| 568 } |
| 569 } |
| 570 |
| 571 void doLogicalExpression(Token token) { |
| 572 Expression argument = popForValue(); |
| 573 Expression receiver = popForValue(); |
| 574 push(new LogicalExpression(receiver, token.stringValue, argument)); |
| 575 } |
| 576 |
| 577 /// Handle `a ?? b`. |
| 578 void doIfNull(Token token) { |
| 579 Expression b = popForValue(); |
| 580 Expression a = popForValue(); |
| 581 VariableDeclaration variable = new VariableDeclaration.forValue(a); |
| 582 push(makeLet(variable, |
| 583 new ConditionalExpression(buildIsNull(new VariableGet(variable)), |
| 584 b, new VariableGet(variable), const DynamicType()))); |
| 585 } |
| 586 |
| 587 /// Handle `a?.b(...)`. |
| 588 void doIfNotNull(Token token) { |
| 589 IncompleteSend send = pop(); |
| 590 push(send.withReceiver(pop(), isNullAware: true)); |
| 591 } |
| 592 |
| 593 void doDotOrCascadeExpression(Token token) { |
| 594 // TODO(ahe): Handle null-aware. |
| 595 IncompleteSend send = pop(); |
| 596 Object receiver = optional(".", token) ? pop() : popForValue(); |
| 597 push(send.withReceiver(receiver)); |
| 598 } |
| 599 |
| 600 Expression toSuperMethodInvocation(MethodInvocation node) { |
| 601 Member target = lookupSuperMember(node.name); |
| 602 bool isNoSuchMethod = target == null; |
| 603 if (target is Procedure) { |
| 604 if (!target.isAccessor) { |
| 605 if (areArgumentsCompatible(target.function, node.arguments)) { |
| 606 // TODO(ahe): Use [DirectMethodInvocation] when possible. |
| 607 Expression result = new DirectMethodInvocation(new ThisExpression(), |
| 608 target, node.arguments); |
| 609 result = new SuperMethodInvocation(node.name, node.arguments, null); |
| 610 return result; |
| 611 } else { |
| 612 isNoSuchMethod = true; |
| 613 } |
| 614 } |
| 615 } |
| 616 if (isNoSuchMethod) { |
| 617 return throwNoSuchMethodError( |
| 618 node.name.name, node.arguments, node.fileOffset, isSuper: true); |
| 619 } |
| 620 // TODO(ahe): Use [DirectPropertyGet] when possible. |
| 621 Expression receiver = new DirectPropertyGet(new ThisExpression(), target); |
| 622 receiver = new SuperPropertyGet(node.name, target); |
| 623 return buildMethodInvocation(receiver, callName, node.arguments, |
| 624 node.fileOffset); |
| 625 } |
| 626 |
| 627 bool areArgumentsCompatible(FunctionNode function, Arguments arguments) { |
| 628 // TODO(ahe): Implement this. |
| 629 return true; |
| 630 } |
| 631 |
| 632 Expression throwNoSuchMethodError(String name, Arguments arguments, |
| 633 int charOffset, {bool isSuper: false, isGetter: false, isSetter: false}) { |
| 634 return builder_accessors.throwNoSuchMethodError(name, arguments, uri, |
| 635 charOffset, coreTypes, isSuper: isSuper, isGetter: isGetter, |
| 636 isSetter: isSetter); |
| 637 } |
| 638 |
| 639 Member lookupSuperMember(Name name, {bool isSetter: false}) { |
| 640 Class superclass = classBuilder.cls.superclass; |
| 641 return superclass == null |
| 642 ? null |
| 643 : hierarchy.getDispatchTarget(superclass, name, setter: isSetter); |
| 644 } |
| 645 |
| 646 Constructor lookupConstructor(Name name, {bool isSuper}) { |
| 647 Class cls = classBuilder.cls; |
| 648 if (isSuper) { |
| 649 cls = cls.superclass; |
| 650 while (cls.isMixinApplication) { |
| 651 cls = cls.superclass; |
| 652 } |
| 653 } |
| 654 if (cls != null) { |
| 655 for (Constructor constructor in cls.constructors) { |
| 656 if (constructor.name == name) return constructor; |
| 657 } |
| 658 } |
| 659 return null; |
| 660 } |
| 661 |
| 662 void beginExpression(Token token) { |
| 663 debugEvent("beginExpression"); |
| 664 isFirstIdentifier = true; |
| 665 } |
| 666 |
| 667 Builder computeSetter(Builder builder, Scope scope, String name) { |
| 668 if (builder.isSetter) return builder; |
| 669 if (builder.isGetter) return scope.lookupSetter(name); |
| 670 return builder.isField ? (builder.isFinal ? null : builder) : null; |
| 671 } |
| 672 |
| 673 void handleIdentifier(Token token) { |
| 674 debugEvent("handleIdentifier"); |
| 675 String name = token.value; |
| 676 if (isFirstIdentifier) { |
| 677 assert(!inInitializer || this.scope == enclosingScope || |
| 678 this.scope.parent == enclosingScope); |
| 679 // This deals with this kind of initializer: `C(a) : a = a;` |
| 680 Scope scope = inInitializer ? enclosingScope : this.scope; |
| 681 Builder builder = scope.lookup(name); |
| 682 push(builderToFirstExpression(builder, name, token.charOffset)); |
| 683 } else { |
| 684 push(new Identifier(name)..fileOffset = token.charOffset); |
| 685 } |
| 686 } |
| 687 |
| 688 builderToFirstExpression(Builder builder, String name, int charOffset, |
| 689 {bool isPrefix: false}) { |
| 690 if (builder == null || (!isInstanceContext && builder.isInstanceMember)) { |
| 691 if (!isPrefix && identical(name, "dynamic") && builder == null) { |
| 692 return new KernelInterfaceTypeBuilder(name, null); |
| 693 } |
| 694 Name n = new Name(name, library.library); |
| 695 if (!isPrefix && isInstanceContext) { |
| 696 assert(builder == null); |
| 697 return new ThisPropertyAccessor(this, charOffset, n, null, null); |
| 698 } else { |
| 699 return new UnresolvedIdentifier(n) |
| 700 ..fileOffset = charOffset; |
| 701 } |
| 702 } else if (builder.isTypeDeclaration) { |
| 703 return builder; |
| 704 } else if (builder.isLocal) { |
| 705 return new VariableAccessor(this, charOffset, builder.target); |
| 706 } else if (builder.isInstanceMember) { |
| 707 return new ThisPropertyAccessor(this, charOffset, |
| 708 new Name(name, library.library), null, null); |
| 709 } else if (builder.isRegularMethod) { |
| 710 assert(builder.isStatic || builder.isTopLevel); |
| 711 return new StaticAccessor(this, charOffset, builder.target, null); |
| 712 } else if (builder is PrefixBuilder) { |
| 713 return builder; |
| 714 } else if (builder is MixedAccessor) { |
| 715 return new StaticAccessor(this, charOffset, builder.getter.target, |
| 716 builder.setter.target); |
| 717 } else { |
| 718 if (builder is AccessErrorBuilder) { |
| 719 AccessErrorBuilder error = builder; |
| 720 builder = error.builder; |
| 721 } |
| 722 if (builder.target == null) { |
| 723 return internalError("Unhandled: ${builder}"); |
| 724 } |
| 725 Member getter = builder.target.hasGetter ? builder.target : null; |
| 726 Member setter = builder.target.hasSetter ? builder.target : null; |
| 727 setter ??= computeSetter(builder, scope, name)?.target; |
| 728 return |
| 729 new StaticAccessor(this, charOffset, getter, setter); |
| 730 } |
| 731 } |
| 732 |
| 733 void handleQualified(Token period) { |
| 734 debugEvent("Qualified"); |
| 735 Identifier name = pop(); |
| 736 var receiver = pop(); |
| 737 push([receiver, name]); |
| 738 } |
| 739 |
| 740 void beginLiteralString(Token token) { |
| 741 debugEvent("beginLiteralString"); |
| 742 push(token); |
| 743 } |
| 744 |
| 745 void handleStringPart(Token token) { |
| 746 debugEvent("StringPart"); |
| 747 push(token); |
| 748 } |
| 749 |
| 750 void endLiteralString(int interpolationCount) { |
| 751 debugEvent("endLiteralString"); |
| 752 if (interpolationCount == 0) { |
| 753 Token token = pop(); |
| 754 push(new StringLiteral(unescapeString(token.value))); |
| 755 } else { |
| 756 List parts = popList(1 + interpolationCount * 2); |
| 757 Token first = parts.first; |
| 758 Token last = parts.last; |
| 759 Quote quote = analyzeQuote(first.value); |
| 760 List<Expression> expressions = <Expression>[]; |
| 761 expressions.add(new StringLiteral(unescapeFirstStringPart( |
| 762 first.value, quote))); |
| 763 for (int i = 1; i < parts.length - 1; i++) { |
| 764 var part = parts[i]; |
| 765 if (part is Token) { |
| 766 expressions.add(new StringLiteral(unescape(part.value, quote))); |
| 767 } else { |
| 768 expressions.add(toValue(part)); |
| 769 } |
| 770 } |
| 771 expressions.add( |
| 772 new StringLiteral(unescapeLastStringPart(last.value, quote))); |
| 773 push(new StringConcatenation(expressions)); |
| 774 } |
| 775 } |
| 776 |
| 777 void handleStringJuxtaposition(int literalCount) { |
| 778 debugEvent("StringJuxtaposition"); |
| 779 List<Expression> parts = popListForValue(literalCount); |
| 780 List<Expression> expressions; |
| 781 // Flatten string juxtapositions of string interpolation. |
| 782 for (int i = 0; i < parts.length; i++) { |
| 783 Expression part = parts[i]; |
| 784 if (part is StringConcatenation) { |
| 785 if (expressions == null) { |
| 786 expressions = parts.sublist(0, i); |
| 787 } |
| 788 expressions.addAll(part.expressions); |
| 789 } else { |
| 790 if (expressions != null) { |
| 791 expressions.add(part); |
| 792 } |
| 793 } |
| 794 } |
| 795 push(new StringConcatenation(expressions ?? parts)); |
| 796 } |
| 797 |
| 798 void handleLiteralInt(Token token) { |
| 799 debugEvent("LiteralInt"); |
| 800 push(new IntLiteral(int.parse(token.value))); |
| 801 } |
| 802 |
| 803 void endReturnStatement( |
| 804 bool hasExpression, Token beginToken, Token endToken) { |
| 805 debugEvent("ReturnStatement"); |
| 806 Expression expression = hasExpression ? popForValue() : null; |
| 807 if (expression != null && inConstructor) { |
| 808 push(buildCompileTimeErrorStatement("Can't return from a constructor.", |
| 809 beginToken.charOffset)); |
| 810 } else { |
| 811 push(new ReturnStatement(expression)); |
| 812 } |
| 813 } |
| 814 |
| 815 void endIfStatement(Token ifToken, Token elseToken) { |
| 816 Statement elsePart = popStatementIfNotNull(elseToken); |
| 817 Statement thenPart = popStatement(); |
| 818 Expression condition = popForValue(); |
| 819 push(new IfStatement(condition, thenPart, elsePart)); |
| 820 } |
| 821 |
| 822 void endInitializer(Token assignmentOperator) { |
| 823 debugEvent("Initializer"); |
| 824 assert(assignmentOperator.stringValue == "="); |
| 825 Expression initializer = popForValue(); |
| 826 Identifier identifier = pop(); |
| 827 push(new VariableDeclaration(identifier.name, initializer: initializer)); |
| 828 } |
| 829 |
| 830 void endInitializedIdentifier() { |
| 831 // TODO(ahe): Use [InitializedIdentifier] here? |
| 832 debugEvent("InitializedIdentifier"); |
| 833 TreeNode node = pop(); |
| 834 VariableDeclaration variable; |
| 835 if (node is VariableDeclaration) { |
| 836 variable = node; |
| 837 } else if (node is Identifier) { |
| 838 variable = new VariableDeclaration(node.name); |
| 839 } else { |
| 840 internalError("unhandled identifier: ${node.runtimeType}"); |
| 841 } |
| 842 push(variable); |
| 843 scope[variable.name] = new KernelVariableBuilder(variable); |
| 844 } |
| 845 |
| 846 void endVariablesDeclaration(int count, Token endToken) { |
| 847 debugEvent("VariablesDeclaration"); |
| 848 List<VariableDeclaration> variables = popList(count); |
| 849 DartType type = pop(); |
| 850 int modifiers = Modifier.validate(pop()); |
| 851 bool isConst = (modifiers & constMask) != 0; |
| 852 bool isFinal = (modifiers & finalMask) != 0; |
| 853 if (type != null || isConst || isFinal) { |
| 854 type ??= const DynamicType(); |
| 855 for (VariableDeclaration variable in variables) { |
| 856 variable |
| 857 ..type = type |
| 858 ..isConst = isConst |
| 859 ..isFinal = isFinal; |
| 860 } |
| 861 } |
| 862 if (variables.length != 1) { |
| 863 push(variables); |
| 864 } else { |
| 865 push(variables.single); |
| 866 } |
| 867 } |
| 868 |
| 869 void endBlock(int count, Token beginToken, Token endToken) { |
| 870 debugEvent("Block"); |
| 871 Block block = popBlock(count); |
| 872 exitLocalScope(); |
| 873 push(block); |
| 874 } |
| 875 |
| 876 void handleAssignmentExpression(Token token) { |
| 877 debugEvent("AssignmentExpression"); |
| 878 Expression value = popForValue(); |
| 879 var accessor = pop(); |
| 880 if (accessor is TypeDeclarationBuilder) { |
| 881 push(wrapInvalid( |
| 882 new TypeLiteral(accessor.buildTypesWithBuiltArguments(null)))); |
| 883 } else if (accessor is! BuilderAccessor) { |
| 884 push(buildCompileTimeError("Can't assign to this.", token.charOffset)); |
| 885 } else { |
| 886 push(new DelayedAssignment(this, token.charOffset, accessor, value, |
| 887 token.stringValue)); |
| 888 } |
| 889 } |
| 890 |
| 891 void enterLoop() { |
| 892 if (peek() is LabelTarget) { |
| 893 LabelTarget target = peek(); |
| 894 enterBreakTarget(target.breakTarget); |
| 895 enterContinueTarget(target.continueTarget); |
| 896 } else{ |
| 897 enterBreakTarget(); |
| 898 enterContinueTarget(); |
| 899 } |
| 900 } |
| 901 |
| 902 void exitLoopOrSwitch(Statement statement) { |
| 903 if (compileTimeErrorInLoopOrSwitch != null) { |
| 904 push(compileTimeErrorInLoopOrSwitch); |
| 905 compileTimeErrorInLoopOrSwitch = null; |
| 906 } else { |
| 907 push(statement); |
| 908 } |
| 909 } |
| 910 |
| 911 void endForStatement( |
| 912 int updateExpressionCount, Token beginToken, Token endToken) { |
| 913 debugEvent("ForStatement"); |
| 914 Statement body = popStatement(); |
| 915 List<Expression> updates = popListForEffect(updateExpressionCount); |
| 916 Statement conditionStatement = popStatement(); |
| 917 Expression condition = null; |
| 918 if (conditionStatement is ExpressionStatement) { |
| 919 condition = conditionStatement.expression; |
| 920 } else { |
| 921 assert(conditionStatement is EmptyStatement); |
| 922 } |
| 923 List<VariableDeclaration> variables = <VariableDeclaration>[]; |
| 924 var variableOrExpression = pop(); |
| 925 Statement begin; |
| 926 if (variableOrExpression is BuilderAccessor) { |
| 927 variableOrExpression = variableOrExpression.buildForEffect(); |
| 928 } |
| 929 if (variableOrExpression is VariableDeclaration) { |
| 930 variables.add(variableOrExpression); |
| 931 } else if (variableOrExpression is List) { |
| 932 variables.addAll(variableOrExpression); |
| 933 } else if (variableOrExpression == null) { |
| 934 // Do nothing. |
| 935 } else if (variableOrExpression is Expression) { |
| 936 begin = new ExpressionStatement(variableOrExpression); |
| 937 } else { |
| 938 return internalError("Unhandled: ${variableOrExpression.runtimeType}"); |
| 939 } |
| 940 exitLocalScope(); |
| 941 JumpTarget continueTarget = exitContinueTarget(); |
| 942 JumpTarget breakTarget = exitBreakTarget(); |
| 943 if (continueTarget.hasUsers) { |
| 944 body = new LabeledStatement(body); |
| 945 continueTarget.resolveContinues(body); |
| 946 } |
| 947 Statement result = new ForStatement(variables, condition, updates, body); |
| 948 if (begin != null) { |
| 949 result = new Block(<Statement>[begin, result]); |
| 950 } |
| 951 if (breakTarget.hasUsers) { |
| 952 result = new LabeledStatement(result); |
| 953 breakTarget.resolveBreaks(result); |
| 954 } |
| 955 exitLoopOrSwitch(result); |
| 956 } |
| 957 |
| 958 void endAwaitExpression(Token beginToken, Token endToken) { |
| 959 debugEvent("AwaitExpression"); |
| 960 push(new AwaitExpression(popForValue())); |
| 961 } |
| 962 |
| 963 void handleAsyncModifier(Token asyncToken, Token starToken) { |
| 964 debugEvent("AsyncModifier"); |
| 965 push(asyncMarkerFromTokens(asyncToken, starToken)); |
| 966 } |
| 967 |
| 968 void handleLiteralList( |
| 969 int count, Token beginToken, Token constKeyword, Token endToken) { |
| 970 debugEvent("LiteralList"); |
| 971 List<Expression> expressions = popListForValue(count); |
| 972 List<DartType> typeArguments = pop(); |
| 973 DartType typeArgument = const DynamicType(); |
| 974 if (typeArguments != null) { |
| 975 typeArgument = typeArguments.first; |
| 976 if (typeArguments.length > 1) { |
| 977 typeArgument = const DynamicType(); |
| 978 warning("Too many type arguments on List literal.", |
| 979 beginToken.charOffset); |
| 980 } |
| 981 } |
| 982 push(new ListLiteral(expressions, typeArgument: typeArgument, |
| 983 isConst: constKeyword != null)); |
| 984 } |
| 985 |
| 986 void handleLiteralBool(Token token) { |
| 987 debugEvent("LiteralBool"); |
| 988 bool value = optional("true", token); |
| 989 assert(value || optional("false", token)); |
| 990 push(new BoolLiteral(value)); |
| 991 } |
| 992 |
| 993 void handleLiteralDouble(Token token) { |
| 994 debugEvent("LiteralDouble"); |
| 995 push(new DoubleLiteral(double.parse(token.value))); |
| 996 } |
| 997 |
| 998 void handleLiteralNull(Token token) { |
| 999 debugEvent("LiteralNull"); |
| 1000 push(new NullLiteral()); |
| 1001 } |
| 1002 |
| 1003 void handleLiteralMap( |
| 1004 int count, Token beginToken, Token constKeyword, Token endToken) { |
| 1005 debugEvent("LiteralMap"); |
| 1006 List<MapEntry> entries = popList(count) ?? <MapEntry>[]; |
| 1007 List<DartType> typeArguments = pop(); |
| 1008 DartType keyType = const DynamicType(); |
| 1009 DartType valueType = const DynamicType(); |
| 1010 if (typeArguments != null) { |
| 1011 if (typeArguments.length != 2) { |
| 1012 keyType = const DynamicType(); |
| 1013 valueType = const DynamicType(); |
| 1014 warning("Map literal requires two type arguments.", |
| 1015 beginToken.charOffset); |
| 1016 } else { |
| 1017 keyType = typeArguments[0]; |
| 1018 valueType = typeArguments[1]; |
| 1019 } |
| 1020 } |
| 1021 push(new MapLiteral(entries, keyType: keyType, valueType: valueType, |
| 1022 isConst: constKeyword != null)); |
| 1023 } |
| 1024 |
| 1025 void endLiteralMapEntry(Token colon, Token endToken) { |
| 1026 debugEvent("LiteralMapEntry"); |
| 1027 Expression value = popForValue(); |
| 1028 Expression key = popForValue(); |
| 1029 push(new MapEntry(key, value)); |
| 1030 } |
| 1031 |
| 1032 void beginLiteralSymbol(Token token) { |
| 1033 isFirstIdentifier = false; |
| 1034 } |
| 1035 |
| 1036 String symbolPartToString(name) { |
| 1037 if (name is Identifier) { |
| 1038 return name.name; |
| 1039 } else if (name is Operator) { |
| 1040 return name.name; |
| 1041 } else { |
| 1042 return internalError("Unhandled: ${name.runtimeType}"); |
| 1043 } |
| 1044 } |
| 1045 |
| 1046 void endLiteralSymbol(Token hashToken, int identifierCount) { |
| 1047 debugEvent("LiteralSymbol"); |
| 1048 String value; |
| 1049 if (identifierCount == 1) { |
| 1050 value = symbolPartToString(popForValue()); |
| 1051 } else { |
| 1052 List parts = popList(identifierCount); |
| 1053 value = symbolPartToString(parts.first); |
| 1054 for (int i = 1; i < parts.length; i++) { |
| 1055 value += ".${symbolPartToString(parts[i])}"; |
| 1056 } |
| 1057 } |
| 1058 push(new SymbolLiteral(value)); |
| 1059 } |
| 1060 |
| 1061 DartType toKernelType(String name, List<DartType> arguments) { |
| 1062 if (identical(name, "void")) return const VoidType(); |
| 1063 if (identical(name, "dynamic")) return const DynamicType(); |
| 1064 Builder builder = scope.lookup(name); |
| 1065 if (builder is TypeDeclarationBuilder) { |
| 1066 return builder.buildTypesWithBuiltArguments(arguments); |
| 1067 } |
| 1068 if (builder == null) { |
| 1069 print("$uri: Type not found: $name"); |
| 1070 } else { |
| 1071 print("$uri: Not a type: $name"); |
| 1072 } |
| 1073 // TODO(ahe): Create an error somehow. |
| 1074 return const DynamicType(); |
| 1075 } |
| 1076 |
| 1077 void endType(Token beginToken, Token endToken) { |
| 1078 // TODO(ahe): The scope is wrong for return types of generic functions. |
| 1079 debugEvent("Type"); |
| 1080 List<DartType> arguments = pop(); |
| 1081 var name = pop(); |
| 1082 if (name is List) { |
| 1083 if (name.length != 2) { |
| 1084 return internalError("Unexpected: $name.length"); |
| 1085 } |
| 1086 var prefix = name[0]; |
| 1087 if (prefix is Identifier) { |
| 1088 prefix = prefix.name; |
| 1089 } |
| 1090 var suffix = name[1]; |
| 1091 if (suffix is Identifier) { |
| 1092 suffix = suffix.name; |
| 1093 } |
| 1094 Builder builder; |
| 1095 if (prefix is Builder) { |
| 1096 builder = prefix; |
| 1097 } else { |
| 1098 builder = scope.lookup(prefix); |
| 1099 } |
| 1100 if (builder is PrefixBuilder) { |
| 1101 name = builder.exports[suffix]; |
| 1102 } else { |
| 1103 return inputError( |
| 1104 "Can't be used as a type: '${debugName(prefix, suffix)}'.", |
| 1105 beginToken.charOffset); |
| 1106 } |
| 1107 } |
| 1108 if (name is Identifier) { |
| 1109 name = name.name; |
| 1110 } |
| 1111 if (name is BuilderAccessor) { |
| 1112 warning("'${beginToken.value}' isn't a type.", beginToken.charOffset); |
| 1113 push(const DynamicType()); |
| 1114 } else if (name is UnresolvedIdentifier) { |
| 1115 warning("'${name.name}' isn't a type.", beginToken.charOffset); |
| 1116 push(const DynamicType()); |
| 1117 } else if (name is TypeVariableBuilder) { |
| 1118 push(name.buildTypesWithBuiltArguments(arguments)); |
| 1119 } else if (name is TypeDeclarationBuilder) { |
| 1120 push(name.buildTypesWithBuiltArguments(arguments)); |
| 1121 } else if (name is TypeBuilder) { |
| 1122 push(name.build()); |
| 1123 } else { |
| 1124 push(toKernelType(name, arguments)); |
| 1125 } |
| 1126 if (peek() is TypeParameterType) { |
| 1127 TypeParameterType type = peek(); |
| 1128 if (!isInstanceContext && type.parameter.parent is Class) { |
| 1129 pop(); |
| 1130 warning("Type variables can only be used in instance methods.", |
| 1131 beginToken.charOffset); |
| 1132 push(const DynamicType()); |
| 1133 } |
| 1134 } |
| 1135 } |
| 1136 |
| 1137 void handleVoidKeyword(Token token) { |
| 1138 debugEvent("VoidKeyword"); |
| 1139 push(const VoidType()); |
| 1140 } |
| 1141 |
| 1142 void handleAsOperator(Token operator, Token endToken) { |
| 1143 debugEvent("AsOperator"); |
| 1144 DartType type = pop(); |
| 1145 Expression expression = popForValue(); |
| 1146 push(new AsExpression(expression, type)); |
| 1147 } |
| 1148 |
| 1149 void handleIsOperator(Token operator, Token not, Token endToken) { |
| 1150 debugEvent("IsOperator"); |
| 1151 DartType type = pop(); |
| 1152 Expression expression = popForValue(); |
| 1153 expression = new IsExpression(expression, type); |
| 1154 if (not != null) { |
| 1155 expression = new Not(expression); |
| 1156 } |
| 1157 push(expression); |
| 1158 } |
| 1159 |
| 1160 void handleConditionalExpression(Token question, Token colon) { |
| 1161 debugEvent("ConditionalExpression"); |
| 1162 Expression elseExpression = popForValue(); |
| 1163 Expression thenExpression = popForValue(); |
| 1164 Expression condition = popForValue(); |
| 1165 push(new ConditionalExpression( |
| 1166 condition, thenExpression, elseExpression, const DynamicType())); |
| 1167 } |
| 1168 |
| 1169 void endThrowExpression(Token throwToken, Token endToken) { |
| 1170 debugEvent("ThrowExpression"); |
| 1171 Expression expression = popForValue(); |
| 1172 push(new Throw(expression)); |
| 1173 } |
| 1174 |
| 1175 void endFormalParameter(Token thisKeyword) { |
| 1176 debugEvent("FormalParameter"); |
| 1177 if (thisKeyword != null) { |
| 1178 if (!inConstructor) { |
| 1179 return inputError("'this' parameters can only be used on constructors.", |
| 1180 thisKeyword.charOffset); |
| 1181 } |
| 1182 } |
| 1183 Identifier name = pop(); |
| 1184 DartType type = pop(); |
| 1185 pop(); // Modifiers. |
| 1186 ignore(Unhandled.Metadata); |
| 1187 VariableDeclaration variable; |
| 1188 if (!inCatchClause && functionNestingLevel == 0) { |
| 1189 var builder = formalParameterScope.lookup(name.name); |
| 1190 if (builder == null) { |
| 1191 return inputError("'${name.name}' isn't a field in this class.", |
| 1192 name.fileOffset); |
| 1193 } |
| 1194 if (thisKeyword == null) { |
| 1195 variable = builder.build(); |
| 1196 variable.initializer = name.initializer; |
| 1197 } else if (builder.isField && builder.parent == classBuilder) { |
| 1198 FieldBuilder field = builder; |
| 1199 if (type != null) { |
| 1200 nit("Ignoring type on 'this' parameter '${name.name}'.", |
| 1201 name.fileOffset); |
| 1202 } |
| 1203 type = field.target.type ?? const DynamicType(); |
| 1204 variable = new VariableDeclaration(name.name, type: type, |
| 1205 initializer: name.initializer); |
| 1206 } else { |
| 1207 return inputError("'${name.name}' isn't a field in this class.", |
| 1208 name.fileOffset); |
| 1209 } |
| 1210 } else { |
| 1211 variable = new VariableDeclaration(name.name, |
| 1212 type: type ?? const DynamicType(), initializer: name.initializer); |
| 1213 } |
| 1214 push(variable); |
| 1215 } |
| 1216 |
| 1217 void endOptionalFormalParameters( |
| 1218 int count, Token beginToken, Token endToken) { |
| 1219 debugEvent("OptionalFormalParameters"); |
| 1220 FormalParameterType kind = optional("{", beginToken) |
| 1221 ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL; |
| 1222 push(new OptionalFormals(kind, popList(count))); |
| 1223 } |
| 1224 |
| 1225 void beginFunctionTypedFormalParameter(Token token) { |
| 1226 debugEvent("beginFunctionTypedFormalParameter"); |
| 1227 functionNestingLevel++; |
| 1228 } |
| 1229 |
| 1230 void handleFunctionTypedFormalParameter(Token token) { |
| 1231 debugEvent("FunctionTypedFormalParameter"); |
| 1232 if (inCatchClause || functionNestingLevel != 0) { |
| 1233 exitLocalScope(); |
| 1234 } |
| 1235 FormalParameters formals = pop(); |
| 1236 ignore(Unhandled.TypeVariables); |
| 1237 Identifier name = pop(); |
| 1238 DartType returnType = pop(); |
| 1239 push(formals.toFunctionType(returnType)); |
| 1240 push(name); |
| 1241 functionNestingLevel--; |
| 1242 } |
| 1243 |
| 1244 void handleValuedFormalParameter(Token equals, Token token) { |
| 1245 debugEvent("ValuedFormalParameter"); |
| 1246 Expression initializer = popForValue(); |
| 1247 Identifier name = pop(); |
| 1248 push(new InitializedIdentifier(name.name, initializer)); |
| 1249 } |
| 1250 |
| 1251 void endFormalParameters(int count, Token beginToken, Token endToken) { |
| 1252 debugEvent("FormalParameters"); |
| 1253 OptionalFormals optional; |
| 1254 if (count > 0 && peek() is OptionalFormals) { |
| 1255 optional = pop(); |
| 1256 count--; |
| 1257 } |
| 1258 FormalParameters formals = new FormalParameters( |
| 1259 popList(count) ?? <VariableDeclaration>[], optional); |
| 1260 push(formals); |
| 1261 if (inCatchClause || functionNestingLevel != 0) { |
| 1262 enterLocalScope(formals.computeFormalParameterScope(scope)); |
| 1263 } |
| 1264 } |
| 1265 |
| 1266 void beginCatchClause(Token token) { |
| 1267 debugEvent("beginCatchClause"); |
| 1268 inCatchClause = true; |
| 1269 } |
| 1270 |
| 1271 void endCatchClause(Token token) { |
| 1272 debugEvent("CatchClause"); |
| 1273 inCatchClause = false; |
| 1274 } |
| 1275 |
| 1276 void handleCatchBlock(Token onKeyword, Token catchKeyword) { |
| 1277 debugEvent("CatchBlock"); |
| 1278 Block body = pop(); |
| 1279 if (catchKeyword != null) { |
| 1280 exitLocalScope(); |
| 1281 } |
| 1282 FormalParameters catchParameters = popIfNotNull(catchKeyword); |
| 1283 DartType type = popIfNotNull(onKeyword) ?? const DynamicType(); |
| 1284 VariableDeclaration exception; |
| 1285 VariableDeclaration stackTrace; |
| 1286 if (catchParameters != null) { |
| 1287 if (catchParameters.required.length > 0) { |
| 1288 exception = catchParameters.required[0]; |
| 1289 } |
| 1290 if (catchParameters.required.length > 1) { |
| 1291 stackTrace = catchParameters.required[1]; |
| 1292 } |
| 1293 if (catchParameters.required.length > 2 || |
| 1294 catchParameters.optional != null) { |
| 1295 body = new Block(<Statement>[new InvalidStatement()]); |
| 1296 compileTimeErrorInTry ??= buildCompileTimeErrorStatement( |
| 1297 "Invalid catch arguments.", catchKeyword.next.charOffset); |
| 1298 } |
| 1299 } |
| 1300 push(new Catch(exception, body, guard: type, stackTrace: stackTrace)); |
| 1301 } |
| 1302 |
| 1303 void endTryStatement( |
| 1304 int catchCount, Token tryKeyword, Token finallyKeyword) { |
| 1305 Statement finallyBlock = popStatementIfNotNull(finallyKeyword); |
| 1306 List<Catch> catches = popList(catchCount); |
| 1307 Statement tryBlock = popStatement(); |
| 1308 if (compileTimeErrorInTry == null) { |
| 1309 if (catches != null) { |
| 1310 tryBlock = new TryCatch(tryBlock, catches); |
| 1311 } |
| 1312 if (finallyBlock != null) { |
| 1313 tryBlock = new TryFinally(tryBlock, finallyBlock); |
| 1314 } |
| 1315 push(tryBlock); |
| 1316 } else { |
| 1317 push(compileTimeErrorInTry); |
| 1318 compileTimeErrorInTry = null; |
| 1319 } |
| 1320 } |
| 1321 |
| 1322 void handleNoExpression(Token token) { |
| 1323 debugEvent("NoExpression"); |
| 1324 push(NullValue.Expression); |
| 1325 } |
| 1326 |
| 1327 void handleIndexedExpression( |
| 1328 Token openCurlyBracket, Token closeCurlyBracket) { |
| 1329 debugEvent("IndexedExpression"); |
| 1330 Expression index = popForValue(); |
| 1331 Expression receiver = popForValue(); |
| 1332 push(IndexAccessor.make(this, openCurlyBracket.charOffset, receiver, index, |
| 1333 null, null)); |
| 1334 } |
| 1335 |
| 1336 void handleUnaryPrefixExpression(Token token) { |
| 1337 debugEvent("UnaryPrefixExpression"); |
| 1338 Expression expression = popForValue(); |
| 1339 if (optional("!", token)) { |
| 1340 push(new Not(expression)); |
| 1341 } else { |
| 1342 String operator = token.stringValue; |
| 1343 if (optional("-", token)) { |
| 1344 operator = "unary-"; |
| 1345 } |
| 1346 push(buildMethodInvocation(expression, new Name(operator), |
| 1347 new Arguments.empty(), token.charOffset)); |
| 1348 } |
| 1349 } |
| 1350 |
| 1351 Name incrementOperator(Token token) { |
| 1352 if (optional("++", token)) return plusName; |
| 1353 if (optional("--", token)) return minusName; |
| 1354 return internalError("Unknown increment operator: ${token.value}"); |
| 1355 } |
| 1356 |
| 1357 void handleUnaryPrefixAssignmentExpression(Token token) { |
| 1358 debugEvent("UnaryPrefixAssignmentExpression"); |
| 1359 var accessor = pop(); |
| 1360 if (accessor is BuilderAccessor) { |
| 1361 push(accessor.buildPrefixIncrement(incrementOperator(token))); |
| 1362 } else { |
| 1363 push(wrapInvalid(toValue(accessor))); |
| 1364 } |
| 1365 } |
| 1366 |
| 1367 void handleUnaryPostfixAssignmentExpression(Token token) { |
| 1368 debugEvent("UnaryPostfixAssignmentExpression"); |
| 1369 var accessor = pop(); |
| 1370 if (accessor is BuilderAccessor) { |
| 1371 push(new DelayedPostfixIncrement(this, token.charOffset, accessor, |
| 1372 incrementOperator(token), null)); |
| 1373 } else { |
| 1374 push(wrapInvalid(toValue(accessor))); |
| 1375 } |
| 1376 } |
| 1377 |
| 1378 void endConstructorReference( |
| 1379 Token start, Token periodBeforeName, Token endToken) { |
| 1380 debugEvent("ConstructorReference"); |
| 1381 // A constructor reference can contain up to three identifiers: |
| 1382 // |
| 1383 // a) type <type-arguments>? |
| 1384 // b) type <type-arguments>? . name |
| 1385 // c) prefix . type <type-arguments>? |
| 1386 // d) prefix . type <type-arguments>? . name |
| 1387 // |
| 1388 // This isn't a legal constructor reference: |
| 1389 // |
| 1390 // type . name <type-arguments> |
| 1391 // |
| 1392 // But the parser can't tell this from type c) above. |
| 1393 // |
| 1394 // This method pops 2 (or 3 if periodBeforeName != null) values from the |
| 1395 // stack and pushes 3 values: a type, a list of type arguments, and a name. |
| 1396 // |
| 1397 // If the constructor reference can be resolved, type is either a |
| 1398 // ClassBuilder, or a ThisPropertyAccessor. Otherwise, it's an error that |
| 1399 // should be handled later. |
| 1400 Identifier suffix = popIfNotNull(periodBeforeName); |
| 1401 Identifier identifier; |
| 1402 List<DartType> typeArguments = pop(); |
| 1403 var type = pop(); |
| 1404 if (type is List) { |
| 1405 var prefix = type[0]; |
| 1406 identifier = type[1]; |
| 1407 if (prefix is PrefixBuilder) { |
| 1408 // TODO(ahe): Handle privacy in prefix.exports. |
| 1409 type = builderToFirstExpression( |
| 1410 prefix.exports[identifier.name], identifier.name, start.charOffset); |
| 1411 identifier = null; |
| 1412 } else if (prefix is ClassBuilder) { |
| 1413 type = prefix; |
| 1414 } else { |
| 1415 type = new Identifier(start.value)..fileOffset = start.charOffset; |
| 1416 } |
| 1417 } |
| 1418 String name; |
| 1419 if (identifier != null && suffix != null) { |
| 1420 name = "${identifier.name}.${suffix.name}"; |
| 1421 } else if (identifier != null) { |
| 1422 name = identifier.name; |
| 1423 } else if (suffix != null) { |
| 1424 name = suffix.name; |
| 1425 } else { |
| 1426 name = ""; |
| 1427 } |
| 1428 push(type); |
| 1429 push(typeArguments ?? NullValue.TypeArguments); |
| 1430 push(name); |
| 1431 } |
| 1432 |
| 1433 Expression buildStaticInvocation(Member target, Arguments arguments, |
| 1434 {bool isConst: false}) { |
| 1435 List<TypeParameter> typeParameters = target.function.typeParameters; |
| 1436 if (target is Constructor) { |
| 1437 typeParameters = target.enclosingClass.typeParameters; |
| 1438 } |
| 1439 if (!addDefaultArguments(target.function, arguments, typeParameters)) { |
| 1440 return throwNoSuchMethodError(target.name.name, arguments, -1); |
| 1441 } |
| 1442 if (target is Constructor) { |
| 1443 return new ConstructorInvocation(target, arguments) |
| 1444 ..isConst = isConst; |
| 1445 } else { |
| 1446 return new StaticInvocation(target, arguments) |
| 1447 ..isConst = isConst; |
| 1448 } |
| 1449 } |
| 1450 |
| 1451 bool addDefaultArguments(FunctionNode function, Arguments arguments, |
| 1452 List<TypeParameter> typeParameters) { |
| 1453 bool missingInitializers = false; |
| 1454 |
| 1455 Expression defaultArgumentFrom(Expression expression) { |
| 1456 if (expression == null) { |
| 1457 missingInitializers = true; |
| 1458 return null; |
| 1459 } |
| 1460 cloner ??= new CloneVisitor(); |
| 1461 return cloner.clone(expression); |
| 1462 } |
| 1463 |
| 1464 if (arguments.positional.length < function.requiredParameterCount || |
| 1465 arguments.positional.length > function.positionalParameters.length) { |
| 1466 return false; |
| 1467 } |
| 1468 for (int i = arguments.positional.length; |
| 1469 i < function.positionalParameters.length; |
| 1470 i++) { |
| 1471 var expression = |
| 1472 defaultArgumentFrom(function.positionalParameters[i].initializer); |
| 1473 expression?.parent = arguments; |
| 1474 arguments.positional.add(expression); |
| 1475 } |
| 1476 Map<String, VariableDeclaration> names; |
| 1477 if (function.namedParameters.isNotEmpty) { |
| 1478 names = <String, VariableDeclaration>{}; |
| 1479 for (VariableDeclaration parameter in function.namedParameters) { |
| 1480 names[parameter.name] = parameter; |
| 1481 } |
| 1482 } |
| 1483 if (arguments.named.isNotEmpty) { |
| 1484 if (names == null) return false; |
| 1485 for (NamedExpression argument in arguments.named) { |
| 1486 VariableDeclaration parameter = names.remove(argument.name); |
| 1487 if (parameter == null) { |
| 1488 return false; |
| 1489 } |
| 1490 } |
| 1491 } |
| 1492 if (names != null) { |
| 1493 for (String name in names.keys) { |
| 1494 VariableDeclaration parameter = names[name]; |
| 1495 arguments.named.add( |
| 1496 new NamedExpression( |
| 1497 name, defaultArgumentFrom(parameter.initializer)) |
| 1498 ..parent = arguments); |
| 1499 } |
| 1500 } |
| 1501 if (typeParameters.length != arguments.types.length) { |
| 1502 arguments.types.clear(); |
| 1503 for (int i = 0; i < typeParameters.length; i++) { |
| 1504 arguments.types.add(const DynamicType()); |
| 1505 } |
| 1506 } |
| 1507 |
| 1508 if (missingInitializers) { |
| 1509 library.addArgumentsWithMissingDefaultValues(arguments, function); |
| 1510 } |
| 1511 return true; |
| 1512 } |
| 1513 |
| 1514 void handleNewExpression(Token token) { |
| 1515 debugEvent("NewExpression"); |
| 1516 Arguments arguments = pop(); |
| 1517 String name = pop(); |
| 1518 List typeArguments = pop(); |
| 1519 var type = pop(); |
| 1520 |
| 1521 if (typeArguments != null) { |
| 1522 assert(arguments.types.isEmpty); |
| 1523 arguments.types.addAll(typeArguments); |
| 1524 } |
| 1525 |
| 1526 String errorName; |
| 1527 if (type is ClassBuilder) { |
| 1528 Builder b = type.findConstructorOrFactory(name); |
| 1529 Member target; |
| 1530 if (b == null) { |
| 1531 // Not found. Reported below. |
| 1532 } else if (b.isConstructor) { |
| 1533 if (type.cls.isAbstract) { |
| 1534 // TODO(ahe): Generate abstract instantiation error. |
| 1535 } else { |
| 1536 target = b.target; |
| 1537 } |
| 1538 } else if (b.isFactory) { |
| 1539 target = getRedirectionTarget(b.target); |
| 1540 if (target == null) { |
| 1541 push(buildCompileTimeError( |
| 1542 "Cyclic definition of factory '${name}'.", |
| 1543 token.charOffset)); |
| 1544 return; |
| 1545 } |
| 1546 } |
| 1547 if (target is Constructor || |
| 1548 (target is Procedure && target.kind == ProcedureKind.Factory)) { |
| 1549 push(buildStaticInvocation( |
| 1550 target, arguments, isConst: optional("const", token))); |
| 1551 return; |
| 1552 } else { |
| 1553 errorName = debugName(type.cls.name, name); |
| 1554 } |
| 1555 } else { |
| 1556 errorName = debugName(getNodeName(type), name); |
| 1557 } |
| 1558 errorName ??= name; |
| 1559 push(throwNoSuchMethodError(errorName, arguments, token.charOffset)); |
| 1560 } |
| 1561 |
| 1562 void handleConstExpression(Token token) { |
| 1563 debugEvent("ConstExpression"); |
| 1564 handleNewExpression(token); |
| 1565 } |
| 1566 |
| 1567 void endTypeArguments(int count, Token beginToken, Token endToken) { |
| 1568 debugEvent("TypeArguments"); |
| 1569 push(popList(count)); |
| 1570 } |
| 1571 |
| 1572 void handleThisExpression(Token token) { |
| 1573 debugEvent("ThisExpression"); |
| 1574 if (isFirstIdentifier && isInstanceContext) { |
| 1575 push(new ThisAccessor(this, token.charOffset, inInitializer)); |
| 1576 } else { |
| 1577 push(new IncompleteError( |
| 1578 this, token.charOffset, "Expected identifier, but got 'this'.")); |
| 1579 } |
| 1580 } |
| 1581 |
| 1582 void handleSuperExpression(Token token) { |
| 1583 debugEvent("SuperExpression"); |
| 1584 if (isFirstIdentifier && isInstanceContext) { |
| 1585 Member member = this.member.target; |
| 1586 member.transformerFlags |= TransformerFlag.superCalls; |
| 1587 push(new ThisAccessor(this, token.charOffset, inInitializer, |
| 1588 isSuper: true)); |
| 1589 } else { |
| 1590 push(new IncompleteError( |
| 1591 this, token.charOffset, "Expected identifier, but got 'super'.")); |
| 1592 } |
| 1593 } |
| 1594 |
| 1595 void handleNamedArgument(Token colon) { |
| 1596 debugEvent("NamedArgument"); |
| 1597 Expression value = popForValue(); |
| 1598 Identifier identifier = pop(); |
| 1599 push(new NamedExpression(identifier.name, value)); |
| 1600 } |
| 1601 |
| 1602 void endFunctionName(Token token) { |
| 1603 debugEvent("FunctionName"); |
| 1604 Identifier name = pop(); |
| 1605 VariableDeclaration variable = new VariableDeclaration( |
| 1606 name.name, isFinal: true); |
| 1607 push(new FunctionDeclaration(variable, |
| 1608 new FunctionNode(new InvalidStatement()))); |
| 1609 scope[variable.name] = new KernelVariableBuilder(variable); |
| 1610 enterLocalScope(); |
| 1611 } |
| 1612 |
| 1613 void beginFunction(Token token) { |
| 1614 debugEvent("beginFunction"); |
| 1615 functionNestingLevel++; |
| 1616 } |
| 1617 |
| 1618 void beginUnnamedFunction(Token token) { |
| 1619 debugEvent("beginUnnamedFunction"); |
| 1620 functionNestingLevel++; |
| 1621 } |
| 1622 |
| 1623 void endFunction(Token getOrSet, Token endToken) { |
| 1624 debugEvent("Function"); |
| 1625 Statement body = popStatement(); |
| 1626 AsyncMarker asyncModifier = pop(); |
| 1627 if (functionNestingLevel != 0) { |
| 1628 exitLocalScope(); |
| 1629 } |
| 1630 FormalParameters formals = pop(); |
| 1631 List typeParameters = pop(); |
| 1632 push(formals.addToFunction(new FunctionNode(body, |
| 1633 typeParameters: typeParameters, asyncMarker: asyncModifier))); |
| 1634 functionNestingLevel--; |
| 1635 } |
| 1636 |
| 1637 void endFunctionDeclaration(Token token) { |
| 1638 debugEvent("FunctionDeclaration"); |
| 1639 FunctionNode function = pop(); |
| 1640 exitLocalScope(); |
| 1641 FunctionDeclaration declaration = pop(); |
| 1642 function.returnType = pop() ?? const DynamicType(); |
| 1643 pop(); // Modifiers. |
| 1644 declaration.function = function; |
| 1645 function.parent = declaration; |
| 1646 push(declaration); |
| 1647 } |
| 1648 |
| 1649 void endUnnamedFunction(Token token) { |
| 1650 debugEvent("UnnamedFunction"); |
| 1651 Statement body = popStatement(); |
| 1652 AsyncMarker asyncModifier = pop(); |
| 1653 exitLocalScope(); |
| 1654 FormalParameters formals = pop(); |
| 1655 List typeParameters = pop(); |
| 1656 FunctionNode function = formals.addToFunction(new FunctionNode(body, |
| 1657 typeParameters: typeParameters, asyncMarker: asyncModifier)); |
| 1658 push(new FunctionExpression(function)); |
| 1659 functionNestingLevel--; |
| 1660 } |
| 1661 |
| 1662 void endDoWhileStatement( |
| 1663 Token doKeyword, Token whileKeyword, Token endToken) { |
| 1664 debugEvent("DoWhileStatement"); |
| 1665 Expression condition = popForValue(); |
| 1666 Statement body = popStatement(); |
| 1667 JumpTarget continueTarget = exitContinueTarget(); |
| 1668 JumpTarget breakTarget = exitBreakTarget(); |
| 1669 if (continueTarget.hasUsers) { |
| 1670 body = new LabeledStatement(body); |
| 1671 continueTarget.resolveContinues(body); |
| 1672 } |
| 1673 Statement result = new DoStatement(body, condition); |
| 1674 if (breakTarget.hasUsers) { |
| 1675 result = new LabeledStatement(result); |
| 1676 breakTarget.resolveBreaks(result); |
| 1677 } |
| 1678 exitLoopOrSwitch(result); |
| 1679 } |
| 1680 |
| 1681 void endForIn( |
| 1682 Token awaitToken, Token forToken, Token inKeyword, Token endToken) { |
| 1683 debugEvent("ForIn"); |
| 1684 Statement body = popStatement(); |
| 1685 Expression expression = popForValue(); |
| 1686 var lvalue = pop(); |
| 1687 exitLocalScope(); |
| 1688 JumpTarget continueTarget = exitContinueTarget(); |
| 1689 JumpTarget breakTarget = exitBreakTarget(); |
| 1690 if (continueTarget.hasUsers) { |
| 1691 body = new LabeledStatement(body); |
| 1692 continueTarget.resolveContinues(body); |
| 1693 } |
| 1694 VariableDeclaration variable; |
| 1695 if (lvalue is VariableDeclaration) { |
| 1696 variable = lvalue; |
| 1697 } else if (lvalue is BuilderAccessor) { |
| 1698 /// We are in this case, where `lvalue` isn't a [VariableDeclaration]: |
| 1699 /// |
| 1700 /// for (lvalue in expression) body |
| 1701 /// |
| 1702 /// This is normalized to: |
| 1703 /// |
| 1704 /// for (final #t in expression) { |
| 1705 /// lvalue = #t; |
| 1706 /// body; |
| 1707 /// } |
| 1708 variable = new VariableDeclaration.forValue(null); |
| 1709 body = combineStatements( |
| 1710 new ExpressionStatement( |
| 1711 lvalue.buildAssignment( |
| 1712 new VariableGet(variable), voidContext: true)), |
| 1713 body); |
| 1714 } else { |
| 1715 throw inputError("Expected lvalue, but got ${lvalue}", |
| 1716 forToken.next.next.charOffset); |
| 1717 } |
| 1718 Statement result = new ForInStatement(variable, expression, body, |
| 1719 isAsync: awaitToken != null); |
| 1720 if (breakTarget.hasUsers) { |
| 1721 result = new LabeledStatement(result); |
| 1722 breakTarget.resolveBreaks(result); |
| 1723 } |
| 1724 exitLoopOrSwitch(result); |
| 1725 } |
| 1726 |
| 1727 void handleLabel(Token token) { |
| 1728 debugEvent("Label"); |
| 1729 Identifier identifier = pop(); |
| 1730 push(new Label(identifier.name)); |
| 1731 } |
| 1732 |
| 1733 void beginLabeledStatement(Token token, int labelCount) { |
| 1734 debugEvent("beginLabeledStatement"); |
| 1735 List<Label> labels = popList(labelCount); |
| 1736 enterLocalScope(); |
| 1737 LabelTarget target = new LabelTarget(); |
| 1738 for (Label label in labels) { |
| 1739 scope[label.name] = target; |
| 1740 } |
| 1741 push(target); |
| 1742 } |
| 1743 |
| 1744 void endLabeledStatement(int labelCount) { |
| 1745 debugEvent("LabeledStatement"); |
| 1746 Statement statement = popStatement(); |
| 1747 LabelTarget target = pop(); |
| 1748 exitLocalScope(); |
| 1749 if (target.breakTarget.hasUsers) { |
| 1750 if (statement is! LabeledStatement) { |
| 1751 statement = new LabeledStatement(statement); |
| 1752 } |
| 1753 target.breakTarget.resolveBreaks(statement); |
| 1754 } |
| 1755 if (target.continueTarget.hasUsers) { |
| 1756 if (statement is! LabeledStatement) { |
| 1757 statement = new LabeledStatement(statement); |
| 1758 } |
| 1759 target.continueTarget.resolveContinues(statement); |
| 1760 } |
| 1761 push(statement); |
| 1762 } |
| 1763 |
| 1764 void endRethrowStatement(Token throwToken, Token endToken) { |
| 1765 debugEvent("RethrowStatement"); |
| 1766 push(new ExpressionStatement(new Rethrow())); |
| 1767 } |
| 1768 |
| 1769 void handleFinallyBlock(Token finallyKeyword) { |
| 1770 debugEvent("FinallyBlock"); |
| 1771 // Do nothing, handled by [endTryStatement]. |
| 1772 } |
| 1773 |
| 1774 void endWhileStatement(Token whileKeyword, Token endToken) { |
| 1775 debugEvent("WhileStatement"); |
| 1776 Statement body = popStatement(); |
| 1777 Expression condition = popForValue(); |
| 1778 JumpTarget continueTarget = exitContinueTarget(); |
| 1779 JumpTarget breakTarget = exitBreakTarget(); |
| 1780 if (continueTarget.hasUsers) { |
| 1781 body = new LabeledStatement(body); |
| 1782 continueTarget.resolveContinues(body); |
| 1783 } |
| 1784 Statement result = new WhileStatement(condition, body); |
| 1785 if (breakTarget.hasUsers) { |
| 1786 result = new LabeledStatement(result); |
| 1787 breakTarget.resolveBreaks(result); |
| 1788 } |
| 1789 exitLoopOrSwitch(result); |
| 1790 } |
| 1791 |
| 1792 void handleEmptyStatement(Token token) { |
| 1793 debugEvent("EmptyStatement"); |
| 1794 push(new EmptyStatement()); |
| 1795 } |
| 1796 |
| 1797 void handleAssertStatement( |
| 1798 Token assertKeyword, Token commaToken, Token semicolonToken) { |
| 1799 debugEvent("AssertStatement"); |
| 1800 Expression message = popIfNotNull(commaToken); |
| 1801 Expression condition = popForValue(); |
| 1802 push(new AssertStatement(condition, message)); |
| 1803 } |
| 1804 |
| 1805 void endYieldStatement(Token yieldToken, Token starToken, Token endToken) { |
| 1806 debugEvent("YieldStatement"); |
| 1807 push(new YieldStatement(popForValue(), isYieldStar: starToken != null)); |
| 1808 } |
| 1809 |
| 1810 void beginSwitchBlock(Token token) { |
| 1811 debugEvent("beginSwitchBlock"); |
| 1812 enterLocalScope(); |
| 1813 enterSwitchScope(); |
| 1814 enterBreakTarget(); |
| 1815 } |
| 1816 |
| 1817 void beginSwitchCase(int labelCount, int expressionCount, Token firstToken) { |
| 1818 debugEvent("beginSwitchCase"); |
| 1819 List labelsAndExpressions = popList(labelCount + expressionCount); |
| 1820 List<Label> labels = <Label>[]; |
| 1821 List<Expression> expressions = <Expression>[]; |
| 1822 if (labelsAndExpressions != null) { |
| 1823 for (var labelOrExpression in labelsAndExpressions) { |
| 1824 if (labelOrExpression is Label) { |
| 1825 labels.add(labelOrExpression); |
| 1826 } else { |
| 1827 expressions.add(toValue(labelOrExpression)); |
| 1828 } |
| 1829 } |
| 1830 } |
| 1831 assert(scope == switchScope); |
| 1832 for (Label label in labels) { |
| 1833 Builder existing = scope.local[label.name]; |
| 1834 if (existing == null) { |
| 1835 scope[label.name] = createGotoTarget(); |
| 1836 } else { |
| 1837 // TODO(ahe): Should validate this is a goto target and not duplicated. |
| 1838 } |
| 1839 } |
| 1840 push(expressions); |
| 1841 push(labels); |
| 1842 enterLocalScope(); |
| 1843 } |
| 1844 |
| 1845 void handleSwitchCase( |
| 1846 int labelCount, |
| 1847 int expressionCount, |
| 1848 Token defaultKeyword, |
| 1849 int statementCount, |
| 1850 Token firstToken, |
| 1851 Token endToken) { |
| 1852 debugEvent("SwitchCase"); |
| 1853 Block block = popBlock(statementCount); |
| 1854 exitLocalScope(); |
| 1855 List<Label> labels = pop(); |
| 1856 List<Expression> expressions = pop(); |
| 1857 push(new SwitchCase(expressions, block, isDefault: defaultKeyword != null)); |
| 1858 push(labels); |
| 1859 } |
| 1860 |
| 1861 void endSwitchStatement(Token switchKeyword, Token endToken) { |
| 1862 debugEvent("SwitchStatement"); |
| 1863 // Do nothing. Handled by [endSwitchBlock]. |
| 1864 } |
| 1865 |
| 1866 void endSwitchBlock(int caseCount, Token beginToken, Token endToken) { |
| 1867 debugEvent("SwitchBlock"); |
| 1868 List<SwitchCase> cases = |
| 1869 new List<SwitchCase>.filled(caseCount, null, growable: true); |
| 1870 for (int i = caseCount - 1; i >= 0; i--) { |
| 1871 List<Label> labels = pop(); |
| 1872 SwitchCase current = cases[i] = pop(); |
| 1873 for (Label label in labels) { |
| 1874 JumpTarget target = switchScope.lookup(label.name); |
| 1875 if (target != null) { |
| 1876 target.resolveGotos(current); |
| 1877 } |
| 1878 } |
| 1879 // TODO(ahe): Validate that there's only one default and it's last. |
| 1880 } |
| 1881 JumpTarget target = exitBreakTarget(); |
| 1882 exitSwitchScope(); |
| 1883 exitLocalScope(); |
| 1884 Expression expression = popForValue(); |
| 1885 Statement result = new SwitchStatement(expression, cases); |
| 1886 if (target.hasUsers) { |
| 1887 result = new LabeledStatement(result); |
| 1888 target.resolveBreaks(result); |
| 1889 } |
| 1890 exitLoopOrSwitch(result); |
| 1891 } |
| 1892 |
| 1893 void handleCaseMatch(Token caseKeyword, Token colon) { |
| 1894 debugEvent("CaseMatch"); |
| 1895 // Do nothing. Handled by [handleSwitchCase]. |
| 1896 } |
| 1897 |
| 1898 void handleBreakStatement( |
| 1899 bool hasTarget, Token breakKeyword, Token endToken) { |
| 1900 debugEvent("BreakStatement"); |
| 1901 var target = breakTarget; |
| 1902 String name; |
| 1903 if (hasTarget) { |
| 1904 Identifier identifier = pop(); |
| 1905 name = identifier.name; |
| 1906 target = scope.lookup(identifier.name); |
| 1907 } |
| 1908 if (target == null && name == null) { |
| 1909 push(compileTimeErrorInLoopOrSwitch = |
| 1910 buildCompileTimeErrorStatement( |
| 1911 "No target of break.", breakKeyword.charOffset)); |
| 1912 } else if (target == null || target is! JumpTarget |
| 1913 || !target.isBreakTarget) { |
| 1914 push(compileTimeErrorInLoopOrSwitch = |
| 1915 buildCompileTimeErrorStatement("Can't break to '$name'.", |
| 1916 breakKeyword.next.charOffset)); |
| 1917 } else { |
| 1918 BreakStatement statement = new BreakStatement(null); |
| 1919 target.addBreak(statement); |
| 1920 push(statement); |
| 1921 } |
| 1922 } |
| 1923 |
| 1924 void handleContinueStatement( |
| 1925 bool hasTarget, Token continueKeyword, Token endToken) { |
| 1926 debugEvent("ContinueStatement"); |
| 1927 var target = continueTarget; |
| 1928 String name; |
| 1929 if (hasTarget) { |
| 1930 Identifier identifier = pop(); |
| 1931 name = identifier.name; |
| 1932 target = scope.lookup(identifier.name); |
| 1933 if (target != null && target is! JumpTarget) { |
| 1934 push(compileTimeErrorInLoopOrSwitch = |
| 1935 buildCompileTimeErrorStatement( |
| 1936 "Target of continue must be a label.", |
| 1937 continueKeyword.charOffset)); |
| 1938 return; |
| 1939 } |
| 1940 if (target == null) { |
| 1941 if (switchScope == null) { |
| 1942 push(buildCompileTimeErrorStatement("Can't find label '$name'.", |
| 1943 continueKeyword.next.charOffset)); |
| 1944 return; |
| 1945 } |
| 1946 switchScope[identifier.name] = target = createGotoTarget(); |
| 1947 } |
| 1948 if (target.isGotoTarget) { |
| 1949 ContinueSwitchStatement statement = new ContinueSwitchStatement(null); |
| 1950 target.addGoto(statement); |
| 1951 push(statement); |
| 1952 return; |
| 1953 } |
| 1954 } |
| 1955 if (target == null) { |
| 1956 push(compileTimeErrorInLoopOrSwitch = |
| 1957 buildCompileTimeErrorStatement("No target of continue.", |
| 1958 continueKeyword.charOffset)); |
| 1959 } else if (!target.isContinueTarget) { |
| 1960 push(compileTimeErrorInLoopOrSwitch = |
| 1961 buildCompileTimeErrorStatement("Can't continue at '$name'.", |
| 1962 continueKeyword.next.charOffset)); |
| 1963 } else { |
| 1964 BreakStatement statement = new BreakStatement(null); |
| 1965 target.addContinue(statement); |
| 1966 push(statement); |
| 1967 } |
| 1968 } |
| 1969 |
| 1970 void endTypeVariable(Token token, Token extendsOrSuper) { |
| 1971 logEvent("TypeVariable"); |
| 1972 // TODO(ahe): Implement this when enabling generic method syntax. |
| 1973 } |
| 1974 |
| 1975 void endTypeVariables(int count, Token beginToken, Token endToken) { |
| 1976 logEvent("TypeVariables"); |
| 1977 // TODO(ahe): Implement this when enabling generic method syntax. |
| 1978 } |
| 1979 |
| 1980 void handleModifier(Token token) { |
| 1981 debugEvent("Modifier"); |
| 1982 // TODO(ahe): Copied from outline_builder.dart. |
| 1983 push(new Modifier.fromString(token.stringValue)); |
| 1984 } |
| 1985 |
| 1986 void handleModifiers(int count) { |
| 1987 debugEvent("Modifiers"); |
| 1988 // TODO(ahe): Copied from outline_builder.dart. |
| 1989 push(popList(count) ?? NullValue.Modifiers); |
| 1990 } |
| 1991 |
| 1992 void reportErrorHelper(Token token, ErrorKind kind, Map arguments) { |
| 1993 super.reportErrorHelper(token, kind, arguments); |
| 1994 if (!hasParserError) { |
| 1995 print("$uri:${recoverableErrors.last}"); |
| 1996 } |
| 1997 hasParserError = true; |
| 1998 } |
| 1999 |
| 2000 Token expectedExpression(Token token) { |
| 2001 if (token is ErrorToken) { |
| 2002 reportErrorToken(token); |
| 2003 push(new Throw(new StringLiteral("${recoverableErrors.last}"))); |
| 2004 do { |
| 2005 token = token.next; |
| 2006 } while (token is ErrorToken); |
| 2007 return token; |
| 2008 } else { |
| 2009 push(new InvalidExpression()); |
| 2010 return super.expectedExpression(token); |
| 2011 } |
| 2012 } |
| 2013 |
| 2014 Token expected(String string, Token token) { |
| 2015 if (token is ErrorToken) { |
| 2016 reportErrorToken(token); |
| 2017 do { |
| 2018 token = token.next; |
| 2019 } while (token is ErrorToken); |
| 2020 return token; |
| 2021 } |
| 2022 const List<String> trailing = const <String>[")", "}", ";", ","]; |
| 2023 if (trailing.contains(token.stringValue) && trailing.contains(string)) { |
| 2024 // We're just trying to get out an error. |
| 2025 if (recoverableErrors.isNotEmpty) { |
| 2026 reportError(token, ErrorKind.UNSPECIFIED, |
| 2027 {"text": "Expected: '$string', but got '${token.value}'"}); |
| 2028 } |
| 2029 return token; |
| 2030 } |
| 2031 return super.expected(string, token); |
| 2032 } |
| 2033 |
| 2034 void warning(error, [int charOffset = -1]) { |
| 2035 String message = new InputError(uri, charOffset, error).format(); |
| 2036 print(message); |
| 2037 } |
| 2038 |
| 2039 void nit(error, [int charOffset = -1]) { |
| 2040 if (!showNits) return; |
| 2041 String message = new InputError(uri, charOffset, error).format(); |
| 2042 print(message); |
| 2043 } |
| 2044 |
| 2045 Expression buildCompileTimeError(error, [int charOffset = -1]) { |
| 2046 String message = new InputError(uri, charOffset, error).format(); |
| 2047 print(message); |
| 2048 return new Throw(new StringLiteral(message)); |
| 2049 } |
| 2050 |
| 2051 Statement buildCompileTimeErrorStatement(error, [int charOffset = -1]) { |
| 2052 return new ExpressionStatement(buildCompileTimeError(error, charOffset)); |
| 2053 } |
| 2054 |
| 2055 Initializer buildCompileTimeErrorIntializer(error, [int charOffset = -1]) { |
| 2056 return new LocalInitializer( |
| 2057 new VariableDeclaration.forValue( |
| 2058 buildCompileTimeError(error, charOffset))); |
| 2059 } |
| 2060 |
| 2061 |
| 2062 Expression buildProblemExpression(Builder builder, String name) { |
| 2063 if (builder is AmbiguousBuilder) { |
| 2064 return buildCompileTimeError("Duplicated named: '$name'."); |
| 2065 } else if (builder is AccessErrorBuilder) { |
| 2066 return buildCompileTimeError("Access error: '$name'."); |
| 2067 } else { |
| 2068 return internalError("Unhandled: ${builder.runtimeType}"); |
| 2069 } |
| 2070 } |
| 2071 |
| 2072 void handleOperator(Token token) { |
| 2073 debugEvent("Operator"); |
| 2074 push(new Operator(token.stringValue)..fileOffset = token.charOffset); |
| 2075 } |
| 2076 |
| 2077 dynamic inputError(String message, [int charOffset = -1]) { |
| 2078 return errors.inputError(uri, charOffset, message); |
| 2079 } |
| 2080 |
| 2081 void debugEvent(String name) { |
| 2082 // printEvent(name); |
| 2083 } |
| 2084 } |
| 2085 |
| 2086 // TODO(ahe): Shouldn't need to be an expression. |
| 2087 class UnresolvedIdentifier extends InvalidExpression { |
| 2088 final Name name; |
| 2089 |
| 2090 UnresolvedIdentifier(this.name); |
| 2091 |
| 2092 String toString() => "unresolved-identifier($name)"; |
| 2093 } |
| 2094 |
| 2095 // TODO(ahe): Shouldn't need to be an expression. |
| 2096 class Identifier extends InvalidExpression { |
| 2097 final String name; |
| 2098 |
| 2099 Identifier(this.name); |
| 2100 |
| 2101 Expression get initializer => null; |
| 2102 |
| 2103 String toString() => "identifier($name)"; |
| 2104 } |
| 2105 |
| 2106 // TODO(ahe): Shouldn't need to be an expression. |
| 2107 class Operator extends InvalidExpression { |
| 2108 final String name; |
| 2109 |
| 2110 Operator(this.name); |
| 2111 |
| 2112 String toString() => "operator($name)"; |
| 2113 } |
| 2114 |
| 2115 class InitializedIdentifier extends Identifier { |
| 2116 final Expression initializer; |
| 2117 |
| 2118 InitializedIdentifier(String name, this.initializer) |
| 2119 : super(name); |
| 2120 |
| 2121 String toString() => "initialized-identifier($name, $initializer)"; |
| 2122 } |
| 2123 |
| 2124 // TODO(ahe): Shouldn't need to be an expression. |
| 2125 class Label extends InvalidExpression { |
| 2126 String name; |
| 2127 |
| 2128 Label(this.name); |
| 2129 |
| 2130 String toString() => "label($name)"; |
| 2131 } |
| 2132 |
| 2133 class CascadeReceiver extends Let { |
| 2134 Let nextCascade; |
| 2135 |
| 2136 CascadeReceiver(VariableDeclaration variable) |
| 2137 : super(variable, |
| 2138 makeLet(new VariableDeclaration.forValue(new InvalidExpression()), |
| 2139 new VariableGet(variable))) { |
| 2140 nextCascade = body; |
| 2141 } |
| 2142 |
| 2143 void extend() { |
| 2144 assert(nextCascade.variable.initializer is! InvalidExpression); |
| 2145 Let newCascade = makeLet( |
| 2146 new VariableDeclaration.forValue(new InvalidExpression()), |
| 2147 nextCascade.body); |
| 2148 nextCascade.body = newCascade; |
| 2149 newCascade.parent = nextCascade; |
| 2150 nextCascade = newCascade; |
| 2151 } |
| 2152 |
| 2153 void finalize(Expression expression) { |
| 2154 assert(nextCascade.variable.initializer is InvalidExpression); |
| 2155 nextCascade.variable.initializer = expression; |
| 2156 expression.parent = nextCascade.variable; |
| 2157 } |
| 2158 } |
| 2159 |
| 2160 abstract class ContextAccessor extends BuilderAccessor { |
| 2161 final BuilderHelper helper; |
| 2162 |
| 2163 final int charOffset; |
| 2164 |
| 2165 final BuilderAccessor accessor; |
| 2166 |
| 2167 ContextAccessor(this.helper, this.charOffset, this.accessor); |
| 2168 |
| 2169 String get plainNameForRead => internalError("Unsupported operation."); |
| 2170 |
| 2171 Expression doInvocation(int charOffset, Arguments arguments) { |
| 2172 print("$uri:$charOffset: Internal error: Unhandled: ${runtimeType}"); |
| 2173 return internalError("Unhandled: ${runtimeType}"); |
| 2174 } |
| 2175 |
| 2176 Expression buildSimpleRead(); |
| 2177 |
| 2178 Expression buildForEffect(); |
| 2179 |
| 2180 Expression buildAssignment(Expression value, {bool voidContext: false}) { |
| 2181 return internalError("not supported"); |
| 2182 } |
| 2183 |
| 2184 Expression buildNullAwareAssignment(Expression value, DartType type, |
| 2185 {bool voidContext: false}) { |
| 2186 return internalError("not supported"); |
| 2187 } |
| 2188 |
| 2189 Expression buildCompoundAssignment(Name binaryOperator, Expression value, |
| 2190 {bool voidContext: false, Procedure interfaceTarget}) { |
| 2191 return internalError("not supported"); |
| 2192 } |
| 2193 |
| 2194 Expression buildPrefixIncrement(Name binaryOperator, |
| 2195 {bool voidContext: false, Procedure interfaceTarget}) { |
| 2196 return internalError("not supported"); |
| 2197 } |
| 2198 |
| 2199 Expression buildPostfixIncrement(Name binaryOperator, |
| 2200 {bool voidContext: false, Procedure interfaceTarget}) { |
| 2201 return internalError("not supported"); |
| 2202 } |
| 2203 |
| 2204 makeInvalidRead() => internalError("not supported"); |
| 2205 |
| 2206 makeInvalidWrite(Expression value) => internalError("not supported"); |
| 2207 } |
| 2208 |
| 2209 class DelayedAssignment extends ContextAccessor { |
| 2210 final Expression value; |
| 2211 |
| 2212 final String assignmentOperator; |
| 2213 |
| 2214 DelayedAssignment(BuilderHelper helper, int charOffset, |
| 2215 BuilderAccessor accessor, this.value, this.assignmentOperator) |
| 2216 : super(helper, charOffset, accessor); |
| 2217 |
| 2218 Expression buildSimpleRead() { |
| 2219 return handleAssignment(false); |
| 2220 } |
| 2221 |
| 2222 Expression buildForEffect() { |
| 2223 return handleAssignment(true); |
| 2224 } |
| 2225 |
| 2226 Expression handleAssignment(bool voidContext) { |
| 2227 if (identical("=", assignmentOperator)) { |
| 2228 return accessor.buildAssignment(value, voidContext: voidContext); |
| 2229 } else if (identical("+=", assignmentOperator)) { |
| 2230 return accessor.buildCompoundAssignment( |
| 2231 plusName, value, voidContext: voidContext); |
| 2232 } else if (identical("-=", assignmentOperator)) { |
| 2233 return accessor.buildCompoundAssignment( |
| 2234 minusName, value, voidContext: voidContext); |
| 2235 } else if (identical("*=", assignmentOperator)) { |
| 2236 return accessor.buildCompoundAssignment( |
| 2237 multiplyName, value, voidContext: voidContext); |
| 2238 } else if (identical("%=", assignmentOperator)) { |
| 2239 return accessor.buildCompoundAssignment( |
| 2240 percentName, value, voidContext: voidContext); |
| 2241 } else if (identical("&=", assignmentOperator)) { |
| 2242 return accessor.buildCompoundAssignment( |
| 2243 ampersandName, value, voidContext: voidContext); |
| 2244 } else if (identical("/=", assignmentOperator)) { |
| 2245 return accessor.buildCompoundAssignment( |
| 2246 divisionName, value, voidContext: voidContext); |
| 2247 } else if (identical("<<=", assignmentOperator)) { |
| 2248 return accessor.buildCompoundAssignment( |
| 2249 leftShiftName, value, voidContext: voidContext); |
| 2250 } else if (identical(">>=", assignmentOperator)) { |
| 2251 return accessor.buildCompoundAssignment( |
| 2252 rightShiftName, value, voidContext: voidContext); |
| 2253 } else if (identical("??=", assignmentOperator)) { |
| 2254 return accessor.buildNullAwareAssignment( |
| 2255 value, const DynamicType(), voidContext: voidContext); |
| 2256 } else if (identical("^=", assignmentOperator)) { |
| 2257 return accessor.buildCompoundAssignment( |
| 2258 caretName, value, voidContext: voidContext); |
| 2259 } else if (identical("|=", assignmentOperator)) { |
| 2260 return accessor.buildCompoundAssignment( |
| 2261 barName, value, voidContext: voidContext); |
| 2262 } else if (identical("~/=", assignmentOperator)) { |
| 2263 return accessor.buildCompoundAssignment( |
| 2264 mustacheName, value, voidContext: voidContext); |
| 2265 } else { |
| 2266 return internalError("Unhandled: $assignmentOperator"); |
| 2267 } |
| 2268 } |
| 2269 |
| 2270 Initializer buildFieldInitializer( |
| 2271 Map<String, FieldInitializer> initializers) { |
| 2272 if (!identical("=", assignmentOperator) || |
| 2273 !accessor.isThisPropertyAccessor) { |
| 2274 return accessor.buildFieldInitializer(initializers); |
| 2275 } |
| 2276 String name = accessor.plainNameForRead; |
| 2277 FieldInitializer initializer = initializers[name]; |
| 2278 if (initializer != null && initializer.value == null) { |
| 2279 initializers.remove(name); |
| 2280 initializer.value = value |
| 2281 ..parent = initializer; |
| 2282 return initializer; |
| 2283 } |
| 2284 return accessor.buildFieldInitializer(initializers); |
| 2285 } |
| 2286 } |
| 2287 |
| 2288 class DelayedPostfixIncrement extends ContextAccessor { |
| 2289 final Name binaryOperator; |
| 2290 |
| 2291 final Procedure interfaceTarget; |
| 2292 |
| 2293 DelayedPostfixIncrement(BuilderHelper helper, int charOffset, |
| 2294 BuilderAccessor accessor, this.binaryOperator, this.interfaceTarget) |
| 2295 : super(helper, charOffset, accessor); |
| 2296 |
| 2297 Expression buildSimpleRead() { |
| 2298 return accessor.buildPostfixIncrement(binaryOperator, voidContext: false, |
| 2299 interfaceTarget: interfaceTarget); |
| 2300 } |
| 2301 |
| 2302 Expression buildForEffect() { |
| 2303 return accessor.buildPostfixIncrement(binaryOperator, voidContext: true, |
| 2304 interfaceTarget: interfaceTarget); |
| 2305 } |
| 2306 } |
| 2307 |
| 2308 class JumpTarget extends Builder { |
| 2309 final List<Statement> users = <Statement>[]; |
| 2310 |
| 2311 final JumpTargetKind kind; |
| 2312 |
| 2313 JumpTarget(this.kind); |
| 2314 |
| 2315 bool get isBreakTarget => kind == JumpTargetKind.Break; |
| 2316 |
| 2317 bool get isContinueTarget => kind == JumpTargetKind.Continue; |
| 2318 |
| 2319 bool get isGotoTarget => kind == JumpTargetKind.Goto; |
| 2320 |
| 2321 bool get hasUsers => users.isNotEmpty; |
| 2322 |
| 2323 void addBreak(BreakStatement statement) { |
| 2324 assert(isBreakTarget); |
| 2325 users.add(statement); |
| 2326 } |
| 2327 |
| 2328 void addContinue(BreakStatement statement) { |
| 2329 assert(isContinueTarget); |
| 2330 users.add(statement); |
| 2331 } |
| 2332 |
| 2333 void addGoto(ContinueSwitchStatement statement) { |
| 2334 assert(isGotoTarget); |
| 2335 users.add(statement); |
| 2336 } |
| 2337 |
| 2338 void resolveBreaks(LabeledStatement target) { |
| 2339 assert(isBreakTarget); |
| 2340 for (BreakStatement user in users) { |
| 2341 user.target = target; |
| 2342 } |
| 2343 users.clear(); |
| 2344 } |
| 2345 |
| 2346 void resolveContinues(LabeledStatement target) { |
| 2347 assert(isContinueTarget); |
| 2348 for (BreakStatement user in users) { |
| 2349 user.target = target; |
| 2350 } |
| 2351 users.clear(); |
| 2352 } |
| 2353 |
| 2354 void resolveGotos(SwitchCase target) { |
| 2355 assert(isGotoTarget); |
| 2356 for (ContinueSwitchStatement user in users) { |
| 2357 user.target = target; |
| 2358 } |
| 2359 users.clear(); |
| 2360 } |
| 2361 } |
| 2362 |
| 2363 class LabelTarget extends Builder implements JumpTarget { |
| 2364 final JumpTarget breakTarget = new JumpTarget(JumpTargetKind.Break); |
| 2365 |
| 2366 final JumpTarget continueTarget = new JumpTarget(JumpTargetKind.Continue); |
| 2367 |
| 2368 LabelTarget(); |
| 2369 |
| 2370 bool get hasUsers => breakTarget.hasUsers || continueTarget.hasUsers; |
| 2371 |
| 2372 List<Statement> get users => internalError("Unsupported operation."); |
| 2373 |
| 2374 JumpTargetKind get kind => internalError("Unsupported operation."); |
| 2375 |
| 2376 bool get isBreakTarget => true; |
| 2377 |
| 2378 bool get isContinueTarget => true; |
| 2379 |
| 2380 bool get isGotoTarget => false; |
| 2381 |
| 2382 void addBreak(BreakStatement statement) { |
| 2383 breakTarget.addBreak(statement); |
| 2384 } |
| 2385 |
| 2386 void addContinue(BreakStatement statement) { |
| 2387 continueTarget.addContinue(statement); |
| 2388 } |
| 2389 |
| 2390 void addGoto(ContinueSwitchStatement statement) { |
| 2391 internalError("Unsupported operation."); |
| 2392 } |
| 2393 |
| 2394 void resolveBreaks(LabeledStatement target) { |
| 2395 breakTarget.resolveBreaks(target); |
| 2396 } |
| 2397 |
| 2398 void resolveContinues(LabeledStatement target) { |
| 2399 continueTarget.resolveContinues(target); |
| 2400 } |
| 2401 |
| 2402 void resolveGotos(SwitchCase target) { |
| 2403 internalError("Unsupported operation."); |
| 2404 } |
| 2405 } |
| 2406 |
| 2407 class OptionalFormals { |
| 2408 final FormalParameterType kind; |
| 2409 |
| 2410 final List<VariableDeclaration> formals; |
| 2411 |
| 2412 OptionalFormals(this.kind, this.formals); |
| 2413 } |
| 2414 |
| 2415 class FormalParameters { |
| 2416 final List<VariableDeclaration> required; |
| 2417 final OptionalFormals optional; |
| 2418 |
| 2419 FormalParameters(this.required, this.optional); |
| 2420 |
| 2421 FunctionNode addToFunction(FunctionNode function) { |
| 2422 function.requiredParameterCount = required.length; |
| 2423 function.positionalParameters.addAll(required); |
| 2424 if (optional != null) { |
| 2425 if (optional.kind.isPositional) { |
| 2426 function.positionalParameters.addAll(optional.formals); |
| 2427 } else { |
| 2428 function.namedParameters.addAll(optional.formals); |
| 2429 setParents(function.namedParameters, function); |
| 2430 } |
| 2431 } |
| 2432 setParents(function.positionalParameters, function); |
| 2433 return function; |
| 2434 } |
| 2435 |
| 2436 FunctionType toFunctionType(DartType returnType) { |
| 2437 returnType ??= const DynamicType(); |
| 2438 int requiredParameterCount = required.length; |
| 2439 List<DartType> positionalParameters = <DartType>[]; |
| 2440 List<NamedType> namedParameters = const <NamedType>[]; |
| 2441 for (VariableDeclaration parameter in required) { |
| 2442 positionalParameters.add(parameter.type); |
| 2443 } |
| 2444 if (optional != null) { |
| 2445 if (optional.kind.isPositional) { |
| 2446 for (VariableDeclaration parameter in optional.formals) { |
| 2447 positionalParameters.add(parameter.type); |
| 2448 } |
| 2449 } else { |
| 2450 namedParameters = <NamedType>[]; |
| 2451 for (VariableDeclaration parameter in optional.formals) { |
| 2452 namedParameters.add(new NamedType(parameter.name, parameter.type)); |
| 2453 } |
| 2454 namedParameters.sort(); |
| 2455 } |
| 2456 } |
| 2457 return new FunctionType(positionalParameters, returnType, |
| 2458 namedParameters: namedParameters, |
| 2459 requiredParameterCount: requiredParameterCount); |
| 2460 } |
| 2461 |
| 2462 Scope computeFormalParameterScope(Scope parent) { |
| 2463 if (required.length == 0 && optional == null) return parent; |
| 2464 Map<String, Builder> local = <String, Builder>{}; |
| 2465 for (VariableDeclaration parameter in required) { |
| 2466 local[parameter.name] = new KernelVariableBuilder(parameter); |
| 2467 } |
| 2468 if (optional != null) { |
| 2469 for (VariableDeclaration parameter in optional.formals) { |
| 2470 local[parameter.name] = new KernelVariableBuilder(parameter); |
| 2471 } |
| 2472 } |
| 2473 return new Scope(local, parent, isModifiable: false); |
| 2474 } |
| 2475 } |
| 2476 |
| 2477 /// Returns a block like this: |
| 2478 /// |
| 2479 /// { |
| 2480 /// statement; |
| 2481 /// body; |
| 2482 /// } |
| 2483 /// |
| 2484 /// If [body] is a [Block], it's returned with [statement] prepended to it. |
| 2485 Block combineStatements(Statement statement, Statement body) { |
| 2486 if (body is Block) { |
| 2487 body.statements.insert(0, statement); |
| 2488 statement.parent = body; |
| 2489 return body; |
| 2490 } else { |
| 2491 return new Block(<Statement>[statement, body]); |
| 2492 } |
| 2493 } |
| 2494 |
| 2495 String debugName(String className, String name, [String prefix]) { |
| 2496 String result = name.isEmpty ? className : "$className.$name"; |
| 2497 return prefix == null ? result : "$prefix.result"; |
| 2498 } |
| 2499 |
| 2500 String getNodeName(Object node) { |
| 2501 if (node is Identifier) { |
| 2502 return node.name; |
| 2503 } else if (node is UnresolvedIdentifier) { |
| 2504 return node.name.name; |
| 2505 } else if (node is TypeDeclarationBuilder) { |
| 2506 return node.name; |
| 2507 } else if (node is PrefixBuilder) { |
| 2508 return node.name; |
| 2509 } else if (node is ThisPropertyAccessor) { |
| 2510 return node.name.name; |
| 2511 } else { |
| 2512 return internalError("Unhandled: ${node.runtimeType}"); |
| 2513 } |
| 2514 } |
OLD | NEW |