Skip to content

Commit 6cbc6df

Browse files
fishythefishCommit Queue
authored andcommitted
[dart2wasm] Add export name minification to dynamic modules.
Change-Id: Id316930bc9d65566d16a12b494caacc846f899ff Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/441989 Reviewed-by: Nate Biggs <[email protected]> Reviewed-by: Ömer Ağacan <[email protected]>
1 parent 17c6e3c commit 6cbc6df

File tree

10 files changed

+237
-37
lines changed

10 files changed

+237
-37
lines changed

pkg/dart2wasm/lib/constants.dart

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,9 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
498498
return info;
499499
}
500500

501-
static String _dynamicModuleConstantExportName(int id) => '#c$id';
502-
static String _dynamicModuleInitFunctionExportName(int id) => '#cf$id';
501+
static String _dynamicModuleConstantExportName(int id) => '#constant$id';
502+
static String _dynamicModuleInitFunctionExportName(int id) =>
503+
'#constantFunction$id';
503504

504505
static int _nextGlobalId = 0;
505506
String _constantName(Constant constant) {
@@ -577,11 +578,15 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
577578
translator.typesBuilder.defineFunction(const [], [type]);
578579

579580
if (mainModuleExportId != null) {
580-
global = targetModule.globals.import(translator.mainModule.moduleName,
581-
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
581+
global = targetModule.globals.import(
582+
translator.mainModule.moduleName,
583+
translator.dynamicModuleExports!
584+
.getConstantName(mainModuleExportId)!,
585+
globalType);
582586
initFunction = targetModule.functions.import(
583587
translator.mainModule.moduleName,
584-
_dynamicModuleInitFunctionExportName(mainModuleExportId),
588+
translator.dynamicModuleExports!
589+
.getConstantInitializerName(mainModuleExportId)!,
585590
ftype);
586591
} else {
587592
final name = _constantName(constant);
@@ -597,10 +602,16 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
597602
final exportId = dynamicModuleConstantIdMap[constant] =
598603
dynamicModuleConstantIdMap.length;
599604

600-
targetModule.exports.export(
601-
_dynamicModuleConstantExportName(exportId), definedGlobal);
602-
targetModule.exports
603-
.export(_dynamicModuleInitFunctionExportName(exportId), function);
605+
translator.exporter.exportConstant(
606+
targetModule,
607+
_dynamicModuleConstantExportName(exportId),
608+
definedGlobal,
609+
exportId);
610+
translator.exporter.exportConstantInitializer(
611+
targetModule,
612+
_dynamicModuleInitFunctionExportName(exportId),
613+
function,
614+
exportId);
604615
}
605616
final b2 = function.body;
606617
generator(b2);
@@ -622,8 +633,11 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
622633
final globalType = w.GlobalType(type, mutable: false);
623634
w.Global global;
624635
if (mainModuleExportId != null) {
625-
global = targetModule.globals.import(translator.mainModule.moduleName,
626-
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
636+
global = targetModule.globals.import(
637+
translator.mainModule.moduleName,
638+
translator.dynamicModuleExports!
639+
.getConstantName(mainModuleExportId)!,
640+
globalType);
627641
} else {
628642
constants.currentlyCreating = true;
629643
final definedGlobal = global =
@@ -636,8 +650,11 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
636650
final exportId = dynamicModuleConstantIdMap[constant] =
637651
dynamicModuleConstantIdMap.length;
638652

639-
targetModule.exports.export(
640-
_dynamicModuleConstantExportName(exportId), definedGlobal);
653+
translator.exporter.exportConstant(
654+
targetModule,
655+
_dynamicModuleConstantExportName(exportId),
656+
definedGlobal,
657+
exportId);
641658
}
642659
}
643660

pkg/dart2wasm/lib/dynamic_module_kernel_metadata.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import 'class_info.dart';
2727
import 'compiler_options.dart';
2828
import 'dispatch_table.dart';
2929
import 'dynamic_modules.dart';
30+
import 'exports.dart';
3031
import 'js/method_collector.dart' show JSMethods;
3132
import 'serialization.dart';
3233
import 'translator.dart';
@@ -474,6 +475,7 @@ Future<(Component, JSMethods)> generateDynamicSubmoduleComponent(
474475
final newComponent = Component()
475476
..addMetadataRepository(DynamicModuleGlobalIdRepository())
476477
..addMetadataRepository(DynamicModuleConstantRepository())
478+
..addMetadataRepository(DynamicModuleExportRepository())
477479
..addMetadataRepository(ProcedureAttributesMetadataRepository())
478480
..addMetadataRepository(TableSelectorMetadataRepository())
479481
..addMetadataRepository(DirectCallMetadataRepository())

pkg/dart2wasm/lib/dynamic_modules.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'compiler_options.dart';
2222
import 'constants.dart' show maxArrayNewFixedLength;
2323
import 'dispatch_table.dart';
2424
import 'dynamic_module_kernel_metadata.dart';
25+
import 'exports.dart';
2526
import 'intrinsics.dart' show MemberIntrinsic;
2627
import 'kernel_nodes.dart';
2728
import 'modules.dart';
@@ -129,6 +130,7 @@ class DynamicMainModuleStrategy extends ModuleStrategy with KernelNodes {
129130

130131
component.addMetadataRepository(DynamicModuleConstantRepository());
131132
component.addMetadataRepository(DynamicModuleGlobalIdRepository());
133+
component.addMetadataRepository(DynamicModuleExportRepository());
132134
}
133135

134136
@override

pkg/dart2wasm/lib/exports.dart

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:kernel/ast.dart';
6+
import 'package:wasm_builder/wasm_builder.dart';
7+
8+
import 'util.dart';
9+
10+
class DynamicModuleExportRepository
11+
extends MetadataRepository<DynamicModuleExports> {
12+
static const repositoryTag = 'wasm.dynamic-modules.exports';
13+
14+
@override
15+
final String tag = repositoryTag;
16+
17+
@override
18+
final Map<TreeNode, DynamicModuleExports> mapping = {};
19+
20+
@override
21+
DynamicModuleExports readFromBinary(_, BinarySource source) {
22+
return DynamicModuleExports._readFromBinary(source);
23+
}
24+
25+
@override
26+
void writeToBinary(DynamicModuleExports exports, Node node, BinarySink sink) {
27+
exports._writeToBinary(sink);
28+
}
29+
}
30+
31+
class DynamicModuleExports {
32+
final Map<int, String> _callableNames = {};
33+
final Map<int, String> _constantNames = {};
34+
final Map<int, String> _constantInitializerNames = {};
35+
36+
DynamicModuleExports();
37+
38+
factory DynamicModuleExports._readFromBinary(BinarySource source) {
39+
void readMap(Map<int, String> map) {
40+
final length = source.readUInt30();
41+
for (int i = 0; i < length; i++) {
42+
final id = source.readUInt30();
43+
final name = source.readStringReference();
44+
map[id] = name;
45+
}
46+
}
47+
48+
final exports = DynamicModuleExports();
49+
readMap(exports._callableNames);
50+
readMap(exports._constantNames);
51+
readMap(exports._constantInitializerNames);
52+
return exports;
53+
}
54+
55+
void _writeToBinary(BinarySink sink) {
56+
void writeMap(Map<int, String> map) {
57+
sink.writeUInt30(map.length);
58+
map.forEach((id, name) {
59+
sink.writeUInt30(id);
60+
sink.writeStringReference(name);
61+
});
62+
}
63+
64+
writeMap(_callableNames);
65+
writeMap(_constantNames);
66+
writeMap(_constantInitializerNames);
67+
}
68+
69+
String? getCallableName(int callableReferenceId) =>
70+
_callableNames[callableReferenceId];
71+
72+
String? getConstantName(int constantId) => _constantNames[constantId];
73+
74+
String? getConstantInitializerName(int constantId) =>
75+
_constantInitializerNames[constantId];
76+
}
77+
78+
/// A generator for export names (when minification is enabled).
79+
///
80+
/// For simplicity and readability of generated names, we just use the base64
81+
/// encoding of an integer counter.
82+
class ExportNamer {
83+
final bool minify;
84+
85+
ExportNamer(this.minify);
86+
87+
final Set<String> _reservedNames = {};
88+
89+
/// Mark a name as reserved by the `wasm:export` or `wasm:weak-export`
90+
/// annotations so that it will not be generated as a minified export name.
91+
void reserveName(String name) {
92+
if (!minify) return;
93+
final added = _reservedNames.add(name);
94+
assert(added, "Name '$name' is already reserved");
95+
}
96+
97+
int _nameCounter = 0;
98+
99+
String _getExportName(String name) {
100+
if (!minify) return name;
101+
do {
102+
name = intToBase64(_nameCounter++);
103+
} while (_reservedNames.contains(name));
104+
return name;
105+
}
106+
}
107+
108+
/// Manages exporting entities in a minification-aware way.
109+
///
110+
/// The [Exporter] stores associations between entities exported from a dynamic
111+
/// main module and their export names in [dynamicModuleExports] so that dynamic
112+
/// submodules can import these entities with those names.
113+
///
114+
/// The `export___` methods add relevant associations to [dynamicModuleExports]
115+
/// and add the given exportable to the Wasm exports of the specified module
116+
/// under the computed export name.
117+
class Exporter {
118+
final ExportNamer _namer;
119+
120+
Exporter(this._namer);
121+
122+
final DynamicModuleExports dynamicModuleExports = DynamicModuleExports();
123+
124+
void exportCallable(ModuleBuilder module, String name,
125+
FunctionBuilder function, int callableReferenceId) {
126+
dynamicModuleExports._callableNames[callableReferenceId] =
127+
_export(module, name, function);
128+
}
129+
130+
void exportConstant(ModuleBuilder module, String name, GlobalBuilder constant,
131+
int constantId) {
132+
dynamicModuleExports._constantNames[constantId] =
133+
_export(module, name, constant);
134+
}
135+
136+
void exportConstantInitializer(ModuleBuilder module, String name,
137+
FunctionBuilder initializer, int constantId) {
138+
dynamicModuleExports._constantInitializerNames[constantId] =
139+
_export(module, name, initializer);
140+
}
141+
142+
String _export(ModuleBuilder module, String name, Exportable exportable) {
143+
final exportName = _namer._getExportName(name);
144+
module.exports.export(exportName, exportable);
145+
return exportName;
146+
}
147+
}

pkg/dart2wasm/lib/functions.dart

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,18 @@ class FunctionCollector {
6767
}
6868

6969
// Ensure any procedures marked as exported are enqueued.
70-
String? exportName =
71-
translator.getPragma(member, "wasm:export", member.name.text);
70+
final text = member.name.text;
71+
String? exportName = translator.getPragma(member, "wasm:export", text);
7272
if (exportName != null) {
7373
getFunction(member.reference);
7474
}
75+
76+
// Whether a procedure is strongly or weakly exported, we must not use its
77+
// name as the export name of a different function.
78+
exportName ??= translator.getPragma(member, "wasm:weak-export", text);
79+
if (exportName != null) {
80+
translator.exportNamer.reserveName(exportName);
81+
}
7582
}
7683

7784
/// If the member with the reference [target] is exported, get the export
@@ -158,9 +165,11 @@ class FunctionCollector {
158165
final callableReferenceId =
159166
translator.dynamicModuleInfo?.metadata.callableReferenceIds[target];
160167
if (callableReferenceId != null) {
161-
translator.mainModule.exports.export(
168+
translator.exporter.exportCallable(
169+
translator.mainModule,
162170
_generateDynamicSubmoduleCallableName(callableReferenceId),
163-
function);
171+
function,
172+
callableReferenceId);
164173
}
165174
}
166175

@@ -170,7 +179,8 @@ class FunctionCollector {
170179
});
171180
}
172181

173-
String _generateDynamicSubmoduleCallableName(int key) => '#dc$key';
182+
String _generateDynamicSubmoduleCallableName(int key) =>
183+
'#dynamicCallable$key';
174184

175185
w.BaseFunction _importFunctionToDynamicSubmodule(Reference target) {
176186
assert(translator.isDynamicSubmodule);
@@ -186,8 +196,8 @@ class FunctionCollector {
186196
}
187197
return translator.dynamicSubmodule.functions.import(
188198
translator.mainModule.moduleName,
189-
_generateDynamicSubmoduleCallableName(
190-
dynamicSubmoduleCallableReferenceId),
199+
translator.dynamicModuleExports!
200+
.getCallableName(dynamicSubmoduleCallableReferenceId)!,
191201
translator.signatureForMainModule(target),
192202
getFunctionName(target));
193203
}

pkg/dart2wasm/lib/symbols.dart

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'dart:convert';
6-
75
import 'package:kernel/ast.dart';
86

7+
import 'util.dart';
8+
99
class Symbols {
1010
final bool minify;
1111
Symbols(this.minify);
@@ -31,7 +31,7 @@ class Symbols {
3131
final Map<SymbolConstant, int> symbolOrdinals = {};
3232
String getMangledSymbolName(SymbolConstant symbol) {
3333
if (minify) {
34-
return _intToBase64(
34+
return intToBase64(
3535
symbolOrdinals.putIfAbsent(symbol, () => symbolOrdinals.length));
3636
}
3737

@@ -50,16 +50,3 @@ class Symbols {
5050
return '${symbol.name}@${libraryReference.asLibrary.importUri.hashCode}';
5151
}
5252
}
53-
54-
List<int> _intToLittleEndianBytes(int i) {
55-
List<int> bytes = [];
56-
bytes.add(i & 0xFF);
57-
i >>>= 8;
58-
while (i != 0) {
59-
bytes.add(i & 0xFF);
60-
i >>>= 8;
61-
}
62-
return bytes;
63-
}
64-
65-
String _intToBase64(int i) => base64.encode(_intToLittleEndianBytes(i));

0 commit comments

Comments
 (0)