loadWebModule static method
Loads the Emscripten glue script for libName and instantiates its
module. Idempotent: concurrent and repeated calls share one load.
jsUrl is the URL of the MODULARIZE glue .js (the .wasm is fetched
relative to it). Defaults to the Flutter asset path
assets/packages/<package>/assets/web/<libName>.js when assetPackage
is given, else assets/web/<libName>.js relative to the page.
Implementation
static Future<NitroWasmModule> loadWebModule(
String libName, {
String? jsUrl,
String? assetPackage,
}) {
final cached = _moduleCache[libName];
if (cached != null) {
_libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
return Future.value(cached);
}
// Every awaiter of a shared in-flight load must count as a holder, and a
// FAILED load must not stay cached — otherwise a transient network error
// poisons the module forever, and N concurrent callers share one refcount
// so the first releaseLib evicts a module the others still hold.
final inFlight = _loading[libName];
if (inFlight != null) {
// Count only on success, so a shared FAILED load leaks no reference.
return inFlight.then((m) {
_libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
return m;
});
}
return _loading.putIfAbsent(libName, () async {
final url = jsUrl ?? (assetPackage != null ? 'assets/packages/$assetPackage/assets/web/$libName.js' : 'assets/web/$libName.js');
_log(NitroLogLevel.verbose, 'loadWebModule', 'Loading WASM module: $libName from $url');
final sw = Stopwatch()..start();
await _injectScript(url, libName);
final factoryName = nitroWebExportName(libName);
final factory = globalContext.getProperty(factoryName.toJS);
if (factory == null || !factory.typeofEquals('function')) {
throw StateError(
'$libName: $url loaded but did not define $factoryName(). Build the '
'module with -sMODULARIZE=1 -sEXPORT_NAME=$factoryName '
'(web/build_web.sh does this).',
);
}
final promise = (factory as JSFunction).callAsFunction(null, JSObject())! as JSPromise;
final raw = await promise.toDart;
final module = NitroWasmModule(libName, EmscriptenModule(raw! as JSObject));
_registerPostFn(module);
sw.stop();
_log(NitroLogLevel.verbose, 'loadWebModule', 'Loaded: $libName in ${sw.elapsedMicroseconds} µs');
_moduleCache[libName] = module;
_libRefCount[libName] = (_libRefCount[libName] ?? 0) + 1;
return module;
}).whenComplete(() => _loading.remove(libName));
}