d43d9df1a7
Change-Id: If92f55296dfe83b64165a2bd07eaefb7d137198c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497341 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
205 lines
6.7 KiB
Dart
205 lines
6.7 KiB
Dart
// 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.
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'js/util.dart' show JsInteropMemberData;
|
|
import 'translator.dart' show TranslatorOptions;
|
|
import 'util.dart';
|
|
|
|
/// Provides wasm import/export names for a module.
|
|
///
|
|
/// Each instance of [Namer] defines a namespace. Uniqueness is only guaranteed
|
|
/// within a namespace. Names passed to [getName] must themselves be unique for
|
|
/// a given namespace.
|
|
///
|
|
/// If [jsSafeNames] is true, the returned names will be safe to use as a
|
|
/// JavaScript identifier. This is useful if the name will be used as a key in a
|
|
/// JS object. Names only referenced from wasm modules can be any ASCII string.
|
|
///
|
|
/// Each name should appear exactly once in the wasm module (defining the
|
|
/// import/export) and JS code so we don't consider frequency when naming.
|
|
class Namer {
|
|
final bool minify;
|
|
final Set<String> _usedNames = {};
|
|
int _nextId = 0;
|
|
|
|
Namer({this.minify = false});
|
|
|
|
/// Mark a name as reserved in case the name might collide with a
|
|
/// pre-specified name such as an export.
|
|
void reserveName(String name) {
|
|
if (!minify) return;
|
|
final added = _usedNames.add(name);
|
|
assert(added, "Name '$name' is already reserved");
|
|
}
|
|
|
|
static String _jsSanitizeName(String name) {
|
|
return name.replaceAll(RegExp(r'[^a-zA-Z0-9_$]'), '_');
|
|
}
|
|
|
|
/// Get an assigned name for the given name.
|
|
///
|
|
|
|
String getName(String name, {bool jsSafeName = false}) {
|
|
if (!minify) {
|
|
String basename = name = jsSafeName ? _jsSanitizeName(name) : name;
|
|
int counter = 0;
|
|
while (!_usedNames.add(name)) {
|
|
name = '${basename}_${counter++}';
|
|
}
|
|
return name;
|
|
}
|
|
do {
|
|
name = jsSafeName
|
|
? intToMinJsSafeString(_nextId++)
|
|
: intToMinString(_nextId++);
|
|
} while (!_usedNames.add(name));
|
|
return name;
|
|
}
|
|
}
|
|
|
|
/// Manages naming for external interop members.
|
|
///
|
|
/// Members annotated with `@pragma('wasm:import', '...')` or
|
|
/// `@pragma('wasm:export', '...')` or `@pragma('wasm:weak-export', '...')`
|
|
/// are considered external members. These members are visible from outside the
|
|
/// Dart program and are therefore not minified. We do make sure that other
|
|
/// names do not collide with these names.
|
|
class _ExternalMemberNamer {
|
|
final Namer _exportNamer;
|
|
final CoreTypes coreTypes;
|
|
|
|
_ExternalMemberNamer(this.coreTypes, this._exportNamer);
|
|
ImportName? getImportName(Member member) {
|
|
return getWasmImportPragma(coreTypes, member);
|
|
}
|
|
|
|
String? getExportName(Member member) {
|
|
// External export names should be registered already so we don't have to
|
|
// reserve them with the namer here.
|
|
return getWasmExportPragma(coreTypes, member) ??
|
|
getWasmWeakExportPragma(coreTypes, member);
|
|
}
|
|
|
|
bool registerExportName(Member member) {
|
|
final name = getWasmExportPragma(coreTypes, member);
|
|
if (name != null) {
|
|
_exportNamer.reserveName(name);
|
|
return true;
|
|
}
|
|
final weakName = getWasmWeakExportPragma(coreTypes, member);
|
|
if (weakName != null) {
|
|
_exportNamer.reserveName(weakName);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Manages naming for internal interop helper members.
|
|
///
|
|
/// These members may import code from the support JS modules but are not
|
|
/// visible from outside the Dart program and are only used by the Dart runtime.
|
|
/// Their names can therefore be minified.
|
|
class _InteropHelperMemberNamer {
|
|
final Namer _interopHelperNamer;
|
|
final Namer _exportNamer;
|
|
final CoreTypes coreTypes;
|
|
final String interopModuleName;
|
|
final String thisModuleSetterName;
|
|
final Map<Member, String> interopMemberNames = {};
|
|
|
|
_InteropHelperMemberNamer(
|
|
this._exportNamer,
|
|
this.coreTypes,
|
|
TranslatorOptions options,
|
|
) : interopModuleName = options.minify ? '_' : 'dart2wasm',
|
|
_interopHelperNamer = Namer(
|
|
minify: options.minify || options.minifyInteropNames,
|
|
),
|
|
thisModuleSetterName = _exportNamer.getName(
|
|
'\$setThisModule',
|
|
jsSafeName: true,
|
|
);
|
|
|
|
ImportName? getImportName(Member member) {
|
|
final annotationInfo = JsInteropMemberData.fromMember(member, coreTypes);
|
|
if (annotationInfo == null) return null;
|
|
if (annotationInfo.isImport) {
|
|
return ImportName(
|
|
interopModuleName,
|
|
interopMemberNames[member] ??= _interopHelperNamer.getName(
|
|
member.name.text,
|
|
jsSafeName: true,
|
|
),
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
String? getExportName(Member member) {
|
|
final annotationInfo = JsInteropMemberData.fromMember(member, coreTypes);
|
|
if (annotationInfo == null) return null;
|
|
if (annotationInfo.isWeakExport) {
|
|
return interopMemberNames[member] ??= _exportNamer.getName(
|
|
member.name.text,
|
|
jsSafeName: true,
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// Manages naming for [Member]s associated with JS interop.
|
|
///
|
|
/// Some of these members are visible from JS outside of the Dart program and
|
|
/// are therefore not minified. Others are only helpers used by the Dart runtime
|
|
/// and can be minified.
|
|
class InteropMemberNamer {
|
|
final _ExternalMemberNamer _externalMemberNamer;
|
|
final _InteropHelperMemberNamer _interopHelperMemberNamer;
|
|
|
|
InteropMemberNamer(
|
|
CoreTypes coreTypes,
|
|
Namer exportNamer,
|
|
TranslatorOptions options,
|
|
) : _externalMemberNamer = _ExternalMemberNamer(coreTypes, exportNamer),
|
|
_interopHelperMemberNamer = _InteropHelperMemberNamer(
|
|
exportNamer,
|
|
coreTypes,
|
|
options,
|
|
);
|
|
|
|
String get interopHelperModuleName =>
|
|
_interopHelperMemberNamer.interopModuleName;
|
|
String get thisModuleSetterName =>
|
|
_interopHelperMemberNamer.thisModuleSetterName;
|
|
|
|
/// Returns the import name for the given member.
|
|
///
|
|
/// Returns null if the member is not an import. Checks both external and
|
|
/// interop helper imports.
|
|
ImportName? getImportName(Member member) {
|
|
return _externalMemberNamer.getImportName(member) ??
|
|
_interopHelperMemberNamer.getImportName(member);
|
|
}
|
|
|
|
/// Returns the export name for the given member.
|
|
///
|
|
/// Returns null if the member is not an export. Checks both external and
|
|
/// interop helper exports.
|
|
String? getExportName(Member member) {
|
|
final externalName = _externalMemberNamer.getExportName(member);
|
|
if (externalName != null) return externalName;
|
|
return _interopHelperMemberNamer.getExportName(member);
|
|
}
|
|
|
|
/// Registers the export name for the given member with the [Namer].
|
|
///
|
|
/// Returns true if the member is a strong external export.
|
|
bool registerExternalExportName(Member member) {
|
|
return _externalMemberNamer.registerExportName(member);
|
|
}
|
|
}
|