| 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;
|
| - }
|
| -}
|
|
|