| // 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 'package:kernel/ast.dart' as ir; |
| |
| import '../closure.dart'; |
| import '../common.dart'; |
| import '../common/elements.dart'; |
| import '../constants/values.dart'; |
| import '../deferred_load/output_unit.dart' show OutputUnit, OutputUnitData; |
| import '../elements/entities.dart'; |
| import '../elements/types.dart'; |
| import '../inferrer/abstract_value_strategy.dart'; |
| import '../ir/closure.dart'; |
| import '../js_backend/annotations.dart'; |
| import '../js_backend/backend_usage.dart'; |
| import '../js_backend/field_analysis.dart'; |
| import '../js_backend/interceptor_data.dart'; |
| import '../js_backend/native_data.dart'; |
| import '../js_backend/no_such_method_registry.dart'; |
| import '../js_backend/runtime_types_resolution.dart'; |
| import '../kernel/kernel_world.dart'; |
| import '../options.dart'; |
| import '../universe/class_hierarchy.dart'; |
| import '../universe/class_set.dart'; |
| import '../universe/feature.dart'; |
| import '../universe/member_usage.dart'; |
| import '../universe/record_shape.dart'; |
| import '../universe/selector.dart'; |
| import 'closure.dart'; |
| import 'elements.dart'; |
| import 'element_map_impl.dart'; |
| import 'js_to_frontend_map.dart'; |
| import 'js_world.dart'; |
| import 'records.dart'; |
| |
| class JClosedWorldBuilder { |
| final JsKernelToElementMap _elementMap; |
| final Map<ClassEntity, ClassHierarchyNode> _classHierarchyNodes = |
| ClassHierarchyNodesMap(); |
| final Map<ClassEntity, ClassSet> _classSets = <ClassEntity, ClassSet>{}; |
| final ClosureDataBuilder _closureDataBuilder; |
| final RecordDataBuilder _recordDataBuilder; |
| final CompilerOptions _options; |
| final DiagnosticReporter _reporter; |
| final AbstractValueStrategy _abstractValueStrategy; |
| |
| JClosedWorldBuilder( |
| this._elementMap, |
| this._closureDataBuilder, |
| this._recordDataBuilder, |
| this._options, |
| this._reporter, |
| this._abstractValueStrategy, |
| ); |
| |
| ElementEnvironment get _elementEnvironment => _elementMap.elementEnvironment; |
| CommonElements get _commonElements => _elementMap.commonElements; |
| DartTypes get _dartTypes => _elementMap.types; |
| |
| JClosedWorld convertClosedWorld( |
| KClosedWorld closedWorld, |
| Map<MemberEntity, ClosureScopeModel> closureModels, |
| OutputUnitData kOutputUnitData, |
| ) { |
| final map = JsToFrontendMap(_elementMap); |
| |
| NativeData nativeData = closedWorld.nativeData.convert( |
| map, |
| _elementEnvironment, |
| ); |
| _elementMap.nativeData = nativeData; |
| InterceptorData interceptorData = _convertInterceptorData( |
| map, |
| nativeData, |
| closedWorld.interceptorData as InterceptorDataImpl, |
| ); |
| |
| Set<ClassEntity> implementedClasses = <ClassEntity>{}; |
| |
| /// Converts [node] from the frontend world to the corresponding |
| /// [ClassHierarchyNode] for the backend world. |
| ClassHierarchyNode convertClassHierarchyNode(ClassHierarchyNode node) { |
| ClassEntity cls = map.toBackendClass(node.cls); |
| if (closedWorld.isImplemented(node.cls)) { |
| implementedClasses.add(cls); |
| } |
| ClassHierarchyNode newNode = _classHierarchyNodes.putIfAbsent(cls, () { |
| ClassHierarchyNode? parentNode; |
| if (node.parentNode != null) { |
| parentNode = convertClassHierarchyNode(node.parentNode!); |
| } |
| return ClassHierarchyNode(parentNode, cls, node.hierarchyDepth); |
| }); |
| newNode.isAbstractlyInstantiated = node.isAbstractlyInstantiated; |
| newNode.isDirectlyInstantiated = node.isDirectlyInstantiated; |
| return newNode; |
| } |
| |
| /// Converts [classSet] from the frontend world to the corresponding |
| /// [ClassSet] for the backend world. |
| ClassSet convertClassSet(ClassSet classSet) { |
| ClassEntity cls = map.toBackendClass(classSet.cls); |
| return _classSets.putIfAbsent(cls, () { |
| ClassHierarchyNode newNode = convertClassHierarchyNode(classSet.node); |
| ClassSet newClassSet = ClassSet(newNode); |
| for (ClassHierarchyNode subtype in classSet.subtypeNodes) { |
| ClassHierarchyNode newSubtype = convertClassHierarchyNode(subtype); |
| newClassSet.addSubtype(newSubtype); |
| } |
| return newClassSet; |
| }); |
| } |
| |
| closedWorld.classHierarchy |
| .getClassHierarchyNode(closedWorld.commonElements.objectClass) |
| .forEachSubclass((ClassEntity cls) { |
| convertClassSet(closedWorld.classHierarchy.getClassSet(cls)); |
| return IterationStep.continue_; |
| }, ClassHierarchyNode.all); |
| |
| Set<MemberEntity> liveInstanceMembers = map.toBackendMemberSet( |
| closedWorld.liveInstanceMembers, |
| ); |
| Set<MemberEntity> liveAbstractInstanceMembers = map.toBackendMemberSet( |
| closedWorld.liveAbstractInstanceMembers, |
| ); |
| |
| Map<ClassEntity, Set<ClassEntity>> mixinUses = map.toBackendClassMap( |
| closedWorld.mixinUses, |
| map.toBackendClassSet, |
| ); |
| |
| Map<ClassEntity, Set<ClassEntity>> typesImplementedBySubclasses = map |
| .toBackendClassMap( |
| closedWorld.typesImplementedBySubclasses, |
| map.toBackendClassSet, |
| ); |
| |
| Set<MemberEntity> assignedInstanceMembers = map.toBackendMemberSet( |
| closedWorld.assignedInstanceMembers, |
| ); |
| |
| Set<ClassEntity> liveNativeClasses = map.toBackendClassSet( |
| closedWorld.liveNativeClasses, |
| ); |
| |
| Set<MemberEntity> processedMembers = map.toBackendMemberSet( |
| closedWorld.liveMemberUsage.keys, |
| ); |
| |
| Set<ClassEntity> extractTypeArgumentsInterfaces = {}; |
| |
| RuntimeTypesNeed rtiNeed; |
| |
| List<FunctionEntity> callMethods = <FunctionEntity>[]; |
| ClosureData closureData; |
| RecordData recordData; |
| |
| if (_options.disableRtiOptimization) { |
| rtiNeed = TrivialRuntimeTypesNeed(_elementMap.elementEnvironment); |
| closureData = _closureDataBuilder.createClosureEntities( |
| this, |
| map.toBackendMemberMap(closureModels, identity), |
| const TrivialClosureRtiNeed(), |
| callMethods, |
| ); |
| } else { |
| RuntimeTypesNeedImpl kernelRtiNeed = |
| closedWorld.rtiNeed as RuntimeTypesNeedImpl; |
| Set<ir.LocalFunction> localFunctionsNodesNeedingSignature = |
| <ir.LocalFunction>{}; |
| for (Local localFunction |
| in kernelRtiNeed.localFunctionsNeedingSignature) { |
| ir.LocalFunction node = (localFunction as JLocalFunction).node; |
| localFunctionsNodesNeedingSignature.add(node); |
| } |
| Set<ir.LocalFunction> localFunctionsNodesNeedingTypeArguments = |
| <ir.LocalFunction>{}; |
| for (Local localFunction |
| in kernelRtiNeed.localFunctionsNeedingTypeArguments) { |
| ir.LocalFunction node = (localFunction as JLocalFunction).node; |
| localFunctionsNodesNeedingTypeArguments.add(node); |
| } |
| |
| RuntimeTypesNeedImpl jRtiNeed = _convertRuntimeTypesNeed( |
| map, |
| kernelRtiNeed, |
| ); |
| closureData = _closureDataBuilder.createClosureEntities( |
| this, |
| map.toBackendMemberMap(closureModels, identity), |
| JsClosureRtiNeed( |
| jRtiNeed, |
| localFunctionsNodesNeedingTypeArguments, |
| localFunctionsNodesNeedingSignature, |
| ), |
| callMethods, |
| ); |
| |
| List<FunctionEntity> callMethodsNeedingSignature = <FunctionEntity>[]; |
| for (ir.LocalFunction node in localFunctionsNodesNeedingSignature) { |
| callMethodsNeedingSignature.add( |
| closureData.getClosureInfo(node).callMethod!, |
| ); |
| } |
| List<FunctionEntity> callMethodsNeedingTypeArguments = <FunctionEntity>[]; |
| for (ir.LocalFunction node in localFunctionsNodesNeedingTypeArguments) { |
| callMethodsNeedingTypeArguments.add( |
| closureData.getClosureInfo(node).callMethod!, |
| ); |
| } |
| jRtiNeed.methodsNeedingSignature.addAll(callMethodsNeedingSignature); |
| jRtiNeed.methodsNeedingTypeArguments.addAll( |
| callMethodsNeedingTypeArguments, |
| ); |
| |
| rtiNeed = jRtiNeed; |
| } |
| |
| map.registerClosureData(closureData); |
| final recordTypes = Set<RecordType>.from( |
| map.toBackendTypeSet(closedWorld.instantiatedRecordTypes), |
| ); |
| recordData = _recordDataBuilder.createRecordData(this, recordTypes); |
| |
| BackendUsage backendUsage = _convertBackendUsage( |
| map, |
| closedWorld.backendUsage as BackendUsageImpl, |
| ); |
| |
| NoSuchMethodData oldNoSuchMethodData = closedWorld.noSuchMethodData; |
| NoSuchMethodData noSuchMethodData = NoSuchMethodData( |
| map.toBackendFunctionSet(oldNoSuchMethodData.throwingImpls), |
| map.toBackendFunctionSet(oldNoSuchMethodData.otherImpls), |
| map.toBackendFunctionSet(oldNoSuchMethodData.forwardingSyntaxImpls), |
| ); |
| |
| JFieldAnalysis allocatorAnalysis = JFieldAnalysis.from( |
| closedWorld, |
| map, |
| _options, |
| ); |
| |
| AnnotationsDataImpl oldAnnotationsData = |
| closedWorld.annotationsData as AnnotationsDataImpl; |
| AnnotationsData annotationsData = AnnotationsDataImpl( |
| _options, |
| _reporter, |
| map.toBackendMemberMap(oldAnnotationsData.pragmaAnnotations, identity), |
| ); |
| |
| OutputUnitData outputUnitData = _convertOutputUnitData( |
| map, |
| kOutputUnitData, |
| closureData, |
| recordData, |
| ); |
| |
| Map<MemberEntity, MemberAccess> memberAccess = map.toBackendMemberMap( |
| closedWorld.liveMemberUsage, |
| (MemberUsage usage) => |
| MemberAccess(usage.reads, usage.writes, usage.invokes), |
| ); |
| |
| return JClosedWorld( |
| _elementMap, |
| nativeData, |
| interceptorData, |
| backendUsage, |
| rtiNeed, |
| allocatorAnalysis, |
| noSuchMethodData, |
| implementedClasses, |
| liveNativeClasses, |
| // TODO(johnniwinther): Include the call method when we can also |
| // represent the synthesized call methods for static and instance method |
| // closurizations. |
| liveInstanceMembers /*..addAll(callMethods)*/, |
| liveAbstractInstanceMembers, |
| assignedInstanceMembers, |
| processedMembers, |
| extractTypeArgumentsInterfaces, |
| mixinUses, |
| typesImplementedBySubclasses, |
| ClassHierarchyImpl( |
| _elementMap.commonElements, |
| _classHierarchyNodes, |
| _classSets, |
| ), |
| _abstractValueStrategy, |
| annotationsData, |
| closureData, |
| recordData, |
| outputUnitData, |
| memberAccess, |
| ); |
| } |
| |
| BackendUsage _convertBackendUsage( |
| JsToFrontendMap map, |
| BackendUsageImpl backendUsage, |
| ) { |
| Set<FunctionEntity> globalFunctionDependencies = map.toBackendFunctionSet( |
| backendUsage.globalFunctionDependencies, |
| ); |
| Set<ClassEntity> globalClassDependencies = map.toBackendClassSet( |
| backendUsage.globalClassDependencies, |
| ); |
| Set<FunctionEntity> helperFunctionsUsed = map.toBackendFunctionSet( |
| backendUsage.helperFunctionsUsed, |
| ); |
| Set<ClassEntity> helperClassesUsed = map.toBackendClassSet( |
| backendUsage.helperClassesUsed, |
| ); |
| Set<RuntimeTypeUse> runtimeTypeUses = backendUsage.runtimeTypeUses.map(( |
| RuntimeTypeUse runtimeTypeUse, |
| ) { |
| return RuntimeTypeUse( |
| runtimeTypeUse.kind, |
| map.toBackendType(runtimeTypeUse.receiverType)!, |
| map.toBackendType(runtimeTypeUse.argumentType), |
| ); |
| }).toSet(); |
| |
| return BackendUsageImpl( |
| globalFunctionDependencies: globalFunctionDependencies, |
| globalClassDependencies: globalClassDependencies, |
| helperFunctionsUsed: helperFunctionsUsed, |
| helperClassesUsed: helperClassesUsed, |
| needToInitializeIsolateAffinityTag: |
| backendUsage.needToInitializeIsolateAffinityTag, |
| needToInitializeDispatchProperty: |
| backendUsage.needToInitializeDispatchProperty, |
| requiresPreamble: backendUsage.requiresPreamble, |
| requiresStartupMetrics: backendUsage.requiresStartupMetrics, |
| runtimeTypeUses: runtimeTypeUses, |
| isFunctionApplyUsed: backendUsage.isFunctionApplyUsed, |
| isNoSuchMethodUsed: backendUsage.isNoSuchMethodUsed, |
| ); |
| } |
| |
| InterceptorDataImpl _convertInterceptorData( |
| JsToFrontendMap map, |
| NativeData nativeData, |
| InterceptorDataImpl interceptorData, |
| ) { |
| Map<String, Set<MemberEntity>> interceptedMembers = |
| <String, Set<MemberEntity>>{}; |
| interceptorData.interceptedMembers.forEach(( |
| String name, |
| Set<MemberEntity> members, |
| ) { |
| interceptedMembers[name] = map.toBackendMemberSet(members); |
| }); |
| return InterceptorDataImpl( |
| nativeData, |
| _commonElements, |
| interceptedMembers, |
| map.toBackendClassSet(interceptorData.interceptedClasses), |
| map.toBackendClassSet(interceptorData.classesMixedIntoInterceptedClasses), |
| ); |
| } |
| |
| RuntimeTypesNeedImpl _convertRuntimeTypesNeed( |
| JsToFrontendMap map, |
| RuntimeTypesNeedImpl rtiNeed, |
| ) { |
| Set<ClassEntity> classesNeedingTypeArguments = map.toBackendClassSet( |
| rtiNeed.classesNeedingTypeArguments, |
| ); |
| Set<FunctionEntity> methodsNeedingTypeArguments = map.toBackendFunctionSet( |
| rtiNeed.methodsNeedingTypeArguments, |
| ); |
| Set<FunctionEntity> methodsNeedingSignature = map.toBackendFunctionSet( |
| rtiNeed.methodsNeedingSignature, |
| ); |
| Set<Selector> selectorsNeedingTypeArguments = |
| rtiNeed.selectorsNeedingTypeArguments; |
| return RuntimeTypesNeedImpl( |
| _elementEnvironment, |
| classesNeedingTypeArguments, |
| methodsNeedingSignature, |
| methodsNeedingTypeArguments, |
| const {}, |
| const {}, |
| selectorsNeedingTypeArguments, |
| rtiNeed.instantiationsNeedingTypeArguments, |
| ); |
| } |
| |
| /// Construct a closure class and set up the necessary class inference |
| /// hierarchy. |
| JsClosureClassInfo buildClosureClass( |
| MemberEntity member, |
| ir.FunctionNode originalClosureFunctionNode, |
| JLibrary enclosingLibrary, |
| Map<ir.VariableDeclaration, JContextField> boxedVariables, |
| KernelScopeInfo info, { |
| required bool createSignatureMethod, |
| }) { |
| ClassEntity superclass = _chooseClosureSuperclass( |
| originalClosureFunctionNode, |
| ); |
| |
| JsClosureClassInfo closureClassInfo = _elementMap.constructClosureClass( |
| member, |
| originalClosureFunctionNode, |
| enclosingLibrary, |
| boxedVariables, |
| info, |
| _dartTypes.interfaceType(superclass, const []), |
| createSignatureMethod: createSignatureMethod, |
| ); |
| |
| // Tell the hierarchy that this is the super class. then we can use |
| // .getSupertypes(class) |
| ClassHierarchyNode parentNode = _classHierarchyNodes[superclass]!; |
| ClassHierarchyNode node = ClassHierarchyNode( |
| parentNode, |
| closureClassInfo.closureClassEntity, |
| parentNode.hierarchyDepth + 1, |
| ); |
| _classHierarchyNodes[closureClassInfo.closureClassEntity] = node; |
| _classSets[closureClassInfo.closureClassEntity] = ClassSet(node); |
| node.isDirectlyInstantiated = true; |
| |
| return closureClassInfo; |
| } |
| |
| ClassEntity _chooseClosureSuperclass(ir.FunctionNode node) { |
| // Choose a superclass so that similar closures can share the metadata used |
| // by `Function.apply`. |
| int requiredParameterCount = node.requiredParameterCount; |
| if (node.typeParameters.isEmpty && |
| node.namedParameters.isEmpty && |
| requiredParameterCount == node.positionalParameters.length) { |
| if (requiredParameterCount == 0) return _commonElements.closureClass0Args; |
| if (requiredParameterCount == 2) return _commonElements.closureClass2Args; |
| } |
| // Note that the base closure class has specialized metadata for the common |
| // case of single-argument functions. |
| return _commonElements.closureClass; |
| } |
| |
| /// Called once per [shape]. The class can be used for a record with the |
| /// specified shape, or subclassed to provide specialized methods. [getters] |
| /// is an out parameter that gathers all the getters created for this shape. |
| ClassEntity buildRecordShapeClass( |
| RecordShape shape, |
| List<MemberEntity> getters, |
| ) { |
| ClassEntity superclass = _commonElements.recordArityClass(shape.fieldCount); |
| final recordClass = _elementMap.generateRecordShapeClass( |
| shape, |
| _dartTypes.interfaceType(superclass, const []), |
| getters, |
| ); |
| |
| // Tell the hierarchy about the superclass so we can use |
| // .getSupertypes(class) |
| ClassHierarchyNode parentNode = _classHierarchyNodes[superclass]!; |
| ClassHierarchyNode node = ClassHierarchyNode( |
| parentNode, |
| recordClass, |
| parentNode.hierarchyDepth + 1, |
| ); |
| _classHierarchyNodes[recordClass] = node; |
| _classSets[recordClass] = ClassSet(node); |
| node.isDirectlyInstantiated = true; |
| |
| return recordClass; |
| } |
| |
| OutputUnitData _convertOutputUnitData( |
| JsToFrontendMap map, |
| OutputUnitData data, |
| ClosureData closureDataLookup, |
| RecordData recordData, |
| ) { |
| // Convert front-end maps containing K-class and K-local function keys to a |
| // backend map using J-classes as keys. |
| Map<ClassEntity, OutputUnit> convertClassMap( |
| Map<ClassEntity, OutputUnit> classMap, |
| Map<Local, OutputUnit> localFunctionMap, |
| ) { |
| final result = <ClassEntity, OutputUnit>{}; |
| classMap.forEach((ClassEntity entity, OutputUnit unit) { |
| final backendEntity = map.toBackendClass(entity); |
| result[backendEntity] = unit; |
| }); |
| localFunctionMap.forEach((Local entity, OutputUnit unit) { |
| // Ensure closure classes are included in the output unit corresponding |
| // to the local function. |
| if (entity is JLocalFunction) { |
| var closureInfo = closureDataLookup.getClosureInfo(entity.node); |
| result[closureInfo.closureClassEntity!] = unit; |
| } |
| }); |
| // TODO(51016): Determine which record classes can go in deferred units. |
| for (final cls in recordData.allClasses) { |
| result[cls] ??= data.mainOutputUnit; |
| } |
| return result; |
| } |
| |
| // Convert front-end maps containing K-member and K-local function keys to |
| // a backend map using J-members as keys. |
| Map<MemberEntity, OutputUnit> convertMemberMap( |
| Map<MemberEntity, OutputUnit> memberMap, |
| Map<Local, OutputUnit> localFunctionMap, |
| ) { |
| final result = <MemberEntity, OutputUnit>{}; |
| memberMap.forEach((MemberEntity entity, OutputUnit unit) { |
| MemberEntity? backendEntity = map.toBackendMember(entity); |
| if (backendEntity != null) { |
| result[backendEntity] = unit; |
| } |
| }); |
| localFunctionMap.forEach((Local entity, OutputUnit unit) { |
| // Ensure closure call-methods are included in the output unit |
| // corresponding to the local function. |
| if (entity is JLocalFunction) { |
| var closureInfo = closureDataLookup.getClosureInfo(entity.node); |
| result[closureInfo.callMethod!] = unit; |
| if (closureInfo.signatureMethod != null) { |
| result[closureInfo.signatureMethod!] = unit; |
| } |
| } |
| }); |
| return result; |
| } |
| |
| return OutputUnitData.from( |
| data, |
| map.toBackendLibrary, |
| convertClassMap, |
| convertMemberMap, |
| (m) => convertMap<ConstantValue, OutputUnit, OutputUnit>( |
| m, |
| map.toBackendConstant, |
| (v) => v, |
| ), |
| ); |
| } |
| } |
| |
| class TrivialClosureRtiNeed implements ClosureRtiNeed { |
| const TrivialClosureRtiNeed(); |
| |
| @override |
| bool localFunctionNeedsSignature(ir.Node node) => true; |
| |
| @override |
| bool classNeedsTypeArguments(ClassEntity cls) => true; |
| |
| @override |
| bool methodNeedsTypeArguments(FunctionEntity method) => true; |
| |
| @override |
| bool localFunctionNeedsTypeArguments(ir.Node node) => true; |
| |
| @override |
| bool selectorNeedsTypeArguments(Selector selector) => true; |
| |
| @override |
| bool methodNeedsSignature(MemberEntity method) => true; |
| |
| @override |
| bool instantiationNeedsTypeArguments( |
| FunctionType? functionType, |
| int typeArgumentCount, |
| ) => true; |
| } |
| |
| class JsClosureRtiNeed implements ClosureRtiNeed { |
| final RuntimeTypesNeed rtiNeed; |
| final Set<ir.LocalFunction> localFunctionsNodesNeedingTypeArguments; |
| final Set<ir.LocalFunction> localFunctionsNodesNeedingSignature; |
| |
| JsClosureRtiNeed( |
| this.rtiNeed, |
| this.localFunctionsNodesNeedingTypeArguments, |
| this.localFunctionsNodesNeedingSignature, |
| ); |
| |
| @override |
| bool localFunctionNeedsSignature(ir.LocalFunction node) { |
| return localFunctionsNodesNeedingSignature.contains(node); |
| } |
| |
| @override |
| bool classNeedsTypeArguments(ClassEntity cls) => |
| rtiNeed.classNeedsTypeArguments(cls); |
| |
| @override |
| bool methodNeedsTypeArguments(FunctionEntity method) => |
| rtiNeed.methodNeedsTypeArguments(method); |
| |
| @override |
| bool localFunctionNeedsTypeArguments(ir.LocalFunction node) { |
| return localFunctionsNodesNeedingTypeArguments.contains(node); |
| } |
| |
| @override |
| bool selectorNeedsTypeArguments(Selector selector) => |
| rtiNeed.selectorNeedsTypeArguments(selector); |
| |
| @override |
| bool methodNeedsSignature(MemberEntity method) => |
| rtiNeed.methodNeedsSignature(method as FunctionEntity); |
| |
| @override |
| bool instantiationNeedsTypeArguments( |
| FunctionType? functionType, |
| int typeArgumentCount, |
| ) => rtiNeed.instantiationNeedsTypeArguments(functionType, typeArgumentCount); |
| } |