blob: d74cc3d94424f3cf29e62548f737555703a32e88 [file] [log] [blame] [edit]
// Copyright (c) 2022, 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:convert';
import 'dart:io' show File, FileSystemException;
import 'dart:vmservice_io' show getResidentCompilerInfoFileConsideringArgsImpl;
import 'package:args/args.dart';
import 'package:frontend_server/resident_frontend_server_utils.dart'
show sendAndReceiveResponse;
import 'package:path/path.dart' as p;
import 'commands/compilation_server.dart' show CompilationServerCommand;
import 'resident_frontend_constants.dart';
/// The Resident Frontend Compiler's shutdown command.
final residentServerShutdownCommand = jsonEncode(
<String, Object>{
commandString: shutdownString,
},
);
File? getResidentCompilerInfoFileConsideringArgs(final ArgResults args) =>
getResidentCompilerInfoFileConsideringArgsImpl(
args[CompilationServerCommand.residentCompilerInfoFileFlag] ??
args[CompilationServerCommand.legacyResidentServerInfoFileFlag]);
final String packageConfigName = p.join('.dart_tool', 'package_config.json');
/// Removes the [serverInfoFile].
void cleanupResidentServerInfo(File serverInfoFile) {
if (serverInfoFile.existsSync()) {
try {
serverInfoFile.deleteSync();
} catch (_) {}
}
}
/// First, attempts to shut down the Resident Frontend Compiler associated with
/// [infoFile]. If successful, [infoFile] is deleted. If any error occurs
/// preventing shutdown, the error is ignored and [infoFile] is deleted, making
/// the compiler be forgetten. The forgotten compiler will shut itself down a
/// certain period of inactivity (see the inactivityTimeout parameter of
/// residentListenAndCompile in
/// pkg/frontend_server/lib/src/resident_frontend_server.dart).
Future<void> shutDownOrForgetResidentFrontendCompiler(File infoFile) async {
try {
// As explained in the doc comment above, this function ignores errors. So,
// we ignore the return value of [sendAndReceiveResponse].
await sendAndReceiveResponse(
residentServerShutdownCommand,
infoFile,
);
} on FileSystemException catch (_) {
// As explained in the doc comment above, this function ignores errors. We
// only catch [FileSystemException]s because [sendAndReceiveResponse] cannot
// throw any other type of error.
}
cleanupResidentServerInfo(infoFile);
}
Future<bool> isFileKernelFile(final File file) async {
final bytes = await file.openRead(0, 4).expand((i) => i).toList();
if (bytes.length < 4) {
return false;
}
// Check for the magic number at the start of kernel files.
return bytes[0] == 0x90 &&
bytes[1] == 0xab &&
bytes[2] == 0xcd &&
bytes[3] == 0xef;
}
Future<bool> isFileAppJitSnapshot(final File file) async {
final bytes = await file.openRead(0, 8).expand((i) => i).toList();
if (bytes.length < 8) {
return false;
}
// Check for the magic number at the start of AppJIT snapshots.
return bytes[0] == 0xdc &&
bytes[1] == 0xdc &&
bytes[2] == 0xf6 &&
bytes[3] == 0xf6 &&
bytes[4] == 0 &&
bytes[5] == 0 &&
bytes[6] == 0 &&
bytes[7] == 0;
}
Future<bool> isFileAotSnapshot(final File file) async {
// Check for any of the magic numbers that can be found at the start of an
// AOT snapshot.
final bytes = await file.openRead(0, 4).expand((i) => i).toList();
// Check for the COFF magic numbers.
if (bytes.length < 2) {
return false;
}
if (bytes[0] == 0x01 && bytes[1] == 0xc0 || // arm32
bytes[0] == 0xaa && bytes[1] == 0x64 || // arm64
bytes[0] == 0x50 && bytes[1] == 0x32 || // riscv32
bytes[0] == 0x50 && bytes[1] == 0x64 /* riscv64 */) {
return true;
}
if (bytes.length < 4) {
return false;
}
// Check for the ELF magic number.
if (bytes[0] == 0x7f &&
bytes[1] == 0x45 &&
bytes[2] == 0x4c &&
bytes[3] == 0x46) {
return true;
}
// Check for the Mach-O magic numbers.
if (bytes[0] == 0xfe &&
bytes[1] == 0xed &&
bytes[2] == 0xfa &&
bytes[3] == 0xce) {
// macho32
return true;
}
if (bytes[0] == 0xfe &&
bytes[1] == 0xed &&
bytes[2] == 0xfa &&
bytes[3] == 0xcf) {
// macho64
return true;
}
if (bytes[0] == 0xcf &&
bytes[1] == 0xfa &&
bytes[2] == 0xed &&
bytes[3] == 0xfe) {
// macho64_arm64
return true;
}
return false;
}
/// Used to create compile requests for the run CLI command.
///
/// Returns a JSON string that the resident compiler will be able to interpret.
String createCompileJitJson({
required String executable,
required String outputDill,
required ArgResults args,
String? packages,
bool verbose = false,
}) {
return jsonEncode(
<String, Object?>{
commandString: compileString,
sourceString: executable,
outputString: outputDill,
if (args.wasParsed(defineOption))
defineOption: args.multiOption(defineOption),
if (args.options.contains(enableAssertsOption) &&
args.wasParsed(enableAssertsOption))
enableAssertsOption: true,
if (args.wasParsed(enableExperimentOption))
enableExperimentOption: args
.multiOption(enableExperimentOption)
.map((e) => '--enable-experiment=$e')
.toList(),
if (packages != null) packageString: packages,
if (args.wasParsed(verbosityOption))
verbosityOption: args[verbosityOption],
},
);
}