blob: 25dd4f84fb88ec66837a8c88ebf8b72f00396cf4 [file] [log] [blame]
// Copyright (c) 2025, 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:io';
import 'dart:io' as io;
import 'dart:typed_data';
import 'package:args/args.dart';
import 'package:path/path.dart' as path;
import 'package:wasm_builder/src/ir/ir.dart';
import 'package:wasm_builder/src/serialize/deserializer.dart';
import 'package:wasm_builder/src/serialize/printer.dart';
import 'util.dart';
void main(List<String> args) async {
final result = argParser.parse(args);
final help = result.flag('help');
final write = result.flag('write');
final runFromSource = result.flag('src');
final filter = result.option('filter');
if (help) {
print('Usage:\n${argParser.usage}');
io.exit(0);
}
if (result.rest.isNotEmpty) {
print('Unknown arguments: ${result.rest.join(' ')}');
print('Usage:\n${argParser.usage}');
io.exit(1);
}
final filterRegExp = filter != null ? RegExp(filter) : null;
await withTempDir((String tempDir) async {
for (final dartFilename in listIrTests()) {
if (filterRegExp != null && !filterRegExp.hasMatch(dartFilename)) {
continue;
}
void failTest() {
print('-> test "$dartFilename" failed\n');
io.exitCode = 254;
}
final dartCode = File(dartFilename).readAsStringSync();
final watFile = File(path.setExtension(dartFilename, '.wat'));
final wasmFile = File(path.join(
tempDir, path.setExtension(path.basename(dartFilename), '.wasm')));
print('\nTesting $dartFilename');
final result = await Process.run('/usr/bin/env', [
'bash',
'pkg/dart2wasm/tool/compile_benchmark',
if (runFromSource) '--src',
'--no-strip-wasm',
'-o',
wasmFile.path,
dartFilename
]);
if (result.exitCode != 0) {
print('Compilation failed:');
print('stdout:\n${result.stdout}');
print('stderr:\n${result.stderr}\n');
failTest();
continue;
}
final wasmBytes = wasmFile.readAsBytesSync();
final wat =
moduleToString(parseModule(wasmBytes), parseNameFilters(dartCode));
if (write) {
print('-> Updated expectation file: ${watFile.path}');
watFile.writeAsStringSync(wat);
continue;
}
if (!watFile.existsSync()) {
print('Expected "${watFile.path}" to exist.');
failTest();
continue;
}
final oldWat = watFile.readAsStringSync();
if (oldWat != wat) {
print(
'-> Expectation mismatch. Run with `-w` to update expectation file.');
failTest();
continue;
}
}
});
}
final argParser = ArgParser()
..addFlag('help',
abbr: 'h', defaultsTo: false, help: 'Prints available options.')
..addFlag('src', defaultsTo: false, help: 'Runs the compiler from source.')
..addOption('filter',
abbr: 'f', help: 'Runs only tests that match the filter.')
..addFlag('write',
abbr: 'w', defaultsTo: false, help: 'Writes new expectation files.');
Iterable<String> listIrTests() {
return Directory('pkg/dart2wasm/test/ir_tests')
.listSync(recursive: true)
.whereType<File>()
.map((file) => file.path)
.where((path) => path.endsWith('.dart'));
}
Module parseModule(Uint8List wasmBytes) {
final deserializer = Deserializer(wasmBytes);
return Module.deserialize(deserializer);
}
String moduleToString(Module module, List<RegExp> functionNameFilters) {
bool printFunctionBody(BaseFunction function) {
final name = function.functionName;
if (name == null) return false;
return functionNameFilters.any((pattern) => name.contains(pattern));
}
final mp = ModulePrinter(module, printFunctionBody: printFunctionBody);
for (final function in module.functions.defined) {
if (printFunctionBody(function)) {
mp.enqueueFunction(function);
}
}
return mp.print();
}
List<RegExp> parseNameFilters(String dartCode) {
const functionFilter = '// functionFilter=';
final filters = <RegExp>[];
for (final line in dartCode.split('\n')) {
if (line.startsWith(functionFilter)) {
final filter = line.substring(functionFilter.length).trim();
if (filter.isNotEmpty) {
filters.add(RegExp(filter));
}
}
}
return filters;
}