56505e0575
The `Match.operator[]` does the same thing and is generally recommended (and shorter). (I want to deprecate `group` and `groups`) Tested: Refactoring. CoreLibraryReviewExempt: Calling equivalent function. Change-Id: I4c758968ae622fe16b7322be1b29b05b91e7fcd9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/489021 Reviewed-by: Paul Berry <paulberry@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com>
172 lines
5.0 KiB
Dart
172 lines
5.0 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.
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:source_maps/parser.dart';
|
|
|
|
import 'utils.dart';
|
|
|
|
final isMinified = const bool.fromEnvironment('dart.tool.dart2wasm.minify');
|
|
|
|
void f() {
|
|
g();
|
|
}
|
|
|
|
void g() {
|
|
throw 'hi';
|
|
}
|
|
|
|
runtimeFalse() => int.parse('1') == 0;
|
|
|
|
// `expectedFrames` is (file, line, column, name) of the frames we check.
|
|
//
|
|
// Information we don't check are "null": we don't want to check line/column
|
|
// of standard library functions to avoid breaking the test with unrelated
|
|
// changes to the standard library.
|
|
void testMain(
|
|
String testName,
|
|
List<(String?, int?, int?, String?)?> expectedFrames,
|
|
) {
|
|
// Use `f` and `g` in a few places to make sure wasm-opt won't inline them
|
|
// in the test.
|
|
final fTearOff = f;
|
|
final gTearOff = g;
|
|
|
|
if (runtimeFalse()) f();
|
|
if (runtimeFalse()) g();
|
|
|
|
// Get some simple stack trace.
|
|
String? stackTraceString;
|
|
try {
|
|
f();
|
|
} catch (e, st) {
|
|
stackTraceString = st.toString();
|
|
}
|
|
|
|
// Print the stack trace to make it easy to update the test.
|
|
print("-----");
|
|
print(stackTraceString);
|
|
print("-----");
|
|
|
|
final actualFrames = parseStack(
|
|
testName,
|
|
getSourceMapping(testName),
|
|
stackTraceString!,
|
|
);
|
|
print('Got stack trace:');
|
|
for (final frame in actualFrames) {
|
|
print(' $frame');
|
|
}
|
|
print('Matching against:');
|
|
for (final frame in expectedFrames) {
|
|
print(' $frame');
|
|
}
|
|
|
|
if (actualFrames.length < expectedFrames.length) {
|
|
throw 'Less actual frames than expected';
|
|
}
|
|
|
|
for (int i = 0; i < expectedFrames.length; i++) {
|
|
final expected = expectedFrames[i];
|
|
final actual = actualFrames[i];
|
|
if (expected == null) continue;
|
|
if (actual == null) {
|
|
throw 'Mismatch:\n Expected: $expected\n Actual: <no mapping>';
|
|
}
|
|
if ((expected.$1 != null && actual.$1 != expected.$1) ||
|
|
(expected.$2 != null && actual.$2 != expected.$2) ||
|
|
(expected.$3 != null && actual.$3 != expected.$3) ||
|
|
(expected.$4 != null && actual.$4 != expected.$4)) {
|
|
throw 'Mismatch:\n Expected: $expected\n Actual: $actual';
|
|
}
|
|
}
|
|
}
|
|
|
|
Mapping getSourceMapping(String testName) {
|
|
final allMappings = MappingBundle();
|
|
// Read source map of the current program.
|
|
final mainFilename = getSourceMapFilePath(testName, 0);
|
|
final mainSourceMapFile = readfile(mainFilename);
|
|
final mainSourceMap = parse(utf8.decode(mainSourceMapFile)) as SingleMapping;
|
|
mainSourceMap.targetUrl = mainFilename;
|
|
allMappings.addMapping(mainSourceMap);
|
|
|
|
int i = 1;
|
|
while (true) {
|
|
// All the modules will have consecutive names. Keep reading them until we
|
|
// fail to find one.
|
|
try {
|
|
final filename = getSourceMapFilePath(testName, i);
|
|
final fileContents = readfile(filename);
|
|
final sourceMap = parse(utf8.decode(fileContents)) as SingleMapping;
|
|
sourceMap.targetUrl = filename;
|
|
allMappings.addMapping(sourceMap);
|
|
i++;
|
|
} catch (e) {
|
|
break;
|
|
}
|
|
}
|
|
return allMappings;
|
|
}
|
|
|
|
List<(String?, int?, int?, String?)?> parseStack(
|
|
String testName,
|
|
Mapping mapping,
|
|
String stackTraceString,
|
|
) {
|
|
final parsed = <(String?, int?, int?, String?)?>[];
|
|
for (final line in stackTraceString.split('\n')) {
|
|
if (line.contains('.mjs') || line.contains('.js')) {
|
|
parsed.add(null);
|
|
continue;
|
|
}
|
|
|
|
final hexOffsetMatch = stackTraceHexOffsetRegExp.firstMatch(line);
|
|
if (hexOffsetMatch == null) {
|
|
throw 'Unable to parse hex offset in frame "$line"';
|
|
}
|
|
final hexOffsetStr = hexOffsetMatch[1]!; // includes '0x'
|
|
final offset = int.tryParse(hexOffsetStr);
|
|
if (offset == null) {
|
|
throw 'Unable to parse hex number in frame "$line"';
|
|
}
|
|
final moduleIdMatch = stackTraceModuleNameRegExp.firstMatch(line);
|
|
if (moduleIdMatch == null) {
|
|
throw 'Unable to parse module name in frame "$line"';
|
|
}
|
|
final moduleIdString = moduleIdMatch[1]!;
|
|
final moduleId = isMinified
|
|
? parseMinifiedModule(moduleIdString)
|
|
: int.parse(moduleIdString.replaceAll('module', ''));
|
|
final uri = getSourceMapFilePath(testName, moduleId);
|
|
final span = mapping.spanFor(0, offset, uri: uri);
|
|
if (span == null) {
|
|
print('Stack frame "$line" does not have a source mapping');
|
|
parsed.add(null);
|
|
continue;
|
|
}
|
|
final filename = span.sourceUrl!.pathSegments.last;
|
|
final lineNumber = span.start.line;
|
|
final columnNumber = span.start.column;
|
|
final symbolName = span.text;
|
|
parsed.add((filename, 1 + lineNumber, 1 + columnNumber, symbolName));
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
int parseMinifiedModule(String moduleName) {
|
|
final codeUnits = moduleName.codeUnits;
|
|
int result = 0;
|
|
int power = 1;
|
|
for (final codeUnit in codeUnits) {
|
|
result += (codeUnit - 35) * power;
|
|
power *= 92;
|
|
}
|
|
return result - 1;
|
|
}
|
|
|
|
final stackTraceHexOffsetRegExp = RegExp(r'wasm-function.*(0x[0-9a-fA-F]+)\)$');
|
|
final stackTraceModuleNameRegExp = RegExp(r'wasm/(.*)-[0-9a-z]+:');
|