// Copyright (c) 2023, 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:typed_data'; import 'package:compiler/compiler_api.dart' as api; import 'package:compiler/src/commandline_options.dart'; import 'package:compiler/src/util/memory_compiler.dart'; import 'package:expect/expect.dart'; const int numShards = 4; const List dataDirs = ['data', '../inference/data']; const Set needsLibs = {'deferred.dart'}; const jsOutFilename = 'out.js'; const sourceMapFilename = '${jsOutFilename}.map'; const dumpInfoFilename = 'out.info.json'; typedef CompiledOutput = Map>; Future compileWithSerialization({ Uri? entryPoint, required Map memorySourceFiles, required List options, }) async { final cfeDillUri = 'memory:cfe.dill'; final closedWorldUri = 'memory:world.data'; final globalDataUri = 'memory:global.data'; final codegenUri = 'memory:codegen'; final jsOutUri = 'memory:$jsOutFilename'; Future compile(List options) async { final outputProvider = OutputCollector(); CompilationResult result = await runCompiler( entryPoint: entryPoint, memorySourceFiles: memorySourceFiles, outputProvider: outputProvider, options: options, ); Expect.isTrue(result.isSuccess); outputProvider.binaryOutputMap.forEach((fileName, binarySink) { memorySourceFiles[fileName.path] = Uint8List.fromList(binarySink.list); }); return outputProvider.clear(); } final commonFlags = [ '${Flags.closedWorldUri}=$closedWorldUri', '${Flags.globalInferenceUri}=$globalDataUri', '${Flags.codegenUri}=$codegenUri', '${Flags.codegenShards}=2', ]; await compile( [...options, '--out=$cfeDillUri', '${Flags.stage}=cfe'] + commonFlags, ); await compile( [ ...options, '${Flags.inputDill}=$cfeDillUri', '${Flags.stage}=closed-world', ] + commonFlags, ); await compile( [ ...options, '${Flags.inputDill}=$cfeDillUri', '${Flags.stage}=global-inference', ] + commonFlags, ); await compile( [ ...options, '${Flags.inputDill}=$cfeDillUri', '${Flags.stage}=codegen', '${Flags.codegenShard}=0', ] + commonFlags, ); await compile( [ ...options, '${Flags.inputDill}=$cfeDillUri', '${Flags.stage}=codegen', '${Flags.codegenShard}=1', ] + commonFlags, ); final output = await compile( [ ...options, '${Flags.inputDill}=$cfeDillUri', '--out=$jsOutUri', '${Flags.stage}=emit-js', ] + commonFlags, ); return output; } Future compileWithoutSerialization({ Uri? entryPoint, required Map memorySourceFiles, required List options, }) async { final jsOutUri = 'memory:$jsOutFilename'; final outputProvider = OutputCollector(); CompilationResult result = await runCompiler( entryPoint: entryPoint, memorySourceFiles: memorySourceFiles, outputProvider: outputProvider, options: [...options, '--out=$jsOutUri'], ); Expect.isTrue(result.isSuccess); return outputProvider.clear(); } // TODO(natebiggs): Check dump info diff when the results are aligned with and // without serialization. Set _outputTypesToIgnore = {api.OutputType.dumpInfo}; Future compareResults( CompiledOutput serializationResult, CompiledOutput noSerializationResult, ) async { serializationResult.forEach((outputType, outputs) { if (_outputTypesToIgnore.contains(outputType)) return; Expect.isTrue(noSerializationResult.containsKey(outputType)); final otherOutputs = noSerializationResult[outputType]!; Expect.mapEquals(outputs, otherOutputs); }); } Future runTests(List args, int shard) async { assert( shard >= 0 && shard < numShards, 'Shard must be between 0 and ${numShards - 1} (inclusive)', ); final libDirectory = Directory.fromUri(Platform.script.resolve('libs')); final testFiles = dataDirs .map((e) => Directory.fromUri(Platform.script.resolve('data'))) .expand((dataDir) => dataDir.listSync()); int i = 0; for (final testFile in testFiles) { if (!testFile.uri.pathSegments.last.endsWith('.dart')) continue; if (i++ % numShards != shard) continue; String name = testFile.uri.pathSegments.last; List testOptions = [Flags.dumpInfo]; print('----------------------------------------------------------------'); print('Test file: ${testFile.uri}'); String mainCode = await File.fromUri(testFile.uri).readAsString(); final mainUri = _createUri('src/main.dart'); Map memorySourceFiles = {mainUri.path: mainCode}; if (needsLibs.contains(name)) { print('Supporting libraries:'); String filePrefix = name.substring(0, name.lastIndexOf('.')); await for (final libFile in libDirectory.list()) { String libFileName = libFile.uri.pathSegments.last; if (libFileName.startsWith(filePrefix)) { print(' - libs/$libFileName'); Uri libFileUri = _createUri('libs/$libFileName'); String libCode = await File.fromUri(libFile.uri).readAsString(); memorySourceFiles[libFileUri.path] = libCode; } } } final serializationResult = await compileWithSerialization( entryPoint: mainUri, memorySourceFiles: memorySourceFiles, options: testOptions, ); final noSerializationResult = await compileWithoutSerialization( entryPoint: mainUri, memorySourceFiles: memorySourceFiles, options: testOptions, ); await compareResults(serializationResult, noSerializationResult); } } // Pretend this is a dart2js_native test to allow use of 'native' keyword // and import of private libraries. Uri _createUri(String fileName) { return Uri.parse('memory:sdk/tests/web/native/$fileName'); }