blob: e47986c4b714b61b4c95444b34aaa61182975c85 [file] [log] [blame]
// Copyright (c) 2018, 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 '../closure.dart';
import '../common.dart';
import '../constants/constant_system.dart' as constant_system;
import '../constants/values.dart';
import '../elements/entities.dart';
import '../elements/types.dart';
import 'element_map_impl.dart';
import 'elements.dart';
/// Map from 'frontend' to 'backend' elements.
///
/// Frontend elements are what we read in, these typically represents concepts
/// in Dart. Backend elements are what we generate, these may include elements
/// that do not correspond to a Dart concept, such as closure classes.
///
/// Querying for the frontend element for a backend-only element throws an
/// exception.
class JsToFrontendMap {
final JsKernelToElementMap _backend;
ClosureData? _closureData;
JsToFrontendMap(this._backend);
ClassEntity toBackendClass(ClassEntity cls) => cls;
LibraryEntity toBackendLibrary(LibraryEntity library) => library;
Entity? toBackendEntity(Entity entity) {
if (entity is ClassEntity) return toBackendClass(entity);
if (entity is MemberEntity) return toBackendMember(entity);
if (entity is TypeVariableEntity) {
return toBackendTypeVariable(entity);
}
assert(entity is LibraryEntity, 'unexpected entity ${entity.runtimeType}');
return toBackendLibrary(entity as LibraryEntity);
}
TypeVariableEntity toBackendTypeVariable(TypeVariableEntity typeVariable) {
if (typeVariable is JLocalTypeVariable) {
if (_closureData == null) {
failedAt(
typeVariable,
'ClosureData needs to be registered before converting type variable'
' $typeVariable',
);
}
ClosureRepresentationInfo info = _closureData!.getClosureInfo(
typeVariable.typeDeclaration.node,
);
return _backend.elementEnvironment
.getFunctionTypeVariables(info.callMethod!)[typeVariable.index]
.element;
}
return typeVariable;
}
ConstantValue? toBackendConstant(
ConstantValue? constant, {
bool allowNull = false,
}) {
if (constant == null) {
if (!allowNull) {
throw UnsupportedError('Null not allowed as constant value.');
}
return null;
}
return constant.accept(
_ConstantConverter(_backend.types, toBackendEntity),
null,
);
}
MemberEntity? toBackendMember(MemberEntity member) =>
_backend.kToJMembers[member];
DartType? toBackendType(DartType? type, {bool allowFreeVariables = false}) =>
type == null
? null
: _TypeConverter(
_backend.types,
allowFreeVariables: allowFreeVariables,
).visit(type, toBackendEntity);
void registerClosureData(ClosureData closureData) {
assert(_closureData == null, "Closure data has already been registered.");
_closureData = closureData;
}
Set<DartType> toBackendTypeSet(Iterable<DartType> set) {
return {for (final type in set.map(toBackendType)) type!};
}
Set<ClassEntity> toBackendClassSet(Iterable<ClassEntity> set) {
return Set.of(set);
}
Set<MemberEntity> toBackendMemberSet(Iterable<MemberEntity> set) {
return {
for (final member in set.map(toBackendMember))
// Members that are not live don't have a corresponding backend member.
if (member != null) member,
};
}
Set<FieldEntity> toBackendFieldSet(Iterable<FieldEntity> set) {
return {
for (final member in set.map(toBackendMember))
// Members that are not live don't have a corresponding backend member.
if (member != null) member as FieldEntity,
};
}
Set<FunctionEntity> toBackendFunctionSet(Iterable<FunctionEntity> set) {
return {
for (final member in set.map(toBackendMember))
// Members that are not live don't have a corresponding backend member.
if (member != null) member as FunctionEntity,
};
}
Map<LibraryEntity, V> toBackendLibraryMap<V>(
Map<LibraryEntity, V> map,
V Function(V value) convert,
) {
return convertMap(map, toBackendLibrary, convert);
}
Map<ClassEntity, V> toBackendClassMap<V>(
Map<ClassEntity, V> map,
V Function(V value) convert,
) {
return convertMap(map, toBackendClass, convert);
}
Map<MemberEntity, V2> toBackendMemberMap<V1, V2>(
Map<MemberEntity, V1> map,
V2 Function(V1 value) convert,
) {
return convertMap(map, toBackendMember, convert);
}
}
E identity<E>(E element) => element;
Map<K, V2> convertMap<K, V1, V2>(
Map<K, V1> map,
K? Function(K key) convertKey,
V2 Function(V1 value) convertValue,
) {
Map<K, V2> newMap = {};
map.forEach((K key, V1 value) {
K? newKey = convertKey(key);
V2 newValue = convertValue(value);
if (newKey != null && newValue != null) {
// Entities that are not used don't have a corresponding backend entity.
newMap[newKey] = newValue;
}
});
return newMap;
}
typedef _EntityConverter = Entity? Function(Entity cls);
class _TypeConverter implements DartTypeVisitor<DartType, _EntityConverter> {
final DartTypes _dartTypes;
final bool allowFreeVariables;
final Map<FunctionTypeVariable, FunctionTypeVariable> _functionTypeVariables =
{};
_TypeConverter(this._dartTypes, {this.allowFreeVariables = false});
List<DartType> convertTypes(
List<DartType> types,
_EntityConverter converter,
) => visitList(types, converter);
@override
DartType visit(DartType type, _EntityConverter converter) {
return type.accept(this, converter);
}
List<DartType> visitList(List<DartType> types, _EntityConverter converter) {
List<DartType> list = <DartType>[];
for (DartType type in types) {
list.add(visit(type, converter));
}
return list;
}
@override
DartType visitNullableType(NullableType type, _EntityConverter converter) =>
_dartTypes.nullableType(visit(type.baseType, converter));
@override
DartType visitNeverType(NeverType type, _EntityConverter converter) => type;
@override
DartType visitDynamicType(DynamicType type, _EntityConverter converter) =>
type;
@override
DartType visitErasedType(ErasedType type, _EntityConverter converter) => type;
@override
DartType visitAnyType(AnyType type, _EntityConverter converter) => type;
@override
DartType visitInterfaceType(InterfaceType type, _EntityConverter converter) {
return _dartTypes.interfaceType(
converter(type.element) as ClassEntity,
visitList(type.typeArguments, converter),
);
}
@override
DartType visitRecordType(RecordType type, _EntityConverter converter) {
return _dartTypes.recordType(type.shape, visitList(type.fields, converter));
}
@override
DartType visitTypeVariableType(
TypeVariableType type,
_EntityConverter converter,
) {
return _dartTypes.typeVariableType(
converter(type.element) as TypeVariableEntity,
);
}
@override
DartType visitFunctionType(FunctionType type, _EntityConverter converter) {
List<FunctionTypeVariable> typeVariables = <FunctionTypeVariable>[];
for (FunctionTypeVariable typeVariable in type.typeVariables) {
typeVariables.add(
_functionTypeVariables[typeVariable] = _dartTypes.functionTypeVariable(
typeVariable.index,
),
);
}
for (FunctionTypeVariable typeVariable in type.typeVariables) {
_functionTypeVariables[typeVariable]!.bound = visit(
typeVariable.bound,
converter,
);
}
DartType returnType = visit(type.returnType, converter);
List<DartType> parameterTypes = visitList(type.parameterTypes, converter);
List<DartType> optionalParameterTypes = visitList(
type.optionalParameterTypes,
converter,
);
List<DartType> namedParameterTypes = visitList(
type.namedParameterTypes,
converter,
);
for (FunctionTypeVariable typeVariable in type.typeVariables) {
_functionTypeVariables.remove(typeVariable);
}
return _dartTypes.functionType(
returnType,
parameterTypes,
optionalParameterTypes,
type.namedParameters,
type.requiredNamedParameters,
namedParameterTypes,
typeVariables,
);
}
@override
DartType visitFunctionTypeVariable(
FunctionTypeVariable type,
_EntityConverter converter,
) {
DartType? result = _functionTypeVariables[type];
if (result == null && allowFreeVariables) {
return type;
}
if (result == null) {
throw failedAt(
currentElementSpannable,
"Function type variable $type not found in $_functionTypeVariables",
);
}
return result;
}
@override
DartType visitVoidType(VoidType type, _EntityConverter converter) =>
_dartTypes.voidType();
@override
DartType visitFutureOrType(FutureOrType type, _EntityConverter converter) =>
_dartTypes.futureOrType(visit(type.typeArgument, converter));
}
class _ConstantConverter implements ConstantValueVisitor<ConstantValue, Null> {
final DartTypes _dartTypes;
final Entity? Function(Entity) toBackendEntity;
final _TypeConverter typeConverter;
_ConstantConverter(this._dartTypes, this.toBackendEntity)
: typeConverter = _TypeConverter(_dartTypes);
@override
NullConstantValue visitNull(NullConstantValue constant, _) => constant;
@override
IntConstantValue visitInt(IntConstantValue constant, _) => constant;
@override
DoubleConstantValue visitDouble(DoubleConstantValue constant, _) => constant;
@override
BoolConstantValue visitBool(BoolConstantValue constant, _) => constant;
@override
StringConstantValue visitString(StringConstantValue constant, _) => constant;
@override
DummyInterceptorConstantValue visitDummyInterceptor(
DummyInterceptorConstantValue constant,
_,
) => constant;
@override
LateSentinelConstantValue visitLateSentinel(
LateSentinelConstantValue constant,
_,
) => constant;
@override
UnreachableConstantValue visitUnreachable(
UnreachableConstantValue constant,
_,
) => constant;
@override
JsNameConstantValue visitJsName(JsNameConstantValue constant, _) => constant;
@override
FunctionConstantValue visitFunction(FunctionConstantValue constant, _) {
return FunctionConstantValue(
toBackendEntity(constant.element) as FunctionEntity,
typeConverter.visit(constant.type, toBackendEntity) as FunctionType,
);
}
@override
ListConstantValue visitList(ListConstantValue constant, _) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
List<ConstantValue> entries = _handleValues(constant.entries);
if (identical(entries, constant.entries) && type == constant.type) {
return constant;
}
return ListConstantValue(type as InterfaceType, entries);
}
@override
constant_system.JavaScriptSetConstant visitSet(
covariant constant_system.JavaScriptSetConstant constant,
_,
) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
List<ConstantValue> values = _handleValues(constant.values);
final constantIndex = constant.indexObject;
final indexObject = constantIndex == null
? null
: visitJavaScriptObject(constantIndex, null);
if (identical(values, constant.values) &&
identical(indexObject, constant.indexObject) &&
type == constant.type) {
return constant;
}
return constant_system.JavaScriptSetConstant(
type as InterfaceType,
values,
indexObject,
);
}
@override
constant_system.JavaScriptMapConstant visitMap(
covariant constant_system.JavaScriptMapConstant constant,
_,
) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
final keyList = visitList(constant.keyList, null);
final valueList = visitList(constant.valueList, null);
final constantIndex = constant.indexObject;
final indexObject = constantIndex == null
? null
: visitJavaScriptObject(constantIndex, null);
if (identical(keyList, constant.keyList) &&
identical(valueList, constant.valueList) &&
identical(indexObject, constant.indexObject) &&
type == constant.type) {
return constant;
}
return constant_system.JavaScriptMapConstant(
type as InterfaceType,
keyList,
valueList,
constant.onlyStringKeys,
indexObject,
);
}
@override
ConstructedConstantValue visitConstructed(
ConstructedConstantValue constant,
_,
) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
Map<FieldEntity, ConstantValue> fields = {};
constant.fields.forEach((f, v) {
FieldEntity backendField = toBackendEntity(f) as FieldEntity;
fields[backendField] = v.accept(this, null);
});
return ConstructedConstantValue(type as InterfaceType, fields);
}
@override
RecordConstantValue visitRecord(RecordConstantValue constant, _) {
// TODO(50081): An alternative is to lower the record to
// ConstructedConstantValue with possible a ListConstantValue argument. One
// way to do this would be to have two constant_systems - a K-system and a
// J-system. The K-system would produce a RecordConstantValue, the J-system
// the lowered form.
List<ConstantValue> values = _handleValues(constant.values);
if (identical(values, constant.values)) return constant;
return RecordConstantValue(constant.shape, values);
}
@override
JavaScriptObjectConstantValue visitJavaScriptObject(
JavaScriptObjectConstantValue constant,
_,
) {
List<ConstantValue> keys = _handleValues(constant.keys);
List<ConstantValue> values = _handleValues(constant.values);
if (identical(keys, constant.keys) && identical(values, constant.values)) {
return constant;
}
return JavaScriptObjectConstantValue(keys, values);
}
@override
TypeConstantValue visitType(TypeConstantValue constant, _) {
DartType type = typeConverter.visit(constant.type, toBackendEntity);
DartType representedType = typeConverter.visit(
constant.representedType,
toBackendEntity,
);
if (type == constant.type && representedType == constant.representedType) {
return constant;
}
return TypeConstantValue(representedType, type as InterfaceType);
}
@override
Never visitInterceptor(InterceptorConstantValue constant, _) {
// Interceptor constants are only created in the SSA graph builder.
throw UnsupportedError(
"Unexpected visitInterceptor ${constant.toStructuredText(_dartTypes)}",
);
}
@override
Never visitDeferredGlobal(DeferredGlobalConstantValue constant, _) {
// Deferred global constants are only created in the SSA graph builder.
throw UnsupportedError(
"Unexpected DeferredGlobalConstantValue ${constant.toStructuredText(_dartTypes)}",
);
}
@override
InstantiationConstantValue visitInstantiation(
InstantiationConstantValue constant,
_,
) {
ConstantValue function = constant.function.accept(this, null);
List<DartType> typeArguments = typeConverter.convertTypes(
constant.typeArguments,
toBackendEntity,
);
return InstantiationConstantValue(
typeArguments,
function as FunctionConstantValue,
);
}
List<ConstantValue> _handleValues(List<ConstantValue> values) {
List<ConstantValue>? result;
for (int i = 0; i < values.length; i++) {
var value = values[i];
var newValue = value.accept(this, null);
if (newValue != value && result == null) {
result = values.sublist(0, i).toList();
}
result?.add(newValue);
}
return result ?? values;
}
}