c878108cc8
All non-pubspec changes generated by `dart format pkg/compiler`. Change-Id: Iadaa70974816d96ccb47813fe72d5a2bb83d6bda Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400181 Reviewed-by: Nate Biggs <natebiggs@google.com> Commit-Queue: Mayank Patke <fishythefish@google.com>
285 lines
8.6 KiB
Dart
285 lines
8.6 KiB
Dart
// Copyright (c) 2015, 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:async';
|
|
import 'dart:io';
|
|
import 'package:_fe_analyzer_shared/src/util/filenames.dart';
|
|
import 'package:expect/async_helper.dart';
|
|
import 'package:expect/expect.dart';
|
|
import 'package:compiler/src/commandline_options.dart';
|
|
import 'package:compiler/src/io/source_information.dart';
|
|
import 'package:compiler/src/js/js_debug.dart';
|
|
import 'package:js_ast/js_ast.dart';
|
|
import '../helpers/sourcemap_helper.dart';
|
|
|
|
typedef CodePointWhiteListFunction WhiteListFunction(
|
|
String configuration,
|
|
String file,
|
|
);
|
|
|
|
typedef bool CodePointWhiteListFunction(CodePoint codePoint);
|
|
|
|
CodePointWhiteListFunction emptyWhiteListFunction(String config, String file) {
|
|
return emptyWhiteList;
|
|
}
|
|
|
|
bool emptyWhiteList(CodePoint codePoint) => false;
|
|
|
|
main(List<String> arguments) {
|
|
test(arguments);
|
|
}
|
|
|
|
void test(
|
|
List<String> arguments, {
|
|
WhiteListFunction whiteListFunction = emptyWhiteListFunction,
|
|
}) {
|
|
Set<String> configurations = Set<String>();
|
|
Map<String, Uri> tests = <String, Uri>{};
|
|
if (!parseArguments(arguments, configurations, tests)) {
|
|
return;
|
|
}
|
|
|
|
asyncTest(() async {
|
|
bool errorsFound = false;
|
|
for (String file in tests.keys) {
|
|
print('==$file=========================================================');
|
|
for (String config in configurations) {
|
|
List<String> options = TEST_CONFIGURATIONS[config]!;
|
|
print('---$config----------------------------------------------------');
|
|
Uri uri = tests[file]!;
|
|
TestResult result = await runTests(config, file, uri, options);
|
|
if (result.missingCodePointsMap.isNotEmpty) {
|
|
errorsFound = result.printMissingCodePoints(
|
|
whiteListFunction(config, file),
|
|
);
|
|
}
|
|
if (result.multipleNodesMap.isNotEmpty) {
|
|
result.printMultipleNodes();
|
|
errorsFound = true;
|
|
}
|
|
if (result.multipleOffsetsMap.isNotEmpty) {
|
|
result.printMultipleOffsets();
|
|
errorsFound = true;
|
|
}
|
|
}
|
|
}
|
|
Expect.isFalse(
|
|
errorsFound,
|
|
"Errors found. "
|
|
"Run the test with a URI option, "
|
|
"`source_mapping_test_viewer [--out=<uri>] [configs] [tests]`, to "
|
|
"create a html visualization of the missing code points.",
|
|
);
|
|
});
|
|
}
|
|
|
|
bool parseArguments(
|
|
List<String> arguments,
|
|
Set<String> configurations,
|
|
Map<String, Uri> tests, {
|
|
bool measure = false,
|
|
}) {
|
|
Set<String>? extra = arguments.contains('--file') ? Set<String>() : null;
|
|
|
|
for (String argument in arguments) {
|
|
if (!parseArgument(argument, configurations, tests, extra)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (configurations.isEmpty) {
|
|
configurations.addAll(TEST_CONFIGURATIONS.keys);
|
|
if (!measure) {
|
|
configurations.remove('old');
|
|
}
|
|
}
|
|
if (extra != null) {
|
|
for (String file in extra) {
|
|
Uri uri = Uri.base.resolve(nativeToUriPath(file));
|
|
tests[uri.pathSegments.last] = uri;
|
|
}
|
|
}
|
|
if (tests.isEmpty) {
|
|
tests.addAll(TEST_FILES);
|
|
}
|
|
if (arguments.contains('--exclude')) {
|
|
List<String> filesToRemove = List<String>.from(tests.keys);
|
|
tests.clear();
|
|
tests.addAll(TEST_FILES);
|
|
filesToRemove.forEach(tests.remove);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Parse [argument] for a valid configuration or test-file option.
|
|
///
|
|
/// On success, the configuration name is added to [configurations] or the
|
|
/// test-file name is added to [testFiles], and `true` is returned.
|
|
/// On failure, a message is printed and `false` is returned.
|
|
///
|
|
/// Unmatching arguments are added to [files] is provided.
|
|
bool parseArgument(
|
|
String argument,
|
|
Set<String> configurations,
|
|
Map<String, Uri> tests,
|
|
Set<String>? extra,
|
|
) {
|
|
if (argument.startsWith('-')) {
|
|
// Skip options.
|
|
return true;
|
|
} else if (TEST_CONFIGURATIONS.containsKey(argument)) {
|
|
configurations.add(argument);
|
|
} else if (TEST_FILES.containsKey(argument)) {
|
|
tests[argument] = TEST_FILES[argument]!;
|
|
} else if (extra != null) {
|
|
extra.add(argument);
|
|
} else {
|
|
print(
|
|
"Unknown configuration or test file '$argument'. "
|
|
"Must be one of '${TEST_CONFIGURATIONS.keys.join("', '")}' or "
|
|
"'${TEST_FILES.keys.join("', '")}'.",
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const Map<String, List<String>> TEST_CONFIGURATIONS = const {
|
|
'kernel': const [],
|
|
};
|
|
|
|
final Map<String, Uri> TEST_FILES = _computeTestFiles();
|
|
|
|
Map<String, Uri> _computeTestFiles() {
|
|
Map<String, Uri> map = <String, Uri>{};
|
|
Directory dataDir = Directory.fromUri(
|
|
Uri.base.resolve('pkg/compiler/test/sourcemaps/data/'),
|
|
);
|
|
for (FileSystemEntity file in dataDir.listSync()) {
|
|
Uri uri = file.uri;
|
|
map[uri.pathSegments.last] = uri;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
Future<TestResult> runTests(
|
|
String config,
|
|
String filename,
|
|
Uri uri,
|
|
List<String> options, {
|
|
bool verbose = true,
|
|
}) async {
|
|
SourceMapProcessor processor = SourceMapProcessor(uri);
|
|
SourceMaps sourceMaps = await processor.process([
|
|
'--csp',
|
|
Flags.disableInlining,
|
|
...options,
|
|
], verbose: verbose);
|
|
TestResult result = TestResult(config, filename, processor);
|
|
for (SourceMapInfo info in sourceMaps.elementSourceMapInfos.values) {
|
|
if (info.element!.library.canonicalUri.isScheme('dart')) continue;
|
|
result.userInfoList.add(info);
|
|
Iterable<CodePoint> missingCodePoints = info.codePoints.where(
|
|
(c) => c.isMissing,
|
|
);
|
|
if (missingCodePoints.isNotEmpty) {
|
|
result.missingCodePointsMap[info] = missingCodePoints;
|
|
}
|
|
Map<int, Set<SourceLocation>> offsetToLocationsMap =
|
|
<int, Set<SourceLocation>>{};
|
|
for (Node node in info.nodeMap.nodes) {
|
|
info.nodeMap[node]!.forEach((
|
|
int targetOffset,
|
|
List<SourceLocation> sourceLocations,
|
|
) {
|
|
if (sourceLocations.length > 1) {
|
|
Map<Node, List<SourceLocation>> multipleMap = result.multipleNodesMap
|
|
.putIfAbsent(info, () => <Node, List<SourceLocation>>{});
|
|
multipleMap[node] = sourceLocations;
|
|
} else {
|
|
offsetToLocationsMap
|
|
.putIfAbsent(targetOffset, () => Set<SourceLocation>())
|
|
.addAll(sourceLocations);
|
|
}
|
|
});
|
|
}
|
|
offsetToLocationsMap.forEach((
|
|
int targetOffset,
|
|
Set<SourceLocation> sourceLocations,
|
|
) {
|
|
if (sourceLocations.length > 1) {
|
|
Map<int, Set<SourceLocation>> multipleMap = result.multipleOffsetsMap
|
|
.putIfAbsent(info, () => <int, Set<SourceLocation>>{});
|
|
multipleMap[targetOffset] = sourceLocations;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class TestResult {
|
|
final String config;
|
|
final String file;
|
|
final SourceMapProcessor processor;
|
|
List<SourceMapInfo> userInfoList = <SourceMapInfo>[];
|
|
Map<SourceMapInfo, Iterable<CodePoint>> missingCodePointsMap =
|
|
<SourceMapInfo, Iterable<CodePoint>>{};
|
|
|
|
/// For each [SourceMapInfo] a map from JS node to multiple source locations
|
|
/// associated with the node.
|
|
Map<SourceMapInfo, Map<Node, List<SourceLocation>>> multipleNodesMap =
|
|
<SourceMapInfo, Map<Node, List<SourceLocation>>>{};
|
|
|
|
/// For each [SourceMapInfo] a map from JS offset to multiple source locations
|
|
/// associated with the offset.
|
|
Map<SourceMapInfo, Map<int, Set<SourceLocation>>> multipleOffsetsMap =
|
|
<SourceMapInfo, Map<int, Set<SourceLocation>>>{};
|
|
|
|
TestResult(this.config, this.file, this.processor);
|
|
|
|
bool printMissingCodePoints([
|
|
CodePointWhiteListFunction codePointWhiteList = emptyWhiteList,
|
|
]) {
|
|
bool allWhiteListed = true;
|
|
missingCodePointsMap.forEach((info, missingCodePoints) {
|
|
print(
|
|
"Missing code points for ${info.element} in '$file' "
|
|
"in config '$config':",
|
|
);
|
|
for (CodePoint codePoint in missingCodePoints) {
|
|
if (codePointWhiteList(codePoint)) {
|
|
print(" $codePoint (white-listed)");
|
|
} else {
|
|
print(" $codePoint");
|
|
allWhiteListed = false;
|
|
}
|
|
}
|
|
});
|
|
return !allWhiteListed;
|
|
}
|
|
|
|
void printMultipleNodes() {
|
|
multipleNodesMap.forEach((info, multipleMap) {
|
|
multipleMap.forEach((node, sourceLocations) {
|
|
print(
|
|
'Multiple source locations:\n ${sourceLocations.join('\n ')}\n'
|
|
'for `${nodeToString(node)}` in ${info.element} in '
|
|
'$file.',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
void printMultipleOffsets() {
|
|
multipleOffsetsMap.forEach((info, multipleMap) {
|
|
multipleMap.forEach((targetOffset, sourceLocations) {
|
|
print(
|
|
'Multiple source locations:\n ${sourceLocations.join('\n ')}\n'
|
|
'for offset $targetOffset in ${info.element} in $file.',
|
|
);
|
|
});
|
|
});
|
|
}
|
|
}
|