blob: 6344a1d99349b81c553e2bb1cbbefb22ca6a7395 [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 {
final Compiler _compiler;
FunctionElement _typeVariableConstructor;
CompileTimeConstantEvaluator _evaluator;
/**
* Set to 'true' on first encounter of a class with type variables.
*/
bool _seenClassesWithTypeVariables = false;
/**
* 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._compiler);
ClassElement get _typeVariableClass => _backend.typeVariableClass;
CodeEmitterTask get _task => _backend.emitter;
MetadataCollector get _metadataCollector => _task.metadataCollector;
JavaScriptBackend get _backend => _compiler.backend;
void registerClassWithTypeVariables(ClassElement cls, Enqueuer enqueuer,
Registry registry) {
if (enqueuer.isResolutionQueue) {
// On first encounter, we have to ensure that the support classes get
// resolved.
if (!_seenClassesWithTypeVariables) {
_backend.enqueueClass(
enqueuer, _typeVariableClass, registry);
_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, registry);
enqueuer.registerInstantiatedType(_typeVariableClass.rawType,
registry);
enqueuer.registerStaticUse(_backend.getCreateRuntimeType());
_seenClassesWithTypeVariables = true;
}
} else {
if (_backend.isAccessibleByReflection(cls)) {
_processTypeVariablesOf(cls);
}
}
}
void _processTypeVariablesOf(ClassElement cls) {
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 StringConstantExpression(
currentTypeVariable.name,
_backend.constantSystem.createString(
new DartString.literal(currentTypeVariable.name)));
int boundIndex = _metadataCollector.reifyType(typeVariableElement.bound);
ConstantExpression bound = new IntConstantExpression(
boundIndex,
_backend.constantSystem.createInt(boundIndex));
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,
typeVariableType,
_typeVariableConstructor,
const CallStructure.unnamed(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;
}
/**
* Adds [c] to [emitter.metadataCollector] 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];
_metadataCollector.globalMetadata[index] = name;
} else {
index = _metadataCollector.addGlobalMetadata(name);
_typeVariableConstants[variable] = index;
}
return index;
}
/**
* Returns the index pointing to the constant in [emitter.metadataCollector]
* 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.
_metadataCollector.globalMetadata.add('"Placeholder for ${variable}"');
return _typeVariableConstants[variable] =
_metadataCollector.globalMetadata.length - 1;
}
List<int> typeVariablesOf(ClassElement classElement) {
List<int> result = _typeVariables[classElement];
if (result == null) {
result = const <int>[];
}
return result;
}
}