[dart2wasm] Fix @pragma('wasm:initialize-at-startup') in deferred loading scenario

The module owning the storage for the static field has to emit the
initialization code in it's start function.

Change-Id: I9089e99d6e12ea275343cebe3e456658a7c7a540
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/485480
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann
2026-03-05 11:10:13 -08:00
committed by Commit Queue
parent c421bc9df2
commit 2c8f4300d0
8 changed files with 134 additions and 15 deletions
+3 -2
View File
@@ -650,8 +650,9 @@ class DynamicModuleInfo {
}
void finishDynamicModule() {
_registerModuleRefs(
isSubmodule ? initFunction.body : translator.initFunction.body);
_registerModuleRefs(isSubmodule
? initFunction.body
: translator.mainModule.startFunction.body);
}
void _registerModuleRefs(w.InstructionsBuilder b) {
+5 -9
View File
@@ -160,15 +160,11 @@ class DartGlobals {
final definition =
_defineGlobalBasedField(field, fieldType, module, true, init, null);
if (module.module == translator.initFunction.enclosingModule) {
// We have to initialize the global field in the same module as where
// the field value is defined in.
// TODO: Once dynamic modules only compile code for the submodule and
// not the main module, we should turn this into an assert.
EagerStaticFieldInitializerCodeGenerator(
translator, field, definition.global)
.generate(translator.initFunction.body, [], null);
}
// We have to initialize the global field in the same module as where
// the field value is defined in.
EagerStaticFieldInitializerCodeGenerator(
translator, field, definition.global)
.generate(module.startFunction.body, [], null);
return definition;
}
+1 -4
View File
@@ -247,7 +247,6 @@ class Translator with KernelNodes {
final Set<Member> membersContainingInnerFunctions = {};
final Set<Member> membersBeingGenerated = {};
final Map<Reference, Closures> constructorClosures = {};
late final w.FunctionBuilder initFunction;
late final w.ValueType voidMarker =
w.RefType.def(w.StructType("void"), nullable: true);
// Lazily import FFI memory if used.
@@ -595,8 +594,6 @@ class Translator with KernelNodes {
Map<ModuleMetadata, w.Module> translate(
Uri Function(String moduleName)? sourceMapUrlGenerator) {
_initModules(sourceMapUrlGenerator);
initFunction = mainModule.startFunction;
closureLayouter.collect();
classInfoCollector.collect();
@@ -637,7 +634,7 @@ class Translator with KernelNodes {
}
});
}
_printFunction(initFunction, "init");
_printFunction(mainModule.startFunction, "init");
// Remove empty modules.
_outputToBuilder.removeWhere((outputModule, moduleBuilder) {
+1
View File
@@ -64,6 +64,7 @@ void main(List<String> args) async {
'pkg/dart2wasm/tool/compile_benchmark',
for (final option in compilerOptions) '--extra-compiler-option=$option',
'--extra-compiler-option=--no-unique-constant-names',
'--extra-compiler-option=--enable-experimental-wasm-interop',
if (runFromSource) '--src',
'--no-strip-wasm',
'-o',
@@ -0,0 +1,29 @@
// Copyright (c) 2026, 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.
//
// functionFilter=write|read|#init
// tableFilter=cross-module-funcs
// globalFilter=array
// typeFilter=NoMatch
// compilerOption=--enable-deferred-loading
// compilerOption=--no-minify
import 'dart:_wasm';
import '' deferred as D;
import 'package:expect/expect.dart';
void main() async {
await D.loadLibrary();
D.write();
Expect.equals('hello', D.read());
}
void write() => array[0] = 'hello';
String read() => array[0]!;
@pragma("wasm:initialize-at-startup")
final WasmArray<String?> array = WasmArray<String?>(1);
@@ -0,0 +1,34 @@
(module $module0
(type $#Top <...>)
(type $Array<String> <...>)
(type $Array<WasmI16> <...>)
(type $Array<int> <...>)
(type $JSExternWrapper <...>)
(type $Object <...>)
(table $cross-module-funcs-0 (export "cross-module-funcs-0") 3 funcref)
(global $"\"1.0\"" (ref $JSExternWrapper) <...>)
(global $BoxedDouble._cacheKeys (mut (ref $Array<int>)) <...>)
(global $BoxedDouble._cacheValues (mut (ref $Array<String>)) <...>)
(global $JSStringImpl._stringFromCodePointBuffer (mut (ref $Array<WasmI16>)) <...>)
(global $_deletedDataMarker (mut (ref $#Top)) <...>)
(elem $cross-module-funcs-0
(set 2 (ref.func $"_TypeError._throwNullCheckErrorWithCurrentStack <noInline>")))
(func $_TypeError._throwNullCheckErrorWithCurrentStack <noInline> (result (ref none)) <...>)
(func $#init
global.get $"\"1.0\""
i32.const 16
array.new $Array<String>
global.set $BoxedDouble._cacheValues
i64.const 4607182418800017408
i32.const 16
array.new $Array<int>
global.set $BoxedDouble._cacheKeys
i32.const 2
array.new_default $Array<WasmI16>
global.set $JSStringImpl._stringFromCodePointBuffer
i32.const 1
i32.const 0
struct.new $Object
global.set $_deletedDataMarker
)
)
@@ -0,0 +1,37 @@
(module $module1
(type $#Top <...>)
(type $Array<String?> <...>)
(type $JSExternWrapper <...>)
(global $"\"hello\"" (import "module0" "global0") (ref $JSExternWrapper))
(table $module0.cross-module-funcs-0 (import "module0" "cross-module-funcs-0") 3 funcref)
(global $array (mut (ref $Array<String?>))
(array.new_fixed $Array<String?> 0))
(elem $module0.cross-module-funcs-0
(set 0 (ref.func $write))
(set 1 (ref.func $read)))
(func $#init
i32.const 1
array.new_default $Array<String?>
global.set $array
)
(func $read (result (ref $JSExternWrapper))
block $label0 (result (ref $JSExternWrapper))
global.get $array
i32.const 0
array.get $Array<String?>
br_on_non_null $label0
i32.const 2
call_indirect (result (ref none))
unreachable
end $label0
)
(func $write (result (ref null $#Top))
(local $var0 (ref $JSExternWrapper))
global.get $array
i32.const 0
global.get $"\"hello\""
local.tee $var0
array.set $Array<String?>
local.get $var0
)
)
@@ -0,0 +1,24 @@
// Copyright (c) 2026, 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.
//
// dart2wasmOptions=--enable-deferred-loading --extra-compiler-option=--enable-experimental-wasm-interop
import 'dart:_wasm';
import '' deferred as D;
import 'package:expect/expect.dart';
void main() async {
await D.loadLibrary();
D.write();
Expect.equals('hello', D.read());
}
void write() => array[0] = 'hello';
String read() => array[0]!;
@pragma("wasm:initialize-at-startup")
final WasmArray<String?> array = WasmArray<String?>(1);