blob: 0ed14f02ac3bc5b6fc929f81817d7d54a10a6d32 [file] [log] [blame]
// Copyright (c) 2020, 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 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:vm/snapshot/instruction_sizes.dart' as instruction_sizes;
void main() async {
if (!Platform.executable.contains('dart-sdk')) {
// If we are not running from the prebuilt SDK then this test does nothing.
return;
}
final sdkBin = path.dirname(Platform.executable);
final dart2native =
path.join(sdkBin, Platform.isWindows ? 'dart2native.bat' : 'dart2native');
if (!File(dart2native).existsSync()) {
throw 'Failed to locate dart2native in the SDK';
}
group('instruction-sizes', () {
test('basic-parsing', () async {
await withTempDir('basic-parsing', (dir) async {
final inputDart = path.join(dir, 'input.dart');
final outputBinary = path.join(dir, 'output.exe');
final sizesJson = path.join(dir, 'sizes.json');
// Create test input.
await File(inputDart).writeAsString("""
@pragma('vm:never-inline')
dynamic makeSomeClosures() {
return [
() => true,
() => false,
() => 11,
];
}
class A {
@pragma('vm:never-inline')
dynamic tornOff() {
return true;
}
}
class B {
@pragma('vm:never-inline')
dynamic tornOff() {
return false;
}
}
@pragma('vm:never-inline')
Function tearOff(dynamic o) {
return o.tornOff;
}
void main(List<String> args) {
for (var cl in makeSomeClosures()) {
print(cl());
}
print(tearOff(args.isEmpty ? A() : B()));
}
""");
// Compile input.dart to native and output instruction sizes.
final result = await Process.run(dart2native, [
'-o',
outputBinary,
'--extra-gen-snapshot-options=--print_instructions_sizes_to=$sizesJson',
inputDart,
]);
expect(result.exitCode, equals(0),
reason: 'Compilation completed successfully');
expect(File(outputBinary).existsSync(), isTrue,
reason: 'Output binary exists');
expect(File(sizesJson).existsSync(), isTrue,
reason: 'Instruction sizes output exists');
final symbols = await instruction_sizes.load(File(sizesJson));
expect(symbols, isNotNull,
reason: 'Sizes file was successfully parsed');
expect(symbols.length, greaterThan(0),
reason: 'Sizes file is non-empty');
// Check for duplicated symbols (using both raw and scrubbed names).
final symbolRawNamesByLibrary = Map<String, Set<String>>();
final symbolScrubbedNamesByLibrary = Map<String, Set<String>>();
Set<String> getSetOfNames(
Map<String, Set<String>> map, String libraryUri) {
// For file uris make sure to canonicalize the path. This prevents
// issues with mismatching case on Windows which has case insensitive
// file system.
if (libraryUri != null) {
final uri = Uri.parse(libraryUri);
if (uri.scheme == 'file') {
libraryUri =
Uri.file(path.canonicalize(uri.toFilePath())).toString();
}
}
return map.putIfAbsent(libraryUri ?? '', () => {});
}
for (var sym in symbols) {
expect(
getSetOfNames(symbolRawNamesByLibrary, sym.libraryUri)
.add(sym.name.raw),
isTrue,
reason:
'All symbols should have unique names (within libraries): ${sym.name.raw}');
expect(
getSetOfNames(symbolScrubbedNamesByLibrary, sym.libraryUri)
.add(sym.name.scrubbed),
isTrue,
reason: 'Scrubbing the name should not make it non-unique');
}
// Check for expected names which should appear in the output.
final inputDartSymbolNames = symbolScrubbedNamesByLibrary[
Uri.file(path.canonicalize(inputDart)).toString()];
expect(inputDartSymbolNames, isNotNull,
reason: 'Symbols from input.dart are included into sizes output');
expect(inputDartSymbolNames, contains('makeSomeClosures'));
final closures = inputDartSymbolNames.where(
(name) => name.startsWith('makeSomeClosures.<anonymous closure'));
expect(closures.length, 3);
expect(inputDartSymbolNames, contains('A.tornOff'));
expect(inputDartSymbolNames, contains('[tear-off] A.tornOff'));
expect(inputDartSymbolNames,
contains('[tear-off-extractor] A.get:tornOff'));
expect(inputDartSymbolNames, contains('B.tornOff'));
expect(inputDartSymbolNames, contains('[tear-off] B.tornOff'));
expect(inputDartSymbolNames,
contains('[tear-off-extractor] B.get:tornOff'));
});
});
});
}
Future withTempDir(String prefix, Future fun(String dir)) async {
final tempDir =
Directory.systemTemp.createTempSync('instruction-sizes-test-${prefix}');
try {
await fun(tempDir.path);
} finally {
tempDir.deleteSync(recursive: true);
}
}