mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:17:14 +00:00
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:
parent
ed28cd5969
commit
728e9b3a9e
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue