blob: fb2425407a0f9f7f60648664ab695545405aad92 [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.
part of js_backend;
/// [ConstantCompilerTask] for compilation of constants for the JavaScript
/// backend.
///
/// Since this task needs to distinguish between frontend and backend constants
/// the actual compilation of the constants is forwarded to a
/// [DartConstantCompiler] for the frontend interpretation of the constants and
/// to a [JavaScriptConstantCompiler] for the backend interpretation.
class JavaScriptConstantTask extends ConstantCompilerTask {
DartConstantCompiler dartConstantCompiler;
JavaScriptConstantCompiler jsConstantCompiler;
JavaScriptConstantTask(Compiler compiler)
: this.dartConstantCompiler = new DartConstantCompiler(compiler),
this.jsConstantCompiler =
new JavaScriptConstantCompiler(compiler),
super(compiler);
String get name => 'ConstantHandler';
ConstantExpression getConstantForVariable(VariableElement element) {
return dartConstantCompiler.getConstantForVariable(element);
}
ConstantExpression compileConstant(VariableElement element) {
return measure(() {
ConstantExpression result = dartConstantCompiler.compileConstant(element);
jsConstantCompiler.compileConstant(element);
return result;
});
}
void compileVariable(VariableElement element) {
measure(() {
jsConstantCompiler.compileVariable(element);
});
}
ConstantExpression compileNode(Node node, TreeElements elements) {
return measure(() {
ConstantExpression result =
dartConstantCompiler.compileNode(node, elements);
jsConstantCompiler.compileNode(node, elements);
return result;
});
}
ConstantExpression compileMetadata(MetadataAnnotation metadata,
Node node,
TreeElements elements) {
return measure(() {
ConstantExpression constant =
dartConstantCompiler.compileMetadata(metadata, node, elements);
jsConstantCompiler.compileMetadata(metadata, node, elements);
return constant;
});
}
}
/**
* The [JavaScriptConstantCompiler] is used to keep track of compile-time
* constants, initializations of global and static fields, and default values of
* optional parameters for the JavaScript interpretation of constants.
*/
class JavaScriptConstantCompiler extends ConstantCompilerBase
implements BackendConstantEnvironment {
/** Set of all registered compiled constants. */
final Set<ConstantValue> compiledConstants = new Set<ConstantValue>();
// TODO(johnniwinther): Move this to the backend constant handler.
/** Caches the statics where the initial value cannot be eagerly compiled. */
final Set<VariableElement> lazyStatics = new Set<VariableElement>();
// Constants computed for constant expressions.
final Map<Node, ConstantExpression> nodeConstantMap =
new Map<Node, ConstantExpression>();
// Constants computed for metadata.
final Map<MetadataAnnotation, ConstantExpression> metadataConstantMap =
new Map<MetadataAnnotation, ConstantExpression>();
JavaScriptConstantCompiler(Compiler compiler)
: super(compiler, JAVA_SCRIPT_CONSTANT_SYSTEM);
ConstantExpression compileVariableWithDefinitions(VariableElement element,
TreeElements definitions,
{bool isConst: false}) {
if (!isConst && lazyStatics.contains(element)) {
return null;
}
ConstantExpression value = super.compileVariableWithDefinitions(
element, definitions, isConst: isConst);
if (!isConst && value == null) {
lazyStatics.add(element);
}
return value;
}
void addCompileTimeConstantForEmission(ConstantValue constant) {
compiledConstants.add(constant);
}
/**
* Returns an [Iterable] of static non final fields that need to be
* initialized. The fields list must be evaluated in order since they might
* depend on each other.
*/
Iterable<VariableElement> getStaticNonFinalFieldsForEmission() {
return initialVariableValues.keys.where((element) {
return element.kind == ElementKind.FIELD &&
!element.isInstanceMember &&
!element.modifiers.isFinal &&
// The const fields are all either emitted elsewhere or inlined.
!element.modifiers.isConst;
});
}
List<VariableElement> getLazilyInitializedFieldsForEmission() {
return new List<VariableElement>.from(lazyStatics);
}
/**
* Returns a list of constants topologically sorted so that dependencies
* appear before the dependent constant. [preSortCompare] is a comparator
* function that gives the constants a consistent order prior to the
* topological sort which gives the constants an ordering that is less
* sensitive to perturbations in the source code.
*/
List<ConstantValue> getConstantsForEmission([preSortCompare]) {
// We must emit dependencies before their uses.
Set<ConstantValue> seenConstants = new Set<ConstantValue>();
List<ConstantValue> result = new List<ConstantValue>();
void addConstant(ConstantValue constant) {
if (!seenConstants.contains(constant)) {
constant.getDependencies().forEach(addConstant);
assert(!seenConstants.contains(constant));
result.add(constant);
seenConstants.add(constant);
}
}
List<ConstantValue> sorted = compiledConstants.toList();
if (preSortCompare != null) {
sorted.sort(preSortCompare);
}
sorted.forEach(addConstant);
return result;
}
ConstantExpression getInitialValueFor(VariableElement element) {
ConstantExpression initialValue =
initialVariableValues[element.declaration];
if (initialValue == null) {
compiler.internalError(element, "No initial value for given element.");
}
return initialValue;
}
ConstantExpression compileNode(Node node, TreeElements elements) {
return compileNodeWithDefinitions(node, elements);
}
ConstantExpression compileNodeWithDefinitions(Node node,
TreeElements definitions,
{bool isConst: true}) {
ConstantExpression constant = nodeConstantMap[node];
if (constant != null) {
return constant;
}
constant =
super.compileNodeWithDefinitions(node, definitions, isConst: isConst);
if (constant != null) {
nodeConstantMap[node] = constant;
}
return constant;
}
ConstantExpression getConstantForNode(Node node, TreeElements definitions) {
ConstantExpression constant = nodeConstantMap[node];
if (constant != null) {
return constant;
}
return definitions.getConstant(node);
}
ConstantExpression getConstantForMetadata(MetadataAnnotation metadata) {
return metadataConstantMap[metadata];
}
ConstantExpression compileMetadata(MetadataAnnotation metadata,
Node node,
TreeElements elements) {
ConstantExpression constant =
super.compileMetadata(metadata, node, elements);
metadataConstantMap[metadata] = constant;
return constant;
}
ConstantExpression createTypeConstant(TypeDeclarationElement element) {
DartType elementType = element.rawType;
DartType constantType =
compiler.backend.typeImplementation.computeType(compiler);
return new TypeConstantExpression(
new TypeConstantValue(elementType, constantType), elementType);
}
void forgetElement(Element element) {
super.forgetElement(element);
element.accept(new ForgetConstantElementVisitor(this));
if (element is AstElement && element.hasNode) {
element.node.accept(new ForgetConstantNodeVisitor(this));
}
}
}
class ForgetConstantElementVisitor extends ElementVisitor {
final JavaScriptConstantCompiler constants;
ForgetConstantElementVisitor(this.constants);
void visitElement(Element e) {
for (MetadataAnnotation data in e.metadata) {
constants.metadataConstantMap.remove(data);
if (data.hasNode) {
data.node.accept(new ForgetConstantNodeVisitor(constants));
}
}
}
void visitFunctionElement(FunctionElement e) {
super.visitFunctionElement(e);
if (e.hasFunctionSignature) {
e.functionSignature.forEachParameter(this.visit);
}
}
}
class ForgetConstantNodeVisitor extends Visitor {
final JavaScriptConstantCompiler constants;
ForgetConstantNodeVisitor(this.constants);
void visitNode(Node node) {
node.visitChildren(this);
constants.nodeConstantMap.remove(node);
// TODO(ahe): This doesn't belong here. Rename this class and generalize.
var closureClassMap =
constants.compiler.closureToClassMapper.closureMappingCache
.remove(node);
if (closureClassMap != null) {
closureClassMap.removeMyselfFrom(
constants.compiler.enqueuer.codegen.universe);
}
}
}