blob: b097c0eb9d160776f0be6f92bac2d53659612072 [file] [log] [blame]
// 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;
}
}