blob: 1f2182a1bfbde0768dfa6eb5bccd19784f86921d [file] [log] [blame]
// 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.
const String _sourceMapExtensionName = "x_org_dartlang_dart2js";
/// Add dart2js source map extension fields to allow mapping "minified:Class123"
/// parts in error messages to unminified class names like "String".
///
/// This is only needed in minified builds. Non-minified builds will have the
/// full class names in the errors.
///
/// The extension format is described in
/// `pkg/compiler/doc/sourcemap_extensions.md`.
void addMinifiedClassNames(
Map<String, Object?> sourceMapJson, List<String?> classNames) {
final List<dynamic> names = sourceMapJson['names'] as List<dynamic>;
// Some of the class names may already be in the names list. Create the
// name->index map with existing names to reuse existing names.
final Map<String, int> nameIndices = {};
for (var i = 0; i < names.length; i += 1) {
nameIndices[names[i]] = i;
}
final Map<String, int> minifiedClassNamesToUnminified = {};
for (var classId = 0; classId < classNames.length; classId += 1) {
final unminifiedClassName = classNames[classId];
if (unminifiedClassName == null) continue;
final minifiedClassName = "Class$classId";
int? unminifiedClassNameIndex = nameIndices[unminifiedClassName];
if (unminifiedClassNameIndex == null) {
unminifiedClassNameIndex = names.length;
names.add(unminifiedClassName);
}
minifiedClassNamesToUnminified[minifiedClassName] =
unminifiedClassNameIndex;
}
// 'names' is updated in-place above with the full class names, add the
// new section.
//
// We don't need the "instance" section, but some of the tools want it to be
// present always.
sourceMapJson[_sourceMapExtensionName] = <String, Object?>{
"minified_names": <String, Object?>{
"global": minifiedClassNamesToUnminified.entries
.map((entry) => "${entry.key},${entry.value}")
.join(","),
"instance": "",
}
};
}
/// If the source map has the custom section added by [addMinifiedClassNames],
/// parse the unminified class names and return the original list* passed to
/// [addMinifiedClassNames].
///
/// (*): With the exception that trailing `null` elements of the original list
/// will be missing in the return value of this function.
List<String?>? getMinifiedClassNames(Map<String, Object?> sourceMapJson) {
final List<dynamic> names = sourceMapJson["names"] as List<dynamic>;
final extensionField = sourceMapJson[_sourceMapExtensionName];
if (extensionField == null) return null;
final List<String> globalMinifiedNames =
(((extensionField as Map)["minified_names"] as Map)["global"] as String)
.split(",");
List<String?> unminifiedClassNames = [];
for (int i = 0; i < globalMinifiedNames.length; i += 2) {
final minifiedClassName = globalMinifiedNames[i];
assert(minifiedClassName.startsWith("Class"));
final classId = int.parse(minifiedClassName.substring("Class".length));
final unminifiedClassNameIndex = int.parse(globalMinifiedNames[i + 1]);
final unminifiedClassName = names[unminifiedClassNameIndex] as String;
while (unminifiedClassNames.length <= classId) {
unminifiedClassNames.add(null);
}
assert(unminifiedClassNames[classId] == null);
unminifiedClassNames[classId] = unminifiedClassName;
}
return unminifiedClassNames;
}