Create a task in the new task model to compute constant dependencies.

This is the first of several planned tasks for constant evaluation.

R=brianwilkerson@google.com, scheglov@google.com

Review URL: https://codereview.chromium.org//1131953004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45624 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
paulberry@google.com 2015-05-08 00:36:45 +00:00
parent ed28cd5969
commit 728e9b3a9e
6 changed files with 198 additions and 86 deletions

View file

@ -22,6 +22,11 @@ import 'source.dart' show Source;
import 'utilities_collection.dart';
import 'utilities_dart.dart' show ParameterKind;
/**
* Callback used by [ReferenceFinder] to report that a dependency was found.
*/
typedef void ReferenceFinderCallback(Element dependency);
/**
* The state of an object representing a boolean value.
*/
@ -186,7 +191,7 @@ class ConstantAstCloner extends AstCloner {
}
/**
* Helper class encapsulating the methods for evaluating constant instance
* Helper class encapsulating the methods for evaluating constants and
* constant instance creation expressions.
*/
class ConstantEvaluationEngine {
@ -302,6 +307,74 @@ class ConstantEvaluationEngine {
return isValidPublicSymbol(name);
}
/**
* Determine which constant elements need to have their values computed
* prior to computing the value of [element], and report them using
* [callback].
*/
void computeDependencies(Element element, ReferenceFinderCallback callback) {
ReferenceFinder referenceFinder = new ReferenceFinder(callback);
if (element is ParameterElement) {
if (element.initializer != null) {
Expression defaultValue =
(element as ConstVariableElement).constantInitializer;
if (defaultValue != null) {
defaultValue.accept(referenceFinder);
}
}
} else if (element is PotentiallyConstVariableElement) {
element.constantInitializer.accept(referenceFinder);
} else if (element is ConstructorElementImpl) {
element.isCycleFree = false;
ConstructorElement redirectedConstructor =
getConstRedirectedConstructor(element);
if (redirectedConstructor != null) {
ConstructorElement redirectedConstructorBase =
ConstantEvaluationEngine._getConstructorBase(redirectedConstructor);
callback(redirectedConstructorBase);
return;
}
bool superInvocationFound = false;
List<ConstructorInitializer> initializers = element.constantInitializers;
for (ConstructorInitializer initializer in initializers) {
if (initializer is SuperConstructorInvocation) {
superInvocationFound = true;
}
initializer.accept(referenceFinder);
}
if (!superInvocationFound) {
// No explicit superconstructor invocation found, so we need to
// manually insert a reference to the implicit superconstructor.
InterfaceType superclass =
(element.returnType as InterfaceType).superclass;
if (superclass != null && !superclass.isObject) {
ConstructorElement unnamedConstructor = ConstantEvaluationEngine
._getConstructorBase(superclass.element.unnamedConstructor);
if (unnamedConstructor != null) {
callback(unnamedConstructor);
}
}
}
for (FieldElement field in element.enclosingElement.fields) {
// Note: non-static const isn't allowed but we handle it anyway so that
// we won't be confused by incorrect code.
if ((field.isFinal || field.isConst) &&
!field.isStatic &&
field.initializer != null) {
callback(field);
}
}
for (ParameterElement parameterElement in element.parameters) {
callback(parameterElement);
}
} else {
// Should not happen.
assert(false);
AnalysisEngine.instance.logger.logError(
"Constant value computer trying to compute the value of a node of type ${element.runtimeType}");
}
}
/**
* Evaluate a call to fromEnvironment() on the bool, int, or String class. The
* [environmentValue] is the value fetched from the environment. The
@ -1005,73 +1078,9 @@ class ConstantValueComputer {
_annotations = _constantFinder.annotations;
for (Element element in _constantsToCompute) {
referenceGraph.addNode(element);
if (element is ParameterElement) {
if (element.initializer != null) {
Expression defaultValue =
(element as ConstVariableElement).constantInitializer;
if (defaultValue != null) {
ReferenceFinder parameterReferenceFinder =
new ReferenceFinder(element, referenceGraph);
defaultValue.accept(parameterReferenceFinder);
}
}
} else if (element is PotentiallyConstVariableElement) {
ReferenceFinder referenceFinder =
new ReferenceFinder(element, referenceGraph);
element.constantInitializer.accept(referenceFinder);
} else if (element is ConstructorElementImpl) {
element.isCycleFree = false;
ConstructorElement redirectedConstructor =
evaluationEngine.getConstRedirectedConstructor(element);
if (redirectedConstructor != null) {
ConstructorElement redirectedConstructorBase =
ConstantEvaluationEngine
._getConstructorBase(redirectedConstructor);
referenceGraph.addEdge(element, redirectedConstructorBase);
continue;
}
ReferenceFinder referenceFinder =
new ReferenceFinder(element, referenceGraph);
bool superInvocationFound = false;
List<ConstructorInitializer> initializers =
element.constantInitializers;
for (ConstructorInitializer initializer in initializers) {
if (initializer is SuperConstructorInvocation) {
superInvocationFound = true;
}
initializer.accept(referenceFinder);
}
if (!superInvocationFound) {
// No explicit superconstructor invocation found, so we need to
// manually insert a reference to the implicit superconstructor.
InterfaceType superclass =
(element.returnType as InterfaceType).superclass;
if (superclass != null && !superclass.isObject) {
ConstructorElement unnamedConstructor = ConstantEvaluationEngine
._getConstructorBase(superclass.element.unnamedConstructor);
if (unnamedConstructor != null) {
referenceGraph.addEdge(element, unnamedConstructor);
}
}
}
for (FieldElement field in element.enclosingElement.fields) {
// Note: non-static const isn't allowed but we handle it anyway so that
// we won't be confused by incorrect code.
if ((field.isFinal || field.isConst) &&
!field.isStatic &&
field.initializer != null) {
referenceGraph.addEdge(element, field);
}
}
for (ParameterElement parameterElement in element.parameters) {
referenceGraph.addEdge(element, parameterElement);
}
} else {
// Should not happen.
assert(false);
AnalysisEngine.instance.logger.logError(
"Constant value computer trying to compute the value of a node of type ${element.runtimeType}");
}
evaluationEngine.computeDependencies(element, (Element dependency) {
referenceGraph.addEdge(element, dependency);
});
}
List<List<Element>> topologicalSort =
referenceGraph.computeTopologicalSort();
@ -4865,25 +4874,17 @@ class NumState extends InstanceState {
*/
class ReferenceFinder extends RecursiveAstVisitor<Object> {
/**
* The element representing the construct that will be visited.
* The callback which should be used to report any dependencies that were
* found.
*/
final Element _source;
/**
* A graph in which the nodes are the constant variables and the edges are
* from each variable to the other constant variables that are referenced in
* the head's initializer.
*/
final DirectedGraph<Element> _referenceGraph;
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 [source] is the element representing the variable whose initializer
* will be visited. The [referenceGraph] is a graph recording which variables
* (heads) reference which other variables (tails) in their initializers.
* The [_callback] will be invoked for every dependency found.
*/
ReferenceFinder(this._source, this._referenceGraph);
ReferenceFinder(this._callback);
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
@ -4891,7 +4892,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
ConstructorElement constructor =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (constructor != null) {
_referenceGraph.addEdge(_source, constructor);
_callback(constructor);
}
}
return super.visitInstanceCreationExpression(node);
@ -4904,7 +4905,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
ConstructorElement target =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (target != null && target.isConst) {
_referenceGraph.addEdge(_source, target);
_callback(target);
}
return null;
}
@ -4917,7 +4918,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
}
if (element is VariableElement) {
if (element.isConst) {
_referenceGraph.addEdge(_source, element);
_callback(element);
}
}
return null;
@ -4929,7 +4930,7 @@ class ReferenceFinder extends RecursiveAstVisitor<Object> {
ConstructorElement constructor =
ConstantEvaluationEngine._getConstructorBase(node.staticElement);
if (constructor != null && constructor.isConst) {
_referenceGraph.addEdge(_source, constructor);
_callback(constructor);
}
return null;
}

View file

@ -72,6 +72,7 @@ class EnginePlugin implements Plugin {
registerExtension(taskId, BuildPublicNamespaceTask.DESCRIPTOR);
registerExtension(taskId, BuildSourceClosuresTask.DESCRIPTOR);
registerExtension(taskId, BuildTypeProviderTask.DESCRIPTOR);
registerExtension(taskId, ComputeConstantDependenciesTask.DESCRIPTOR);
registerExtension(taskId, ContainingLibrariesTask.DESCRIPTOR);
registerExtension(taskId, DartErrorsTask.DESCRIPTOR);
registerExtension(taskId, GatherUsedImportedElementsTask.DESCRIPTOR);

View file

@ -8,6 +8,7 @@ import 'dart:collection';
import 'dart:math' as math;
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/constant.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask;
import 'package:analyzer/src/generated/error.dart';
@ -1639,6 +1640,79 @@ class BuildTypeProviderTask extends SourceBasedAnalysisTask {
}
}
/**
* A task that computes [CONSTANT_DEPENDENCIES] for a constant.
*/
class ComputeConstantDependenciesTask extends AnalysisTask {
/**
* The name of the [RESOLVED_UNIT] input.
*/
static const String UNIT_INPUT = 'UNIT_INPUT';
static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
'ComputeConstantDependenciesTask', createTask, buildInputs,
<ResultDescriptor>[CONSTANT_DEPENDENCIES]);
ComputeConstantDependenciesTask(
InternalAnalysisContext context, AnalysisTarget target)
: super(context, target);
@override
String get description {
Source source = target.source;
String sourceName = source == null ? '<unknown source>' : source.fullName;
return '${descriptor.name} for element $target in source $sourceName';
}
@override
TaskDescriptor get descriptor => DESCRIPTOR;
@override
void internalPerform() {
//
// Prepare inputs.
//
// Note: UNIT_INPUT is not needed. It is merely a bookkeeping dependency
// to ensure that resolution has occurred before we attempt to determine
// constant dependencies.
//
Element element = target;
AnalysisContext context = element.context;
TypeProvider typeProvider = context.typeProvider;
//
// Compute dependencies.
//
List<Element> dependencies = <Element>[];
new ConstantEvaluationEngine(typeProvider, context.declaredVariables)
.computeDependencies(element, dependencies.add);
//
// Record outputs.
//
outputs[CONSTANT_DEPENDENCIES] = dependencies;
}
/**
* Return a map from the names of the inputs of this kind of task to the task
* input descriptors describing those inputs for a task with the
* given [target].
*/
static Map<String, TaskInput> buildInputs(Element target) {
return <String, TaskInput>{
UNIT_INPUT: RESOLVED_UNIT
.of(new LibrarySpecificUnit(target.library.source, target.source))
};
}
/**
* Create a [ResolveReferencesTask] based on the given [target] in
* the given [context].
*/
static ComputeConstantDependenciesTask createTask(
AnalysisContext context, AnalysisTarget target) {
return new ComputeConstantDependenciesTask(context, target);
}
}
/**
* A task that computes a list of the libraries containing the target source.
*/

View file

@ -13,6 +13,16 @@ import 'package:analyzer/src/generated/utilities_general.dart';
import 'package:analyzer/src/task/dart.dart';
import 'package:analyzer/task/model.dart';
/**
* The list of [Element]s which a given constant element depends on.
*
* The result is only available for targets representing a constant [Element]
* (i.e. a constant variable declaration, a constant constructor, or a
* parameter element with a default value).
*/
final ListResultDescriptor<Element> CONSTANT_DEPENDENCIES =
new ListResultDescriptor<Element>('CONSTANT_DEPENDENCIES', <Element>[]);
/**
* The analysis errors associated with a target.
*

View file

@ -8052,7 +8052,9 @@ class ReferenceFinderTest extends EngineTestCase {
expect(tails.first, same(tail));
}
ReferenceFinder _createReferenceFinder(Element source) =>
new ReferenceFinder(source, _referenceGraph);
new ReferenceFinder((Element dependency) {
_referenceGraph.addEdge(source, dependency);
});
SuperConstructorInvocation _makeTailSuperConstructorInvocation(
String name, bool isConst) {
List<ConstructorInitializer> initializers =

View file

@ -33,6 +33,7 @@ main() {
runReflectiveTests(BuildLibraryElementTaskTest);
runReflectiveTests(BuildPublicNamespaceTaskTest);
runReflectiveTests(BuildTypeProviderTaskTest);
runReflectiveTests(ComputeConstantDependenciesTaskTest);
runReflectiveTests(ContainingLibrariesTaskTest);
runReflectiveTests(DartErrorsTaskTest);
runReflectiveTests(GatherUsedImportedElementsTaskTest);
@ -1186,6 +1187,29 @@ class BuildTypeProviderTaskTest extends _AbstractDartTaskTest {
}
}
@reflectiveTest
class ComputeConstantDependenciesTaskTest extends _AbstractDartTaskTest {
test_perform() {
Source source = newSource('/test.dart', '''
const x = y;
const y = 1;
''');
// First compute the library element for the source.
_computeResult(source, LIBRARY_ELEMENT1);
LibraryElement libraryElement = outputs[LIBRARY_ELEMENT1];
// Find the elements for the constants x and y.
List<PropertyAccessorElement> accessors =
libraryElement.definingCompilationUnit.accessors;
Element x = accessors.firstWhere((PropertyAccessorElement accessor) =>
accessor.isGetter && accessor.name == 'x').variable;
Element y = accessors.firstWhere((PropertyAccessorElement accessor) =>
accessor.isGetter && accessor.name == 'y').variable;
// Now compute the dependencies for x, and check that it is the list [y].
_computeResult(x, CONSTANT_DEPENDENCIES);
expect(outputs[CONSTANT_DEPENDENCIES], [y]);
}
}
@reflectiveTest
class ContainingLibrariesTaskTest extends _AbstractDartTaskTest {
test_buildInputs() {