blob: 898e910df67bc4e66886f926ec0dd2fb68e1a677 [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 '../compiler.dart' show Compiler;
import '../constants/expressions.dart';
import '../constants/values.dart';
import '../elements/resolution_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 resolution world computation.
final StagedWorldImpactBuilder impactBuilderForResolution =
new StagedWorldImpactBuilder();
/// Impact builder used for the codegen world computation.
final StagedWorldImpactBuilder impactBuilderForCodegen =
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;
/// Compute the [WorldImpact] for the type variables registered since last
/// flush.
WorldImpact flush({bool forResolution}) {
if (forResolution) {
return impactBuilderForResolution.flush();
} else {
return impactBuilderForCodegen.flush();
}
}
void registerClassWithTypeVariables(ClassElement cls, {bool forResolution}) {
if (forResolution) {
// On first encounter, we have to ensure that the support classes get
// resolved.
if (!_seenClassesWithTypeVariables) {
_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.impactTransformer.registerBackendStaticUse(
impactBuilderForResolution, _typeVariableConstructor);
_backend.impactTransformer.registerBackendInstantiation(
impactBuilderForResolution, _typeVariableClass);
_backend.impactTransformer.registerBackendStaticUse(
impactBuilderForResolution, _backend.helpers.createRuntimeType);
_seenClassesWithTypeVariables = true;
}
} else {
if (_backend.isAccessibleByReflection(cls)) {
processTypeVariablesOf(cls);
}
}
}
void processTypeVariablesOf(ClassElement cls) {
// Do not process classes twice.
if (_typeVariables.containsKey(cls)) return;
ResolutionInterfaceType typeVariableType = _typeVariableClass.thisType;
List<jsAst.Expression> constants = <jsAst.Expression>[];
for (ResolutionTypeVariableType 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, impactBuilderForCodegen, 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;
}
}