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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/kernel/lib/analyzer/ast_from_analyzer.dart
diff --git a/pkg/kernel/lib/analyzer/ast_from_analyzer.dart b/pkg/kernel/lib/analyzer/ast_from_analyzer.dart
deleted file mode 100644
index fc8e730d650c1f6d9ffd5ee645d33896cbfd51e4..0000000000000000000000000000000000000000
--- a/pkg/kernel/lib/analyzer/ast_from_analyzer.dart
+++ /dev/null
@@ -1,3079 +0,0 @@
-// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-library kernel.analyzer.ast_from_analyzer;
-
-import '../ast.dart' as ast;
-import '../frontend/accessors.dart';
-import '../frontend/super_initializers.dart';
-import '../log.dart';
-import '../type_algebra.dart';
-import '../transformations/flags.dart';
-import 'analyzer.dart';
-import 'loader.dart';
-import 'package:analyzer/analyzer.dart';
-import 'package:analyzer/dart/ast/standard_resolution_map.dart';
-import 'package:analyzer/src/generated/parser.dart';
-import 'package:analyzer/src/dart/element/member.dart';
-import 'package:analyzer/src/error/codes.dart';
-
-/// Provides reference-level access to libraries, classes, and members.
-///
-/// "Reference level" objects are incomplete nodes that have no children but
-/// can be used for linking until the loader promotes the node to a higher
-/// loading level.
-///
-/// The [ReferenceScope] is the most restrictive scope in a hierarchy of scopes
-/// that provide increasing amounts of contextual information. [TypeScope] is
-/// used when type parameters might be in scope, and [MemberScope] is used when
-/// building the body of a [ast.Member].
-class ReferenceScope {
- final ReferenceLevelLoader loader;
-
- ReferenceScope(this.loader);
-
- bool get strongMode => loader.strongMode;
-
- ast.Library getLibraryReference(LibraryElement element) {
- if (element == null) return null;
- return loader.getLibraryReference(getBaseElement(element));
- }
-
- ast.Class getRootClassReference() {
- return loader.getRootClassReference();
- }
-
- ast.Class getClassReference(ClassElement element) {
- return loader.getClassReference(getBaseElement(element));
- }
-
- ast.Member getMemberReference(Element element) {
- return loader.getMemberReference(getBaseElement(element));
- }
-
- static Element getBaseElement(Element element) {
- while (element is Member) {
- element = (element as Member).baseElement;
- }
- return element;
- }
-
- static bool supportsConcreteGet(Element element) {
- return (element is PropertyAccessorElement &&
- element.isGetter &&
- !element.isAbstract) ||
- element is FieldElement ||
- element is TopLevelVariableElement ||
- element is MethodElement && !element.isAbstract ||
- isTopLevelFunction(element);
- }
-
- static bool supportsInterfaceGet(Element element) {
- return (element is PropertyAccessorElement && element.isGetter) ||
- element is FieldElement ||
- element is MethodElement;
- }
-
- static bool supportsConcreteSet(Element element) {
- return (element is PropertyAccessorElement &&
- element.isSetter &&
- !element.isAbstract) ||
- element is FieldElement && !element.isFinal && !element.isConst ||
- element is TopLevelVariableElement &&
- !element.isFinal &&
- !element.isConst;
- }
-
- static bool supportsInterfaceSet(Element element) {
- return (element is PropertyAccessorElement && element.isSetter) ||
- element is FieldElement && !element.isFinal && !element.isConst;
- }
-
- static bool supportsConcreteIndexGet(Element element) {
- return element is MethodElement &&
- element.name == '[]' &&
- !element.isAbstract;
- }
-
- static bool supportsInterfaceIndexGet(Element element) {
- return element is MethodElement && element.name == '[]';
- }
-
- static bool supportsConcreteIndexSet(Element element) {
- return element is MethodElement &&
- element.name == '[]=' &&
- !element.isAbstract;
- }
-
- static bool supportsInterfaceIndexSet(Element element) {
- return element is MethodElement && element.name == '[]=';
- }
-
- static bool supportsConcreteMethodCall(Element element) {
- // Note that local functions are not valid targets for method calls because
- // they are not "methods" or even "procedures" in our AST.
- return element is MethodElement && !element.isAbstract ||
- isTopLevelFunction(element) ||
- element is ConstructorElement && element.isFactory;
- }
-
- static bool supportsInterfaceMethodCall(Element element) {
- return element is MethodElement;
- }
-
- static bool supportsConstructorCall(Element element) {
- return element is ConstructorElement && !element.isFactory;
- }
-
- ast.Member _resolveGet(
- Element element, Element auxiliary, bool predicate(Element element)) {
- element = desynthesizeGetter(element);
- if (predicate(element)) return getMemberReference(element);
- if (element is PropertyAccessorElement && element.isSetter) {
- // The getter is sometimes stored as the 'corresponding getter' instead
- // of the 'auxiliary' element.
- auxiliary ??= element.correspondingGetter;
- }
- auxiliary = desynthesizeGetter(auxiliary);
- if (predicate(auxiliary)) return getMemberReference(auxiliary);
- return null;
- }
-
- ast.Member resolveConcreteGet(Element element, Element auxiliary) {
- return _resolveGet(element, auxiliary, supportsConcreteGet);
- }
-
- ast.Member resolveInterfaceGet(Element element, Element auxiliary) {
- if (!strongMode) return null;
- return _resolveGet(element, auxiliary, supportsInterfaceGet);
- }
-
- DartType getterTypeOfElement(Element element) {
- if (element is VariableElement) {
- return element.type;
- } else if (element is PropertyAccessorElement && element.isGetter) {
- return element.returnType;
- } else {
- return null;
- }
- }
-
- /// Returns the interface target of a `call` dispatch to the given member.
- ///
- /// For example, if the member is a field of type C, the target will be the
- /// `call` method of class C, if it has such a method.
- ///
- /// If the class C has a getter or field named `call`, this method returns
- /// `null` - the static type system does support typed calls with indirection.
- ast.Member resolveInterfaceFunctionCall(Element element) {
- if (!strongMode || element == null) return null;
- return resolveInterfaceFunctionCallOnType(getterTypeOfElement(element));
- }
-
- /// Returns the `call` method of [callee], if it has one, otherwise `null`.
- ast.Member resolveInterfaceFunctionCallOnType(DartType callee) {
- return callee is InterfaceType
- ? resolveInterfaceMethod(callee.getMethod('call'))
- : null;
- }
-
- ast.Member _resolveSet(
- Element element, Element auxiliary, bool predicate(Element element)) {
- element = desynthesizeSetter(element);
- if (predicate(element)) {
- return getMemberReference(element);
- }
- if (element is PropertyAccessorElement && element.isSetter) {
- // The setter is sometimes stored as the 'corresponding setter' instead
- // of the 'auxiliary' element.
- auxiliary ??= element.correspondingGetter;
- }
- auxiliary = desynthesizeSetter(auxiliary);
- if (predicate(auxiliary)) {
- return getMemberReference(auxiliary);
- }
- return null;
- }
-
- ast.Member resolveConcreteSet(Element element, Element auxiliary) {
- return _resolveSet(element, auxiliary, supportsConcreteSet);
- }
-
- ast.Member resolveInterfaceSet(Element element, Element auxiliary) {
- if (!strongMode) return null;
- return _resolveSet(element, auxiliary, supportsInterfaceSet);
- }
-
- ast.Member resolveConcreteIndexGet(Element element, Element auxiliary) {
- if (supportsConcreteIndexGet(element)) {
- return getMemberReference(element);
- }
- if (supportsConcreteIndexGet(auxiliary)) {
- return getMemberReference(auxiliary);
- }
- return null;
- }
-
- ast.Member resolveInterfaceIndexGet(Element element, Element auxiliary) {
- if (!strongMode) return null;
- if (supportsInterfaceIndexGet(element)) {
- return getMemberReference(element);
- }
- if (supportsInterfaceIndexGet(auxiliary)) {
- return getMemberReference(auxiliary);
- }
- return null;
- }
-
- ast.Member resolveConcreteIndexSet(Element element, Element auxiliary) {
- if (supportsConcreteIndexSet(element)) {
- return getMemberReference(element);
- }
- if (supportsConcreteIndexSet(auxiliary)) {
- return getMemberReference(auxiliary);
- }
- return null;
- }
-
- ast.Member resolveInterfaceIndexSet(Element element, Element auxiliary) {
- if (!strongMode) return null;
- if (supportsInterfaceIndexSet(element)) {
- return getMemberReference(element);
- }
- if (supportsInterfaceIndexSet(auxiliary)) {
- return getMemberReference(auxiliary);
- }
- return null;
- }
-
- ast.Member resolveConcreteMethod(Element element) {
- if (supportsConcreteMethodCall(element)) {
- return getMemberReference(element);
- }
- return null;
- }
-
- ast.Member resolveInterfaceMethod(Element element) {
- if (!strongMode) return null;
- if (supportsInterfaceMethodCall(element)) {
- return getMemberReference(element);
- }
- return null;
- }
-
- ast.Constructor resolveConstructor(Element element) {
- if (supportsConstructorCall(element)) {
- return getMemberReference(element);
- }
- return null;
- }
-
- ast.Field resolveField(Element element) {
- if (element is FieldElement && !element.isSynthetic) {
- return getMemberReference(element);
- }
- return null;
- }
-
- /// A static accessor that generates a 'throw NoSuchMethodError' when a
- /// read or write access could not be resolved.
- Accessor staticAccess(String name, Element element, [Element auxiliary]) {
- return new _StaticAccessor(
- this,
- name,
- resolveConcreteGet(element, auxiliary),
- resolveConcreteSet(element, auxiliary));
- }
-
- /// An accessor that generates a 'throw NoSuchMethodError' on both read
- /// and write access.
- Accessor unresolvedAccess(String name) {
- return new _StaticAccessor(this, name, null, null);
- }
-}
-
-class TypeScope extends ReferenceScope {
- final Map<TypeParameterElement, ast.TypeParameter> localTypeParameters =
- <TypeParameterElement, ast.TypeParameter>{};
- TypeAnnotationBuilder _typeBuilder;
-
- TypeScope(ReferenceLevelLoader loader) : super(loader) {
- _typeBuilder = new TypeAnnotationBuilder(this);
- }
-
- String get location => '?';
-
- bool get allowClassTypeParameters => false;
-
- ast.DartType get defaultTypeParameterBound => getRootClassReference().rawType;
-
- ast.TypeParameter tryGetTypeParameterReference(TypeParameterElement element) {
- return localTypeParameters[element] ??
- loader.tryGetClassTypeParameter(element);
- }
-
- ast.TypeParameter getTypeParameterReference(TypeParameterElement element) {
- return localTypeParameters[element] ??
- loader.tryGetClassTypeParameter(element) ??
- (localTypeParameters[element] = new ast.TypeParameter(element.name));
- }
-
- ast.TypeParameter makeTypeParameter(TypeParameterElement element,
- {ast.DartType bound}) {
- var typeParameter = getTypeParameterReference(element);
- assert(bound != null);
- typeParameter.bound = bound;
- return typeParameter;
- }
-
- ast.DartType buildType(DartType type) {
- return _typeBuilder.buildFromDartType(type);
- }
-
- ast.Supertype buildSupertype(DartType type) {
- if (type is InterfaceType) {
- var classElement = type.element;
- if (classElement == null) return getRootClassReference().asRawSupertype;
- var classNode = getClassReference(classElement);
- if (classNode.typeParameters.isEmpty ||
- classNode.typeParameters.length != type.typeArguments.length) {
- return classNode.asRawSupertype;
- } else {
- return new ast.Supertype(classNode,
- type.typeArguments.map(buildType).toList(growable: false));
- }
- }
- return getRootClassReference().asRawSupertype;
- }
-
- ast.DartType buildTypeAnnotation(AstNode node) {
- return _typeBuilder.build(node);
- }
-
- ast.DartType buildOptionalTypeAnnotation(AstNode node) {
- return node == null ? null : _typeBuilder.build(node);
- }
-
- ast.DartType getInferredType(Expression node) {
- if (!strongMode) return const ast.DynamicType();
- // TODO: Is this official way to get the strong-mode inferred type?
- return buildType(node.staticType);
- }
-
- ast.DartType getInferredTypeArgument(Expression node, int index) {
- var type = getInferredType(node);
- return type is ast.InterfaceType && index < type.typeArguments.length
- ? type.typeArguments[index]
- : const ast.DynamicType();
- }
-
- ast.DartType getInferredReturnType(Expression node) {
- var type = getInferredType(node);
- return type is ast.FunctionType ? type.returnType : const ast.DynamicType();
- }
-
- List<ast.DartType> getInferredInvocationTypeArguments(
- InvocationExpression node) {
- if (!strongMode) return <ast.DartType>[];
- ast.DartType inferredFunctionType = buildType(node.staticInvokeType);
- ast.DartType genericFunctionType = buildType(node.function.staticType);
- if (genericFunctionType is ast.FunctionType) {
- if (genericFunctionType.typeParameters.isEmpty) return <ast.DartType>[];
- // Attempt to unify the two types to obtain a substitution of the type
- // variables. If successful, use the substituted types in the order
- // they occur in the type parameter list.
- var substitution = unifyTypes(genericFunctionType.withoutTypeParameters,
- inferredFunctionType, genericFunctionType.typeParameters.toSet());
- if (substitution != null) {
- return genericFunctionType.typeParameters
- .map((p) => substitution[p] ?? const ast.DynamicType())
- .toList();
- }
- return new List<ast.DartType>.filled(
- genericFunctionType.typeParameters.length, const ast.DynamicType(),
- growable: true);
- } else {
- return <ast.DartType>[];
- }
- }
-
- List<ast.DartType> buildOptionalTypeArgumentList(TypeArgumentList node) {
- if (node == null) return null;
- return _typeBuilder.buildList(node.arguments);
- }
-
- List<ast.DartType> buildTypeArgumentList(TypeArgumentList node) {
- return _typeBuilder.buildList(node.arguments);
- }
-
- List<ast.TypeParameter> buildOptionalTypeParameterList(TypeParameterList node,
- {bool strongModeOnly: false}) {
- if (node == null) return <ast.TypeParameter>[];
- if (strongModeOnly && !strongMode) return <ast.TypeParameter>[];
- return node.typeParameters.map(buildTypeParameter).toList();
- }
-
- ast.TypeParameter buildTypeParameter(TypeParameter node) {
- return makeTypeParameter(node.element,
- bound: buildOptionalTypeAnnotation(node.bound) ??
- defaultTypeParameterBound);
- }
-
- ConstructorElement findDefaultConstructor(ClassElement class_) {
- for (var constructor in class_.constructors) {
- // Note: isDefaultConstructor checks if the constructor is suitable for
- // being invoked without arguments. It does not imply that it is
- // synthetic.
- if (constructor.isDefaultConstructor && !constructor.isFactory) {
- return constructor;
- }
- }
- return null;
- }
-
- ast.FunctionNode buildFunctionInterface(FunctionTypedElement element) {
- var positional = <ast.VariableDeclaration>[];
- var named = <ast.VariableDeclaration>[];
- int requiredParameterCount = 0;
- // Initialize type parameters in two passes: put them into scope,
- // and compute the bounds afterwards while they are all in scope.
- var typeParameters = <ast.TypeParameter>[];
- var typeParameterElements =
- element is ConstructorElement && element.isFactory
- ? element.enclosingElement.typeParameters
- : element.typeParameters;
- if (strongMode || element is ConstructorElement) {
- for (var parameter in typeParameterElements) {
- var parameterNode = new ast.TypeParameter(parameter.name);
- typeParameters.add(parameterNode);
- localTypeParameters[parameter] = parameterNode;
- }
- }
- for (int i = 0; i < typeParameters.length; ++i) {
- var parameter = typeParameterElements[i];
- var parameterNode = typeParameters[i];
- parameterNode.bound = parameter.bound == null
- ? defaultTypeParameterBound
- : buildType(parameter.bound);
- }
- for (var parameter in element.parameters) {
- var parameterNode = new ast.VariableDeclaration(parameter.name,
- type: buildType(parameter.type));
- switch (parameter.parameterKind) {
- case ParameterKind.REQUIRED:
- positional.add(parameterNode);
- ++requiredParameterCount;
- break;
-
- case ParameterKind.POSITIONAL:
- positional.add(parameterNode);
- break;
-
- case ParameterKind.NAMED:
- named.add(parameterNode);
- break;
- }
- }
- var returnType = element is ConstructorElement
- ? const ast.VoidType()
- : buildType(element.returnType);
- return new ast.FunctionNode(null,
- typeParameters: typeParameters,
- positionalParameters: positional,
- namedParameters: named,
- requiredParameterCount: requiredParameterCount,
- returnType: returnType)..fileOffset = element.nameOffset;
- }
-}
-
-class ExpressionScope extends TypeScope {
- ast.Library currentLibrary;
- final Map<LocalElement, ast.VariableDeclaration> localVariables =
- <LocalElement, ast.VariableDeclaration>{};
-
- ExpressionBuilder _expressionBuilder;
- StatementBuilder _statementBuilder;
-
- ExpressionScope(ReferenceLevelLoader loader, this.currentLibrary)
- : super(loader) {
- assert(currentLibrary != null);
- _expressionBuilder = new ExpressionBuilder(this);
- _statementBuilder = new StatementBuilder(this);
- }
-
- bool get allowThis => false; // Overridden by MemberScope.
-
- ast.Name buildName(SimpleIdentifier node) {
- return new ast.Name(node.name, currentLibrary);
- }
-
- ast.Statement buildStatement(Statement node) {
- return _statementBuilder.build(node);
- }
-
- ast.Statement buildOptionalFunctionBody(FunctionBody body) {
- if (body == null ||
- body is EmptyFunctionBody ||
- body is NativeFunctionBody) {
- return null;
- }
- return buildMandatoryFunctionBody(body);
- }
-
- ast.Statement buildMandatoryFunctionBody(FunctionBody body) {
- try {
- if (body is BlockFunctionBody) {
- return buildStatement(body.block);
- } else if (body is ExpressionFunctionBody) {
- if (bodyHasVoidReturn(body)) {
- return new ast.ExpressionStatement(buildExpression(body.expression));
- } else {
- return new ast.ReturnStatement(buildExpression(body.expression))
- ..fileOffset = body.expression.offset;
- }
- } else {
- return internalError('Missing function body');
- }
- } on _CompilationError catch (e) {
- return new ast.ExpressionStatement(buildThrowCompileTimeError(e.message));
- }
- }
-
- ast.AsyncMarker getAsyncMarker({bool isAsync: false, bool isStar: false}) {
- return ast.AsyncMarker.values[(isAsync ? 2 : 0) + (isStar ? 1 : 0)];
- }
-
- ast.FunctionNode buildFunctionNode(
- FormalParameterList formalParameters, FunctionBody body,
- {TypeName returnType,
- List<ast.TypeParameter> typeParameters,
- ast.DartType inferredReturnType}) {
- // TODO(asgerf): This will in many cases rebuild the interface built by
- // TypeScope.buildFunctionInterface.
- var positional = <ast.VariableDeclaration>[];
- var named = <ast.VariableDeclaration>[];
- int requiredParameterCount = 0;
- var formals = formalParameters?.parameters ?? const <FormalParameter>[];
- for (var parameter in formals) {
- var declaration = makeVariableDeclaration(parameter.element,
- initializer: parameter is DefaultFormalParameter
- ? buildOptionalTopLevelExpression(parameter.defaultValue)
- : null,
- type: buildType(
- resolutionMap.elementDeclaredByFormalParameter(parameter).type));
- switch (parameter.kind) {
- case ParameterKind.REQUIRED:
- positional.add(declaration);
- ++requiredParameterCount;
- declaration.initializer = null;
- break;
-
- case ParameterKind.POSITIONAL:
- positional.add(declaration);
- break;
-
- case ParameterKind.NAMED:
- named.add(declaration);
- break;
- }
- }
- int offset = formalParameters?.offset ?? body.offset;
- int endOffset = body.endToken.offset;
- ast.AsyncMarker asyncMarker =
- getAsyncMarker(isAsync: body.isAsynchronous, isStar: body.isGenerator);
- return new ast.FunctionNode(buildOptionalFunctionBody(body),
- typeParameters: typeParameters,
- positionalParameters: positional,
- namedParameters: named,
- requiredParameterCount: requiredParameterCount,
- returnType: buildOptionalTypeAnnotation(returnType) ??
- inferredReturnType ??
- const ast.DynamicType(),
- asyncMarker: asyncMarker,
- dartAsyncMarker: asyncMarker)
- ..fileOffset = offset
- ..fileEndOffset = endOffset;
- }
-
- ast.Expression buildOptionalTopLevelExpression(Expression node) {
- return node == null ? null : buildTopLevelExpression(node);
- }
-
- ast.Expression buildTopLevelExpression(Expression node) {
- try {
- return _expressionBuilder.build(node);
- } on _CompilationError catch (e) {
- return buildThrowCompileTimeError(e.message);
- }
- }
-
- ast.Expression buildExpression(Expression node) {
- return _expressionBuilder.build(node);
- }
-
- ast.Expression buildOptionalExpression(Expression node) {
- return node == null ? null : _expressionBuilder.build(node);
- }
-
- Accessor buildLeftHandValue(Expression node) {
- return _expressionBuilder.buildLeftHandValue(node);
- }
-
- ast.Expression buildStringLiteral(Expression node) {
- List<ast.Expression> parts = <ast.Expression>[];
- new StringLiteralPartBuilder(this, parts).build(node);
- return parts.length == 1 && parts[0] is ast.StringLiteral
- ? parts[0]
- : new ast.StringConcatenation(parts);
- }
-
- ast.Expression buildThis() {
- return allowThis
- ? new ast.ThisExpression()
- : emitCompileTimeError(CompileTimeErrorCode.INVALID_REFERENCE_TO_THIS);
- }
-
- ast.Initializer buildInitializer(ConstructorInitializer node) {
- try {
- return new InitializerBuilder(this).build(node);
- } on _CompilationError catch (_) {
- return new ast.InvalidInitializer();
- }
- }
-
- bool isFinal(Element element) {
- return element is VariableElement && element.isFinal ||
- element is FunctionElement;
- }
-
- bool isConst(Element element) {
- return element is VariableElement && element.isConst;
- }
-
- ast.VariableDeclaration getVariableReference(LocalElement element) {
- return localVariables.putIfAbsent(element, () {
- return new ast.VariableDeclaration(element.name,
- isFinal: isFinal(element),
- isConst: isConst(element))..fileOffset = element.nameOffset;
- });
- }
-
- ast.DartType getInferredVariableType(Element element) {
- if (!strongMode) return const ast.DynamicType();
- if (element is FunctionTypedElement) {
- return buildType(element.type);
- } else if (element is VariableElement) {
- return buildType(element.type);
- } else {
- log.severe('Unexpected variable element: $element');
- return const ast.DynamicType();
- }
- }
-
- ast.VariableDeclaration makeVariableDeclaration(LocalElement element,
- {ast.DartType type, ast.Expression initializer, int equalsOffset}) {
- var declaration = getVariableReference(element);
- if (equalsOffset != null) declaration.fileEqualsOffset = equalsOffset;
- declaration.type = type ?? getInferredVariableType(element);
- if (initializer != null) {
- declaration.initializer = initializer..parent = declaration;
- }
- return declaration;
- }
-
- /// Returns true if [arguments] can be accepted by [target]
- /// (not taking type checks into account).
- bool areArgumentsCompatible(
- FunctionTypedElement target, ast.Arguments arguments) {
- var positionals = arguments.positional;
- var parameters = target.parameters;
- const required = ParameterKind.REQUIRED; // For avoiding long lines.
- const named = ParameterKind.NAMED;
- // If the first unprovided parameter is required, there are too few
- // positional arguments.
- if (positionals.length < parameters.length &&
- parameters[positionals.length].parameterKind == required) {
- return false;
- }
- // If there are more positional arguments than parameters, or if the last
- // positional argument corresponds to a named parameter, there are too many
- // positional arguments.
- if (positionals.length > parameters.length) return false;
- if (positionals.isNotEmpty &&
- parameters[positionals.length - 1].parameterKind == named) {
- return false; // Too many positional arguments.
- }
- if (arguments.named.isEmpty) return true;
- int firstNamedParameter = positionals.length;
- while (firstNamedParameter < parameters.length &&
- parameters[firstNamedParameter].parameterKind != ParameterKind.NAMED) {
- ++firstNamedParameter;
- }
- namedLoop:
- for (int i = 0; i < arguments.named.length; ++i) {
- String name = arguments.named[i].name;
- for (int j = firstNamedParameter; j < parameters.length; ++j) {
- if (parameters[j].parameterKind == ParameterKind.NAMED &&
- parameters[j].name == name) {
- continue namedLoop;
- }
- }
- return false;
- }
- return true;
- }
-
- /// Throws a NoSuchMethodError corresponding to a call to [memberName] on
- /// [receiver] with the given [arguments].
- ///
- /// If provided, [candiateTarget] provides the expected arity and argument
- /// names for the best candidate target.
- ast.Expression buildThrowNoSuchMethodError(
- ast.Expression receiver, String memberName, ast.Arguments arguments,
- {Element candidateTarget}) {
- // TODO(asgerf): When we have better integration with patch files, use
- // the internal constructor that provides a more detailed error message.
- ast.Expression candidateArgumentNames;
- if (candidateTarget is FunctionTypedElement) {
- candidateArgumentNames = new ast.ListLiteral(candidateTarget.parameters
- .map((p) => new ast.StringLiteral(p.name))
- .toList());
- } else {
- candidateArgumentNames = new ast.NullLiteral();
- }
- return new ast.Throw(new ast.ConstructorInvocation(
- loader.getCoreClassConstructorReference('NoSuchMethodError'),
- new ast.Arguments(<ast.Expression>[
- receiver,
- new ast.SymbolLiteral(memberName),
- new ast.ListLiteral(arguments.positional),
- new ast.MapLiteral(arguments.named.map((arg) {
- return new ast.MapEntry(new ast.SymbolLiteral(arg.name), arg.value);
- }).toList()),
- candidateArgumentNames
- ])));
- }
-
- ast.Expression buildThrowCompileTimeError(String message) {
- // The spec does not mandate a specific behavior in face of a compile-time
- // error. We just throw a string. The VM throws an uncatchable exception
- // for this case.
- // TOOD(asgerf): Should we add uncatchable exceptions to kernel?
- return new ast.Throw(new ast.StringLiteral(message));
- }
-
- ast.Expression buildThrowCompileTimeErrorFromCode(ErrorCode code,
- [List arguments]) {
- return buildThrowCompileTimeError(makeErrorMessage(code, arguments));
- }
-
- static final RegExp _errorMessagePattern = new RegExp(r'\{(\d+)\}');
-
- String makeErrorMessage(ErrorCode error, [List arguments]) {
- String message = error.message;
- if (arguments != null) {
- message = message.replaceAllMapped(_errorMessagePattern, (m) {
- String numberString = m.group(1);
- int index = int.parse(numberString);
- return arguments[index];
- });
- }
- return message;
- }
-
- /// Throws an exception that will be caught at the function level, to replace
- /// the entire function with a throw.
- emitCompileTimeError(ErrorCode error, [List arguments]) {
- throw new _CompilationError(makeErrorMessage(error, arguments));
- }
-
- ast.Expression buildThrowAbstractClassInstantiationError(String name) {
- return new ast.Throw(new ast.ConstructorInvocation(
- loader.getCoreClassConstructorReference(
- 'AbstractClassInstantiationError'),
- new ast.Arguments(<ast.Expression>[new ast.StringLiteral(name)])));
- }
-
- ast.Expression buildThrowFallThroughError() {
- return new ast.Throw(new ast.ConstructorInvocation(
- loader.getCoreClassConstructorReference('FallThroughError'),
- new ast.Arguments.empty()));
- }
-
- emitInvalidConstant([ErrorCode error]) {
- error ??= CompileTimeErrorCode.INVALID_CONSTANT;
- return emitCompileTimeError(error);
- }
-
- internalError(String message) {
- throw 'Internal error when compiling $location: $message';
- }
-
- unsupportedFeature(String feature) {
- throw new _CompilationError('$feature is not supported');
- }
-
- ast.Expression buildAnnotation(Annotation annotation) {
- Element element = annotation.element;
- if (annotation.arguments == null) {
- var target = resolveConcreteGet(element, null);
- return target == null
- ? new ast.InvalidExpression()
- : new ast.StaticGet(target);
- } else if (element is ConstructorElement && element.isConst) {
- var target = resolveConstructor(element);
- return target == null
- ? new ast.InvalidExpression()
- : new ast.ConstructorInvocation(
- target, _expressionBuilder.buildArguments(annotation.arguments),
- isConst: true);
- } else {
- return new ast.InvalidExpression();
- }
- }
-
- void addTransformerFlag(int flags) {
- // Overridden by MemberScope.
- }
-
- /// True if the body of the given method must return nothing.
- bool hasVoidReturn(ExecutableElement element) {
- return (strongMode && element.returnType.isVoid) ||
- (element is PropertyAccessorElement && element.isSetter) ||
- element.name == '[]=';
- }
-
- bool bodyHasVoidReturn(FunctionBody body) {
- AstNode parent = body.parent;
- return parent is MethodDeclaration && hasVoidReturn(parent.element) ||
- parent is FunctionDeclaration && hasVoidReturn(parent.element);
- }
-}
-
-/// A scope in which class type parameters are in scope, while not in scope
-/// of a specific member.
-class ClassScope extends ExpressionScope {
- @override
- bool get allowClassTypeParameters => true;
-
- ClassScope(ReferenceLevelLoader loader, ast.Library library)
- : super(loader, library);
-}
-
-/// Translates expressions, statements, and other constructs into [ast] nodes.
-///
-/// Naming convention:
-/// - `buildX` may not be given null as argument (it may crash the compiler).
-/// - `buildOptionalX` returns null or an empty list if given null
-/// - `buildMandatoryX` returns an invalid node if given null.
-class MemberScope extends ExpressionScope {
- /// A reference to the member currently being upgraded to body level.
- final ast.Member currentMember;
-
- MemberScope(ReferenceLevelLoader loader, ast.Member currentMember)
- : currentMember = currentMember,
- super(loader, currentMember.enclosingLibrary) {
- assert(currentMember != null);
- }
-
- ast.Class get currentClass => currentMember.enclosingClass;
-
- bool get allowThis => _memberHasThis(currentMember);
-
- @override
- bool get allowClassTypeParameters {
- return currentMember.isInstanceMember || currentMember is ast.Constructor;
- }
-
- /// Returns a string for debugging use, indicating the location of the member
- /// being built.
- String get location {
- var library = currentMember.enclosingLibrary?.importUri ?? '<No Library>';
- var className = currentMember.enclosingClass == null
- ? null
- : (currentMember.enclosingClass?.name ?? '<Anonymous Class>');
- var member =
- currentMember.name?.name ?? '<Anonymous ${currentMember.runtimeType}>';
- return [library, className, member].join('::');
- }
-
- bool _memberHasThis(ast.Member member) {
- return member is ast.Procedure && !member.isStatic ||
- member is ast.Constructor;
- }
-
- void addTransformerFlag(int flags) {
- currentMember.transformerFlags |= flags;
- }
-}
-
-class LabelStack {
- final List<String> labels; // Contains null for unlabeled targets.
- final LabelStack next;
- final List<ast.Statement> jumps = <ast.Statement>[];
- bool isSwitchTarget = false;
-
- LabelStack(String label, this.next) : labels = <String>[label];
- LabelStack.unlabeled(this.next) : labels = <String>[null];
- LabelStack.switchCase(String label, this.next)
- : isSwitchTarget = true,
- labels = <String>[label];
- LabelStack.many(this.labels, this.next);
-}
-
-class StatementBuilder extends GeneralizingAstVisitor<ast.Statement> {
- final ExpressionScope scope;
- LabelStack breakStack, continueStack;
-
- StatementBuilder(this.scope, [this.breakStack, this.continueStack]);
-
- ast.Statement build(Statement node) {
- ast.Statement result = node.accept(this);
- result.fileOffset = _getOffset(node);
- return result;
- }
-
- ast.Statement buildOptional(Statement node) {
- ast.Statement result = node?.accept(this);
- result?.fileOffset = _getOffset(node);
- return result;
- }
-
- int _getOffset(AstNode node) {
- return node.offset;
- }
-
- ast.Statement buildInScope(
- Statement node, LabelStack breakNode, LabelStack continueNode) {
- var oldBreak = this.breakStack;
- var oldContinue = this.continueStack;
- breakStack = breakNode;
- continueStack = continueNode;
- var result = build(node);
- this.breakStack = oldBreak;
- this.continueStack = oldContinue;
- return result;
- }
-
- void buildBlockMember(Statement node, List<ast.Statement> output) {
- if (node is LabeledStatement &&
- node.statement is VariableDeclarationStatement) {
- // If a variable is labeled, its scope is part of the enclosing block.
- LabeledStatement labeled = node;
- node = labeled.statement;
- }
- if (node is VariableDeclarationStatement) {
- VariableDeclarationList list = node.variables;
- ast.DartType type = scope.buildOptionalTypeAnnotation(list.type);
- for (VariableDeclaration decl in list.variables) {
- LocalElement local = decl.element as dynamic; // Cross cast.
- output.add(scope.makeVariableDeclaration(local,
- type: type,
- initializer: scope.buildOptionalExpression(decl.initializer),
- equalsOffset: decl.equals?.offset));
- }
- } else {
- output.add(build(node));
- }
- }
-
- ast.Statement makeBreakTarget(ast.Statement node, LabelStack stackNode) {
- if (stackNode.jumps.isEmpty) return node;
- var labeled = new ast.LabeledStatement(node);
- for (var jump in stackNode.jumps) {
- (jump as ast.BreakStatement).target = labeled;
- }
- return labeled;
- }
-
- LabelStack findLabelTarget(String label, LabelStack stack) {
- while (stack != null) {
- if (stack.labels.contains(label)) return stack;
- stack = stack.next;
- }
- return null;
- }
-
- ast.Statement visitAssertStatement(AssertStatement node) {
- return new ast.AssertStatement(scope.buildExpression(node.condition),
- scope.buildOptionalExpression(node.message));
- }
-
- ast.Statement visitBlock(Block node) {
- List<ast.Statement> statements = <ast.Statement>[];
- for (Statement statement in node.statements) {
- buildBlockMember(statement, statements);
- }
- return new ast.Block(statements);
- }
-
- ast.Statement visitBreakStatement(BreakStatement node) {
- var stackNode = findLabelTarget(node.label?.name, breakStack);
- if (stackNode == null) {
- return node.label == null
- ? scope.emitCompileTimeError(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP)
- : scope.emitCompileTimeError(
- CompileTimeErrorCode.LABEL_UNDEFINED, [node.label.name]);
- }
- var result = new ast.BreakStatement(null);
- stackNode.jumps.add(result);
- return result;
- }
-
- ast.Statement visitContinueStatement(ContinueStatement node) {
- var stackNode = findLabelTarget(node.label?.name, continueStack);
- if (stackNode == null) {
- return node.label == null
- ? scope.emitCompileTimeError(ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP)
- : scope.emitCompileTimeError(
- CompileTimeErrorCode.LABEL_UNDEFINED, [node.label.name]);
- }
- var result = stackNode.isSwitchTarget
- ? new ast.ContinueSwitchStatement(null)
- : new ast.BreakStatement(null);
- stackNode.jumps.add(result);
- return result;
- }
-
- void addLoopLabels(Statement loop, LabelStack continueNode) {
- AstNode parent = loop.parent;
- if (parent is LabeledStatement) {
- for (var label in parent.labels) {
- continueNode.labels.add(label.label.name);
- }
- }
- }
-
- ast.Statement visitDoStatement(DoStatement node) {
- LabelStack breakNode = new LabelStack.unlabeled(breakStack);
- LabelStack continueNode = new LabelStack.unlabeled(continueStack);
- addLoopLabels(node, continueNode);
- var body = buildInScope(node.body, breakNode, continueNode);
- var loop = new ast.DoStatement(makeBreakTarget(body, continueNode),
- scope.buildExpression(node.condition));
- return makeBreakTarget(loop, breakNode);
- }
-
- ast.Statement visitWhileStatement(WhileStatement node) {
- LabelStack breakNode = new LabelStack.unlabeled(breakStack);
- LabelStack continueNode = new LabelStack.unlabeled(continueStack);
- addLoopLabels(node, continueNode);
- var body = buildInScope(node.body, breakNode, continueNode);
- var loop = new ast.WhileStatement(scope.buildExpression(node.condition),
- makeBreakTarget(body, continueNode));
- return makeBreakTarget(loop, breakNode);
- }
-
- ast.Statement visitEmptyStatement(EmptyStatement node) {
- return new ast.EmptyStatement();
- }
-
- ast.Statement visitExpressionStatement(ExpressionStatement node) {
- return new ast.ExpressionStatement(scope.buildExpression(node.expression));
- }
-
- static String _getLabelName(Label label) {
- return label.label.name;
- }
-
- ast.Statement visitLabeledStatement(LabeledStatement node) {
- // Only set up breaks here. Loops handle labeling on their own.
- var breakNode = new LabelStack.many(
- node.labels.map(_getLabelName).toList(), breakStack);
- var body = buildInScope(node.statement, breakNode, continueStack);
- return makeBreakTarget(body, breakNode);
- }
-
- static bool isBreakingExpression(ast.Expression node) {
- return node is ast.Throw || node is ast.Rethrow;
- }
-
- static bool isBreakingStatement(ast.Statement node) {
- return node is ast.BreakStatement ||
- node is ast.ContinueSwitchStatement ||
- node is ast.ReturnStatement ||
- node is ast.ExpressionStatement &&
- isBreakingExpression(node.expression);
- }
-
- ast.Statement visitSwitchStatement(SwitchStatement node) {
- // Group all cases into case blocks. Use parallel lists to collect the
- // intermediate terms until we are ready to create the AST nodes.
- LabelStack breakNode = new LabelStack.unlabeled(breakStack);
- LabelStack continueNode = continueStack;
- var cases = <ast.SwitchCase>[];
- var bodies = <List<Statement>>[];
- var labelToNode = <String, ast.SwitchCase>{};
- ast.SwitchCase currentCase = null;
- for (var member in node.members) {
- if (currentCase != null && currentCase.isDefault) {
- var error = member is SwitchCase
- ? ParserErrorCode.SWITCH_HAS_CASE_AFTER_DEFAULT_CASE
- : ParserErrorCode.SWITCH_HAS_MULTIPLE_DEFAULT_CASES;
- return scope.emitCompileTimeError(error);
- }
- if (currentCase == null) {
- currentCase = new ast.SwitchCase(<ast.Expression>[], null);
- cases.add(currentCase);
- }
- if (member is SwitchCase) {
- var expression = scope.buildExpression(member.expression);
- currentCase.expressions.add(expression..parent = currentCase);
- } else {
- currentCase.isDefault = true;
- }
- for (Label label in member.labels) {
- continueNode =
- new LabelStack.switchCase(label.label.name, continueNode);
- labelToNode[label.label.name] = currentCase;
- }
- if (member.statements?.isNotEmpty ?? false) {
- bodies.add(member.statements);
- currentCase = null;
- }
- }
- if (currentCase != null) {
- // Close off a trailing block.
- bodies.add(const <Statement>[]);
- currentCase = null;
- }
- // Now that the label environment is set up, build the bodies.
- var oldBreak = this.breakStack;
- var oldContinue = this.continueStack;
- this.breakStack = breakNode;
- this.continueStack = continueNode;
- for (int i = 0; i < cases.length; ++i) {
- var blockNodes = <ast.Statement>[];
- for (var statement in bodies[i]) {
- buildBlockMember(statement, blockNodes);
- }
- if (blockNodes.isEmpty || !isBreakingStatement(blockNodes.last)) {
- if (i < cases.length - 1) {
- blockNodes.add(
- new ast.ExpressionStatement(scope.buildThrowFallThroughError()));
- } else {
- var jump = new ast.BreakStatement(null);
- blockNodes.add(jump);
- breakNode.jumps.add(jump);
- }
- }
- cases[i].body = new ast.Block(blockNodes)..parent = cases[i];
- }
- // Unwind the stack of case labels and bind their jumps to the case target.
- while (continueNode != oldContinue) {
- for (var jump in continueNode.jumps) {
- (jump as ast.ContinueSwitchStatement).target =
- labelToNode[continueNode.labels.first];
- }
- continueNode = continueNode.next;
- }
- var expression = scope.buildExpression(node.expression);
- var result = new ast.SwitchStatement(expression, cases);
- this.breakStack = oldBreak;
- this.continueStack = oldContinue;
- return makeBreakTarget(result, breakNode);
- }
-
- ast.Statement visitForStatement(ForStatement node) {
- List<ast.VariableDeclaration> variables = <ast.VariableDeclaration>[];
- ast.Expression initialExpression;
- if (node.variables != null) {
- VariableDeclarationList list = node.variables;
- var type = scope.buildOptionalTypeAnnotation(list.type);
- for (var variable in list.variables) {
- LocalElement local = variable.element as dynamic; // Cross cast.
- variables.add(scope.makeVariableDeclaration(local,
- initializer: scope.buildOptionalExpression(variable.initializer),
- type: type,
- equalsOffset: variable.equals?.offset));
- }
- } else if (node.initialization != null) {
- initialExpression = scope.buildExpression(node.initialization);
- }
- var breakNode = new LabelStack.unlabeled(breakStack);
- var continueNode = new LabelStack.unlabeled(continueStack);
- addLoopLabels(node, continueNode);
- var body = buildInScope(node.body, breakNode, continueNode);
- var loop = new ast.ForStatement(
- variables,
- scope.buildOptionalExpression(node.condition),
- node.updaters.map(scope.buildExpression).toList(),
- makeBreakTarget(body, continueNode));
- loop = makeBreakTarget(loop, breakNode);
- if (initialExpression != null) {
- return new ast.Block(<ast.Statement>[
- new ast.ExpressionStatement(initialExpression),
- loop
- ]);
- }
- return loop;
- }
-
- DartType iterableElementType(DartType iterable) {
- if (iterable is InterfaceType) {
- var iterator = iterable.lookUpInheritedGetter('iterator')?.returnType;
- if (iterator is InterfaceType) {
- return iterator.lookUpInheritedGetter('current')?.returnType;
- }
- }
- return null;
- }
-
- DartType streamElementType(DartType stream) {
- if (stream is InterfaceType) {
- var class_ = stream.element;
- if (class_.library.isDartAsync &&
- class_.name == 'Stream' &&
- stream.typeArguments.length == 1) {
- return stream.typeArguments[0];
- }
- }
- return null;
- }
-
- ast.Statement visitForEachStatement(ForEachStatement node) {
- ast.VariableDeclaration variable;
- Accessor leftHand;
- if (node.loopVariable != null) {
- DeclaredIdentifier loopVariable = node.loopVariable;
- variable = scope.makeVariableDeclaration(loopVariable.element,
- type: scope.buildOptionalTypeAnnotation(loopVariable.type));
- } else if (node.identifier != null) {
- leftHand = scope.buildLeftHandValue(node.identifier);
- variable = new ast.VariableDeclaration(null, isFinal: true);
- if (scope.strongMode) {
- var containerType = node.iterable.staticType;
- DartType elementType = node.awaitKeyword != null
- ? streamElementType(containerType)
- : iterableElementType(containerType);
- if (elementType != null) {
- variable.type = scope.buildType(elementType);
- }
- }
- }
- var breakNode = new LabelStack.unlabeled(breakStack);
- var continueNode = new LabelStack.unlabeled(continueStack);
- addLoopLabels(node, continueNode);
- var body = buildInScope(node.body, breakNode, continueNode);
- if (leftHand != null) {
- // Desugar
- //
- // for (x in e) BODY
- //
- // to
- //
- // for (var tmp in e) {
- // x = tmp;
- // BODY
- // }
- body = new ast.Block(<ast.Statement>[
- new ast.ExpressionStatement(leftHand
- .buildAssignment(new ast.VariableGet(variable), voidContext: true)),
- body
- ]);
- }
- var loop = new ast.ForInStatement(
- variable,
- scope.buildExpression(node.iterable),
- makeBreakTarget(body, continueNode),
- isAsync: node.awaitKeyword != null)..fileOffset = node.offset;
- return makeBreakTarget(loop, breakNode);
- }
-
- ast.Statement visitIfStatement(IfStatement node) {
- return new ast.IfStatement(scope.buildExpression(node.condition),
- build(node.thenStatement), buildOptional(node.elseStatement));
- }
-
- ast.Statement visitReturnStatement(ReturnStatement node) {
- return new ast.ReturnStatement(
- scope.buildOptionalExpression(node.expression));
- }
-
- ast.Catch buildCatchClause(CatchClause node) {
- var exceptionVariable = node.exceptionParameter == null
- ? null
- : scope.makeVariableDeclaration(node.exceptionParameter.staticElement);
- var stackTraceVariable = node.stackTraceParameter == null
- ? null
- : scope.makeVariableDeclaration(node.stackTraceParameter.staticElement);
- return new ast.Catch(exceptionVariable, build(node.body),
- stackTrace: stackTraceVariable,
- guard: scope.buildOptionalTypeAnnotation(node.exceptionType) ??
- const ast.DynamicType());
- }
-
- ast.Statement visitTryStatement(TryStatement node) {
- ast.Statement statement = build(node.body);
- if (node.catchClauses.isNotEmpty) {
- statement = new ast.TryCatch(
- statement, node.catchClauses.map(buildCatchClause).toList());
- }
- if (node.finallyBlock != null) {
- statement = new ast.TryFinally(statement, build(node.finallyBlock));
- }
- return statement;
- }
-
- ast.Statement visitVariableDeclarationStatement(
- VariableDeclarationStatement node) {
- // This is only reached when a variable is declared in non-block level,
- // because visitBlock intercepts visits to its children.
- // An example where we hit this case is:
- //
- // if (foo) var x = 5, y = x + 1;
- //
- // We wrap these in a block:
- //
- // if (foo) {
- // var x = 5;
- // var y = x + 1;
- // }
- //
- // Note that the use of a block here is required by the kernel language,
- // even if there is only one variable declaration.
- List<ast.Statement> statements = <ast.Statement>[];
- buildBlockMember(node, statements);
- return new ast.Block(statements);
- }
-
- ast.Statement visitYieldStatement(YieldStatement node) {
- return new ast.YieldStatement(scope.buildExpression(node.expression),
- isYieldStar: node.star != null);
- }
-
- ast.Statement visitFunctionDeclarationStatement(
- FunctionDeclarationStatement node) {
- var declaration = node.functionDeclaration;
- var expression = declaration.functionExpression;
- LocalElement element = declaration.element as dynamic; // Cross cast.
- return new ast.FunctionDeclaration(
- scope.makeVariableDeclaration(element,
- type: scope.buildType(declaration.element.type)),
- scope.buildFunctionNode(expression.parameters, expression.body,
- typeParameters: scope.buildOptionalTypeParameterList(
- expression.typeParameters,
- strongModeOnly: true),
- returnType: declaration.returnType))..fileOffset = node.offset;
- }
-
- @override
- visitStatement(Statement node) {
- return scope.internalError('Unhandled statement ${node.runtimeType}');
- }
-}
-
-class ExpressionBuilder
- extends GeneralizingAstVisitor /* <ast.Expression | Accessor> */ {
- final ExpressionScope scope;
- ast.VariableDeclaration cascadeReceiver;
- ExpressionBuilder(this.scope);
-
- ast.Expression build(Expression node) {
- var result = node.accept(this);
- if (result is Accessor) {
- result = result.buildSimpleRead();
- }
- // For some method invocations we have already set a file offset to
- // override the default behavior of _getOffset.
- if (node is! MethodInvocation || result.fileOffset < 0) {
- result.fileOffset = _getOffset(node);
- }
- return result;
- }
-
- int _getOffset(AstNode node) {
- if (node is MethodInvocation) {
- return node.methodName.offset;
- } else if (node is InstanceCreationExpression) {
- return node.constructorName.offset;
- } else if (node is BinaryExpression) {
- return node.operator.offset;
- } else if (node is PrefixedIdentifier) {
- return node.identifier.offset;
- } else if (node is AssignmentExpression) {
- return _getOffset(node.leftHandSide);
- } else if (node is PropertyAccess) {
- return node.propertyName.offset;
- } else if (node is IsExpression) {
- return node.isOperator.offset;
- } else if (node is AsExpression) {
- return node.asOperator.offset;
- } else if (node is StringLiteral) {
- // Use a catch-all for StringInterpolation and AdjacentStrings:
- // the debugger stops at the end.
- return node.end;
- } else if (node is IndexExpression) {
- return node.leftBracket.offset;
- }
- return node.offset;
- }
-
- Accessor buildLeftHandValue(Expression node) {
- var result = node.accept(this);
- if (result is Accessor) {
- return result;
- } else {
- return new ReadOnlyAccessor(result);
- }
- }
-
- ast.Expression visitAsExpression(AsExpression node) {
- return new ast.AsExpression(
- build(node.expression), scope.buildTypeAnnotation(node.type));
- }
-
- ast.Expression visitAssignmentExpression(AssignmentExpression node) {
- bool voidContext = isInVoidContext(node);
- String operator = node.operator.value();
- var leftHand = buildLeftHandValue(node.leftHandSide);
- var rightHand = build(node.rightHandSide);
- if (operator == '=') {
- return leftHand.buildAssignment(rightHand, voidContext: voidContext);
- } else if (operator == '??=') {
- return leftHand.buildNullAwareAssignment(
- rightHand, scope.buildType(node.staticType),
- voidContext: voidContext);
- } else {
- // Cut off the trailing '='.
- var name = new ast.Name(operator.substring(0, operator.length - 1));
- return leftHand.buildCompoundAssignment(name, rightHand,
- offset: node.offset,
- voidContext: voidContext,
- interfaceTarget: scope.resolveInterfaceMethod(node.staticElement));
- }
- }
-
- ast.Expression visitAwaitExpression(AwaitExpression node) {
- return new ast.AwaitExpression(build(node.expression));
- }
-
- ast.Arguments buildSingleArgument(Expression node) {
- return new ast.Arguments(<ast.Expression>[build(node)]);
- }
-
- ast.Expression visitBinaryExpression(BinaryExpression node) {
- String operator = node.operator.value();
- if (operator == '&&' || operator == '||') {
- return new ast.LogicalExpression(
- build(node.leftOperand), operator, build(node.rightOperand));
- }
- if (operator == '??') {
- ast.Expression leftOperand = build(node.leftOperand);
- if (leftOperand is ast.VariableGet) {
- return new ast.ConditionalExpression(
- buildIsNull(leftOperand, offset: node.leftOperand.offset),
- build(node.rightOperand),
- new ast.VariableGet(leftOperand.variable),
- scope.getInferredType(node));
- } else {
- var variable = new ast.VariableDeclaration.forValue(leftOperand);
- return new ast.Let(
- variable,
- new ast.ConditionalExpression(
- buildIsNull(new ast.VariableGet(variable),
- offset: leftOperand.fileOffset),
- build(node.rightOperand),
- new ast.VariableGet(variable),
- scope.getInferredType(node)));
- }
- }
- bool isNegated = false;
- if (operator == '!=') {
- isNegated = true;
- operator = '==';
- }
- ast.Expression expression;
- if (node.leftOperand is SuperExpression) {
- scope.addTransformerFlag(TransformerFlag.superCalls);
- expression = new ast.SuperMethodInvocation(
- new ast.Name(operator),
- buildSingleArgument(node.rightOperand),
- scope.resolveConcreteMethod(node.staticElement));
- } else {
- expression = new ast.MethodInvocation(
- build(node.leftOperand),
- new ast.Name(operator),
- buildSingleArgument(node.rightOperand),
- scope.resolveInterfaceMethod(node.staticElement));
- }
- return isNegated ? new ast.Not(expression) : expression;
- }
-
- ast.Expression visitBooleanLiteral(BooleanLiteral node) {
- return new ast.BoolLiteral(node.value);
- }
-
- ast.Expression visitDoubleLiteral(DoubleLiteral node) {
- return new ast.DoubleLiteral(node.value);
- }
-
- ast.Expression visitIntegerLiteral(IntegerLiteral node) {
- return new ast.IntLiteral(node.value);
- }
-
- ast.Expression visitNullLiteral(NullLiteral node) {
- return new ast.NullLiteral();
- }
-
- ast.Expression visitSimpleStringLiteral(SimpleStringLiteral node) {
- return new ast.StringLiteral(node.value);
- }
-
- ast.Expression visitStringLiteral(StringLiteral node) {
- return scope.buildStringLiteral(node);
- }
-
- static Object _getTokenValue(Token token) {
- return token.value();
- }
-
- ast.Expression visitSymbolLiteral(SymbolLiteral node) {
- String value = node.components.map(_getTokenValue).join('.');
- return new ast.SymbolLiteral(value);
- }
-
- ast.Expression visitCascadeExpression(CascadeExpression node) {
- var receiver = build(node.target);
- // If receiver is a variable it would be tempting to reuse it, but it
- // might be reassigned in one of the cascade sections.
- var receiverVariable = new ast.VariableDeclaration.forValue(receiver,
- type: scope.getInferredType(node.target));
- var oldReceiver = this.cascadeReceiver;
- cascadeReceiver = receiverVariable;
- ast.Expression result = new ast.VariableGet(receiverVariable);
- for (var section in node.cascadeSections.reversed) {
- var dummy = new ast.VariableDeclaration.forValue(build(section));
- result = new ast.Let(dummy, result);
- }
- cascadeReceiver = oldReceiver;
- return new ast.Let(receiverVariable, result);
- }
-
- ast.Expression makeCascadeReceiver() {
- assert(cascadeReceiver != null);
- return new ast.VariableGet(cascadeReceiver);
- }
-
- ast.Expression visitConditionalExpression(ConditionalExpression node) {
- return new ast.ConditionalExpression(
- build(node.condition),
- build(node.thenExpression),
- build(node.elseExpression),
- scope.getInferredType(node));
- }
-
- ast.Expression visitFunctionExpression(FunctionExpression node) {
- return new ast.FunctionExpression(scope.buildFunctionNode(
- node.parameters, node.body,
- typeParameters: scope.buildOptionalTypeParameterList(
- node.typeParameters,
- strongModeOnly: true),
- inferredReturnType: scope.getInferredReturnType(node)));
- }
-
- ast.Arguments buildArguments(ArgumentList valueArguments,
- {TypeArgumentList explicitTypeArguments,
- List<ast.DartType> inferTypeArguments()}) {
- var positional = <ast.Expression>[];
- var named = <ast.NamedExpression>[];
- for (var argument in valueArguments.arguments) {
- if (argument is NamedExpression) {
- named.add(new ast.NamedExpression(
- argument.name.label.name, build(argument.expression)));
- } else if (named.isNotEmpty) {
- return scope.emitCompileTimeError(
- ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
- } else {
- positional.add(build(argument));
- }
- }
- List<ast.DartType> typeArguments;
- if (explicitTypeArguments != null) {
- typeArguments = scope.buildTypeArgumentList(explicitTypeArguments);
- } else if (inferTypeArguments != null) {
- typeArguments = inferTypeArguments();
- }
- return new ast.Arguments(positional, named: named, types: typeArguments);
- }
-
- ast.Arguments buildArgumentsForInvocation(InvocationExpression node) {
- if (scope.strongMode) {
- return buildArguments(node.argumentList,
- explicitTypeArguments: node.typeArguments,
- inferTypeArguments: () =>
- scope.getInferredInvocationTypeArguments(node));
- } else {
- return buildArguments(node.argumentList);
- }
- }
-
- static final ast.Name callName = new ast.Name('call');
-
- ast.Expression visitFunctionExpressionInvocation(
- FunctionExpressionInvocation node) {
- return new ast.MethodInvocation(
- build(node.function),
- callName,
- buildArgumentsForInvocation(node),
- scope.resolveInterfaceFunctionCallOnType(node.function.staticType));
- }
-
- visitPrefixedIdentifier(PrefixedIdentifier node) {
- switch (ElementKind.of(node.prefix.staticElement)) {
- case ElementKind.CLASS:
- case ElementKind.LIBRARY:
- case ElementKind.PREFIX:
- case ElementKind.IMPORT:
- if (node.identifier.staticElement != null) {
- // Should be resolved to a static access.
- // Do not invoke 'build', because the identifier should be seen as a
- // left-hand value or an expression depending on the context.
- return visitSimpleIdentifier(node.identifier);
- }
- // Unresolved access on a class or library.
- return scope.unresolvedAccess(node.identifier.name);
-
- case ElementKind.DYNAMIC:
- case ElementKind.FUNCTION_TYPE_ALIAS:
- case ElementKind.TYPE_PARAMETER:
- // TODO: Check with the spec to see exactly when a type literal can be
- // used in a property access without surrounding parentheses.
- // For now, just fall through to the property access case.
-
- case ElementKind.FIELD:
- case ElementKind.TOP_LEVEL_VARIABLE:
- case ElementKind.FUNCTION:
- case ElementKind.METHOD:
- case ElementKind.GETTER:
- case ElementKind.SETTER:
- case ElementKind.LOCAL_VARIABLE:
- case ElementKind.PARAMETER:
- case ElementKind.ERROR:
- Element element = node.identifier.staticElement;
- Element auxiliary = node.identifier.auxiliaryElements?.staticElement;
- return PropertyAccessor.make(
- build(node.prefix),
- scope.buildName(node.identifier),
- scope.resolveInterfaceGet(element, auxiliary),
- scope.resolveInterfaceSet(element, auxiliary));
-
- case ElementKind.UNIVERSE:
- case ElementKind.NAME:
- case ElementKind.CONSTRUCTOR:
- case ElementKind.EXPORT:
- case ElementKind.LABEL:
- default:
- return scope.internalError(
- 'Unexpected element kind: ${node.prefix.staticElement}');
- }
- }
-
- bool isStatic(Element element) {
- if (element is ClassMemberElement) {
- return element.isStatic || element.enclosingElement == null;
- }
- if (element is PropertyAccessorElement) {
- return element.isStatic || element.enclosingElement == null;
- }
- if (element is FunctionElement) {
- return element.isStatic;
- }
- return false;
- }
-
- visitSimpleIdentifier(SimpleIdentifier node) {
- Element element = node.staticElement;
- switch (ElementKind.of(element)) {
- case ElementKind.CLASS:
- case ElementKind.DYNAMIC:
- case ElementKind.FUNCTION_TYPE_ALIAS:
- case ElementKind.TYPE_PARAMETER:
- return new ast.TypeLiteral(scope.buildTypeAnnotation(node));
-
- case ElementKind.ERROR: // This covers the case where nothing was found.
- if (!scope.allowThis) {
- return scope.unresolvedAccess(node.name);
- }
- return PropertyAccessor.make(
- scope.buildThis(), scope.buildName(node), null, null);
-
- case ElementKind.FIELD:
- case ElementKind.TOP_LEVEL_VARIABLE:
- case ElementKind.GETTER:
- case ElementKind.SETTER:
- case ElementKind.METHOD:
- Element auxiliary = node.auxiliaryElements?.staticElement;
- if (isStatic(element)) {
- return scope.staticAccess(node.name, element, auxiliary);
- }
- if (!scope.allowThis) {
- return scope.unresolvedAccess(node.name);
- }
- return PropertyAccessor.make(
- scope.buildThis(),
- scope.buildName(node),
- scope.resolveInterfaceGet(element, auxiliary),
- scope.resolveInterfaceSet(element, auxiliary));
-
- case ElementKind.FUNCTION:
- FunctionElement function = element;
- if (isTopLevelFunction(function)) {
- return scope.staticAccess(node.name, function);
- }
- if (function == function.library.loadLibraryFunction) {
- return scope.unsupportedFeature('Deferred loading');
- }
- return new VariableAccessor(scope.getVariableReference(function));
-
- case ElementKind.LOCAL_VARIABLE:
- case ElementKind.PARAMETER:
- VariableElement variable = element;
- var type = identical(node.staticType, variable.type)
- ? null
- : scope.buildType(node.staticType);
- return new VariableAccessor(scope.getVariableReference(element), type);
-
- case ElementKind.IMPORT:
- case ElementKind.LIBRARY:
- case ElementKind.PREFIX:
- return scope.emitCompileTimeError(
- CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
- [node.name]);
-
- case ElementKind.COMPILATION_UNIT:
- case ElementKind.CONSTRUCTOR:
- case ElementKind.EXPORT:
- case ElementKind.LABEL:
- case ElementKind.UNIVERSE:
- case ElementKind.NAME:
- default:
- return scope.internalError('Unexpected element kind: $element');
- }
- }
-
- visitIndexExpression(IndexExpression node) {
- Element element = node.staticElement;
- Element auxiliary = node.auxiliaryElements?.staticElement;
- if (node.isCascaded) {
- return IndexAccessor.make(
- makeCascadeReceiver(),
- build(node.index),
- scope.resolveInterfaceIndexGet(element, auxiliary),
- scope.resolveInterfaceIndexSet(element, auxiliary));
- } else if (node.target is SuperExpression) {
- scope.addTransformerFlag(TransformerFlag.superCalls);
- return new SuperIndexAccessor(
- build(node.index),
- scope.resolveConcreteIndexGet(element, auxiliary),
- scope.resolveConcreteIndexSet(element, auxiliary));
- } else {
- return IndexAccessor.make(
- build(node.target),
- build(node.index),
- scope.resolveInterfaceIndexGet(element, auxiliary),
- scope.resolveInterfaceIndexSet(element, auxiliary));
- }
- }
-
- /// Follows any number of redirecting factories, returning the effective
- /// target or `null` if a cycle is found.
- ///
- /// The returned element is a [Member] if the type arguments to the effective
- /// target are different from the original arguments.
- ConstructorElement getEffectiveFactoryTarget(ConstructorElement element) {
- ConstructorElement anchor = null;
- int n = 1;
- while (element.isFactory && element.redirectedConstructor != null) {
- element = element.redirectedConstructor;
- var base = ReferenceScope.getBaseElement(element);
- if (base == anchor) return null; // Cyclic redirection.
- if (n & ++n == 0) {
- anchor = base;
- }
- }
- return element;
- }
-
- /// Forces the list of type arguments to have the specified length. If the
- /// length was changed, all type arguments are changed to `dynamic`.
- void _coerceTypeArgumentArity(List<ast.DartType> typeArguments, int arity) {
- if (typeArguments.length != arity) {
- typeArguments.length = arity;
- typeArguments.fillRange(0, arity, const ast.DynamicType());
- }
- }
-
- ast.Expression visitInstanceCreationExpression(
- InstanceCreationExpression node) {
- ConstructorElement element = node.staticElement;
- ClassElement classElement = element?.enclosingElement;
- List<ast.DartType> inferTypeArguments() {
- var inferredType = scope.getInferredType(node);
- if (inferredType is ast.InterfaceType) {
- return inferredType.typeArguments.toList();
- }
- int numberOfTypeArguments =
- classElement == null ? 0 : classElement.typeParameters.length;
- return new List<ast.DartType>.filled(
- numberOfTypeArguments, const ast.DynamicType(),
- growable: true);
- }
-
- var arguments = buildArguments(node.argumentList,
- explicitTypeArguments: node.constructorName.type.typeArguments,
- inferTypeArguments: inferTypeArguments);
- ast.Expression noSuchMethodError() {
- return node.isConst
- ? scope.emitInvalidConstant()
- : scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), '${node.constructorName}', arguments,
- candidateTarget: element);
- }
-
- if (element == null) {
- return noSuchMethodError();
- }
- assert(classElement != null);
- var redirect = getEffectiveFactoryTarget(element);
- if (redirect == null) {
- return scope.buildThrowCompileTimeError(
- CompileTimeErrorCode.RECURSIVE_FACTORY_REDIRECT.message);
- }
- if (redirect != element) {
- ast.InterfaceType returnType = scope.buildType(redirect.returnType);
- arguments.types
- ..clear()
- ..addAll(returnType.typeArguments);
- element = redirect;
- classElement = element.enclosingElement;
- }
- element = ReferenceScope.getBaseElement(element);
- if (node.isConst && !element.isConst) {
- return scope
- .emitInvalidConstant(CompileTimeErrorCode.CONST_WITH_NON_CONST);
- }
- if (classElement.isEnum) {
- return scope.emitCompileTimeError(CompileTimeErrorCode.INSTANTIATE_ENUM);
- }
- _coerceTypeArgumentArity(
- arguments.types, classElement.typeParameters.length);
- if (element.isFactory) {
- ast.Member target = scope.resolveConcreteMethod(element);
- if (target is ast.Procedure &&
- scope.areArgumentsCompatible(element, arguments)) {
- return new ast.StaticInvocation(target, arguments,
- isConst: node.isConst);
- } else {
- return noSuchMethodError();
- }
- }
- if (classElement.isAbstract) {
- return node.isConst
- ? scope.emitInvalidConstant()
- : scope.buildThrowAbstractClassInstantiationError(classElement.name);
- }
- ast.Constructor constructor = scope.resolveConstructor(element);
- if (constructor != null &&
- scope.areArgumentsCompatible(element, arguments)) {
- return new ast.ConstructorInvocation(constructor, arguments,
- isConst: node.isConst);
- } else {
- return noSuchMethodError();
- }
- }
-
- ast.Expression visitIsExpression(IsExpression node) {
- if (node.notOperator != null) {
- // Put offset on the IsExpression for "is!" cases:
- // As it is wrapped in a not, it won't get an offset otherwise.
- return new ast.Not(new ast.IsExpression(
- build(node.expression), scope.buildTypeAnnotation(node.type))
- ..fileOffset = _getOffset(node));
- } else {
- return new ast.IsExpression(
- build(node.expression), scope.buildTypeAnnotation(node.type));
- }
- }
-
- /// Emit a method invocation, either as a direct call `o.f(x)` or decomposed
- /// into a getter and function invocation `o.f.call(x)`.
- ast.Expression buildDecomposableMethodInvocation(ast.Expression receiver,
- ast.Name name, ast.Arguments arguments, Element targetElement) {
- // Try to emit a typed call to an interface method.
- ast.Procedure targetMethod = scope.resolveInterfaceMethod(targetElement);
- if (targetMethod != null) {
- return new ast.MethodInvocation(receiver, name, arguments, targetMethod);
- }
- // Try to emit a typed call to getter or field and call the returned
- // function.
- ast.Member targetGetter = scope.resolveInterfaceGet(targetElement, null);
- if (targetGetter != null) {
- return new ast.MethodInvocation(
- new ast.PropertyGet(receiver, name, targetGetter),
- callName,
- arguments,
- scope.resolveInterfaceFunctionCall(targetElement));
- }
- // Emit a dynamic call.
- return new ast.MethodInvocation(receiver, name, arguments);
- }
-
- ast.Expression visitMethodInvocation(MethodInvocation node) {
- Element element = node.methodName.staticElement;
- if (element != null && element == element.library?.loadLibraryFunction) {
- return scope.unsupportedFeature('Deferred loading');
- }
- var target = node.target;
- if (node.isCascaded) {
- return buildDecomposableMethodInvocation(
- makeCascadeReceiver(),
- scope.buildName(node.methodName),
- buildArgumentsForInvocation(node),
- element);
- } else if (target is SuperExpression) {
- scope.addTransformerFlag(TransformerFlag.superCalls);
- return new ast.SuperMethodInvocation(
- scope.buildName(node.methodName),
- buildArgumentsForInvocation(node),
- scope.resolveConcreteMethod(element));
- } else if (isLocal(element)) {
- // Set the offset directly: Normally the offset is at the start of the
- // method, but in this case, because we insert a '.call', we want it at
- // the end instead.
- return new ast.MethodInvocation(
- new ast.VariableGet(scope.getVariableReference(element)),
- callName,
- buildArgumentsForInvocation(node),
- scope.resolveInterfaceFunctionCall(element))
- ..fileOffset = node.methodName.end;
- } else if (isStaticMethod(element)) {
- var method = scope.resolveConcreteMethod(element);
- var arguments = buildArgumentsForInvocation(node);
- if (method == null || !scope.areArgumentsCompatible(element, arguments)) {
- return scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), node.methodName.name, arguments,
- candidateTarget: element);
- }
- return new ast.StaticInvocation(method, arguments);
- } else if (isStaticVariableOrGetter(element)) {
- var method = scope.resolveConcreteGet(element, null);
- if (method == null) {
- return scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), node.methodName.name, new ast.Arguments([]),
- candidateTarget: element);
- }
- // Set the offset directly: Normally the offset is at the start of the
- // method, but in this case, because we insert a '.call', we want it at
- // the end instead.
- return new ast.MethodInvocation(
- new ast.StaticGet(method),
- callName,
- buildArgumentsForInvocation(node),
- scope.resolveInterfaceFunctionCall(element))
- ..fileOffset = node.methodName.end;
- } else if (target == null && !scope.allowThis ||
- target is Identifier && target.staticElement is ClassElement ||
- target is Identifier && target.staticElement is PrefixElement) {
- return scope.buildThrowNoSuchMethodError(new ast.NullLiteral(),
- node.methodName.name, buildArgumentsForInvocation(node),
- candidateTarget: element);
- } else if (target == null) {
- return buildDecomposableMethodInvocation(
- scope.buildThis(),
- scope.buildName(node.methodName),
- buildArgumentsForInvocation(node),
- element);
- } else if (node.operator.value() == '?.') {
- var receiver = makeOrReuseVariable(build(target));
- return makeLet(
- receiver,
- new ast.ConditionalExpression(
- buildIsNull(new ast.VariableGet(receiver)),
- new ast.NullLiteral(),
- buildDecomposableMethodInvocation(
- new ast.VariableGet(receiver),
- scope.buildName(node.methodName),
- buildArgumentsForInvocation(node),
- element)..fileOffset = node.methodName.offset,
- scope.buildType(node.staticType)));
- } else {
- return buildDecomposableMethodInvocation(
- build(node.target),
- scope.buildName(node.methodName),
- buildArgumentsForInvocation(node),
- element);
- }
- }
-
- ast.Expression visitNamedExpression(NamedExpression node) {
- return scope.internalError('Unexpected named expression');
- }
-
- ast.Expression visitParenthesizedExpression(ParenthesizedExpression node) {
- return build(node.expression);
- }
-
- bool isInVoidContext(Expression node) {
- AstNode parent = node.parent;
- return parent is ForStatement &&
- (parent.updaters.contains(node) || parent.initialization == node) ||
- parent is ExpressionStatement ||
- parent is ExpressionFunctionBody && scope.bodyHasVoidReturn(parent);
- }
-
- ast.Expression visitPostfixExpression(PostfixExpression node) {
- String operator = node.operator.value();
- switch (operator) {
- case '++':
- case '--':
- var leftHand = buildLeftHandValue(node.operand);
- var binaryOperator = new ast.Name(operator[0]);
- return leftHand.buildPostfixIncrement(binaryOperator,
- offset: node.operator.offset,
- voidContext: isInVoidContext(node),
- interfaceTarget: scope.resolveInterfaceMethod(node.staticElement));
-
- default:
- return scope.internalError('Invalid postfix operator $operator');
- }
- }
-
- ast.Expression visitPrefixExpression(PrefixExpression node) {
- String operator = node.operator.value();
- switch (operator) {
- case '-':
- case '~':
- var name = new ast.Name(operator == '-' ? 'unary-' : '~');
- if (node.operand is SuperExpression) {
- scope.addTransformerFlag(TransformerFlag.superCalls);
- return new ast.SuperMethodInvocation(name, new ast.Arguments.empty(),
- scope.resolveConcreteMethod(node.staticElement));
- }
- return new ast.MethodInvocation(
- build(node.operand),
- name,
- new ast.Arguments.empty(),
- scope.resolveInterfaceMethod(node.staticElement));
-
- case '!':
- return new ast.Not(build(node.operand));
-
- case '++':
- case '--':
- var leftHand = buildLeftHandValue(node.operand);
- var binaryOperator = new ast.Name(operator[0]);
- return leftHand.buildPrefixIncrement(binaryOperator,
- offset: node.offset,
- interfaceTarget: scope.resolveInterfaceMethod(node.staticElement));
-
- default:
- return scope.internalError('Invalid prefix operator $operator');
- }
- }
-
- visitPropertyAccess(PropertyAccess node) {
- Element element = node.propertyName.staticElement;
- Element auxiliary = node.propertyName.auxiliaryElements?.staticElement;
- var getter = scope.resolveInterfaceGet(element, auxiliary);
- var setter = scope.resolveInterfaceSet(element, auxiliary);
- Expression target = node.target;
- if (node.isCascaded) {
- return PropertyAccessor.make(makeCascadeReceiver(),
- scope.buildName(node.propertyName), getter, setter);
- } else if (node.target is SuperExpression) {
- scope.addTransformerFlag(TransformerFlag.superCalls);
- return new SuperPropertyAccessor(
- scope.buildName(node.propertyName),
- scope.resolveConcreteGet(element, auxiliary),
- scope.resolveConcreteSet(element, auxiliary));
- } else if (target is Identifier && target.staticElement is ClassElement) {
- // Note that this case also covers null-aware static access on a class,
- // which is equivalent to a regular static access.
- return scope.staticAccess(node.propertyName.name, element, auxiliary);
- } else if (node.operator.value() == '?.') {
- return new NullAwarePropertyAccessor(
- build(target),
- scope.buildName(node.propertyName),
- getter,
- setter,
- scope.buildType(node.staticType));
- } else {
- return PropertyAccessor.make(
- build(target), scope.buildName(node.propertyName), getter, setter);
- }
- }
-
- ast.Expression visitRethrowExpression(RethrowExpression node) {
- return new ast.Rethrow();
- }
-
- ast.Expression visitSuperExpression(SuperExpression node) {
- return scope
- .emitCompileTimeError(CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT);
- }
-
- ast.Expression visitThisExpression(ThisExpression node) {
- return scope.buildThis();
- }
-
- ast.Expression visitThrowExpression(ThrowExpression node) {
- return new ast.Throw(build(node.expression));
- }
-
- ast.Expression visitListLiteral(ListLiteral node) {
- ast.DartType type = node.typeArguments?.arguments?.isNotEmpty ?? false
- ? scope.buildTypeAnnotation(node.typeArguments.arguments[0])
- : scope.getInferredTypeArgument(node, 0);
- return new ast.ListLiteral(node.elements.map(build).toList(),
- typeArgument: type, isConst: node.constKeyword != null);
- }
-
- ast.Expression visitMapLiteral(MapLiteral node) {
- ast.DartType key, value;
- if (node.typeArguments != null && node.typeArguments.arguments.length > 1) {
- key = scope.buildTypeAnnotation(node.typeArguments.arguments[0]);
- value = scope.buildTypeAnnotation(node.typeArguments.arguments[1]);
- } else {
- key = scope.getInferredTypeArgument(node, 0);
- value = scope.getInferredTypeArgument(node, 1);
- }
- return new ast.MapLiteral(node.entries.map(buildMapEntry).toList(),
- keyType: key, valueType: value, isConst: node.constKeyword != null);
- }
-
- ast.MapEntry buildMapEntry(MapLiteralEntry node) {
- return new ast.MapEntry(build(node.key), build(node.value));
- }
-
- ast.Expression visitExpression(Expression node) {
- return scope.internalError('Unhandled expression ${node.runtimeType}');
- }
-}
-
-class StringLiteralPartBuilder extends GeneralizingAstVisitor<Null> {
- final ExpressionScope scope;
- final List<ast.Expression> output;
- StringLiteralPartBuilder(this.scope, this.output);
-
- void build(Expression node) {
- node.accept(this);
- }
-
- void buildInterpolationElement(InterpolationElement node) {
- node.accept(this);
- }
-
- visitSimpleStringLiteral(SimpleStringLiteral node) {
- output.add(new ast.StringLiteral(node.value));
- }
-
- visitAdjacentStrings(AdjacentStrings node) {
- node.strings.forEach(build);
- }
-
- visitStringInterpolation(StringInterpolation node) {
- node.elements.forEach(buildInterpolationElement);
- }
-
- visitInterpolationString(InterpolationString node) {
- output.add(new ast.StringLiteral(node.value));
- }
-
- visitInterpolationExpression(InterpolationExpression node) {
- output.add(scope.buildExpression(node.expression));
- }
-}
-
-class TypeAnnotationBuilder extends GeneralizingAstVisitor<ast.DartType> {
- final TypeScope scope;
-
- TypeAnnotationBuilder(this.scope);
-
- ast.DartType build(AstNode node) {
- return node.accept(this);
- }
-
- List<ast.DartType> buildList(Iterable<AstNode> node) {
- return node.map(build).toList();
- }
-
- /// Replace unbound type variables in [type] with 'dynamic' and convert
- /// to an [ast.DartType].
- ast.DartType buildClosedTypeFromDartType(DartType type) {
- return convertType(type, <TypeParameterElement>[]);
- }
-
- /// Convert to an [ast.DartType] and keep type variables.
- ast.DartType buildFromDartType(DartType type) {
- return convertType(type, null);
- }
-
- /// True if [parameter] should not be reified, because spec mode does not
- /// currently reify generic method type parameters.
- bool isUnreifiedTypeParameter(TypeParameterElement parameter) {
- return !scope.strongMode && parameter.enclosingElement is! ClassElement;
- }
-
- /// Converts [type] to an [ast.DartType], while replacing unbound type
- /// variables with 'dynamic'.
- ///
- /// If [boundVariables] is null, no type variables are replaced, otherwise all
- /// type variables except those in [boundVariables] are replaced. In other
- /// words, it represents the bound variables, or "all variables" if omitted.
- ast.DartType convertType(
- DartType type, List<TypeParameterElement> boundVariables) {
- if (type is TypeParameterType) {
- if (isUnreifiedTypeParameter(type.element)) {
- return const ast.DynamicType();
- }
- if (boundVariables == null || boundVariables.contains(type)) {
- var typeParameter = scope.tryGetTypeParameterReference(type.element);
- if (typeParameter == null) {
- // The analyzer sometimes gives us a type parameter that was not
- // bound anywhere. Make sure we do not emit a dangling reference.
- if (type.element.bound != null) {
- return convertType(type.element.bound, []);
- }
- return const ast.DynamicType();
- }
- if (!scope.allowClassTypeParameters &&
- typeParameter.parent is ast.Class) {
- return const ast.InvalidType();
- }
- return new ast.TypeParameterType(typeParameter);
- } else {
- return const ast.DynamicType();
- }
- } else if (type is InterfaceType) {
- var classNode = scope.getClassReference(type.element);
- if (type.typeArguments.length == 0) {
- return classNode.rawType;
- }
- if (type.typeArguments.length != classNode.typeParameters.length) {
- log.warning('Type parameter arity error in $type');
- return const ast.InvalidType();
- }
- return new ast.InterfaceType(
- classNode, convertTypeList(type.typeArguments, boundVariables));
- } else if (type is FunctionType) {
- // TODO: Avoid infinite recursion in case of illegal circular typedef.
- boundVariables?.addAll(type.typeParameters);
- var positionals =
- concatenate(type.normalParameterTypes, type.optionalParameterTypes);
- var result = new ast.FunctionType(
- convertTypeList(positionals, boundVariables),
- convertType(type.returnType, boundVariables),
- typeParameters:
- convertTypeParameterList(type.typeFormals, boundVariables),
- namedParameters:
- convertTypeMap(type.namedParameterTypes, boundVariables),
- requiredParameterCount: type.normalParameterTypes.length);
- boundVariables?.removeRange(
- boundVariables.length - type.typeParameters.length,
- boundVariables.length);
- return result;
- } else if (type.isUndefined) {
- log.warning('Unresolved type found in ${scope.location}');
- return const ast.InvalidType();
- } else if (type.isVoid) {
- return const ast.VoidType();
- } else if (type.isDynamic) {
- return const ast.DynamicType();
- } else {
- log.severe('Unexpected DartType: $type');
- return const ast.InvalidType();
- }
- }
-
- static Iterable/*<E>*/ concatenate/*<E>*/(
- Iterable/*<E>*/ x, Iterable/*<E>*/ y) =>
- <Iterable<dynamic/*=E*/ >>[x, y].expand((z) => z);
-
- ast.TypeParameter convertTypeParameter(TypeParameterElement typeParameter,
- List<TypeParameterElement> boundVariables) {
- return scope.makeTypeParameter(typeParameter,
- bound: typeParameter.bound == null
- ? scope.defaultTypeParameterBound
- : convertType(typeParameter.bound, boundVariables));
- }
-
- List<ast.TypeParameter> convertTypeParameterList(
- Iterable<TypeParameterElement> typeParameters,
- List<TypeParameterElement> boundVariables) {
- if (typeParameters.isEmpty) return const <ast.TypeParameter>[];
- return typeParameters
- .map((tp) => convertTypeParameter(tp, boundVariables))
- .toList();
- }
-
- List<ast.DartType> convertTypeList(
- Iterable<DartType> types, List<TypeParameterElement> boundVariables) {
- if (types.isEmpty) return const <ast.DartType>[];
- return types.map((t) => convertType(t, boundVariables)).toList();
- }
-
- List<ast.NamedType> convertTypeMap(
- Map<String, DartType> types, List<TypeParameterElement> boundVariables) {
- if (types.isEmpty) return const <ast.NamedType>[];
- List<ast.NamedType> result = <ast.NamedType>[];
- types.forEach((name, type) {
- result.add(new ast.NamedType(name, convertType(type, boundVariables)));
- });
- sortAndRemoveDuplicates(result);
- return result;
- }
-
- ast.DartType visitSimpleIdentifier(SimpleIdentifier node) {
- Element element = node.staticElement;
- switch (ElementKind.of(element)) {
- case ElementKind.CLASS:
- return scope.getClassReference(element).rawType;
-
- case ElementKind.DYNAMIC:
- return const ast.DynamicType();
-
- case ElementKind.FUNCTION_TYPE_ALIAS:
- FunctionTypeAliasElement functionType = element;
- return buildClosedTypeFromDartType(functionType.type);
-
- case ElementKind.TYPE_PARAMETER:
- var typeParameter = scope.getTypeParameterReference(element);
- if (!scope.allowClassTypeParameters &&
- typeParameter.parent is ast.Class) {
- return const ast.InvalidType();
- }
- if (isUnreifiedTypeParameter(element)) {
- return const ast.DynamicType();
- }
- return new ast.TypeParameterType(typeParameter);
-
- case ElementKind.COMPILATION_UNIT:
- case ElementKind.CONSTRUCTOR:
- case ElementKind.EXPORT:
- case ElementKind.IMPORT:
- case ElementKind.LABEL:
- case ElementKind.LIBRARY:
- case ElementKind.PREFIX:
- case ElementKind.UNIVERSE:
- case ElementKind.ERROR: // This covers the case where nothing was found.
- case ElementKind.FIELD:
- case ElementKind.TOP_LEVEL_VARIABLE:
- case ElementKind.GETTER:
- case ElementKind.SETTER:
- case ElementKind.METHOD:
- case ElementKind.LOCAL_VARIABLE:
- case ElementKind.PARAMETER:
- case ElementKind.FUNCTION:
- case ElementKind.NAME:
- default:
- log.severe('Invalid type annotation: $element');
- return const ast.InvalidType();
- }
- }
-
- visitPrefixedIdentifier(PrefixedIdentifier node) {
- return build(node.identifier);
- }
-
- visitTypeName(TypeName node) {
- return buildFromDartType(node.type);
- }
-
- visitNode(AstNode node) {
- log.severe('Unexpected type annotation: $node');
- return new ast.InvalidType();
- }
-}
-
-class InitializerBuilder extends GeneralizingAstVisitor<ast.Initializer> {
- final MemberScope scope;
-
- InitializerBuilder(this.scope);
-
- ast.Initializer build(ConstructorInitializer node) {
- return node.accept(this);
- }
-
- visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
- var target = scope.resolveField(node.fieldName.staticElement);
- if (target == null) {
- return new ast.InvalidInitializer();
- }
- return new ast.FieldInitializer(
- target, scope.buildExpression(node.expression));
- }
-
- visitSuperConstructorInvocation(SuperConstructorInvocation node) {
- var target = scope.resolveConstructor(node.staticElement);
- if (target == null) {
- return new ast.InvalidInitializer();
- }
- scope.addTransformerFlag(TransformerFlag.superCalls);
- return new ast.SuperInitializer(
- target, scope._expressionBuilder.buildArguments(node.argumentList));
- }
-
- visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
- var target = scope.resolveConstructor(node.staticElement);
- if (target == null) {
- return new ast.InvalidInitializer();
- }
- return new ast.RedirectingInitializer(
- target, scope._expressionBuilder.buildArguments(node.argumentList));
- }
-
- visitNode(AstNode node) {
- log.severe('Unexpected constructor initializer: ${node.runtimeType}');
- return new ast.InvalidInitializer();
- }
-}
-
-/// Brings a class from hierarchy level to body level.
-//
-// TODO(asgerf): Error recovery during class construction is currently handled
-// locally, but this can in theory break global invariants in the kernel IR.
-// To safely compile code with compile-time errors, we may need a recovery
-// pass to enforce all kernel invariants before it is given to the backend.
-class ClassBodyBuilder extends GeneralizingAstVisitor<Null> {
- final ClassScope scope;
- final ExpressionScope annotationScope;
- final ast.Class currentClass;
- final ClassElement element;
- ast.Library get currentLibrary => currentClass.enclosingLibrary;
-
- ClassBodyBuilder(
- ReferenceLevelLoader loader, ast.Class currentClass, this.element)
- : this.currentClass = currentClass,
- scope = new ClassScope(loader, currentClass.enclosingLibrary),
- annotationScope =
- new ExpressionScope(loader, currentClass.enclosingLibrary);
-
- void build(CompilationUnitMember node) {
- if (node == null) {
- buildBrokenClass();
- return;
- }
- node.accept(this);
- }
-
- /// Builds an empty class for broken classes that have no AST.
- ///
- /// This should only be used to recover from a compile-time error.
- void buildBrokenClass() {
- currentClass.name = element.name;
- currentClass.supertype = scope.getRootClassReference().asRawSupertype;
- currentClass.constructors.add(
- new ast.Constructor(new ast.FunctionNode(new ast.InvalidStatement()))
- ..fileOffset = element.nameOffset);
- }
-
- void addAnnotations(List<Annotation> annotations) {
- // Class type parameters are not in scope in the annotation list.
- for (var annotation in annotations) {
- currentClass.addAnnotation(annotationScope.buildAnnotation(annotation));
- }
- }
-
- void _buildMemberBody(ast.Member member, Element element, AstNode node) {
- new MemberBodyBuilder(scope.loader, member, element).build(node);
- }
-
- /// True if the given class member should not be emitted, and does not
- /// correspond to any Kernel member.
- ///
- /// This is true for redirecting factories with a resolved target. These are
- /// always bypassed at the call site.
- bool _isIgnoredMember(ClassMember node) {
- if (node is ConstructorDeclaration && node.factoryKeyword != null) {
- var element = resolutionMap.elementDeclaredByConstructorDeclaration(node);
- return element.redirectedConstructor != null &&
- (element.isSynthetic || scope.loader.ignoreRedirectingFactories);
- } else {
- return false;
- }
- }
-
- visitClassDeclaration(ClassDeclaration node) {
- addAnnotations(node.metadata);
- ast.Class classNode = currentClass;
- assert(classNode.members.isEmpty); // All members will be added here.
-
- bool foundConstructor = false;
- for (var member in node.members) {
- if (_isIgnoredMember(member)) continue;
- if (member is FieldDeclaration) {
- for (var variable in member.fields.variables) {
- // Ignore fields inserted through error recovery.
- if (variable.isSynthetic || variable.length == 0) continue;
- var field = scope.getMemberReference(variable.element);
- classNode.addMember(field);
- _buildMemberBody(field, variable.element, variable);
- }
- } else {
- var memberNode = scope.getMemberReference(member.element);
- classNode.addMember(memberNode);
- _buildMemberBody(memberNode, member.element, member);
- if (member is ConstructorDeclaration) {
- foundConstructor = true;
- }
- }
- }
-
- if (!foundConstructor) {
- var defaultConstructor = scope.findDefaultConstructor(node.element);
- if (defaultConstructor != null) {
- assert(defaultConstructor.enclosingElement == node.element);
- if (!defaultConstructor.isSynthetic) {
- throw 'Non-synthetic default constructor not in list of members. '
- '${node} $element $defaultConstructor';
- }
- var memberNode = scope.getMemberReference(defaultConstructor);
- classNode.addMember(memberNode);
- buildDefaultConstructor(memberNode, defaultConstructor);
- }
- }
-
- addDefaultInstanceFieldInitializers(classNode);
- }
-
- void buildDefaultConstructor(
- ast.Constructor constructor, ConstructorElement element) {
- var function = constructor.function;
- function.body = new ast.EmptyStatement()..parent = function;
- var class_ = element.enclosingElement;
- if (class_.supertype != null) {
- // DESIGN TODO: If the super class is a mixin application, we will link to
- // a constructor not in the immediate super class. This is a problem due
- // to the fact that mixed-in fields come with initializers which need to
- // be executed by a constructor. The mixin transformer takes care of
- // this by making forwarding constructors and the super initializers will
- // be rewritten to use them (see `transformations/mixin_full_resolution`).
- var superConstructor =
- scope.findDefaultConstructor(class_.supertype.element);
- var target = scope.resolveConstructor(superConstructor);
- if (target == null) {
- constructor.initializers
- .add(new ast.InvalidInitializer()..parent = constructor);
- } else {
- var arguments = new ast.Arguments.empty();
- constructor.initializers.add(
- new ast.SuperInitializer(target, arguments)..parent = constructor);
- }
- }
- }
-
- /// Adds initializers to instance fields that are have no initializer and are
- /// not initialized by all constructors in the class.
- void addDefaultInstanceFieldInitializers(ast.Class node) {
- List<ast.Field> uninitializedFields = new List<ast.Field>();
- for (var field in node.fields) {
- if (field.initializer != null || field.isStatic) continue;
- uninitializedFields.add(field);
- }
- if (uninitializedFields.isEmpty) return;
- constructorLoop:
- for (var constructor in node.constructors) {
- var remainingFields = uninitializedFields.toSet();
- for (var initializer in constructor.initializers) {
- if (initializer is ast.FieldInitializer) {
- remainingFields.remove(initializer.field);
- } else if (initializer is ast.RedirectingInitializer) {
- // The target constructor will be checked in another iteration.
- continue constructorLoop;
- }
- }
- for (var field in remainingFields) {
- if (field.initializer == null) {
- field.initializer = new ast.NullLiteral()..parent = field;
- }
- }
- }
- }
-
- /// True for the `values` field of an `enum` class.
- static bool _isValuesField(FieldElement field) => field.name == 'values';
-
- /// True for the `index` field of an `enum` class.
- static bool _isIndexField(FieldElement field) => field.name == 'index';
-
- visitEnumDeclaration(EnumDeclaration node) {
- addAnnotations(node.metadata);
- ast.Class classNode = currentClass;
-
- var intType = scope.loader.getCoreClassReference('int').rawType;
- var indexFieldElement = element.fields.firstWhere(_isIndexField);
- ast.Field indexField = scope.getMemberReference(indexFieldElement);
- indexField.type = intType;
- classNode.addMember(indexField);
-
- var stringType = scope.loader.getCoreClassReference('String').rawType;
- ast.Field nameField = new ast.Field(
- new ast.Name('_name', scope.currentLibrary),
- type: stringType,
- isFinal: true,
- fileUri: classNode.fileUri);
- classNode.addMember(nameField);
-
- var indexParameter = new ast.VariableDeclaration('index', type: intType);
- var nameParameter = new ast.VariableDeclaration('name', type: stringType);
- var function = new ast.FunctionNode(new ast.EmptyStatement(),
- positionalParameters: [indexParameter, nameParameter]);
- var superConstructor = scope.loader.getRootClassConstructorReference();
- var constructor = new ast.Constructor(function,
- name: new ast.Name(''),
- isConst: true,
- initializers: [
- new ast.FieldInitializer(
- indexField, new ast.VariableGet(indexParameter)),
- new ast.FieldInitializer(
- nameField, new ast.VariableGet(nameParameter)),
- new ast.SuperInitializer(superConstructor, new ast.Arguments.empty())
- ])..fileOffset = element.nameOffset;
- classNode.addMember(constructor);
-
- int index = 0;
- var enumConstantFields = <ast.Field>[];
- for (var constant in node.constants) {
- ast.Field field = scope.getMemberReference(constant.element);
- field.initializer = new ast.ConstructorInvocation(
- constructor,
- new ast.Arguments([
- new ast.IntLiteral(index),
- new ast.StringLiteral('${classNode.name}.${field.name.name}')
- ]),
- isConst: true)..parent = field;
- field.type = classNode.rawType;
- classNode.addMember(field);
- ++index;
- enumConstantFields.add(field);
- }
-
- // Add the 'values' field.
- var valuesFieldElement = element.fields.firstWhere(_isValuesField);
- ast.Field valuesField = scope.getMemberReference(valuesFieldElement);
- var enumType = classNode.rawType;
- valuesField.type = new ast.InterfaceType(
- scope.loader.getCoreClassReference('List'), <ast.DartType>[enumType]);
- valuesField.initializer = new ast.ListLiteral(
- enumConstantFields.map(_makeStaticGet).toList(),
- isConst: true,
- typeArgument: enumType)..parent = valuesField;
- classNode.addMember(valuesField);
-
- // Add the 'toString()' method.
- var body = new ast.ReturnStatement(
- new ast.DirectPropertyGet(new ast.ThisExpression(), nameField));
- var toStringFunction = new ast.FunctionNode(body, returnType: stringType);
- var toStringMethod = new ast.Procedure(
- new ast.Name('toString'), ast.ProcedureKind.Method, toStringFunction,
- fileUri: classNode.fileUri);
- classNode.addMember(toStringMethod);
- }
-
- visitClassTypeAlias(ClassTypeAlias node) {
- addAnnotations(node.metadata);
- assert(node.withClause != null && node.withClause.mixinTypes.isNotEmpty);
- ast.Class classNode = currentClass;
- for (var constructor in element.constructors) {
- var constructorNode = scope.getMemberReference(constructor);
- classNode.addMember(constructorNode);
- buildMixinConstructor(constructorNode, constructor);
- }
- }
-
- void buildMixinConstructor(
- ast.Constructor constructor, ConstructorElement element) {
- var function = constructor.function;
- function.body = new ast.EmptyStatement()..parent = function;
- // Call the corresponding constructor in super class.
- ClassElement classElement = element.enclosingElement;
- var targetConstructor = classElement.supertype.element.constructors
- .firstWhere((c) => c.name == element.name);
- var positionalArguments = constructor.function.positionalParameters
- .map(_makeVariableGet)
- .toList();
- var namedArguments = constructor.function.namedParameters
- .map(_makeNamedExpressionFrom)
- .toList();
- constructor.initializers.add(new ast.SuperInitializer(
- scope.getMemberReference(targetConstructor),
- new ast.Arguments(positionalArguments, named: namedArguments))
- ..parent = constructor);
- }
-
- visitNode(AstNode node) {
- throw 'Unsupported class declaration: ${node.runtimeType}';
- }
-}
-
-/// Brings a member from reference level to body level.
-class MemberBodyBuilder extends GeneralizingAstVisitor<Null> {
- final MemberScope scope;
- final Element element;
- ast.Member get currentMember => scope.currentMember;
-
- MemberBodyBuilder(
- ReferenceLevelLoader loader, ast.Member member, this.element)
- : scope = new MemberScope(loader, member);
-
- void build(AstNode node) {
- if (node != null) {
- currentMember.fileEndOffset = node.endToken.offset;
- node.accept(this);
- } else {
- buildBrokenMember();
- }
- }
-
- /// Builds an empty member.
- ///
- /// This should only be used to recover from a compile-time error.
- void buildBrokenMember() {
- var member = currentMember;
- member.name = new ast.Name(element.name, scope.currentLibrary);
- if (member is ast.Procedure) {
- member.function = new ast.FunctionNode(new ast.InvalidStatement())
- ..parent = member;
- } else if (member is ast.Constructor) {
- member.function = new ast.FunctionNode(new ast.InvalidStatement())
- ..parent = member;
- }
- }
-
- void addAnnotations(List<Annotation> annotations) {
- for (var annotation in annotations) {
- currentMember.addAnnotation(scope.buildAnnotation(annotation));
- }
- }
-
- void handleNativeBody(FunctionBody body) {
- if (body is NativeFunctionBody) {
- currentMember.isExternal = true;
- currentMember.addAnnotation(new ast.ConstructorInvocation(
- scope.loader.getCoreClassConstructorReference('ExternalName',
- library: 'dart:_internal'),
- new ast.Arguments(<ast.Expression>[
- new ast.StringLiteral(body.stringLiteral.stringValue)
- ]),
- isConst: true));
- }
- }
-
- visitConstructorDeclaration(ConstructorDeclaration node) {
- if (node.factoryKeyword != null) {
- buildFactoryConstructor(node);
- } else {
- buildGenerativeConstructor(node);
- }
- }
-
- void buildGenerativeConstructor(ConstructorDeclaration node) {
- if (currentMember is! ast.Constructor) {
- buildBrokenMember();
- return;
- }
- addAnnotations(node.metadata);
- ast.Constructor constructor = currentMember;
- constructor.function = scope.buildFunctionNode(node.parameters, node.body,
- inferredReturnType: const ast.VoidType())..parent = constructor;
- handleNativeBody(node.body);
- if (node.body is EmptyFunctionBody && !constructor.isExternal) {
- var function = constructor.function;
- function.body = new ast.EmptyStatement()..parent = function;
- }
- for (var parameter in node.parameters.parameterElements) {
- if (parameter is FieldFormalParameterElement) {
- ast.Initializer initializer;
- if (parameter.field == null) {
- initializer = new ast.LocalInitializer(
- new ast.VariableDeclaration.forValue(scope
- .buildThrowCompileTimeErrorFromCode(
- CompileTimeErrorCode.INITIALIZER_FOR_NON_EXISTENT_FIELD,
- [parameter.name])));
- } else {
- initializer = new ast.FieldInitializer(
- scope.getMemberReference(parameter.field),
- new ast.VariableGet(scope.getVariableReference(parameter)));
- }
- constructor.initializers.add(initializer..parent = constructor);
- }
- }
- bool hasExplicitConstructorCall = false;
- for (var initializer in node.initializers) {
- var node = scope.buildInitializer(initializer);
- constructor.initializers.add(node..parent = constructor);
- if (node is ast.SuperInitializer || node is ast.RedirectingInitializer) {
- hasExplicitConstructorCall = true;
- }
- }
- ClassElement classElement = resolutionMap
- .elementDeclaredByConstructorDeclaration(node)
- .enclosingElement;
- if (classElement.supertype != null && !hasExplicitConstructorCall) {
- ConstructorElement targetElement =
- scope.findDefaultConstructor(classElement.supertype.element);
- ast.Constructor target = scope.resolveConstructor(targetElement);
- ast.Initializer initializer = target == null
- ? new ast.InvalidInitializer()
- : new ast.SuperInitializer(
- target, new ast.Arguments(<ast.Expression>[]));
- constructor.initializers.add(initializer..parent = constructor);
- } else {
- moveSuperInitializerLast(constructor);
- }
- }
-
- void buildFactoryConstructor(ConstructorDeclaration node) {
- if (currentMember is! ast.Procedure) {
- buildBrokenMember();
- return;
- }
- addAnnotations(node.metadata);
- ast.Procedure procedure = currentMember;
- ClassElement classElement = resolutionMap
- .elementDeclaredByConstructorDeclaration(node)
- .enclosingElement;
- ast.Class classNode = procedure.enclosingClass;
- var types = getFreshTypeParameters(classNode.typeParameters);
- for (int i = 0; i < classElement.typeParameters.length; ++i) {
- scope.localTypeParameters[classElement.typeParameters[i]] =
- types.freshTypeParameters[i];
- }
- var inferredReturnType = types.freshTypeParameters.isEmpty
- ? classNode.rawType
- : new ast.InterfaceType(
- classNode,
- types.freshTypeParameters
- .map(makeTypeParameterType)
- .toList(growable: false));
- var function = scope.buildFunctionNode(node.parameters, node.body,
- typeParameters: types.freshTypeParameters,
- inferredReturnType: inferredReturnType);
- procedure.function = function..parent = procedure;
- handleNativeBody(node.body);
- if (node.redirectedConstructor != null) {
- // Add a new synthetic field to [classNode] for representing factory
- // constructors. This is used by the new frontend engine to support
- // resolving source code.
- //
- // The synthetic field looks like this:
- //
- // final _redirecting# = [c1, ..., cn];
- //
- // Where each c1 ... cn are an instance of [StaticGet] whose target is
- // the redirecting factory created above. The new frontend engine reads
- // this field and rewrites them.
- //
- // TODO(ahe): Generate the correct factory body instead. This requires
- // access to default values from other files, we'll probably never do
- // that in this file, and instead rely on the new compiler for this.
- var element = resolutionMap.elementDeclaredByConstructorDeclaration(node);
- assert(!element.isSynthetic);
- var expression;
- if (node.element.redirectedConstructor != null) {
- assert(!scope.loader.ignoreRedirectingFactories);
- ConstructorElement element = node.element.redirectedConstructor;
- while (element.isFactory && element.redirectedConstructor != null) {
- element = element.redirectedConstructor;
- }
- ast.Member target = scope.getMemberReference(element);
- assert(target != null);
- expression = new ast.Let(
- new ast.VariableDeclaration.forValue(new ast.StaticGet(target)),
- new ast.InvalidExpression());
- ast.Name constructors =
- new ast.Name("_redirecting#", scope.currentLibrary);
- ast.Field constructorsField;
- for (ast.Field field in classNode.fields) {
- if (field.name == constructors) {
- constructorsField = field;
- break;
- }
- }
- if (constructorsField == null) {
- ast.ListLiteral literal = new ast.ListLiteral(<ast.Expression>[]);
- constructorsField = new ast.Field(constructors,
- isStatic: true,
- initializer: literal,
- fileUri: classNode.fileUri)..fileOffset = classNode.fileOffset;
- classNode.addMember(constructorsField);
- }
- ast.ListLiteral literal = constructorsField.initializer;
- literal.expressions.add(new ast.StaticGet(procedure)..parent = literal);
- } else {
- var name = node.redirectedConstructor.type.name.name;
- if (node.redirectedConstructor.name != null) {
- name += '.' + node.redirectedConstructor.name.name;
- }
- // TODO(asgerf): Sometimes a TypeError should be thrown.
- expression = scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), name, new ast.Arguments.empty());
- }
- var function = procedure.function;
- function.body = new ast.ExpressionStatement(expression)
- ..parent = function;
- }
- }
-
- visitMethodDeclaration(MethodDeclaration node) {
- addAnnotations(node.metadata);
- ast.Procedure procedure = currentMember;
- procedure.function = scope.buildFunctionNode(node.parameters, node.body,
- returnType: node.returnType,
- inferredReturnType: scope.buildType(
- resolutionMap.elementDeclaredByMethodDeclaration(node).returnType),
- typeParameters: scope.buildOptionalTypeParameterList(
- node.typeParameters,
- strongModeOnly: true))..parent = procedure;
- handleNativeBody(node.body);
- }
-
- visitVariableDeclaration(VariableDeclaration node) {
- addAnnotations(node.metadata);
- ast.Field field = currentMember;
- field.type = scope.buildType(
- resolutionMap.elementDeclaredByVariableDeclaration(node).type);
- if (node.initializer != null) {
- field.initializer = scope.buildTopLevelExpression(node.initializer)
- ..parent = field;
- } else if (field.isStatic) {
- // Add null initializer to static fields without an initializer.
- // For instance fields, this is handled when building the class.
- field.initializer = new ast.NullLiteral()..parent = field;
- }
- }
-
- visitFunctionDeclaration(FunctionDeclaration node) {
- addAnnotations(node.metadata);
- var function = node.functionExpression;
- ast.Procedure procedure = currentMember;
- procedure.function = scope.buildFunctionNode(
- function.parameters, function.body,
- returnType: node.returnType,
- typeParameters: scope.buildOptionalTypeParameterList(
- function.typeParameters,
- strongModeOnly: true))..parent = procedure;
- handleNativeBody(function.body);
- }
-
- visitNode(AstNode node) {
- log.severe('Unexpected class or library member: $node');
- }
-}
-
-/// Internal exception thrown from the expression or statement builder when a
-/// compilation error is found.
-///
-/// This is then caught at the function level to replace the entire function
-/// body (or field initializer) with a throw.
-class _CompilationError {
- String message;
-
- _CompilationError(this.message);
-}
-
-/// Constructor alias for [ast.TypeParameterType], use instead of a closure.
-ast.DartType makeTypeParameterType(ast.TypeParameter parameter) {
- return new ast.TypeParameterType(parameter);
-}
-
-/// Constructor alias for [ast.VariableGet], use instead of a closure.
-ast.VariableGet _makeVariableGet(ast.VariableDeclaration variable) {
- return new ast.VariableGet(variable);
-}
-
-/// Constructor alias for [ast.StaticGet], use instead of a closure.
-ast.StaticGet _makeStaticGet(ast.Field field) {
- return new ast.StaticGet(field);
-}
-
-/// Create a named expression with the name and value of the given variable.
-ast.NamedExpression _makeNamedExpressionFrom(ast.VariableDeclaration variable) {
- return new ast.NamedExpression(variable.name, new ast.VariableGet(variable));
-}
-
-/// A [StaticAccessor] that throws a NoSuchMethodError when a suitable target
-/// could not be resolved.
-class _StaticAccessor extends StaticAccessor {
- final ExpressionScope scope;
- final String name;
-
- _StaticAccessor(
- this.scope, this.name, ast.Member readTarget, ast.Member writeTarget)
- : super(readTarget, writeTarget);
-
- @override
- makeInvalidRead() {
- return scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), name, new ast.Arguments([]));
- }
-
- @override
- makeInvalidWrite(ast.Expression value) {
- return scope.buildThrowNoSuchMethodError(
- new ast.NullLiteral(), name, new ast.Arguments([value]));
- }
-}
-
-bool isTopLevelFunction(Element element) {
- return element is FunctionElement &&
- element.enclosingElement is CompilationUnitElement;
-}
-
-bool isLocalFunction(Element element) {
- return element is FunctionElement &&
- element.enclosingElement is! CompilationUnitElement &&
- element.enclosingElement is! LibraryElement;
-}
-
-bool isLocal(Element element) {
- return isLocalFunction(element) ||
- element is LocalVariableElement ||
- element is ParameterElement;
-}
-
-bool isInstanceMethod(Element element) {
- return element is MethodElement && !element.isStatic;
-}
-
-bool isStaticMethod(Element element) {
- return element is MethodElement && element.isStatic ||
- isTopLevelFunction(element);
-}
-
-bool isStaticVariableOrGetter(Element element) {
- element = desynthesizeGetter(element);
- return element is FieldElement && element.isStatic ||
- element is TopLevelVariableElement;
-}
-
-Element desynthesizeGetter(Element element) {
- if (element == null || !element.isSynthetic) return element;
- if (element is PropertyAccessorElement) return element.variable;
- if (element is FieldElement) return element.getter;
- return element;
-}
-
-Element desynthesizeSetter(Element element) {
- if (element == null || !element.isSynthetic) return element;
- if (element is PropertyAccessorElement) return element.variable;
- if (element is FieldElement) return element.setter;
- return element;
-}
-
-void sortAndRemoveDuplicates/*<T extends Comparable<T>>*/(List/*<T>*/ list) {
- list.sort();
- int deleted = 0;
- for (int i = 1; i < list.length; ++i) {
- var item = list[i];
- if (list[i - 1].compareTo(item) == 0) {
- ++deleted;
- } else if (deleted > 0) {
- list[i - deleted] = item;
- }
- }
- if (deleted > 0) {
- list.length -= deleted;
- }
-}
« 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