[dart2wasm] Separate deferred map json creation from use of load ids.

Currently the 'load-ids' controls both how deferred modules are loaded and whether or not the compiler emits a deferred map JSON file.

This change separates these 2 actions into 2 separate flags. 'deferred-map' now provides the URI for the JSON file. And 'use-load-ids' controls how deferred modules are loaded.

This is needed for some tools that might not want to use load ids but still need to process the deferred map file.

Change-Id: I6909307f058d8f97dd8987e421f693ee97d6572f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/499520
Reviewed-by: Martin Kustermann <kustermann@google.com>
Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
Nate Biggs
2026-04-30 09:10:58 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent c783ae42a4
commit 9f33810b01
9 changed files with 41 additions and 28 deletions
+3 -3
View File
@@ -651,9 +651,9 @@ Future<CompilationResult> _runCodegenPhase(
final supportJs = _generateSupportJs(options.translatorOptions);
final loadIdsFile = options.loadsIdsUri;
if (loadIdsFile != null) {
await writeLoadIdsFile(component, coreTypes, options, loadingMap);
final deferredMapFile = options.deferredMapUri;
if (deferredMapFile != null) {
await writeDeferredMapFile(component, coreTypes, options, loadingMap);
}
final wasmOutputFilename = path.basename(options.outputFile);
+12 -3
View File
@@ -42,7 +42,8 @@ class WasmCompilerOptions {
Uri mainUri;
String outputFile;
String? depFile;
Uri? loadsIdsUri;
Uri? deferredMapUri;
bool useLoadIds = false;
Uri? programSplitConstraintsUri;
Map<String, String> environment = {};
Map<fe.ExperimentalFlag, bool> feExperimentalFlags = const {};
@@ -125,12 +126,20 @@ class WasmCompilerOptions {
}
if (!translatorOptions.enableDeferredLoading) {
if (loadsIdsUri != null) {
if (deferredMapUri != null) {
throw ArgumentError(
"--load-ids can only be used with "
"--deferred-map can only be used with "
"--enable-deferred-loading",
);
}
if (useLoadIds) {
throw ArgumentError(
"--use-load-ids can only be used with "
"--enable-deferred-loading",
);
}
} else if (useLoadIds && deferredMapUri == null) {
throw ArgumentError("--use-load-ids requires --deferred-map");
}
if (translatorOptions.standalone) {
+1 -1
View File
@@ -37,7 +37,7 @@ class ConstantEvaluator extends kernel.ConstantEvaluator
_deferredLoadingEnabled =
options.translatorOptions.enableDeferredLoading ||
options.translatorOptions.enableMultiModuleStressTestMode,
_deferredLoadingViaEmbedderLoadId = options.loadsIdsUri != null,
_deferredLoadingViaEmbedderLoadId = options.useLoadIds,
_dartInternalCheckBoundsGetter = libraryIndex.tryGetProcedure(
"dart:_internal",
LibraryIndex.topLevel,
+2 -1
View File
@@ -179,7 +179,8 @@ final List<Option> options = [
(o, value) => o.translatorOptions.enableDeferredLoading = value,
defaultsTo: _d.translatorOptions.enableDeferredLoading,
),
UriOption("load-ids", (o, value) => o.loadsIdsUri = value),
UriOption("deferred-map", (o, value) => o.deferredMapUri = value),
Flag("use-load-ids", (o, value) => o.useLoadIds = value),
UriOption(
"read-program-split",
(o, value) => o.programSplitConstraintsUri = value,
+2 -2
View File
@@ -235,13 +235,13 @@ class StressTestModuleStrategy extends ModuleStrategy {
ModuleOutputData buildModuleOutputData() => moduleOutputData;
}
Future<void> writeLoadIdsFile(
Future<void> writeDeferredMapFile(
Component component,
CoreTypes coreTypes,
WasmCompilerOptions options,
DeferredModuleLoadingMap loadingMap,
) async {
final file = File.fromUri(options.loadsIdsUri!);
final file = File.fromUri(options.deferredMapUri!);
await file.create(recursive: true);
await file.writeAsString(
_generateDeferredMapJson(
+7 -6
View File
@@ -56,12 +56,13 @@ class CompiledApp {
// loadDeferredModules should return a Promise that resolves when all the
// modules have been loaded and the callback promises have resolved.
// `loadDeferredId` is a JS function that takes load ID produced by the
// compiler when the `load-ids` option is passed. Each load ID maps to one
// or more wasm files as specified in the emitted JSON file. It also takes a
// callback that should be invoked for each loaded module with 2 arugments:
// (1) the module name, (2) the loaded module in a format supported by
// `WebAssembly.compile` or `WebAssembly.compileStreaming`. The callback
// returns a Promise that resolves when the module is instantiated.
// compiler when the `use-load-ids` option is passed. Each load ID maps to
// one or more wasm files as specified in the emitted JSON file. It also
// takes a callback that should be invoked for each loaded module with 2
// arugments: (1) the module name, (2) the loaded module in a format
// supported by `WebAssembly.compile` or `WebAssembly.compileStreaming`.
// The callback returns a Promise that resolves when the module is
// instantiated.
// loadDeferredModules should return a Promise that resolves when all the
// modules have been loaded and the callback promises have resolved.
async instantiate(additionalImports, {loadDeferredModules, loadDeferredId} = {}) {
+2 -2
View File
@@ -599,12 +599,12 @@ class Translator with KernelNodes {
getThisModuleGlobal(moduleBuilder);
}
// This getter will be null if we pass e.g. `--load-ids=<uri>` as the
// This getter will be null if we pass e.g. `--use-load-ids` as the
// runtime code will then be pruned to call out to embedder instead of
// consulting the load mapping bundled in the app.
final loadingMapGetter = dartInternalLoadingMapGetter;
if (loadingMapGetter != null && !options.standalone) {
// This function will be null if we didn't pass `--load-ids=<uri>` but we
// This function will be null if we didn't pass `--use-load-ids` but we
// ended up not having any actual deferred code (e.g. `await
// foo.loadLibrary()` is never called anywhere).
final function =
@@ -1,4 +1,4 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// 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.
@@ -15,7 +15,7 @@ const String helperJsLoadIdLookupToken = 'LOAD_ID_LOOKUP';
const String helperJsModuleDirToken = 'MODULE_DIR';
final String goldenPath =
'${path.dirname(Platform.script.path)}/data/deferred_load_ids.golden.json';
'${path.dirname(Platform.script.path)}/data/deferred_map.golden.json';
final String mainDart = '${path.dirname(Platform.script.path)}/data/main.dart';
final String helperJs = '${path.dirname(Platform.script.path)}/helper.js';
@@ -27,7 +27,7 @@ Future<void> main(List<String> args) async {
await withTempDir((tmpDirPath) async {
final tmpDir = File(tmpDirPath);
final loadIdsUri = tmpDir.uri.resolve('deferred_load_ids.json');
final deferredMapUri = tmpDir.uri.resolve('deferred_map.json');
final outFilename = '${tmpDir.path}/out.wasm';
// Compile the test
await run([
@@ -36,25 +36,27 @@ Future<void> main(List<String> args) async {
mainDart,
'--platform=$platformDill',
'--enable-deferred-loading',
'--load-ids=$loadIdsUri',
'--deferred-map=$deferredMapUri',
outFilename,
]);
final loadIdsContent = await File.fromUri(loadIdsUri).readAsString();
final deferredMapContent = await File.fromUri(
deferredMapUri,
).readAsString();
final goldenFile = File(goldenPath);
if (argsResult.flag('update-golden')) {
print('Updating golden:\n$loadIdsContent');
await goldenFile.writeAsString(loadIdsContent);
print('Updating golden:\n$deferredMapContent');
await goldenFile.writeAsString(deferredMapContent);
return;
}
// Ensure the load IDs JSON matches the golden.
Expect.equals(await goldenFile.readAsString(), loadIdsContent);
// Ensure the deferred map JSON matches the golden.
Expect.equals(await goldenFile.readAsString(), deferredMapContent);
// Extract a map from each load ID to its corresponding module.
final loadIdToModules = <String, List<String>>{};
final json = const JsonDecoder().convert(loadIdsContent);
final json = const JsonDecoder().convert(deferredMapContent);
(json as Map).forEach((_, info) {
((info as Map)['imports'] as Map).forEach((loadId, modules) {
loadIdToModules[loadId] = (modules as List).cast<String>();