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