| // 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; |
| } |
| } |