blob: e2df21345a214eabf22592e7d0e8a1b3dbede9e7 [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 '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);
}