blob: 7711314376ae7a025468b49bfb09ef089fd76e60 [file] [log] [blame] [edit]
// 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' show File, Directory, Process, ProcessResult;
import 'dart:typed_data';
import 'package:build_integration/file_system/multi_root.dart'
show MultiRootFileSystemEntity, MultiRootFileSystem;
import 'package:front_end/src/api_prototype/file_system.dart' show FileSystem;
import 'package:kernel/ast.dart' show Component;
import 'package:kernel/binary/ast_from_binary.dart'
show BinaryBuilderWithMetadata;
import 'package:kernel/kernel.dart'
show writeComponentToBinary, writeComponentToText;
import 'package:path/path.dart' as path;
import 'compiler_options.dart';
class CompilerPhaseInputOutputManager {
final FileSystem fileSystem;
final WasmCompilerOptions options;
CompilerPhaseInputOutputManager(FileSystem fileSystem, this.options)
: fileSystem = options.multiRootScheme != null
? MultiRootFileSystem(
options.multiRootScheme!,
options.multiRoots.isEmpty ? [Uri.base] : options.multiRoots,
fileSystem)
: fileSystem;
String _moduleNameToWasmFile(String prefix, String moduleName) {
return path.join(path.dirname(prefix), moduleName);
}
String _moduleNameToSourceMapFile(String prefix, String moduleName) {
return '${_moduleNameToWasmFile(prefix, moduleName)}.map';
}
Uri _moduleNameToRelativeSourceMapUri(String moduleName) {
return Uri.file(path
.basename(_moduleNameToSourceMapFile(options.outputFile, moduleName)));
}
Uri Function(String)? get sourceMapUrlGenerator =>
options.translatorOptions.generateSourceMaps
? _moduleNameToRelativeSourceMapUri
: null;
Future<String> readString(Uri uri) async {
return await File.fromUri((await resolveUri(uri))!).readAsString();
}
Future<List<int>> readBytes(Uri uri) async {
return await File.fromUri((await resolveUri(uri))!).readAsBytes();
}
Future<void> readComponent(Uri componentUri, Component component) async {
BinaryBuilderWithMetadata(
await File.fromUri((await resolveUri(componentUri))!).readAsBytes())
.readComponent(component);
}
Future<void> writeComponent(Component component, String path,
{bool includeSource = true}) {
return writeComponentToBinary(component, path,
includeSource: includeSource);
}
void writeComponentAsText(Component component, String path) {
writeComponentToText(component, path: path, showMetadata: true);
}
Future<void> writeWasmModule(Uint8List wasmModule, String moduleName) {
final wasmFileName = _moduleNameToWasmFile(options.outputFile, moduleName);
final Directory dir = Directory(path.dirname(wasmFileName));
// Do this synchronously to make sure it happens before subsequent async
// operations.
if (!dir.existsSync()) {
dir.createSync(recursive: true);
}
return File(wasmFileName).writeAsBytes(wasmModule);
}
Future<void> writeWasmSourceMap(String sourceMap, String moduleName) {
return File(_moduleNameToSourceMapFile(options.outputFile, moduleName))
.writeAsString(sourceMap);
}
Future<void> writeJsRuntime(String jsRuntime) {
return File(path.setExtension(options.outputFile, '.mjs'))
.writeAsString(jsRuntime);
}
Future<void> writeSupportJs(String supportJs) {
return File(path.setExtension(options.outputFile, '.support.js'))
.writeAsString(supportJs);
}
Future<void> runWasmOpt(
String mainWasmModule, int moduleId, List<String> flags) async {
final inputModuleName = options.moduleNameForId(mainWasmModule, moduleId);
final outputModuleName =
options.moduleNameForId(options.outputFile, moduleId);
final wasmOutName =
_moduleNameToWasmFile(options.outputFile, outputModuleName);
final wasmInName = _moduleNameToWasmFile(mainWasmModule, inputModuleName);
final args = [
...flags,
wasmInName,
'-o',
wasmOutName,
if (options.translatorOptions.generateSourceMaps) ...[
'-ism',
_moduleNameToSourceMapFile(mainWasmModule, inputModuleName),
'-osm',
_moduleNameToSourceMapFile(options.outputFile, outputModuleName),
],
if (!options.stripWasm) '-g',
];
if (options.saveUnopt) {
await File(wasmInName)
.copy(path.setExtension(wasmOutName, '.unopt.wasm'));
}
final wasmOptPath = options.wasmOptPath?.toFilePath() ?? 'wasm-opt';
final result = await _runProcess(wasmOptPath, args);
if (result.exitCode != 0) {
throw Exception('wasm-opt failed with exit code ${result.exitCode}:'
'\n${result.stdout}\n${result.stderr}');
}
}
Future<ProcessResult> _runProcess(
String executable, List<String> args) async {
return await Process.run(executable, args);
}
Future<int> getModuleCount(Uri mainWasmFile) async {
final mainPath = (await resolveUri(mainWasmFile))!.toFilePath();
final files = (await Directory(path.dirname(mainPath)).list().toList());
final prefix = path.basenameWithoutExtension(mainPath);
bool isMultiModule = false;
int maxModuleId = 0;
for (final file in files) {
if (file is! File) continue;
final fileBase = path.basename(file.path);
if (!fileBase.startsWith(prefix)) continue;
if (path.extension(fileBase) != '.wasm') continue;
final fileSuffix =
path.setExtension(fileBase, '').substring(prefix.length);
if (!fileSuffix.startsWith('_module')) continue;
isMultiModule = true;
final moduleId = int.tryParse(fileSuffix.substring('_module'.length));
if (moduleId == null) continue;
maxModuleId = moduleId > maxModuleId ? moduleId : maxModuleId;
}
return isMultiModule ? maxModuleId + 1 : 1;
}
Future<Uint8List> readMainDynModuleMetadataBytes() async {
final filename = options.dynamicModuleMetadataFile ??
Uri.parse(path.setExtension(
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
return await File.fromUri(filename).readAsBytes();
}
Future<void> writeMainDynModuleMetadataBytes(Uint8List bytes) async {
final filename = options.dynamicModuleMetadataFile ??
Uri.parse(path.setExtension(
options.dynamicMainModuleUri!.toFilePath(), '.dyndata'));
await File.fromUri(filename).writeAsBytes(bytes);
}
Future<Uri?> resolveUri(Uri? uri) async {
if (uri == null) return null;
var fileSystemEntity = fileSystem.entityForUri(uri);
if (fileSystemEntity is MultiRootFileSystemEntity) {
fileSystemEntity = await fileSystemEntity.delegate;
}
return fileSystemEntity.uri;
}
}