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