| // Copyright (c) 2014, 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 analyzer.src.dart.constant.utilities; |
| |
| import 'dart:collection'; |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/standard_resolution_map.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/ast/utilities.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/handle.dart' |
| show ConstructorElementHandle; |
| import 'package:analyzer/src/dart/element/member.dart'; |
| import 'package:analyzer/src/task/dart.dart'; |
| |
| ConstructorElementImpl getConstructorImpl(ConstructorElement constructor) { |
| while (constructor is ConstructorMember) { |
| constructor = (constructor as ConstructorMember).baseElement; |
| } |
| if (constructor is ConstructorElementHandle) { |
| constructor = (constructor as ConstructorElementHandle).actualElement; |
| } |
| return constructor; |
| } |
| |
| /** |
| * Callback used by [ReferenceFinder] to report that a dependency was found. |
| */ |
| typedef void ReferenceFinderCallback(ConstantEvaluationTarget dependency); |
| |
| /** |
| * An [AstCloner] that copies the necessary information from the AST to allow |
| * constants to be evaluated. |
| */ |
| class ConstantAstCloner extends AstCloner { |
| ConstantAstCloner() : super(true); |
| |
| @override |
| Annotation visitAnnotation(Annotation node) { |
| Annotation annotation = super.visitAnnotation(node); |
| annotation.element = node.element; |
| return annotation; |
| } |
| |
| @override |
| ConstructorName visitConstructorName(ConstructorName node) { |
| ConstructorName name = super.visitConstructorName(node); |
| name.staticElement = node.staticElement; |
| return name; |
| } |
| |
| @override |
| FunctionExpression visitFunctionExpression(FunctionExpression node) { |
| FunctionExpression expression = super.visitFunctionExpression(node); |
| expression.element = node.element; |
| return expression; |
| } |
| |
| @override |
| InstanceCreationExpression visitInstanceCreationExpression( |
| InstanceCreationExpression node) { |
| InstanceCreationExpression expression = |
| super.visitInstanceCreationExpression(node); |
| expression.staticElement = node.staticElement; |
| return expression; |
| } |
| |
| @override |
| RedirectingConstructorInvocation visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node) { |
| RedirectingConstructorInvocation invocation = |
| super.visitRedirectingConstructorInvocation(node); |
| invocation.staticElement = node.staticElement; |
| return invocation; |
| } |
| |
| @override |
| SimpleIdentifier visitSimpleIdentifier(SimpleIdentifier node) { |
| SimpleIdentifier identifier = super.visitSimpleIdentifier(node); |
| identifier.staticElement = node.staticElement; |
| return identifier; |
| } |
| |
| @override |
| SuperConstructorInvocation visitSuperConstructorInvocation( |
| SuperConstructorInvocation node) { |
| SuperConstructorInvocation invocation = |
| super.visitSuperConstructorInvocation(node); |
| invocation.staticElement = node.staticElement; |
| return invocation; |
| } |
| |
| @override |
| TypeName visitTypeName(TypeName node) { |
| TypeName typeName = super.visitTypeName(node); |
| typeName.type = node.type; |
| return typeName; |
| } |
| } |
| |
| /** |
| * A visitor used to traverse the AST structures of all of the compilation units |
| * being resolved and build the full set of dependencies for all constant |
| * expressions. |
| */ |
| class ConstantExpressionsDependenciesFinder extends RecursiveAstVisitor { |
| /** |
| * The constants whose values need to be computed. |
| */ |
| HashSet<ConstantEvaluationTarget> dependencies = |
| new HashSet<ConstantEvaluationTarget>(); |
| |
| @override |
| void visitInstanceCreationExpression(InstanceCreationExpression node) { |
| if (node.isConst) { |
| _find(node); |
| } else { |
| super.visitInstanceCreationExpression(node); |
| } |
| } |
| |
| @override |
| void visitListLiteral(ListLiteral node) { |
| if (node.constKeyword != null) { |
| _find(node); |
| } else { |
| super.visitListLiteral(node); |
| } |
| } |
| |
| @override |
| void visitMapLiteral(MapLiteral node) { |
| if (node.constKeyword != null) { |
| _find(node); |
| } else { |
| super.visitMapLiteral(node); |
| } |
| } |
| |
| @override |
| void visitSwitchCase(SwitchCase node) { |
| _find(node.expression); |
| node.statements.accept(this); |
| } |
| |
| void _find(Expression node) { |
| if (node != null) { |
| ReferenceFinder referenceFinder = new ReferenceFinder(dependencies.add); |
| node.accept(referenceFinder); |
| } |
| } |
| } |
| |
| /** |
| * A visitor used to traverse the AST structures of all of the compilation units |
| * being resolved and build tables of the constant variables, constant |
| * constructors, constant constructor invocations, and annotations found in |
| * those compilation units. |
| */ |
| class ConstantFinder extends RecursiveAstVisitor<Object> { |
| /** |
| * The elements and AST nodes whose constant values need to be computed. |
| */ |
| List<ConstantEvaluationTarget> constantsToCompute = |
| <ConstantEvaluationTarget>[]; |
| |
| /** |
| * A flag indicating whether instance variables marked as "final" should be |
| * treated as "const". |
| */ |
| bool treatFinalInstanceVarAsConst = false; |
| |
| @override |
| Object visitAnnotation(Annotation node) { |
| super.visitAnnotation(node); |
| ElementAnnotation elementAnnotation = node.elementAnnotation; |
| if (elementAnnotation == null) { |
| // Analyzer ignores annotations on "part of" directives and on enum |
| // constant declarations. |
| assert(node.parent is PartOfDirective || |
| node.parent is EnumConstantDeclaration); |
| } else { |
| constantsToCompute.add(elementAnnotation); |
| } |
| return null; |
| } |
| |
| @override |
| Object visitClassDeclaration(ClassDeclaration node) { |
| bool prevTreatFinalInstanceVarAsConst = treatFinalInstanceVarAsConst; |
| if (resolutionMap |
| .elementDeclaredByClassDeclaration(node) |
| .constructors |
| .any((ConstructorElement e) => e.isConst)) { |
| // Instance vars marked "final" need to be included in the dependency |
| // graph, since constant constructors implicitly use the values in their |
| // initializers. |
| treatFinalInstanceVarAsConst = true; |
| } |
| try { |
| return super.visitClassDeclaration(node); |
| } finally { |
| treatFinalInstanceVarAsConst = prevTreatFinalInstanceVarAsConst; |
| } |
| } |
| |
| @override |
| Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| super.visitConstructorDeclaration(node); |
| if (node.constKeyword != null) { |
| ConstructorElement element = node.element; |
| if (element != null) { |
| constantsToCompute.add(element); |
| constantsToCompute.addAll(element.parameters); |
| } |
| } |
| return null; |
| } |
| |
| @override |
| Object visitDefaultFormalParameter(DefaultFormalParameter node) { |
| super.visitDefaultFormalParameter(node); |
| Expression defaultValue = node.defaultValue; |
| if (defaultValue != null && node.element != null) { |
| constantsToCompute |
| .add(resolutionMap.elementDeclaredByFormalParameter(node)); |
| } |
| return null; |
| } |
| |
| @override |
| Object visitVariableDeclaration(VariableDeclaration node) { |
| super.visitVariableDeclaration(node); |
| Expression initializer = node.initializer; |
| VariableElement element = node.element; |
| if (initializer != null && |
| (node.isConst || |
| treatFinalInstanceVarAsConst && |
| element is FieldElement && |
| node.isFinal && |
| !element.isStatic)) { |
| if (element != null) { |
| constantsToCompute.add(element); |
| } |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * An object used to add reference information for a given variable to the |
| * bi-directional mapping used to order the evaluation of constants. |
| */ |
| class ReferenceFinder extends RecursiveAstVisitor<Object> { |
| /** |
| * The callback which should be used to report any dependencies that were |
| * found. |
| */ |
| final ReferenceFinderCallback _callback; |
| |
| /** |
| * Initialize a newly created reference finder to find references from a given |
| * variable to other variables and to add those references to the given graph. |
| * The [_callback] will be invoked for every dependency found. |
| */ |
| ReferenceFinder(this._callback); |
| |
| @override |
| Object visitInstanceCreationExpression(InstanceCreationExpression node) { |
| if (node.isConst) { |
| ConstructorElement constructor = getConstructorImpl(node.staticElement); |
| if (constructor != null) { |
| _callback(constructor); |
| } |
| } |
| return super.visitInstanceCreationExpression(node); |
| } |
| |
| @override |
| Object visitLabel(Label node) { |
| // We are visiting the "label" part of a named expression in a function |
| // call (presumably a constructor call), e.g. "const C(label: ...)". We |
| // don't want to visit the SimpleIdentifier for the label because that's a |
| // reference to a function parameter that needs to be filled in; it's not a |
| // constant whose value we depend on. |
| return null; |
| } |
| |
| @override |
| Object visitRedirectingConstructorInvocation( |
| RedirectingConstructorInvocation node) { |
| super.visitRedirectingConstructorInvocation(node); |
| ConstructorElement target = getConstructorImpl(node.staticElement); |
| if (target != null) { |
| _callback(target); |
| } |
| return null; |
| } |
| |
| @override |
| Object visitSimpleIdentifier(SimpleIdentifier node) { |
| Element staticElement = node.staticElement; |
| Element element = staticElement is PropertyAccessorElement |
| ? staticElement.variable |
| : staticElement; |
| if (element is VariableElement && element.isConst) { |
| _callback(element); |
| } |
| return null; |
| } |
| |
| @override |
| Object visitSuperConstructorInvocation(SuperConstructorInvocation node) { |
| super.visitSuperConstructorInvocation(node); |
| ConstructorElement constructor = getConstructorImpl(node.staticElement); |
| if (constructor != null) { |
| _callback(constructor); |
| } |
| return null; |
| } |
| } |