[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 <natebiggs@google.com> Reviewed-by: Ömer Ağacan <omersa@google.com>
This commit is contained in:
committed by
Commit Queue
parent
17c6e3cd49
commit
6cbc6df06a
@@ -498,8 +498,9 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
||||
return info;
|
||||
}
|
||||
|
||||
static String _dynamicModuleConstantExportName(int id) => '#c$id';
|
||||
static String _dynamicModuleInitFunctionExportName(int id) => '#cf$id';
|
||||
static String _dynamicModuleConstantExportName(int id) => '#constant$id';
|
||||
static String _dynamicModuleInitFunctionExportName(int id) =>
|
||||
'#constantFunction$id';
|
||||
|
||||
static int _nextGlobalId = 0;
|
||||
String _constantName(Constant constant) {
|
||||
@@ -577,11 +578,15 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
||||
translator.typesBuilder.defineFunction(const [], [type]);
|
||||
|
||||
if (mainModuleExportId != null) {
|
||||
global = targetModule.globals.import(translator.mainModule.moduleName,
|
||||
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
|
||||
global = targetModule.globals.import(
|
||||
translator.mainModule.moduleName,
|
||||
translator.dynamicModuleExports!
|
||||
.getConstantName(mainModuleExportId)!,
|
||||
globalType);
|
||||
initFunction = targetModule.functions.import(
|
||||
translator.mainModule.moduleName,
|
||||
_dynamicModuleInitFunctionExportName(mainModuleExportId),
|
||||
translator.dynamicModuleExports!
|
||||
.getConstantInitializerName(mainModuleExportId)!,
|
||||
ftype);
|
||||
} else {
|
||||
final name = _constantName(constant);
|
||||
@@ -597,10 +602,16 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
||||
final exportId = dynamicModuleConstantIdMap[constant] =
|
||||
dynamicModuleConstantIdMap.length;
|
||||
|
||||
targetModule.exports.export(
|
||||
_dynamicModuleConstantExportName(exportId), definedGlobal);
|
||||
targetModule.exports
|
||||
.export(_dynamicModuleInitFunctionExportName(exportId), function);
|
||||
translator.exporter.exportConstant(
|
||||
targetModule,
|
||||
_dynamicModuleConstantExportName(exportId),
|
||||
definedGlobal,
|
||||
exportId);
|
||||
translator.exporter.exportConstantInitializer(
|
||||
targetModule,
|
||||
_dynamicModuleInitFunctionExportName(exportId),
|
||||
function,
|
||||
exportId);
|
||||
}
|
||||
final b2 = function.body;
|
||||
generator(b2);
|
||||
@@ -622,8 +633,11 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
||||
final globalType = w.GlobalType(type, mutable: false);
|
||||
w.Global global;
|
||||
if (mainModuleExportId != null) {
|
||||
global = targetModule.globals.import(translator.mainModule.moduleName,
|
||||
_dynamicModuleConstantExportName(mainModuleExportId), globalType);
|
||||
global = targetModule.globals.import(
|
||||
translator.mainModule.moduleName,
|
||||
translator.dynamicModuleExports!
|
||||
.getConstantName(mainModuleExportId)!,
|
||||
globalType);
|
||||
} else {
|
||||
constants.currentlyCreating = true;
|
||||
final definedGlobal = global =
|
||||
@@ -636,8 +650,11 @@ class ConstantCreator extends ConstantVisitor<ConstantInfo?>
|
||||
final exportId = dynamicModuleConstantIdMap[constant] =
|
||||
dynamicModuleConstantIdMap.length;
|
||||
|
||||
targetModule.exports.export(
|
||||
_dynamicModuleConstantExportName(exportId), definedGlobal);
|
||||
translator.exporter.exportConstant(
|
||||
targetModule,
|
||||
_dynamicModuleConstantExportName(exportId),
|
||||
definedGlobal,
|
||||
exportId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'class_info.dart';
|
||||
import 'compiler_options.dart';
|
||||
import 'dispatch_table.dart';
|
||||
import 'dynamic_modules.dart';
|
||||
import 'exports.dart';
|
||||
import 'js/method_collector.dart' show JSMethods;
|
||||
import 'serialization.dart';
|
||||
import 'translator.dart';
|
||||
@@ -474,6 +475,7 @@ Future<(Component, JSMethods)> generateDynamicSubmoduleComponent(
|
||||
final newComponent = Component()
|
||||
..addMetadataRepository(DynamicModuleGlobalIdRepository())
|
||||
..addMetadataRepository(DynamicModuleConstantRepository())
|
||||
..addMetadataRepository(DynamicModuleExportRepository())
|
||||
..addMetadataRepository(ProcedureAttributesMetadataRepository())
|
||||
..addMetadataRepository(TableSelectorMetadataRepository())
|
||||
..addMetadataRepository(DirectCallMetadataRepository())
|
||||
|
||||
@@ -22,6 +22,7 @@ import 'compiler_options.dart';
|
||||
import 'constants.dart' show maxArrayNewFixedLength;
|
||||
import 'dispatch_table.dart';
|
||||
import 'dynamic_module_kernel_metadata.dart';
|
||||
import 'exports.dart';
|
||||
import 'intrinsics.dart' show MemberIntrinsic;
|
||||
import 'kernel_nodes.dart';
|
||||
import 'modules.dart';
|
||||
@@ -129,6 +130,7 @@ class DynamicMainModuleStrategy extends ModuleStrategy with KernelNodes {
|
||||
|
||||
component.addMetadataRepository(DynamicModuleConstantRepository());
|
||||
component.addMetadataRepository(DynamicModuleGlobalIdRepository());
|
||||
component.addMetadataRepository(DynamicModuleExportRepository());
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:wasm_builder/wasm_builder.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class DynamicModuleExportRepository
|
||||
extends MetadataRepository<DynamicModuleExports> {
|
||||
static const repositoryTag = 'wasm.dynamic-modules.exports';
|
||||
|
||||
@override
|
||||
final String tag = repositoryTag;
|
||||
|
||||
@override
|
||||
final Map<TreeNode, DynamicModuleExports> mapping = {};
|
||||
|
||||
@override
|
||||
DynamicModuleExports readFromBinary(_, BinarySource source) {
|
||||
return DynamicModuleExports._readFromBinary(source);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeToBinary(DynamicModuleExports exports, Node node, BinarySink sink) {
|
||||
exports._writeToBinary(sink);
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicModuleExports {
|
||||
final Map<int, String> _callableNames = {};
|
||||
final Map<int, String> _constantNames = {};
|
||||
final Map<int, String> _constantInitializerNames = {};
|
||||
|
||||
DynamicModuleExports();
|
||||
|
||||
factory DynamicModuleExports._readFromBinary(BinarySource source) {
|
||||
void readMap(Map<int, String> map) {
|
||||
final length = source.readUInt30();
|
||||
for (int i = 0; i < length; i++) {
|
||||
final id = source.readUInt30();
|
||||
final name = source.readStringReference();
|
||||
map[id] = name;
|
||||
}
|
||||
}
|
||||
|
||||
final exports = DynamicModuleExports();
|
||||
readMap(exports._callableNames);
|
||||
readMap(exports._constantNames);
|
||||
readMap(exports._constantInitializerNames);
|
||||
return exports;
|
||||
}
|
||||
|
||||
void _writeToBinary(BinarySink sink) {
|
||||
void writeMap(Map<int, String> map) {
|
||||
sink.writeUInt30(map.length);
|
||||
map.forEach((id, name) {
|
||||
sink.writeUInt30(id);
|
||||
sink.writeStringReference(name);
|
||||
});
|
||||
}
|
||||
|
||||
writeMap(_callableNames);
|
||||
writeMap(_constantNames);
|
||||
writeMap(_constantInitializerNames);
|
||||
}
|
||||
|
||||
String? getCallableName(int callableReferenceId) =>
|
||||
_callableNames[callableReferenceId];
|
||||
|
||||
String? getConstantName(int constantId) => _constantNames[constantId];
|
||||
|
||||
String? getConstantInitializerName(int constantId) =>
|
||||
_constantInitializerNames[constantId];
|
||||
}
|
||||
|
||||
/// A generator for export names (when minification is enabled).
|
||||
///
|
||||
/// For simplicity and readability of generated names, we just use the base64
|
||||
/// encoding of an integer counter.
|
||||
class ExportNamer {
|
||||
final bool minify;
|
||||
|
||||
ExportNamer(this.minify);
|
||||
|
||||
final Set<String> _reservedNames = {};
|
||||
|
||||
/// Mark a name as reserved by the `wasm:export` or `wasm:weak-export`
|
||||
/// annotations so that it will not be generated as a minified export name.
|
||||
void reserveName(String name) {
|
||||
if (!minify) return;
|
||||
final added = _reservedNames.add(name);
|
||||
assert(added, "Name '$name' is already reserved");
|
||||
}
|
||||
|
||||
int _nameCounter = 0;
|
||||
|
||||
String _getExportName(String name) {
|
||||
if (!minify) return name;
|
||||
do {
|
||||
name = intToBase64(_nameCounter++);
|
||||
} while (_reservedNames.contains(name));
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/// Manages exporting entities in a minification-aware way.
|
||||
///
|
||||
/// The [Exporter] stores associations between entities exported from a dynamic
|
||||
/// main module and their export names in [dynamicModuleExports] so that dynamic
|
||||
/// submodules can import these entities with those names.
|
||||
///
|
||||
/// The `export___` methods add relevant associations to [dynamicModuleExports]
|
||||
/// and add the given exportable to the Wasm exports of the specified module
|
||||
/// under the computed export name.
|
||||
class Exporter {
|
||||
final ExportNamer _namer;
|
||||
|
||||
Exporter(this._namer);
|
||||
|
||||
final DynamicModuleExports dynamicModuleExports = DynamicModuleExports();
|
||||
|
||||
void exportCallable(ModuleBuilder module, String name,
|
||||
FunctionBuilder function, int callableReferenceId) {
|
||||
dynamicModuleExports._callableNames[callableReferenceId] =
|
||||
_export(module, name, function);
|
||||
}
|
||||
|
||||
void exportConstant(ModuleBuilder module, String name, GlobalBuilder constant,
|
||||
int constantId) {
|
||||
dynamicModuleExports._constantNames[constantId] =
|
||||
_export(module, name, constant);
|
||||
}
|
||||
|
||||
void exportConstantInitializer(ModuleBuilder module, String name,
|
||||
FunctionBuilder initializer, int constantId) {
|
||||
dynamicModuleExports._constantInitializerNames[constantId] =
|
||||
_export(module, name, initializer);
|
||||
}
|
||||
|
||||
String _export(ModuleBuilder module, String name, Exportable exportable) {
|
||||
final exportName = _namer._getExportName(name);
|
||||
module.exports.export(exportName, exportable);
|
||||
return exportName;
|
||||
}
|
||||
}
|
||||
@@ -67,11 +67,18 @@ class FunctionCollector {
|
||||
}
|
||||
|
||||
// Ensure any procedures marked as exported are enqueued.
|
||||
String? exportName =
|
||||
translator.getPragma(member, "wasm:export", member.name.text);
|
||||
final text = member.name.text;
|
||||
String? exportName = translator.getPragma(member, "wasm:export", text);
|
||||
if (exportName != null) {
|
||||
getFunction(member.reference);
|
||||
}
|
||||
|
||||
// Whether a procedure is strongly or weakly exported, we must not use its
|
||||
// name as the export name of a different function.
|
||||
exportName ??= translator.getPragma(member, "wasm:weak-export", text);
|
||||
if (exportName != null) {
|
||||
translator.exportNamer.reserveName(exportName);
|
||||
}
|
||||
}
|
||||
|
||||
/// If the member with the reference [target] is exported, get the export
|
||||
@@ -158,9 +165,11 @@ class FunctionCollector {
|
||||
final callableReferenceId =
|
||||
translator.dynamicModuleInfo?.metadata.callableReferenceIds[target];
|
||||
if (callableReferenceId != null) {
|
||||
translator.mainModule.exports.export(
|
||||
translator.exporter.exportCallable(
|
||||
translator.mainModule,
|
||||
_generateDynamicSubmoduleCallableName(callableReferenceId),
|
||||
function);
|
||||
function,
|
||||
callableReferenceId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +179,8 @@ class FunctionCollector {
|
||||
});
|
||||
}
|
||||
|
||||
String _generateDynamicSubmoduleCallableName(int key) => '#dc$key';
|
||||
String _generateDynamicSubmoduleCallableName(int key) =>
|
||||
'#dynamicCallable$key';
|
||||
|
||||
w.BaseFunction _importFunctionToDynamicSubmodule(Reference target) {
|
||||
assert(translator.isDynamicSubmodule);
|
||||
@@ -186,8 +196,8 @@ class FunctionCollector {
|
||||
}
|
||||
return translator.dynamicSubmodule.functions.import(
|
||||
translator.mainModule.moduleName,
|
||||
_generateDynamicSubmoduleCallableName(
|
||||
dynamicSubmoduleCallableReferenceId),
|
||||
translator.dynamicModuleExports!
|
||||
.getCallableName(dynamicSubmoduleCallableReferenceId)!,
|
||||
translator.signatureForMainModule(target),
|
||||
getFunctionName(target));
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// 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.
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
class Symbols {
|
||||
final bool minify;
|
||||
Symbols(this.minify);
|
||||
@@ -31,7 +31,7 @@ class Symbols {
|
||||
final Map<SymbolConstant, int> symbolOrdinals = {};
|
||||
String getMangledSymbolName(SymbolConstant symbol) {
|
||||
if (minify) {
|
||||
return _intToBase64(
|
||||
return intToBase64(
|
||||
symbolOrdinals.putIfAbsent(symbol, () => symbolOrdinals.length));
|
||||
}
|
||||
|
||||
@@ -50,16 +50,3 @@ class Symbols {
|
||||
return '${symbol.name}@${libraryReference.asLibrary.importUri.hashCode}';
|
||||
}
|
||||
}
|
||||
|
||||
List<int> _intToLittleEndianBytes(int i) {
|
||||
List<int> bytes = [];
|
||||
bytes.add(i & 0xFF);
|
||||
i >>>= 8;
|
||||
while (i != 0) {
|
||||
bytes.add(i & 0xFF);
|
||||
i >>>= 8;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
String _intToBase64(int i) => base64.encode(_intToLittleEndianBytes(i));
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'dispatch_table.dart';
|
||||
import 'dynamic_forwarders.dart';
|
||||
import 'dynamic_module_kernel_metadata.dart';
|
||||
import 'dynamic_modules.dart';
|
||||
import 'exports.dart';
|
||||
import 'functions.dart';
|
||||
import 'globals.dart';
|
||||
import 'kernel_nodes.dart';
|
||||
@@ -125,6 +126,9 @@ class Translator with KernelNodes {
|
||||
|
||||
final Symbols symbols;
|
||||
|
||||
final ExportNamer exportNamer;
|
||||
late final Exporter exporter;
|
||||
|
||||
// Kernel input and context.
|
||||
@override
|
||||
final Component component;
|
||||
@@ -177,6 +181,8 @@ class Translator with KernelNodes {
|
||||
late final CompilationQueue compilationQueue;
|
||||
late final FunctionCollector functions;
|
||||
|
||||
late final DynamicModuleExports? dynamicModuleExports;
|
||||
|
||||
// Information about the program used and updated by the various phases.
|
||||
|
||||
/// [ClassInfo]s of classes in the compilation unit and the [ClassInfo] for
|
||||
@@ -452,6 +458,7 @@ class Translator with KernelNodes {
|
||||
{bool enableDynamicModules = false,
|
||||
required MainModuleMetadata mainModuleMetadata})
|
||||
: symbols = Symbols(options.minify),
|
||||
exportNamer = ExportNamer(options.minify),
|
||||
libraries = component.libraries,
|
||||
hierarchy =
|
||||
ClassHierarchy(component, coreTypes) as ClosedWorldClassHierarchy {
|
||||
@@ -473,6 +480,13 @@ class Translator with KernelNodes {
|
||||
functions = FunctionCollector(this);
|
||||
types = Types(this);
|
||||
exceptionTag = ExceptionTag(this);
|
||||
|
||||
exporter = Exporter(exportNamer);
|
||||
|
||||
dynamicModuleExports =
|
||||
(component.metadata[DynamicModuleExportRepository.repositoryTag]
|
||||
as DynamicModuleExportRepository?)
|
||||
?.mapping[component] ??= exporter.dynamicModuleExports;
|
||||
}
|
||||
|
||||
void _initLoadLibraryImportMap() {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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.
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:kernel/ast.dart';
|
||||
import 'package:kernel/core_types.dart';
|
||||
|
||||
@@ -52,3 +54,16 @@ T addPragma<T extends Annotatable>(
|
||||
coreTypes.pragmaName.fieldReference: StringConstant(pragmaName),
|
||||
coreTypes.pragmaOptions.fieldReference: value ?? NullConstant(),
|
||||
})));
|
||||
|
||||
List<int> _intToLittleEndianBytes(int i) {
|
||||
List<int> bytes = [];
|
||||
bytes.add(i & 0xFF);
|
||||
i >>>= 8;
|
||||
while (i != 0) {
|
||||
bytes.add(i & 0xFF);
|
||||
i >>>= 8;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
String intToBase64(int i) => base64.encode(_intToLittleEndianBytes(i));
|
||||
|
||||
@@ -10,7 +10,12 @@ import 'shared/shared.dart' show Child, Other;
|
||||
void main() async {
|
||||
final o1 = Other();
|
||||
Child();
|
||||
Expect.equals('Instance of \'Other\'', o1.toString());
|
||||
|
||||
// Ensure that the overrides correctly call `super.toString()`, eventually
|
||||
// ending at `Object.toString`. The class name may be minified, so we don't
|
||||
// want to make it part of the expectation.
|
||||
Expect.isTrue(o1.toString().startsWith('Instance of'));
|
||||
|
||||
Expect.equals(3, await helper.load('entry1.dart'));
|
||||
helper.done();
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ class Dart2wasmExecutor implements TargetExecutor {
|
||||
'--extra-compiler-option=--dynamic-module-main=main.dart.dill',
|
||||
'--extra-compiler-option=--dynamic-module-interface='
|
||||
'$rootScheme:/data/$testName/dynamic_interface.yaml',
|
||||
'--extra-compiler-option=--minify',
|
||||
'$rootScheme:/data/$testName/$source',
|
||||
'$source.wasm',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user