| // 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_elements.dart'; |
| import '../constants/expressions.dart'; |
| import '../constants/values.dart'; |
| import '../deferred_load.dart' show OutputUnit; |
| import '../elements/entities.dart'; |
| import '../elements/types.dart'; |
| 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 ConstantUse; |
| import '../universe/world_impact.dart'; |
| import 'backend.dart'; |
| import 'backend_usage.dart' show BackendUsageBuilder; |
| import 'backend_impact.dart'; |
| import 'mirrors_data.dart'; |
| |
| /// Resolution analysis that prepares for the construction of TypeVariable |
| /// constants needed at runtime. |
| class TypeVariableResolutionAnalysis { |
| final ElementEnvironment _elementEnvironment; |
| final BackendImpacts _impacts; |
| final BackendUsageBuilder _backendUsageBuilder; |
| |
| /** |
| * Set to 'true' on first encounter of a class with type variables. |
| */ |
| bool _seenClassesWithTypeVariables = false; |
| |
| /// Impact builder used for the resolution world computation. |
| final StagedWorldImpactBuilder impactBuilder = new StagedWorldImpactBuilder(); |
| |
| TypeVariableResolutionAnalysis( |
| this._elementEnvironment, this._impacts, this._backendUsageBuilder); |
| |
| /// Compute the [WorldImpact] for the type variables registered since last |
| /// flush. |
| WorldImpact flush() { |
| return impactBuilder.flush(); |
| } |
| |
| void registerClassWithTypeVariables(ClassEntity cls) { |
| // On first encounter, we have to ensure that the support classes get |
| // resolved. |
| if (!_seenClassesWithTypeVariables) { |
| _impacts.typeVariableMirror |
| .registerImpact(impactBuilder, _elementEnvironment); |
| _backendUsageBuilder.processBackendImpact(_impacts.typeVariableMirror); |
| _seenClassesWithTypeVariables = true; |
| } |
| } |
| } |
| |
| /// Codegen handler that creates TypeVariable constants needed at runtime. |
| class TypeVariableCodegenAnalysis { |
| final ElementEnvironment _elementEnvironment; |
| final JavaScriptBackend _backend; |
| final CommonElements _commonElements; |
| final MirrorsData _mirrorsData; |
| |
| /** |
| * Maps a class element to a list with indices that point to type variables |
| * constants for each of the class' type variables. |
| */ |
| Map<ClassEntity, List<jsAst.Expression>> _typeVariables = |
| new Map<ClassEntity, List<jsAst.Expression>>(); |
| |
| /** |
| * Maps a TypeVariableType to the index pointing to the constant representing |
| * the corresponding type variable at runtime. |
| */ |
| Map<TypeVariableEntity, jsAst.Expression> _typeVariableConstants = |
| new Map<TypeVariableEntity, jsAst.Expression>(); |
| |
| /// Impact builder used for the codegen world computation. |
| final StagedWorldImpactBuilder _impactBuilder = |
| new StagedWorldImpactBuilder(); |
| |
| TypeVariableCodegenAnalysis(this._elementEnvironment, this._backend, |
| this._commonElements, this._mirrorsData); |
| |
| CodeEmitterTask get _task => _backend.emitter; |
| MetadataCollector get _metadataCollector => _task.metadataCollector; |
| |
| /// Compute the [WorldImpact] for the type variables registered since last |
| /// flush. |
| WorldImpact flush() { |
| return _impactBuilder.flush(); |
| } |
| |
| void registerClassWithTypeVariables(ClassEntity cls) { |
| if (_mirrorsData.isClassAccessibleByReflection(cls)) { |
| processTypeVariablesOf(cls); |
| } |
| } |
| |
| void processTypeVariablesOf(ClassEntity cls) { |
| // Do not process classes twice. |
| if (_typeVariables.containsKey(cls)) return; |
| |
| List<jsAst.Expression> constants = <jsAst.Expression>[]; |
| |
| InterfaceType thisType = _elementEnvironment.getThisType(cls); |
| for (TypeVariableType currentTypeVariable in thisType.typeArguments) { |
| TypeVariableEntity typeVariableElement = currentTypeVariable.element; |
| |
| // TODO(sigmund): use output unit for `cls` (Issue #31032) |
| OutputUnit outputUnit = _backend.outputUnitData.mainOutputUnit; |
| jsAst.Expression boundIndex = _metadataCollector.reifyType( |
| _elementEnvironment.getTypeVariableBound(typeVariableElement), |
| outputUnit); |
| ConstantValue boundValue = new SyntheticConstantValue( |
| SyntheticConstantKind.TYPEVARIABLE_REFERENCE, boundIndex); |
| ClassEntity typeVariableClass = _commonElements.typeVariableClass; |
| ConstantExpression constant = new ConstructedConstantExpression( |
| _elementEnvironment.getThisType(typeVariableClass), |
| _commonElements.typeVariableConstructor, |
| const CallStructure.unnamed(3), [ |
| new TypeConstantExpression( |
| _elementEnvironment.getRawType(cls), cls.name), |
| new StringConstantExpression(typeVariableElement.name), |
| new SyntheticConstantExpression(boundValue) |
| ]); |
| |
| _backend.constants.evaluate(constant); |
| ConstantValue value = _backend.constants.getConstantValue(constant); |
| _impactBuilder |
| .registerConstantUse(new ConstantUse.typeVariableMirror(value)); |
| constants.add(_reifyTypeVariableConstant( |
| value, currentTypeVariable.element, outputUnit)); |
| } |
| _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, TypeVariableEntity variable, OutputUnit outputUnit) { |
| jsAst.Expression name = _task.constantReference(c); |
| jsAst.Expression result = |
| _metadataCollector.reifyExpression(name, outputUnit); |
| 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(TypeVariableEntity variable) { |
| if (_typeVariableConstants.containsKey(variable)) { |
| return _typeVariableConstants[variable]; |
| } |
| |
| Placeholder placeholder = |
| _metadataCollector.getMetadataPlaceholder(variable); |
| return _typeVariableConstants[variable] = placeholder; |
| } |
| |
| List<jsAst.Expression> typeVariablesOf(ClassEntity classElement) { |
| List<jsAst.Expression> result = _typeVariables[classElement]; |
| if (result == null) { |
| result = const <jsAst.Expression>[]; |
| } |
| return result; |
| } |
| } |