Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(641)

Side by Side Diff: pkg/kernel/lib/analyzer/ast_from_analyzer.dart

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

Powered by Google App Engine
This is Rietveld 408576698