4201b2677e
The change in [0] increased the language version of pkg/dart2wasm. That in return changes how the package is formatted by the autoformatter. This CL runs now the formatter to re-format the code. Unfortunately this makes blame lists worse. But not doing it will make us have to disable auto-formatting before saving files which is very annoying. [0] https://dart-review.googlesource.com/c/sdk/+/487944 Change-Id: Ie3cbdc58bbd7f5aba187470554b7c958719fa795 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/496940 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
217 lines
5.8 KiB
Dart
217 lines
5.8 KiB
Dart
// Copyright (c) 2024, 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.
|
|
|
|
/// Represents a mapping from a range of generated instructions to some source
|
|
/// code.
|
|
class SourceMapping {
|
|
/// Start offset of mapped instructions.
|
|
final int instructionOffset;
|
|
|
|
/// Source info for the mapped instructions starting at [instructionOffset].
|
|
///
|
|
/// When `null`, the mapping effectively makes the code unmapped. This is
|
|
/// useful for compiler-generated code that doesn't correstpond to any lines
|
|
/// in the source.
|
|
final SourceInfo? sourceInfo;
|
|
|
|
SourceMapping._(this.instructionOffset, this.sourceInfo);
|
|
|
|
SourceMapping(
|
|
this.instructionOffset,
|
|
Uri fileUri,
|
|
int line,
|
|
int col,
|
|
String? name,
|
|
) : sourceInfo = SourceInfo(fileUri, line, col, name);
|
|
|
|
SourceMapping.unmapped(this.instructionOffset) : sourceInfo = null;
|
|
|
|
SourceMapping shiftBy(int shift) {
|
|
if (shift == 0) return this;
|
|
return SourceMapping._(shift + instructionOffset, sourceInfo);
|
|
}
|
|
|
|
@override
|
|
String toString() => '$instructionOffset -> $sourceInfo';
|
|
}
|
|
|
|
class SourceInfo {
|
|
/// URI of the compiled code's file.
|
|
final Uri fileUri;
|
|
|
|
/// 0-based line number of the compiled code.
|
|
final int line;
|
|
|
|
/// 0-based column number of the compiled code.
|
|
final int col;
|
|
|
|
/// Name of the mapped code. This is usually the name of the function that
|
|
/// contains the code.
|
|
final String? name;
|
|
|
|
SourceInfo(this.fileUri, this.line, this.col, this.name);
|
|
|
|
@override
|
|
String toString() => '$fileUri:$line:$col ($name)';
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) {
|
|
return true;
|
|
}
|
|
|
|
if (other is! SourceInfo) {
|
|
return false;
|
|
}
|
|
|
|
return fileUri == other.fileUri &&
|
|
line == other.line &&
|
|
col == other.col &&
|
|
name == other.name;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(fileUri, line, col, name);
|
|
}
|
|
|
|
class SourceMapSerializer {
|
|
final List<SourceMapping> mappings = [];
|
|
|
|
void addMapping(int instructionOffset, SourceInfo? sourceInfo) {
|
|
final mapping = SourceMapping._(instructionOffset, sourceInfo);
|
|
mappings.add(mapping);
|
|
}
|
|
|
|
void copyMappings(SourceMapSerializer other, int offset) {
|
|
for (final mapping in other.mappings) {
|
|
mappings.add(
|
|
SourceMapping._(mapping.instructionOffset + offset, mapping.sourceInfo),
|
|
);
|
|
}
|
|
}
|
|
|
|
Map<String, Object?> serializeAsJson() => _sourceMapToJson(mappings);
|
|
}
|
|
|
|
Map<String, Object?> _sourceMapToJson(List<SourceMapping> mappings) {
|
|
final Set<Uri> sourcesSet = {};
|
|
for (final mapping in mappings) {
|
|
if (mapping.sourceInfo?.fileUri != null) {
|
|
sourcesSet.add(mapping.sourceInfo!.fileUri);
|
|
}
|
|
}
|
|
|
|
final List<Uri> sourcesList = sourcesSet.toList();
|
|
|
|
// Maps sources to their indices in the 'sources' list.
|
|
final Map<Uri, int> sourceIndices = {};
|
|
for (Uri source in sourcesList) {
|
|
sourceIndices[source] = sourceIndices.length;
|
|
}
|
|
|
|
final Set<String> namesSet = {};
|
|
for (final mapping in mappings) {
|
|
if (mapping.sourceInfo?.name != null) {
|
|
namesSet.add(mapping.sourceInfo!.name!);
|
|
}
|
|
}
|
|
|
|
final List<String> namesList = namesSet.toList();
|
|
|
|
// Maps names to their index in the 'names' list.
|
|
final Map<String, int> nameIndices = {};
|
|
for (String name in namesList) {
|
|
nameIndices[name] = nameIndices.length;
|
|
}
|
|
|
|
// Generate the 'mappings' field.
|
|
final StringBuffer mappingsStr = StringBuffer();
|
|
|
|
int lastTargetColumn = 0;
|
|
int lastSourceIndex = 0;
|
|
int lastSourceLine = 0;
|
|
int lastSourceColumn = 0;
|
|
int lastNameIndex = 0;
|
|
|
|
bool first = true;
|
|
|
|
for (int i = 0; i < mappings.length; ++i) {
|
|
final mapping = mappings[i];
|
|
final sourceInfo = mapping.sourceInfo;
|
|
|
|
if (sourceInfo == null && first) {
|
|
// Initial parts of the code will be unmapped my default, we don't need to
|
|
// explicitly unmap them. More importantly, current version of binaryen
|
|
// cannot handle single-segment mappings at the beginning of the mappings.
|
|
// We can remove this block of code after switching to a version with
|
|
// https://github.com/WebAssembly/binaryen/pull/6794.
|
|
continue;
|
|
}
|
|
|
|
first = false;
|
|
|
|
lastTargetColumn = _encodeVLQ(
|
|
mappingsStr,
|
|
mapping.instructionOffset,
|
|
lastTargetColumn,
|
|
);
|
|
|
|
if (sourceInfo != null) {
|
|
final sourceIndex = sourceIndices[sourceInfo.fileUri]!;
|
|
|
|
lastSourceIndex = _encodeVLQ(mappingsStr, sourceIndex, lastSourceIndex);
|
|
lastSourceLine = _encodeVLQ(mappingsStr, sourceInfo.line, lastSourceLine);
|
|
lastSourceColumn = _encodeVLQ(
|
|
mappingsStr,
|
|
sourceInfo.col,
|
|
lastSourceColumn,
|
|
);
|
|
|
|
if (sourceInfo.name != null) {
|
|
final nameIndex = nameIndices[sourceInfo.name!]!;
|
|
lastNameIndex = _encodeVLQ(mappingsStr, nameIndex, lastNameIndex);
|
|
}
|
|
}
|
|
|
|
if (i != mappings.length - 1) {
|
|
mappingsStr.write(',');
|
|
}
|
|
}
|
|
|
|
return <String, Object?>{
|
|
"version": 3,
|
|
"sources": sourcesList.map((uri) => uri.toString()).toList(),
|
|
"names": namesList,
|
|
"mappings": mappingsStr.toString(),
|
|
};
|
|
}
|
|
|
|
/// Writes the VLQ of delta between [value] and [offset] into [output] and
|
|
/// return [value].
|
|
int _encodeVLQ(StringSink output, int value, int offset) {
|
|
int delta = value - offset;
|
|
int signBit = 0;
|
|
if (delta < 0) {
|
|
signBit = 1;
|
|
delta = -delta;
|
|
}
|
|
delta = (delta << 1) | signBit;
|
|
do {
|
|
int digit = delta & _vlqBaseMask;
|
|
delta >>= _vlqBaseShift;
|
|
if (delta > 0) {
|
|
digit |= _vlqContinuationBit;
|
|
}
|
|
output.write(_base64Digits[digit]);
|
|
} while (delta > 0);
|
|
return value;
|
|
}
|
|
|
|
const int _vlqBaseShift = 5;
|
|
const int _vlqBaseMask = (1 << 5) - 1;
|
|
const int _vlqContinuationBit = 1 << 5;
|
|
const String _base64Digits =
|
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn'
|
|
'opqrstuvwxyz0123456789+/';
|