3c973fb292
Change-Id: I228058202855900f0adba73c1ab04d35180a6e5d Tested: this is an analyzer diagnostic only change Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/414900 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Devon Carew <devoncarew@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com> Reviewed-by: Nate Biggs <natebiggs@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
185 lines
5.2 KiB
Dart
185 lines
5.2 KiB
Dart
// Copyright (c) 2018, 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.
|
|
|
|
/// Representation of a source-map file with dart2js-specific extensions, and
|
|
/// helper functions to parse them.
|
|
library;
|
|
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:source_maps/source_maps.dart';
|
|
// ignore: implementation_imports
|
|
import 'package:source_maps/src/vlq.dart';
|
|
|
|
import 'util.dart';
|
|
|
|
/// Representation of a source-map file with dart2js-specific extensions.
|
|
///
|
|
/// Dart2js adds a special section that provides: tables of minified names and a
|
|
/// table of inlining frame data.
|
|
class Dart2jsMapping {
|
|
final SingleMapping sourceMap;
|
|
|
|
final Map<String, String> globalNames = {};
|
|
final Map<String, String> instanceNames = {};
|
|
final Map<int, List<FrameEntry>> frames = {};
|
|
late final List<int> frameIndex = frames.keys.toList()..sort();
|
|
|
|
Dart2jsMapping(this.sourceMap, Map json, {Logger? logger}) {
|
|
var extensions = json['x_org_dartlang_dart2js'] as Map?;
|
|
if (extensions == null) return;
|
|
var minifiedNames = extensions['minified_names'];
|
|
if (minifiedNames != null) {
|
|
_extractMinifiedNames(
|
|
minifiedNames['global'] as String,
|
|
sourceMap,
|
|
globalNames,
|
|
logger,
|
|
);
|
|
_extractMinifiedNames(
|
|
minifiedNames['instance'] as String,
|
|
sourceMap,
|
|
instanceNames,
|
|
logger,
|
|
);
|
|
}
|
|
var jsonFrames = extensions['frames'] as String?;
|
|
if (jsonFrames != null) {
|
|
_FrameDecoder(jsonFrames).parseFrames(frames, sourceMap);
|
|
}
|
|
}
|
|
|
|
Dart2jsMapping.json(Map json) : this(parseSingleMapping(json), json);
|
|
}
|
|
|
|
class FrameEntry {
|
|
final String? callUri;
|
|
final int? callLine;
|
|
final int? callColumn;
|
|
final String? inlinedMethodName;
|
|
final bool isEmpty;
|
|
FrameEntry.push(
|
|
this.callUri,
|
|
this.callLine,
|
|
this.callColumn,
|
|
this.inlinedMethodName,
|
|
) : isEmpty = false;
|
|
FrameEntry.pop(this.isEmpty)
|
|
: callUri = null,
|
|
callLine = null,
|
|
callColumn = null,
|
|
inlinedMethodName = null;
|
|
|
|
bool get isPush => callUri != null;
|
|
bool get isPop => callUri == null;
|
|
|
|
@override
|
|
toString() {
|
|
if (isPush) {
|
|
return "push $inlinedMethodName @ $callUri:$callLine:$callColumn";
|
|
}
|
|
return isEmpty ? 'pop: empty' : 'pop';
|
|
}
|
|
}
|
|
|
|
const _marker = "\n//# sourceMappingURL=";
|
|
Dart2jsMapping? parseMappingFor(Uri uri, {Logger? logger}) {
|
|
var file = File.fromUri(uri);
|
|
if (!file.existsSync()) {
|
|
logger?.log('Error: no such file: $uri');
|
|
return null;
|
|
}
|
|
var contents = file.readAsStringSync();
|
|
var urlIndex = contents.indexOf(_marker);
|
|
String sourcemapPath;
|
|
if (urlIndex != -1) {
|
|
sourcemapPath = contents.substring(urlIndex + _marker.length).trim();
|
|
} else {
|
|
logger?.log(
|
|
'Error: source-map url marker not found in $uri\n'
|
|
' trying $uri.map',
|
|
);
|
|
sourcemapPath = '${uri.pathSegments.last}.map';
|
|
}
|
|
|
|
assert(!sourcemapPath.contains('\n'));
|
|
var sourcemapFile = File.fromUri(uri.resolve(sourcemapPath));
|
|
if (!sourcemapFile.existsSync()) {
|
|
logger?.log('Error: no such file: $sourcemapFile');
|
|
return null;
|
|
}
|
|
var json = jsonDecode(sourcemapFile.readAsStringSync());
|
|
return Dart2jsMapping(parseSingleMapping(json), json, logger: logger);
|
|
}
|
|
|
|
class _FrameDecoder implements Iterator<String> {
|
|
final String _internal;
|
|
final int _length;
|
|
int index = -1;
|
|
_FrameDecoder(this._internal) : _length = _internal.length;
|
|
|
|
// Iterator API is used by decodeVlq to consume VLQ entries.
|
|
@override
|
|
bool moveNext() => ++index < _length;
|
|
|
|
@override
|
|
String get current =>
|
|
(index >= 0 && index < _length)
|
|
? _internal[index]
|
|
: throw StateError('No current value available.');
|
|
|
|
bool get hasTokens => index < _length - 1 && _length > 0;
|
|
|
|
int _readDelta() => decodeVlq(this);
|
|
|
|
void parseFrames(Map<int, List<FrameEntry>> frames, SingleMapping sourceMap) {
|
|
var offset = 0;
|
|
var uriId = 0;
|
|
var nameId = 0;
|
|
var line = 0;
|
|
var column = 0;
|
|
while (hasTokens) {
|
|
offset += _readDelta();
|
|
List<FrameEntry> entries = frames[offset] ??= [];
|
|
var marker = _internal[index + 1];
|
|
if (marker == ';') {
|
|
entries.add(FrameEntry.pop(true));
|
|
index++;
|
|
continue;
|
|
} else if (marker == ',') {
|
|
entries.add(FrameEntry.pop(false));
|
|
index++;
|
|
continue;
|
|
} else {
|
|
uriId += _readDelta();
|
|
var uri = sourceMap.urls[uriId];
|
|
line += _readDelta();
|
|
column += _readDelta();
|
|
nameId += _readDelta();
|
|
var name = sourceMap.names[nameId];
|
|
entries.add(FrameEntry.push(uri, line, column, name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _extractMinifiedNames(
|
|
String encodedInput,
|
|
SingleMapping sourceMap,
|
|
Map<String, String> minifiedNames,
|
|
Logger? logger,
|
|
) {
|
|
if (encodedInput.isEmpty) return;
|
|
List<String> input = encodedInput.split(',');
|
|
if (input.length % 2 != 0) {
|
|
logger?.log("Error: expected an even number of entries");
|
|
}
|
|
for (int i = 0; i < input.length; i += 2) {
|
|
String minifiedName = input[i];
|
|
int id = int.parse(input[i + 1]);
|
|
minifiedNames[minifiedName] = sourceMap.names[id];
|
|
}
|
|
}
|