blob: d7c3281f056ff48349ad0bc3dfd0a8d450816848 [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.
import '../common.dart';
import '../common/registry.dart' show Registry;
import '../compiler.dart' show Compiler;
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../dart_types.dart';
import '../elements/elements.dart';
import '../enqueue.dart' show Enqueuer;
import '../js/js.dart' as jsAst;
import '../js_emitter/js_emitter.dart'
show CodeEmitterTask, MetadataCollector, Placeholder;
import '../universe/call_structure.dart' show CallStructure;
import '../universe/use.dart' show StaticUse;
import '../universe/world_impact.dart';
import '../util/util.dart';
import 'backend.dart';
/**
* Handles construction of TypeVariable constants needed at runtime.
*/
class TypeVariableHandler {
final Compiler _compiler;
ConstructorElement _typeVariableConstructor;
/**
* 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<jsAst.Expression>> _typeVariables =
new Map<ClassElement, List<jsAst.Expression>>();
/**
* Maps a TypeVariableType to the index pointing to the constant representing
* the corresponding type variable at runtime.
*/
Map<TypeVariableElement, jsAst.Expression> _typeVariableConstants =
new Map<TypeVariableElement, jsAst.Expression>();
/// Impact builder used for the codegen world computation.
// TODO(johnniwinther): Add impact builder for resolution.
final StagedWorldImpactBuilder impactBuilder = new StagedWorldImpactBuilder();
TypeVariableHandler(this._compiler);
ClassElement get _typeVariableClass => _backend.helpers.typeVariableClass;
CodeEmitterTask get _task => _backend.emitter;
MetadataCollector get _metadataCollector => _task.metadataCollector;
JavaScriptBackend get _backend => _compiler.backend;
DiagnosticReporter get reporter => _compiler.reporter;
void onQueueEmpty(Enqueuer enqueuer) {
if (enqueuer.isResolutionQueue) return;
enqueuer.applyImpact(null, impactBuilder.flush());
}
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.resolution);
Link constructors = _typeVariableClass.constructors;
if (constructors.isEmpty && constructors.tail.isEmpty) {
reporter.internalError(_typeVariableClass,
"Class '$_typeVariableClass' should only have one constructor");
}
_typeVariableConstructor = _typeVariableClass.constructors.head;
_backend.enqueueInResolution(_typeVariableConstructor, registry);
_backend.registerInstantiatedType(
_typeVariableClass.rawType, enqueuer, registry);
enqueuer.registerStaticUse(new StaticUse.staticInvoke(
_backend.registerBackendUse(_backend.helpers.createRuntimeType),
CallStructure.ONE_ARG));
_seenClassesWithTypeVariables = true;
}
} else {
if (_backend.isAccessibleByReflection(cls)) {
processTypeVariablesOf(cls);
}
}
}
void processTypeVariablesOf(ClassElement cls) {
// Do not process classes twice.
if (_typeVariables.containsKey(cls)) return;
InterfaceType typeVariableType = _typeVariableClass.thisType;
List<jsAst.Expression> constants = <jsAst.Expression>[];
for (TypeVariableType currentTypeVariable in cls.typeVariables) {
TypeVariableElement typeVariableElement = currentTypeVariable.element;
jsAst.Expression boundIndex =
_metadataCollector.reifyType(typeVariableElement.bound);
ConstantValue boundValue = new SyntheticConstantValue(
SyntheticConstantKind.TYPEVARIABLE_REFERENCE, boundIndex);
ConstantExpression boundExpression =
new SyntheticConstantExpression(boundValue);
ConstantExpression constant = new ConstructedConstantExpression(
_typeVariableConstructor.enclosingClass.thisType,
_typeVariableConstructor,
const CallStructure.unnamed(3), [
new TypeConstantExpression(cls.rawType),
new StringConstantExpression(currentTypeVariable.name),
new SyntheticConstantExpression(boundValue)
]);
_backend.constants.evaluate(constant);
ConstantValue value = _backend.constants.getConstantValue(constant);
_backend.computeImpactForCompileTimeConstant(value, impactBuilder, false);
_backend.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.
*/
jsAst.Expression _reifyTypeVariableConstant(
ConstantValue c, TypeVariableElement variable) {
jsAst.Expression name = _task.constantReference(c);
jsAst.Expression result = _metadataCollector.reifyExpression(name);
if (_typeVariableConstants.containsKey(variable)) {
Placeholder placeholder = _typeVariableConstants[variable];
placeholder.bind(result);
}
_typeVariableConstants[variable] = result;
return result;
}
/**
* 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.
*/
jsAst.Expression reifyTypeVariable(TypeVariableElement variable) {
if (_typeVariableConstants.containsKey(variable)) {
return _typeVariableConstants[variable];
}
Placeholder placeholder =
_metadataCollector.getMetadataPlaceholder(variable);
return _typeVariableConstants[variable] = placeholder;
}
List<jsAst.Expression> typeVariablesOf(ClassElement classElement) {
List<jsAst.Expression> result = _typeVariables[classElement];
if (result == null) {
result = const <jsAst.Expression>[];
}
return result;
}
}