diff --git a/pkg/dart2wasm/lib/class_info.dart b/pkg/dart2wasm/lib/class_info.dart index 205857534d5..793ebeacd1c 100644 --- a/pkg/dart2wasm/lib/class_info.dart +++ b/pkg/dart2wasm/lib/class_info.dart @@ -373,6 +373,14 @@ class ClassInfoCollector { // conservative and mark it as not being final. struct.hasAnySubtypes = true; } + + if (translator.isDynamicModule) { + final brandIndex = + translator.dynamicModuleInfo!.classMetadata[cls]?.brandIndex; + if (brandIndex != null) { + translator.typesBuilder.addBrandTypeAssignment(struct, brandIndex); + } + } } translator.classesSupersFirst.add(info); translator.classInfo[cls] = info; @@ -700,9 +708,10 @@ class ClassIdNumbering { final implementors = >{}; final classIds = {}; - final savedMapping = translator.dynamicModuleInfo?.classIdMapping; - if (savedMapping != null) { - savedMapping.forEach((cls, classId) { + if (translator.isDynamicModule) { + final savedMapping = translator.dynamicModuleInfo!.classMetadata; + savedMapping.forEach((cls, info) { + final classId = info.classId; classIds[cls] = AbsoluteClassId(classId); savedMaxClassId = max(savedMaxClassId ?? -2, classId); if (!cls.isAbstract && !cls.isAnonymousMixin) { @@ -796,8 +805,7 @@ class ClassIdNumbering { } // Make a list of the depth-first pre-order traversal. - final dfsOrder = - translator.dynamicModuleInfo?.dfsOrderClassIds ?? []; + final dfsOrder = [...?translator.dynamicModuleInfo?.dfsOrderClassIds]; final inDfsOrder = {...dfsOrder}; // Maps any class to a dense range of concrete class ids that are subclasses @@ -907,6 +915,9 @@ final class AbsoluteClassId extends ClassId { int get _localValue => value; AbsoluteClassId(this.value); + + @override + String toString() => 'Absolute($value)'; } final class RelativeClassId extends ClassId { @@ -915,6 +926,9 @@ final class RelativeClassId extends ClassId { int get _localValue => relativeValue; RelativeClassId(this.relativeValue); + + @override + String toString() => 'Relative($relativeValue)'; } // A range of class ids, both ends inclusive. diff --git a/pkg/dart2wasm/lib/compile.dart b/pkg/dart2wasm/lib/compile.dart index 0db0f782576..caf300dc752 100644 --- a/pkg/dart2wasm/lib/compile.dart +++ b/pkg/dart2wasm/lib/compile.dart @@ -158,17 +158,7 @@ Future compileToModule( compilerOptions.compileSdk = true; } - DynamicModuleMetadata? dynamicModuleMetadata; final dynamicMainModuleUri = options.dynamicModuleMainUri; - if (dynamicMainModuleUri != null && options.dynamicInterfaceUri == null) { - final filename = options.dynamicModuleMetadataFile ?? - Uri.parse( - path.setExtension(dynamicMainModuleUri.toFilePath(), '.dyndata')); - final dynamicModuleMetadataBytes = - await File.fromUri(filename).readAsBytes(); - final source = BinaryDataSource(dynamicModuleMetadataBytes); - dynamicModuleMetadata = DynamicModuleMetadata.deserialize(source); - } final dynamicModuleMainUri = await resolveUri(options.dynamicModuleMainUri); final dynamicInterfaceUri = await resolveUri(options.dynamicInterfaceUri); @@ -177,10 +167,7 @@ Future compileToModule( final isDynamicModule = dynamicModuleMainUri != null && dynamicInterfaceUri == null; if (isDynamicModule) { - dynamicModuleMetadata!.verifyDynamicModuleOptions(options); compilerOptions.additionalDills.add(dynamicModuleMainUri); - } else if (isDynamicMainModule) { - DynamicModuleMetadata.verifyMainModuleOptions(options); } CompilerResult? compilerResult; @@ -268,7 +255,20 @@ Future compileToModule( moduleStrategy.prepareComponent(); - if (isDynamicMainModule) { + MainModuleMetadata mainModuleMetadata = + MainModuleMetadata.empty(options.translatorOptions, options.environment); + + if (isDynamicModule) { + final filename = options.dynamicModuleMetadataFile ?? + Uri.parse( + path.setExtension(dynamicMainModuleUri!.toFilePath(), '.dyndata')); + final dynamicModuleMetadataBytes = + await File.fromUri(filename).readAsBytes(); + final source = DataDeserializer(dynamicModuleMetadataBytes, component); + mainModuleMetadata = MainModuleMetadata.deserialize(source); + mainModuleMetadata.verifyDynamicModuleOptions(options); + } else if (isDynamicMainModule) { + MainModuleMetadata.verifyMainModuleOptions(options); writeComponentToBinary(component, dynamicModuleMainUri.path, includeSource: false); } @@ -301,9 +301,13 @@ Future compileToModule( final moduleOutputData = moduleStrategy.buildModuleOutputData(); + if (isDynamicMainModule) { + mainModuleMetadata.initialize(component, coreTypes); + } + var translator = Translator(component, coreTypes, libraryIndex, recordClasses, moduleOutputData, options.translatorOptions, - dynamicModuleMetadata: dynamicModuleMetadata, + mainModuleMetadata: mainModuleMetadata, enableDynamicModules: dynamicModuleMainUri != null); String? depFile = options.depFile; @@ -341,9 +345,9 @@ Future compileToModule( final filename = options.dynamicModuleMetadataFile ?? Uri.parse( path.setExtension(dynamicMainModuleUri!.toFilePath(), '.dyndata')); - final sink = BinaryDataSink(); - translator.dynamicModuleInfo!.toMetadata(options).serialize(sink); - await File.fromUri(filename).writeAsBytes(sink.takeBytes()); + final serializer = DataSerializer(translator.component); + translator.dynamicModuleInfo!.metadata.serialize(translator, serializer); + await File.fromUri(filename).writeAsBytes(serializer.takeBytes()); } return CompilationSuccess(wasmModules, jsRuntime, supportJs); diff --git a/pkg/dart2wasm/lib/dispatch_table.dart b/pkg/dart2wasm/lib/dispatch_table.dart index fb8cc2e50fb..5ab4a460432 100644 --- a/pkg/dart2wasm/lib/dispatch_table.dart +++ b/pkg/dart2wasm/lib/dispatch_table.dart @@ -397,7 +397,8 @@ class DispatchTable { (_dynamicMethods[member.name.text] ??= {}).add(selector); } } - final mainModuleIds = translator.dynamicModuleInfo?.selectorIds?[member]; + + final mainModuleIds = translator.dynamicModuleInfo?.selectorIds[member]; if (mainModuleIds != null) { selector._mainModuleIds[member] = isGetter ? mainModuleIds.$1 : mainModuleIds.$2; diff --git a/pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart b/pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart index d7afdb63ed8..52c8cb360c6 100644 --- a/pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart +++ b/pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart @@ -3,8 +3,15 @@ // BSD-style license that can be found in the LICENSE file. import 'package:kernel/ast.dart'; +import 'package:kernel/core_types.dart'; +import 'package:vm/metadata/procedure_attributes.dart' + show ProcedureAttributesMetadataRepository; +import 'class_info.dart'; import 'compiler_options.dart'; +import 'dynamic_modules.dart'; +import 'intrinsics.dart' show MemberIntrinsic; +import 'reference_extensions.dart'; import 'serialization.dart'; import 'translator.dart'; @@ -30,24 +37,41 @@ class DynamicModuleGlobalIdRepository extends MetadataRepository { void writeToBinary(int globalId, Node node, BinarySink sink) {} } +class ClassMetadata { + final int classId; + final int? brandIndex; + + ClassMetadata(this.classId, this.brandIndex); + + factory ClassMetadata.deserialize(DataDeserializer source) { + final classId = source.readInt() - 1; + final brandIndex = source.readInt(); + return ClassMetadata(classId, brandIndex == 0 ? null : brandIndex - 1); + } + + void serialize(DataSerializer sink) { + sink.writeInt(classId + 1); + sink.writeInt(brandIndex == null ? 0 : brandIndex! + 1); + } +} + /// Metadata produced by the main module. /// /// This data will get serialized as part of the main module compilation process /// and will be provided as an input to be deserialized by subsequent dynamic /// module compilations. -class DynamicModuleMetadata { - /// Global kernel class ID to dart2wasm class hierarchy class ID. - final Map classIds; +class MainModuleMetadata { + /// Class to metadata about the class + final Map classMetadata; - /// Global kernel member ID to getter and setter/method selector ID. - final Map selectorIds; + /// Member to getter and setter/method selector ID. + final Map selectorIds; /// Global kernel class IDs in class hierarchy dfs order. - final List dfsOrderClassIds; + final List dfsOrderClassIds; - /// References for all targets callable from the main module represented as - /// member global ID and reference type. - final List<(int, int)> callableReferences; + /// References for all targets callable from the main module. + final Set callableReferences; /// Key names of updateable functions defined in the main module. final Map updateableFunctionsInMain; @@ -57,8 +81,8 @@ class DynamicModuleMetadata { final TranslatorOptions mainModuleTranslatorOptions; final Map mainModuleEnvironment; - DynamicModuleMetadata( - this.classIds, + MainModuleMetadata._( + this.classMetadata, this.selectorIds, this.dfsOrderClassIds, this.callableReferences, @@ -66,26 +90,78 @@ class DynamicModuleMetadata { this.mainModuleTranslatorOptions, this.mainModuleEnvironment); - void serialize(BinaryDataSink sink) { - sink.writeInt(classIds.length); - classIds.forEach((globalClassId, classId) { - sink.writeInt(globalClassId); - sink.writeClassId(classId); + MainModuleMetadata.empty( + this.mainModuleTranslatorOptions, this.mainModuleEnvironment) + : classMetadata = {}, + selectorIds = {}, + dfsOrderClassIds = [], + callableReferences = {}, + updateableFunctionsInMain = {}; + + void initialize(Component component, CoreTypes coreTypes) { + _initializeCallableReferences(component, coreTypes); + } + + void finalize(Translator translator) { + translator.classInfo.forEach((cls, info) { + final id = + cls.isAnonymousMixin ? -1 : (info.classId as AbsoluteClassId).value; + final structType = info.struct; + final brandIndex = + translator.typesBuilder.brandTypeAssignments[structType]; + classMetadata[cls] = ClassMetadata(id, brandIndex); + }); + + final procedureMetadata = + (translator.component.metadata["vm.procedure-attributes.metadata"] + as ProcedureAttributesMetadataRepository) + .mapping; + for (final library in translator.libraries) { + for (final cls in library.classes) { + for (final member in cls.procedures) { + if (!member.isInstanceMember) continue; + selectorIds[member] = ( + procedureMetadata[member]!.getterSelectorId, + procedureMetadata[member]!.methodOrSetterSelectorId + ); + } + for (final member in cls.fields) { + if (!member.isInstanceMember) continue; + selectorIds[member] = ( + procedureMetadata[member]!.getterSelectorId, + procedureMetadata[member]!.methodOrSetterSelectorId + ); + } + } + } + + for (final cls in translator.classIdNumbering.dfsOrder) { + dfsOrderClassIds.add(cls); + } + } + + void serialize(Translator translator, DataSerializer sink) { + finalize(translator); + + sink.writeInt(classMetadata.length); + classMetadata.forEach((cls, metadata) { + sink.writeClass(cls); + metadata.serialize(sink); }); sink.writeInt(selectorIds.length); - selectorIds.forEach((globalMemberId, selectorIds) { - sink.writeInt(globalMemberId); + selectorIds.forEach((member, selectorIds) { + final wroteMember = sink.writeMember(member); + if (!wroteMember) return; sink.writeInt(selectorIds.$1); sink.writeInt(selectorIds.$2); }); sink.writeInt(dfsOrderClassIds.length); - for (final classId in dfsOrderClassIds) { - sink.writeClassId(classId); + for (final cls in dfsOrderClassIds) { + sink.writeClass(cls); } sink.writeInt(callableReferences.length); - for (final callableMemberId in callableReferences) { - sink.writeInt(callableMemberId.$1); - sink.writeInt(callableMemberId.$2); + for (final reference in callableReferences) { + sink.writeReference(reference); } sink.writeInt(updateableFunctionsInMain.length); updateableFunctionsInMain.forEach((stringKey, key) { @@ -101,33 +177,33 @@ class DynamicModuleMetadata { }); } - static DynamicModuleMetadata deserialize(BinaryDataSource source) { - final classIdMappingLength = source.readInt(); - final classIds = {}; - for (int i = 0; i < classIdMappingLength; i++) { - final globalClassId = source.readInt(); - final classId = source.readClassId(); - classIds[globalClassId] = classId; + static MainModuleMetadata deserialize(DataDeserializer source) { + final classMetadataLength = source.readInt(); + final classMetadata = {}; + for (int i = 0; i < classMetadataLength; i++) { + final cls = source.readClass(); + final metadata = ClassMetadata.deserialize(source); + classMetadata[cls] = metadata; } final selectorIdMappingLength = source.readInt(); - final selectorIds = {}; + final selectorIds = {}; for (int i = 0; i < selectorIdMappingLength; i++) { - final globalMemberId = source.readInt(); + final member = source.readMember(); final getterSelectorId = source.readInt(); final setterOrMethodSelectorId = source.readInt(); - selectorIds[globalMemberId] = - (getterSelectorId, setterOrMethodSelectorId); + selectorIds[member] = (getterSelectorId, setterOrMethodSelectorId); } - final dfsOrderClassIdsLength = source.readInt(); - final dfsOrderClassIds = []; - for (int i = 0; i < dfsOrderClassIdsLength; i++) { - dfsOrderClassIds.add(source.readClassId()); + final dfsOrderClassesLength = source.readInt(); + final dfsOrderClasses = []; + for (int i = 0; i < dfsOrderClassesLength; i++) { + dfsOrderClasses.add(source.readClass()); } - final callableMemberIdsLength = source.readInt(); - final callableMemberIds = <(int, int)>[]; - for (int i = 0; i < callableMemberIdsLength; i++) { - callableMemberIds.add((source.readInt(), source.readInt())); + final callableMembersLength = source.readInt(); + final callableMembers = {}; + for (int i = 0; i < callableMembersLength; i++) { + final reference = source.readReference(); + callableMembers.add(reference); } final updateableFunctionsInMainLength = source.readInt(); final updateableFunctionsInMain = {}; @@ -145,11 +221,11 @@ class DynamicModuleMetadata { mainModuleEnvironment[key] = value; } - return DynamicModuleMetadata( - classIds, + return MainModuleMetadata._( + classMetadata, selectorIds, - dfsOrderClassIds, - callableMemberIds, + dfsOrderClasses, + callableMembers, updateableFunctionsInMain, mainModuleTranslatorOptions, mainModuleEnvironment); @@ -253,4 +329,63 @@ class DynamicModuleMetadata { fail('environment mismatch'); } } + + void _initializeCallableReferences(Component component, CoreTypes coreTypes) { + void collectCallableReference(Reference reference) { + final member = reference.asMember; + + if (member.isExternal) { + final isGeneratedIntrinsic = member is Procedure && + MemberIntrinsic.fromProcedure(coreTypes, member) != null; + if (!isGeneratedIntrinsic) return; + } + callableReferences.add(reference); + } + + final procedureAttributeMetadata = + (component.metadata["vm.procedure-attributes.metadata"] + as ProcedureAttributesMetadataRepository) + .mapping; + void collectCallableReferences(Member member) { + if (member is Procedure) { + collectCallableReference(member.reference); + if (member.isInstanceMember && + member.kind == ProcedureKind.Method && + procedureAttributeMetadata[member]!.hasTearOffUses) { + collectCallableReference(member.tearOffReference); + } + } else if (member is Field) { + collectCallableReference(member.getterReference); + if (member.hasSetter) { + collectCallableReference(member.setterReference!); + } + } else if (member is Constructor) { + if (member.enclosingClass == coreTypes.numClass || + member.enclosingClass == coreTypes.boolClass || + member.enclosingClass == + coreTypes.index.getClass('dart:_boxed_int', 'BoxedInt') || + member.enclosingClass == + coreTypes.index.getClass('dart:_boxed_double', 'BoxedDouble')) { + return; + } + collectCallableReference(member.reference); + collectCallableReference(member.initializerReference); + collectCallableReference(member.constructorBodyReference); + } + } + + for (final lib in component.libraries) { + for (final member in lib.members) { + if (!member.isDynamicModuleCallable(coreTypes)) continue; + collectCallableReferences(member); + } + + for (final cls in lib.classes) { + for (final member in cls.members) { + if (!member.isDynamicModuleCallable(coreTypes)) continue; + collectCallableReferences(member); + } + } + } + } } diff --git a/pkg/dart2wasm/lib/dynamic_modules.dart b/pkg/dart2wasm/lib/dynamic_modules.dart index 367a8c17213..3758d9dea4e 100644 --- a/pkg/dart2wasm/lib/dynamic_modules.dart +++ b/pkg/dart2wasm/lib/dynamic_modules.dart @@ -6,8 +6,6 @@ import 'package:kernel/ast.dart'; import 'package:kernel/class_hierarchy.dart'; import 'package:kernel/core_types.dart'; import 'package:kernel/library_index.dart'; -import 'package:vm/metadata/procedure_attributes.dart' - show ProcedureAttributesMetadataRepository; import 'package:vm/transformations/dynamic_interface_annotator.dart' as dynamic_interface_annotator; import 'package:vm/transformations/pragma.dart'; @@ -21,7 +19,6 @@ import 'dispatch_table.dart'; import 'dynamic_module_kernel_metadata.dart'; import 'kernel_nodes.dart'; import 'modules.dart'; -import 'reference_extensions.dart'; import 'target.dart'; import 'translator.dart'; import 'types.dart' show InstanceConstantInterfaceType; @@ -317,22 +314,18 @@ class DynamicModuleInfo { translator.component.dynamicModuleEntryPoint; bool get isDynamicModule => dynamicEntryPoint != null; late final w.FunctionBuilder initFunction; + late final MainModuleMetadata metadata; - Map? _classIdMapping; - Map? get classIdMapping => _classIdMapping; + Map get classMetadata => metadata.classMetadata; - Map? _selectorIds; - Map? get selectorIds => _selectorIds; + Map get selectorIds => metadata.selectorIds; - List? _callableReferences; - List? get callableReferences => _callableReferences; - - List? _dfsOrderClassIds; - List? get dfsOrderClassIds => _dfsOrderClassIds; + List get dfsOrderClassIds => metadata.dfsOrderClassIds; late final w.Global moduleIdGlobal; - final Map _updateableDispatchKeys; + Map get _updateableDispatchKeys => + metadata.updateableFunctionsInMain; // null is used to indicate that skipDynamic was passed for this key. final Map _updateableFunctions = {}; @@ -346,43 +339,7 @@ class DynamicModuleInfo { late final w.ModuleBuilder dynamicModule = translator.modules.firstWhere((m) => m != translator.mainModule); - DynamicModuleInfo(this.translator, DynamicModuleMetadata? serializedMetadata) - : _updateableDispatchKeys = - serializedMetadata?.updateableFunctionsInMain ?? {} { - if (serializedMetadata != null) { - assert(isDynamicModule); - final globalIdRepository = translator - .component.metadata[DynamicModuleGlobalIdRepository.repositoryTag] - as DynamicModuleGlobalIdRepository; - final Map globalIdToNode = {}; - globalIdRepository.mapping.forEach((node, globalId) { - globalIdToNode[globalId] = node; - }); - - final assignedClassIds = {}; - serializedMetadata.classIds.forEach((globalId, classId) { - assignedClassIds[globalIdToNode[globalId] as Class] = classId; - }); - _classIdMapping = assignedClassIds; - - final assignedSelectorIds = {}; - serializedMetadata.selectorIds.forEach((globalId, selectorIds) { - assignedSelectorIds[globalIdToNode[globalId] as Member] = selectorIds; - }); - _selectorIds = assignedSelectorIds; - - _dfsOrderClassIds = [ - for (final globalId in serializedMetadata.dfsOrderClassIds) - globalIdToNode[globalId] as Class - ]; - - _callableReferences = [ - for (final reference in serializedMetadata.callableReferences) - _referenceForFlag( - globalIdToNode[reference.$1] as Member, reference.$2) - ]; - } - } + DynamicModuleInfo(this.translator, this.metadata); void initDynamicModule() { dynamicModule.functions.start = initFunction = dynamicModule.functions @@ -392,6 +349,8 @@ class DynamicModuleInfo { // Make sure the exception tag is exported from the main module. translator.getExceptionTag(dynamicModule); + _generateDynamicModuleCallableReferences(); + if (isDynamicModule) { _initDynamicModuleId(); _initModuleRtt(); @@ -426,6 +385,37 @@ class DynamicModuleInfo { b.global_set(moduleIdGlobal); } + void _generateDynamicModuleCallableReferences() { + final references = + translator.dynamicModuleInfo!.metadata.callableReferences; + + for (final reference in references) { + final member = reference.asMember; + + if (member.isInstanceMember) { + final selector = translator.dispatchTable.selectorForTarget(reference); + final targetRanges = selector + .targets(unchecked: false, dynamicModule: false) + .targetRanges + .followedBy(selector + .targets(unchecked: true, dynamicModule: false) + .targetRanges); + // Instance members are only callable if their enclosing class is + // allocated. + for (final (:range, :target) in targetRanges) { + if (target != reference) continue; + for (int classId = range.start; classId <= range.end; ++classId) { + translator.functions.recordClassTargetUse(classId, target); + } + } + } else { + // Generate static members immediately since they are unconditionally + // callable. + translator.functions.getFunction(reference); + } + } + } + void finishDynamicModule() { _registerModuleRefs( isDynamicModule ? initFunction.body : translator.initFunction.body); @@ -611,99 +601,6 @@ class DynamicModuleInfo { translator.convertType( b, updatedSignature.outputs.single, signature.outputs.single); } - - DynamicModuleMetadata toMetadata(WasmCompilerOptions options) { - assert(!isDynamicModule); - final globalIdRepository = translator - .component.metadata[DynamicModuleGlobalIdRepository.repositoryTag] - as DynamicModuleGlobalIdRepository; - - final classIdMapping = {}; - translator.classIdNumbering.classIds.forEach((cls, id) { - classIdMapping[globalIdRepository.mapping[cls]!] = - (id as AbsoluteClassId).value; - }); - - final selectorIdMapping = {}; - final procedureMetadata = - (translator.component.metadata["vm.procedure-attributes.metadata"] - as ProcedureAttributesMetadataRepository) - .mapping; - for (final library in translator.libraries) { - for (final cls in library.classes) { - for (final member in cls.procedures) { - if (!member.isInstanceMember) continue; - final globalId = globalIdRepository.mapping[member]; - // TFA might add placeholders for removed members. We can ignore - // these. - if (globalId == null) continue; - selectorIdMapping[globalId] = ( - procedureMetadata[member]!.getterSelectorId, - procedureMetadata[member]!.methodOrSetterSelectorId - ); - } - for (final member in cls.fields) { - if (!member.isInstanceMember) continue; - final globalId = globalIdRepository.mapping[member]; - // TFA might add placeholders for removed members. We can ignore - // these. - if (globalId == null) continue; - selectorIdMapping[globalId] = ( - procedureMetadata[member]!.getterSelectorId, - procedureMetadata[member]!.methodOrSetterSelectorId - ); - } - } - } - - final dfsOrderClassIds = []; - for (final cls in translator.classIdNumbering.dfsOrder) { - dfsOrderClassIds.add(globalIdRepository.mapping[cls]!); - } - - final callableMemberIds = <(int, int)>[]; - for (final export in translator.functions.dynamicModuleCallable) { - final exportMember = export.asMember; - final flag = _flagForReference(export); - callableMemberIds.add((globalIdRepository.mapping[exportMember]!, flag)); - } - return DynamicModuleMetadata( - classIdMapping, - selectorIdMapping, - dfsOrderClassIds, - callableMemberIds, - _updateableDispatchKeys, - options.translatorOptions, - options.environment); - } - - static int _flagForReference(Reference reference) { - if (reference.isImplicitGetter) return 0; - if (reference.isImplicitSetter) return 1; - if (reference.isTearOffReference) return 2; - if (reference.isConstructorBodyReference) return 3; - if (reference.isInitializerReference) return 4; - if (reference.isTypeCheckerReference) return 5; - if (reference.isCheckedEntryReference) return 6; - if (reference.isUncheckedEntryReference) return 7; - if (reference.isBodyReference) return 8; - assert(reference == reference.asMember.reference); - return 9; - } - - static Reference _referenceForFlag(Member member, int flag) { - if (flag == 0) return (member as Field).getterReference; - if (flag == 1) return (member as Field).setterReference!; - if (flag == 2) return (member as Procedure).tearOffReference; - if (flag == 3) return (member as Constructor).constructorBodyReference; - if (flag == 4) return (member as Constructor).initializerReference; - if (flag == 5) return member.typeCheckerReference; - if (flag == 6) return member.checkedEntryReference; - if (flag == 7) return member.uncheckedEntryReference; - if (flag == 8) return member.bodyReference; - assert(flag == 9); - return member.reference; - } } /// Emits code to canonicalize the provided constant value at runtime. diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart index 192ccef6188..bb643fc5991 100644 --- a/pkg/dart2wasm/lib/functions.dart +++ b/pkg/dart2wasm/lib/functions.dart @@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import 'package:kernel/ast.dart'; -import 'package:vm/metadata/procedure_attributes.dart' - show ProcedureAttributesMetadataRepository; import 'package:wasm_builder/wasm_builder.dart' as w; import 'class_info.dart'; @@ -12,7 +10,6 @@ import 'closures.dart'; import 'code_generator.dart'; import 'dispatch_table.dart'; import 'dynamic_modules.dart'; -import 'intrinsics.dart'; import 'reference_extensions.dart'; import 'translator.dart'; @@ -37,9 +34,6 @@ class FunctionCollector { // if an allocation of that class is encountered final Map> _pendingAllocation = {}; - /// Collection of references marked as callable from dynamic modules. - Set dynamicModuleCallable = {}; - late final WasmFunctionImporter _importedDynamicModuleFunctions = WasmFunctionImporter(translator, '#dmf'); @@ -141,10 +135,6 @@ class FunctionCollector { } } - if (translator.dynamicModuleSupportEnabled) { - _generateDynamicModuleCallableReferences(); - } - // Value classes are always implicitly allocated. recordClassAllocation( translator.classInfo[translator.boxedBoolClass]!.classId); @@ -154,98 +144,6 @@ class FunctionCollector { translator.classInfo[translator.boxedDoubleClass]!.classId); } - void _generateDynamicModuleCallableReferences() { - final references = dynamicModuleCallable = translator.isDynamicModule - ? translator.dynamicModuleInfo!.callableReferences!.toSet() - : _generateCallableReferences(); - - for (final reference in references) { - final member = reference.asMember; - - if (member.isInstanceMember) { - final selector = translator.dispatchTable.selectorForTarget(reference); - final targetRanges = selector - .targets(unchecked: false, dynamicModule: false) - .targetRanges - .followedBy(selector - .targets(unchecked: true, dynamicModule: false) - .targetRanges); - // Instance members are only callable if their enclosing class is - // allocated. - for (final (:range, :target) in targetRanges) { - if (target != reference) continue; - for (int classId = range.start; classId <= range.end; ++classId) { - _recordClassTargetUse(classId, target); - } - } - } else { - // Generate static members immediately since they are unconditionally - // callable. - getFunction(reference); - } - } - } - - Set _generateCallableReferences() { - assert( - translator.dynamicModuleSupportEnabled && !translator.isDynamicModule); - - final exports = {}; - void collectCallableReference(Reference reference) { - final member = reference.asMember; - - if (member.isExternal) { - final isGeneratedIntrinsic = member is Procedure && - MemberIntrinsic.fromProcedure(translator.coreTypes, member) != null; - if (!isGeneratedIntrinsic) return; - } - exports.add(reference); - } - - final procedureAttributeMetadata = - (translator.component.metadata["vm.procedure-attributes.metadata"] - as ProcedureAttributesMetadataRepository) - .mapping; - void collectCallableReferences(Member member) { - if (member is Procedure) { - collectCallableReference(member.reference); - if (member.isInstanceMember && - member.kind == ProcedureKind.Method && - procedureAttributeMetadata[member]!.hasTearOffUses) { - collectCallableReference(member.tearOffReference); - } - } else if (member is Field) { - collectCallableReference(member.getterReference); - if (member.hasSetter) { - collectCallableReference(member.setterReference!); - } - } else if (member is Constructor) { - if (translator.classInfo[member.enclosingClass]!.struct - .isSubtypeOf(translator.objectInfo.struct)) { - collectCallableReference(member.reference); - collectCallableReference(member.initializerReference); - collectCallableReference(member.constructorBodyReference); - } - } - } - - for (final lib in translator.libraries) { - for (final member in lib.members) { - if (!member.isDynamicModuleCallable(translator.coreTypes)) continue; - collectCallableReferences(member); - } - - for (final cls in lib.classes) { - for (final member in cls.members) { - if (!member.isDynamicModuleCallable(translator.coreTypes)) continue; - collectCallableReferences(member); - } - } - } - - return exports; - } - w.BaseFunction? getExistingFunction(Reference target) { return _functions[target]; } @@ -261,8 +159,8 @@ class FunctionCollector { // Export the function from the main module if it is callable from dynamic // modules. if (translator.dynamicModuleSupportEnabled && - dynamicModuleCallable.contains(target)) { - assert(translator.dynamicModuleSupportEnabled); + translator.dynamicModuleInfo!.metadata.callableReferences + .contains(target)) { _importedDynamicModuleFunctions.get(function, translator.dynamicModule, exportOnly: true); } @@ -377,7 +275,7 @@ class FunctionCollector { .targets(unchecked: useUncheckedEntry, dynamicModule: false) .targetRanges) { for (int classId = range.start; classId <= range.end; ++classId) { - _recordClassTargetUse(classId, target); + recordClassTargetUse(classId, target); } } @@ -386,14 +284,14 @@ class FunctionCollector { .targets(unchecked: useUncheckedEntry, dynamicModule: true) .targetRanges) { for (int classId = range.start; classId <= range.end; ++classId) { - _recordClassTargetUse(classId, target); + recordClassTargetUse(classId, target); } } } } } - void _recordClassTargetUse(int classId, Reference target) { + void recordClassTargetUse(int classId, Reference target) { if (_allocatedClasses.contains(classId)) { // Class declaring or inheriting member is allocated somewhere. getFunction(target); diff --git a/pkg/dart2wasm/lib/serialization.dart b/pkg/dart2wasm/lib/serialization.dart index d124d5067ff..fb98f59662a 100644 --- a/pkg/dart2wasm/lib/serialization.dart +++ b/pkg/dart2wasm/lib/serialization.dart @@ -5,13 +5,193 @@ import 'dart:convert'; import 'dart:typed_data'; -class BinaryDataSink { +import 'package:kernel/ast.dart'; + +import 'dynamic_module_kernel_metadata.dart'; +import 'reference_extensions.dart'; + +class _EntityToIdMapper { + final Component component; + final Map _ids; + + _EntityToIdMapper(this.component) + : _ids = + (component.metadata[DynamicModuleGlobalIdRepository.repositoryTag] + as DynamicModuleGlobalIdRepository) + .mapping; + + int idForClass(Class cls) { + return _ids[cls]!; + } + + int? idForMember(Member member) { + return _ids[member]; + } + + (int, int)? idForReference(Reference reference) { + final memberId = idForMember(reference.asMember); + if (memberId == null) return null; + return (memberId, _flagForReference(reference)); + } + + static int _flagForReference(Reference reference) { + if (reference.isImplicitGetter) return 0; + if (reference.isImplicitSetter) return 1; + if (reference.isTearOffReference) return 2; + if (reference.isConstructorBodyReference) return 3; + if (reference.isInitializerReference) return 4; + if (reference.isTypeCheckerReference) return 5; + if (reference.isCheckedEntryReference) return 6; + if (reference.isUncheckedEntryReference) return 7; + if (reference.isBodyReference) return 8; + assert(reference == reference.asMember.reference); + return 9; + } +} + +class _IdToEntityMapper { + final Component component; + final Map _mapping; + + static Map _makeIdsMap(Component component) { + final mapping = + (component.metadata[DynamicModuleGlobalIdRepository.repositoryTag] + as DynamicModuleGlobalIdRepository) + .mapping; + final inverse = {}; + mapping.forEach((node, id) { + inverse[id] = node; + }); + return inverse; + } + + _IdToEntityMapper(this.component) : _mapping = _makeIdsMap(component); + + Class classForId(int id) { + return _mapping[id] as Class; + } + + Member memberForId(int id) { + return _mapping[id] as Member; + } + + Reference referenceForId(int classId, int flag) { + final member = _mapping[classId] as Member; + return _referenceForFlag(member, flag); + } + + static Reference _referenceForFlag(Member member, int flag) { + if (flag == 0) return (member as Field).getterReference; + if (flag == 1) return (member as Field).setterReference!; + if (flag == 2) return (member as Procedure).tearOffReference; + if (flag == 3) return (member as Constructor).constructorBodyReference; + if (flag == 4) return (member as Constructor).initializerReference; + if (flag == 5) return member.typeCheckerReference; + if (flag == 6) return member.checkedEntryReference; + if (flag == 7) return member.uncheckedEntryReference; + if (flag == 8) return member.bodyReference; + assert(flag == 9); + return member.reference; + } +} + +class DataSerializer { + final _BinaryDataSink _sink = _BinaryDataSink(); + final _EntityToIdMapper _mapper; + + DataSerializer(Component component) : _mapper = _EntityToIdMapper(component); + + void writeString(String value) { + _sink.writeString(value); + } + + void writeInt(int value) { + _sink.writeInt(value); + } + + void writeBool(bool value) { + _sink.writeInt(value ? 1 : 0); + } + + void writeEnum(E value) { + writeInt(value.index); + } + + void writeClass(Class cls) { + writeInt(_mapper.idForClass(cls)); + } + + bool writeMember(Member member) { + final memberId = _mapper.idForMember(member); + if (memberId == null) return false; + writeInt(memberId); + return true; + } + + bool writeReference(Reference reference) { + final referenceId = _mapper.idForReference(reference); + if (referenceId == null) return false; + writeInt(referenceId.$1); + writeInt(referenceId.$2); + return true; + } + + Uint8List takeBytes() { + return _sink.takeBytes(); + } +} + +class DataDeserializer { + final _BinaryDataSource _source; + final _IdToEntityMapper _mapper; + + DataDeserializer(Uint8List bytes, Component component) + : _source = _BinaryDataSource(bytes), + _mapper = _IdToEntityMapper(component); + + String readString() { + return _source.readString(); + } + + int readInt() { + return _source.readInt(); + } + + bool readBool() { + return _source.readInt() == 1; + } + + E readEnum(List values) { + int index = _source.readInt(); + assert( + 0 <= index && index < values.length, + "Invalid data kind index. " + "Expected one of $values, found index $index."); + return values[index]; + } + + Class readClass() { + return _mapper.classForId(_source.readInt()); + } + + Member readMember() { + return _mapper.memberForId(_source.readInt()); + } + + Reference readReference() { + final classId = _source.readInt(); + final flag = _source.readInt(); + return _mapper.referenceForId(classId, flag); + } +} + +class _BinaryDataSink { static const int _initSinkSize = 50 * 1024; Uint8List _data = Uint8List(_initSinkSize); int _length = 0; - BinaryDataSink(); + _BinaryDataSink(); int get length => _length; @@ -62,15 +242,6 @@ class BinaryDataSink { } } - void writeClassId(int value) { - // Add 1 since some class IDs are -1. - writeInt(value + 1); - } - - void writeEnum(E value) { - writeInt(value.index); - } - Uint8List takeBytes() { final result = Uint8List.sublistView(_data, 0, _length); // Free the reference to the large data list so it can potentially be @@ -80,11 +251,11 @@ class BinaryDataSink { } } -class BinaryDataSource { +class _BinaryDataSource { int _byteOffset = 0; final Uint8List _bytes; - BinaryDataSource(this._bytes); + _BinaryDataSource(this._bytes); void begin(String tag) {} @@ -119,20 +290,6 @@ class BinaryDataSource { } } - int readClassId() { - // Subtract 1 since some class IDs are -1. - return readInt() - 1; - } - - E readEnum(List values) { - int index = readInt(); - assert( - 0 <= index && index < values.length, - "Invalid data kind index. " - "Expected one of $values, found index $index."); - return values[index]; - } - int get length => _bytes.length; int get currentOffset => _byteOffset; } diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index c1317ccf158..dfd91490379 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -61,7 +61,7 @@ class TranslatorOptions { bool requireJsStringBuiltin = false; List watchPoints = []; - void serialize(BinaryDataSink sink) { + void serialize(DataSerializer sink) { sink.writeBool(enableAsserts); sink.writeBool(importSharedMemory); sink.writeBool(inlining); @@ -85,7 +85,7 @@ class TranslatorOptions { sharedMemoryMaxPages == null ? 0 : (sharedMemoryMaxPages! + 1)); } - static TranslatorOptions deserialize(BinaryDataSource source) { + static TranslatorOptions deserialize(DataDeserializer source) { final TranslatorOptions options = TranslatorOptions(); options.enableAsserts = source.readBool(); options.importSharedMemory = source.readBool(); @@ -410,7 +410,7 @@ class Translator with KernelNodes { Translator(this.component, this.coreTypes, this.index, this.recordClasses, this._moduleOutputData, this.options, {bool enableDynamicModules = false, - DynamicModuleMetadata? dynamicModuleMetadata}) + required MainModuleMetadata mainModuleMetadata}) : libraries = component.libraries, hierarchy = ClassHierarchy(component, coreTypes) as ClosedWorldClassHierarchy { @@ -425,7 +425,7 @@ class Translator with KernelNodes { types = Types(this); exceptionTag = ExceptionTag(this); if (enableDynamicModules) { - dynamicModuleInfo = DynamicModuleInfo(this, dynamicModuleMetadata); + dynamicModuleInfo = DynamicModuleInfo(this, mainModuleMetadata); } } diff --git a/pkg/wasm_builder/lib/src/builder/types.dart b/pkg/wasm_builder/lib/src/builder/types.dart index 94ce6d5cacd..174ada40a45 100644 --- a/pkg/wasm_builder/lib/src/builder/types.dart +++ b/pkg/wasm_builder/lib/src/builder/types.dart @@ -75,6 +75,7 @@ class _RecGroupBuilder { late final List> _allRecursiveGroups = _createAllRecursiveGroups(); final List _allDefinedTypes = []; + final Map _brandTypeAssignments = {}; _RecGroupBuilder(); @@ -121,7 +122,7 @@ class _RecGroupBuilder { /// If a rec group is in an equivalence class with multiple groups, it will be /// assigned an index encoded as a brand type. The brand types allows the wasm /// type system to disambiguate members of otherwise equivalent rec groups. - static void _assignBrandTypes(List> groups) { + void _assignBrandTypes(List> groups) { // Collect rec groups joining over: // (1) group length // (2) length of first struct @@ -148,7 +149,12 @@ class _RecGroupBuilder { // All the groups in `equalGroups` are structurally equivalent. // Skip the first group since we can leave one group as-is. for (int i = 1; i < equalGroups.length; i++) { - equalGroups[i].insert(0, _getBrandType(i - 1)); + // Key the assignment on the first element in the group. If a user is + // trying to use the brand index to restore the group, then all other + // elements are implicitly the same. + final typeIndex = _brandTypeAssignments[equalGroups[i].first] ??= i - 1; + final brandType = _getBrandType(typeIndex); + equalGroups[i].insert(0, brandType); } } } @@ -241,6 +247,12 @@ class TypesBuilder with Builder { TypesBuilder(this._module, {TypesBuilder? parent}) : _recGroupBuilder = parent?._recGroupBuilder ?? _RecGroupBuilder(); + Map get brandTypeAssignments => + _recGroupBuilder._brandTypeAssignments; + + void addBrandTypeAssignment(ir.DefType type, int brandIndex) => + _recGroupBuilder._brandTypeAssignments[type] = brandIndex; + /// Add a new function type to the module. /// /// All function types are canonicalized, such that identical types become