@@ -14,6 +14,8 @@ import 'package:wasm_builder/wasm_builder.dart' as w;
1414import 'class_info.dart' ;
1515import 'closures.dart' ;
1616import 'code_generator.dart' ;
17+ import 'dynamic_module_kernel_metadata.dart'
18+ show DynamicModuleConstantRepository;
1719import 'dynamic_modules.dart' ;
1820import 'param_info.dart' ;
1921import 'translator.dart' ;
@@ -71,7 +73,10 @@ typedef ConstantCodeGenerator = void Function(w.InstructionsBuilder);
7173class Constants {
7274 final Translator translator;
7375 final Map <Constant , ConstantInfo > constantInfo = {};
74- final Map <Constant , ConstantInfo > dynamicModuleConstantInfo = {};
76+ late final Map <Constant , int >? dynamicMainModuleConstantId = (translator
77+ .component.metadata[DynamicModuleConstantRepository .repositoryTag]
78+ as DynamicModuleConstantRepository ? )
79+ ? .mapping[translator.component] ?? = {};
7580 w.DataSegmentBuilder ? int32Segment;
7681 late final ClassInfo typeInfo = translator.classInfo[translator.typeClass]! ;
7782
@@ -494,55 +499,106 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
494499 constant = constants._lowerTypeConstant (type);
495500 }
496501
497- final cache = translator.dynamicModuleSupportEnabled &&
498- ! translator.isMainModule (targetModule)
499- ? constants.dynamicModuleConstantInfo
500- : constants.constantInfo;
501- ConstantInfo ? info = cache[constant];
502+ ConstantInfo ? info = constants.constantInfo[constant];
502503 if (info == null ) {
503504 info = constant.accept (this );
504505 if (info != null ) {
505- cache [constant] = info;
506+ constants.constantInfo [constant] = info;
506507 }
507508 }
508509 return info;
509510 }
510511
512+ static String _dynamicModuleConstantExportName (int id) => '#c$id ' ;
513+ static String _dynamicModuleInitFunctionExportName (int id) => '#cf$id ' ;
514+
511515 ConstantInfo createConstant (
512516 Constant constant, w.RefType type, ConstantCodeGenerator generator,
513517 {bool lazy = false }) {
514518 assert (! type.nullable);
515- if (lazy || translator.dynamicModuleSupportEnabled) {
519+
520+ // This function is only called once per [Constant]. If we compile a
521+ // dynamic module then the [dynamicModuleConstantIdMap] is pre-populated and
522+ // we may find an export name. If we compile the main module, then the id
523+ // will be `null`.
524+ final dynamicModuleConstantIdMap = constants.dynamicMainModuleConstantId;
525+ final mainModuleExportId = dynamicModuleConstantIdMap? [constant];
526+ final isShareableAcrossModules = dynamicModuleConstantIdMap != null &&
527+ constant.accept (_ConstantDynamicModuleSharedChecker (translator));
528+ final needsRuntimeCanonicalization = isShareableAcrossModules &&
529+ translator.isDynamicModule &&
530+ mainModuleExportId == null ;
531+
532+ if (lazy || needsRuntimeCanonicalization) {
516533 // Create uninitialized global and function to initialize it.
517- final global =
518- targetModule.globals. define (w. GlobalType (type. withNullability ( true ))) ;
519- global.initializer. ref_null (w. HeapType .none) ;
520- global.initializer. end ();
534+ final globalType = w. GlobalType (type. withNullability ( true ));
535+ w. Global global ;
536+ w. BaseFunction initFunction ;
537+
521538 w.FunctionType ftype =
522539 translator.typesBuilder.defineFunction (const [], [type]);
523- final function = targetModule.functions.define (ftype, "$constant " );
524- final b2 = function.body;
525- generator (b2);
526- if (translator.dynamicModuleSupportEnabled) {
527- final valueLocal = b2.addLocal (type);
528- constant.accept (ConstantCanonicalizer (translator, b2, valueLocal));
540+
541+ if (mainModuleExportId != null ) {
542+ global = targetModule.globals.import (translator.mainModule.moduleName,
543+ _dynamicModuleConstantExportName (mainModuleExportId), globalType);
544+ initFunction = targetModule.functions.import (
545+ translator.mainModule.moduleName,
546+ _dynamicModuleInitFunctionExportName (mainModuleExportId),
547+ ftype);
548+ } else {
549+ final definedGlobal = global = targetModule.globals.define (globalType);
550+ definedGlobal.initializer.ref_null (w.HeapType .none);
551+ definedGlobal.initializer.end ();
552+
553+ final function =
554+ initFunction = targetModule.functions.define (ftype, "$constant " );
555+
556+ if (isShareableAcrossModules) {
557+ final exportId = dynamicModuleConstantIdMap[constant] =
558+ dynamicModuleConstantIdMap.length;
559+
560+ targetModule.exports.export (
561+ _dynamicModuleConstantExportName (exportId), definedGlobal);
562+ targetModule.exports
563+ .export (_dynamicModuleInitFunctionExportName (exportId), function);
564+ }
565+ final b2 = function.body;
566+ generator (b2);
567+ if (needsRuntimeCanonicalization) {
568+ final valueLocal = b2.addLocal (type);
569+ constant.accept (ConstantCanonicalizer (translator, b2, valueLocal));
570+ }
571+ w.Local temp = b2.addLocal (type);
572+ b2.local_tee (temp);
573+ b2.global_set (global);
574+ b2.local_get (temp);
575+ b2.end ();
529576 }
530- w.Local temp = b2.addLocal (type);
531- b2.local_tee (temp);
532- b2.global_set (global);
533- b2.local_get (temp);
534- b2.end ();
535577
536- return ConstantInfo (constant, global, function );
578+ return ConstantInfo (constant, global, initFunction );
537579 } else {
538580 // Create global with the constant in its initializer.
539581 assert (! constants.currentlyCreating);
540- constants.currentlyCreating = true ;
541- final global =
542- targetModule.globals.define (w.GlobalType (type, mutable: false ));
543- generator (global.initializer);
544- global.initializer.end ();
545- constants.currentlyCreating = false ;
582+ final globalType = w.GlobalType (type, mutable: false );
583+ w.Global global;
584+ if (mainModuleExportId != null ) {
585+ global = targetModule.globals.import (translator.mainModule.moduleName,
586+ _dynamicModuleConstantExportName (mainModuleExportId), globalType);
587+ } else {
588+ constants.currentlyCreating = true ;
589+ final definedGlobal = global = targetModule.globals.define (globalType);
590+ generator (definedGlobal.initializer);
591+ definedGlobal.initializer.end ();
592+ constants.currentlyCreating = false ;
593+
594+ if (isShareableAcrossModules) {
595+ final exportId = dynamicModuleConstantIdMap[constant] =
596+ dynamicModuleConstantIdMap.length;
597+
598+ targetModule.exports.export (
599+ _dynamicModuleConstantExportName (exportId), definedGlobal);
600+ }
601+ }
546602
547603 return ConstantInfo (constant, global, null );
548604 }
@@ -651,6 +707,10 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
651707 args = supertype.typeArguments;
652708 }
653709
710+ // If the class ID is relative then it needs to be globalized when
711+ // initializing the object which is a non-const operation.
712+ lazy | = info.classId is RelativeClassId ;
713+
654714 return createConstant (constant, type, lazy: lazy, (b) {
655715 b.pushObjectHeaderFields (translator, info);
656716 for (int i = FieldIndex .objectFieldBase; i < fieldCount; i++ ) {
@@ -910,8 +970,8 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
910970
911971 // The vtable for the target will be stored on a global in the target's
912972 // module.
913- final isLazy =
914- translator.moduleForReference (constant.targetReference) != targetModule ;
973+ final isLazy = translator. moduleForReference (constant.targetReference) !=
974+ translator.mainModule ;
915975 // The dummy struct must be declared before the constant global so that the
916976 // constant's initializer can reference it.
917977 final dummyStructGlobal = translator
@@ -1158,3 +1218,36 @@ List<int> _intToLittleEndianBytes(int i) {
11581218}
11591219
11601220String _intToBase64 (int i) => base64.encode (_intToLittleEndianBytes (i));
1221+
1222+ /// Resolves to true if the visited Constant is accessible from dynamic modules.
1223+ ///
1224+ /// Constants that are accessible from dynamic modules should be:
1225+ /// (1) Exported from the main module if they exist there and then imported
1226+ /// into dynamic modules.
1227+ /// (2) Runtime canonicalized by dynamic modules if they are not in the main
1228+ /// module.
1229+ class _ConstantDynamicModuleSharedChecker extends ConstantVisitor <bool >
1230+ with ConstantVisitorDefaultMixin <bool > {
1231+ final Translator translator;
1232+
1233+ _ConstantDynamicModuleSharedChecker (this .translator);
1234+
1235+ // TODO(natebiggs): Make this more precise by handling more specific
1236+ // constants.
1237+ @override
1238+ bool defaultConstant (Constant constant) => true ;
1239+
1240+ @override
1241+ bool visitInstanceConstant (InstanceConstant constant) {
1242+ final cls = constant.classNode;
1243+ if (! cls.enclosingLibrary.isFromMainModule (translator.coreTypes)) {
1244+ return false ;
1245+ }
1246+ if (cls == translator.wasmArrayClass ||
1247+ cls == translator.immutableWasmArrayClass) {
1248+ return true ;
1249+ }
1250+ return constant.classNode.constructors.any (
1251+ (c) => c.isConst && c.isDynamicModuleCallable (translator.coreTypes));
1252+ }
1253+ }
0 commit comments