blob: 60bf70fdc6c404500e619b743467f09c5efed378 [file] [log] [blame]
// Copyright (c) 2013, 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;
/**
* Handles construction of TypeVariable constants needed at runtime.
*/
class TypeVariableHandler {
JavaScriptBackend backend;
FunctionElement typeVariableConstructor;
CompileTimeConstantEvaluator evaluator;
/**
* Contains all instantiated classes that have type variables and are needed
* for reflection.
*/
List<ClassElement> typeVariableClasses = new List<ClassElement>();
/**
* Maps a class element to a list with indices that point to type variables
* constants for each of the class' type variables.
*/
Map<ClassElement, List<int>> typeVariables =
new Map<ClassElement, List<int>>();
/**
* Maps a TypeVariableType to the index pointing to the constant representing
* the corresponding type variable at runtime.
*/
Map<TypeVariableElement, int> typeVariableConstants =
new Map<TypeVariableElement, int>();
TypeVariableHandler(this.backend);
ClassElement get typeVariableClass => backend.typeVariableClass;
CodeEmitterTask get task => backend.emitter;
MetadataEmitter get emitter => task.oldEmitter.metadataEmitter;
Compiler get compiler => backend.compiler;
void registerClassWithTypeVariables(ClassElement cls) {
if (typeVariableClasses != null) {
typeVariableClasses.add(cls);
}
}
void processTypeVariablesOf(ClassElement cls) {
//TODO(zarah): Running through all the members is suboptimal. Change this
// as part of marking elements for reflection.
bool hasMemberNeededForReflection(ClassElement cls) {
bool result = false;
cls.implementation.forEachMember((ClassElement cls, Element member) {
result = result || backend.referencedFromMirrorSystem(member);
});
return result;
}
if (!backend.referencedFromMirrorSystem(cls) &&
!hasMemberNeededForReflection(cls)) {
return;
}
InterfaceType typeVariableType = typeVariableClass.thisType;
List<int> constants = <int>[];
for (TypeVariableType currentTypeVariable in cls.typeVariables) {
TypeVariableElement typeVariableElement = currentTypeVariable.element;
AstConstant wrapConstant(ConstantExpression constant) {
return new AstConstant(typeVariableElement,
typeVariableElement.node,
constant);
}
ConstantExpression name = new PrimitiveConstantExpression(
backend.constantSystem.createString(
new DartString.literal(currentTypeVariable.name)));
ConstantExpression bound = new PrimitiveConstantExpression(
backend.constantSystem.createInt(
emitter.reifyType(typeVariableElement.bound)));
ConstantExpression type = backend.constants.createTypeConstant(cls);
List<AstConstant> arguments =
[wrapConstant(type), wrapConstant(name), wrapConstant(bound)];
// TODO(johnniwinther): Support a less front-end specific creation of
// constructed constants.
AstConstant constant =
CompileTimeConstantEvaluator.makeConstructedConstant(
compiler,
backend.constants,
typeVariableElement,
typeVariableElement.node,
typeVariableType,
typeVariableConstructor,
new Selector.callConstructor('', null, 3),
arguments,
arguments);
ConstantValue value = constant.value;
backend.registerCompileTimeConstant(value, compiler.globalDependencies);
backend.constants.addCompileTimeConstantForEmission(value);
constants.add(
reifyTypeVariableConstant(value, currentTypeVariable.element));
}
typeVariables[cls] = constants;
}
void onTreeShakingDisabled(Enqueuer enqueuer) {
if (enqueuer.isResolutionQueue) {
backend.enqueueClass(
enqueuer, typeVariableClass, compiler.globalDependencies);
typeVariableClass.ensureResolved(compiler);
Link constructors = typeVariableClass.constructors;
if (constructors.isEmpty && constructors.tail.isEmpty) {
compiler.internalError(typeVariableClass,
"Class '$typeVariableClass' should only have one constructor");
}
typeVariableConstructor = typeVariableClass.constructors.head;
backend.enqueueInResolution(typeVariableConstructor,
compiler.globalDependencies);
enqueuer.registerInstantiatedType(typeVariableClass.rawType,
compiler.globalDependencies);
enqueuer.registerStaticUse(backend.getCreateRuntimeType());
} else if (typeVariableClasses != null) {
List<ClassElement> worklist = typeVariableClasses;
typeVariableClasses = null;
worklist.forEach((cls) => processTypeVariablesOf(cls));
}
}
/**
* Adds [c] to [emitter.globalMetadata] and returns the index pointing to
* the entry.
*
* If the corresponding type variable has already been encountered an
* entry in the list has already been reserved and the constant is added
* there, otherwise a new entry for [c] is created.
*/
int reifyTypeVariableConstant(ConstantValue c, TypeVariableElement variable) {
String name = jsAst.prettyPrint(task.constantReference(c),
compiler).getText();
int index;
if (typeVariableConstants.containsKey(variable)) {
index = typeVariableConstants[variable];
emitter.globalMetadata[index] = name;
} else {
index = emitter.addGlobalMetadata(name);
typeVariableConstants[variable] = index;
}
return index;
}
/**
* Returns the index pointing to the constant in [emitter.globalMetadata]
* representing this type variable.
*
* If the constant has not yet been constructed, an entry is allocated in
* the global metadata list and the index pointing to this entry is returned.
* When the corresponding constant is constructed later,
* [reifyTypeVariableConstant] will be called and the constant will be added
* on the allocated entry.
*/
int reifyTypeVariable(TypeVariableElement variable) {
if (typeVariableConstants.containsKey(variable)) {
return typeVariableConstants[variable];
}
// TODO(15613): Remove quotes.
emitter.globalMetadata.add('"Placeholder for ${variable}"');
return typeVariableConstants[variable] = emitter.globalMetadata.length - 1;
}
List<int> typeVariablesOf(ClassElement classElement) {
List<int> result = typeVariables[classElement];
if (result == null) {
result = const <int>[];
}
return result;
}
}