Files
sdk/pkg/dart2bytecode/lib/dart2bytecode.dart
T
Tess Strickland b6c060a6ab [vm,dyn_modules] Pass additional libraries to the BytecodeGenerator.
In addition to passing in the libraries that should be visited to the
bytecode generator, also pass the libraries which are needed by the
component and either are not in the component or should not be visited
(e.g., libraries from the platform when the platform is not included).

This way, the bytecode generator can create appropriate LibraryIndexes
to detect the use of `dart:` libraries which aren't already in
VmTarget.extraIndexedLibraries, and thus in coreTypes.index (e.g.,
'dart:ffi').

TEST=pkg/dart2bytecode

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-dyn-mac-debug-arm64-try
Change-Id: Ic08f2022753950bf639c446c0b2594e1e269872a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476760
Commit-Queue: Tess Strickland <sstrickl@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
2026-02-06 06:13:22 -08:00

287 lines
10 KiB
Dart

// Copyright (c) 2024, 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:async';
import 'dart:io' as io show exitCode, File, IOSink;
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:front_end/src/api_unstable/vm.dart'
show
CompilerOptions,
InvocationMode,
CfeDiagnosticMessage,
Verbosity,
parseExperimentalArguments,
parseExperimentalFlags,
resolveInputUri;
import 'package:kernel/ast.dart' show Component;
import 'package:vm/kernel_front_end.dart'
show
badUsageExitCode,
compileTimeErrorExitCode,
compileToKernel,
convertToPackageUri,
createFrontEndFileSystem,
createFrontEndTarget,
ErrorDetector,
ErrorPrinter,
KernelCompilationArguments,
parseCommandLineDefines,
successExitCode,
writeDepfile;
import 'bytecode_serialization.dart' show BytecodeSizeStatistics;
import 'bytecode_generator.dart' show generateBytecode;
import 'options.dart' show BytecodeOptions;
final ArgParser _argParser = ArgParser(allowTrailingOptions: true)
..addOption('platform',
help: 'Path to vm_platform.dill file', defaultsTo: null)
..addOption('packages',
help: 'Path to .dart_tool/package_config.json file', defaultsTo: null)
..addOption('output',
abbr: 'o', help: 'Path to resulting bytecode file', defaultsTo: null)
..addOption('depfile', help: 'Path to output Ninja depfile')
..addOption(
'depfile-target',
help: 'Override the target in the generated depfile',
hide: true,
)
..addMultiOption('filesystem-root',
help: 'A base path for the multi-root virtual file system.'
' If multi-root file system is used, the input script and .dart_tool/package_config.json file should be specified using URI.')
..addOption('filesystem-scheme',
help: 'The URI scheme for the multi-root virtual filesystem.')
..addOption('target',
help: 'Target model that determines what core libraries are available',
allowed: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'],
defaultsTo: 'vm')
..addMultiOption('define',
abbr: 'D',
help: 'The values for the environment constants (e.g. -Dkey=value).')
..addOption('import-dill',
help: 'Import libraries from existing dill file', defaultsTo: null)
..addFlag('enable-asserts',
help: 'Whether asserts will be enabled.', defaultsTo: false)
..addMultiOption('bytecode-options',
help: 'Specify options for bytecode generation:',
valueHelp: 'opt1,opt2,...',
allowed: BytecodeOptions.commandLineFlags.keys,
allowedHelp: BytecodeOptions.commandLineFlags)
..addMultiOption('enable-experiment',
help: 'Comma separated list of experimental features to enable.')
..addFlag('help',
abbr: 'h', negatable: false, help: 'Print this help message.')
..addFlag('track-widget-creation',
help: 'Run a kernel transformer to track creation locations for widgets.',
defaultsTo: false)
..addOption('invocation-modes',
help: 'Provides information to the front end about how it is invoked.',
defaultsTo: '')
..addOption('validate',
help:
'Validate dynamic module against specified dynamic interface YAML file',
defaultsTo: null)
..addOption('verbosity',
help: 'Sets the verbosity level used for filtering messages during '
'compilation.',
defaultsTo: Verbosity.defaultValue);
final String _usage = '''
Usage: dart2bytecode --platform vm_platform.dill [--import-dill host_app.dill] [--validate dynamic_interface.yaml] [options] input.dart
Compiles Dart sources to Dart bytecode.
Options:
${_argParser.usage}
''';
Future<void> main(List<String> arguments) async {
io.exitCode = await runCompilerWithCommandLineArguments(arguments);
}
/// Run bytecode compiler tool with given [arguments]
/// and return exit code (0 on success, non-zero on failure).
Future<int> runCompilerWithCommandLineArguments(List<String> arguments) async {
final ArgResults options = _argParser.parse(arguments);
final String? platformKernel = options['platform'];
if (options['help']) {
print(_usage);
return successExitCode;
}
final String? input = options.rest.singleOrNull;
if (input == null || platformKernel == null) {
print(_usage);
return badUsageExitCode;
}
final String outputFileName = options['output'] ?? "$input.bytecode";
final String? packages = options['packages'];
final String targetName = options['target'];
final String? fileSystemScheme = options['filesystem-scheme'];
final String? depfile = options['depfile'];
final String? depfileTarget = options['depfile-target'];
final List<String>? fileSystemRoots = options['filesystem-root'];
final bool enableAsserts = options['enable-asserts'];
final List<String>? experimentalFlags = options['enable-experiment'];
final Map<String, String> environmentDefines = {};
if (!parseCommandLineDefines(options['define'], environmentDefines, _usage)) {
return badUsageExitCode;
}
final String? importDill = options['import-dill'];
final String? validateDynamicInterface = options['validate'];
final String messageVerbosity = options['verbosity'];
final String cfeInvocationModes = options['invocation-modes'];
final bool trackWidgetCreation = options['track-widget-creation'];
final List<String>? bytecodeGeneratorOptions = options['bytecode-options'];
return await runCompilerWithOptions(
input: input,
platformKernel: platformKernel,
outputFileName: outputFileName,
targetName: targetName,
packages: packages,
importDill: importDill,
validateDynamicInterface: validateDynamicInterface,
enableAsserts: enableAsserts,
experimentalFlags: experimentalFlags,
environmentDefines: environmentDefines,
fileSystemScheme: fileSystemScheme,
fileSystemRoots: fileSystemRoots,
messageVerbosity: messageVerbosity,
cfeInvocationModes: cfeInvocationModes,
trackWidgetCreation: trackWidgetCreation,
bytecodeGeneratorOptions: bytecodeGeneratorOptions,
depfile: depfile,
depfileTarget: depfileTarget,
);
}
/// Run bytecode compiler tool with given options
/// and return exit code (0 on success, non-zero on failure).
Future<int> runCompilerWithOptions({
required String input,
required String platformKernel,
required String outputFileName,
required String targetName,
String? packages,
String? importDill,
String? validateDynamicInterface,
bool enableAsserts = false,
List<String>? experimentalFlags,
Map<String, String> environmentDefines = const {},
String? fileSystemScheme,
List<String>? fileSystemRoots,
String messageVerbosity = Verbosity.defaultValue,
void Function(String) printMessage = print,
String cfeInvocationModes = '',
bool trackWidgetCreation = false,
List<String>? bytecodeGeneratorOptions,
String? depfile,
String? depfileTarget,
}) async {
final fileSystem =
createFrontEndFileSystem(fileSystemScheme, fileSystemRoots);
final Uri? packagesUri = packages != null ? resolveInputUri(packages) : null;
final platformKernelUri = Uri.base.resolveUri(new Uri.file(platformKernel));
final List<Uri> additionalDills = <Uri>[];
if (importDill != null) {
additionalDills.add(Uri.base.resolveUri(new Uri.file(importDill)));
}
final Uri? dynamicInterfaceSpecificationUri =
(validateDynamicInterface != null)
? resolveInputUri(validateDynamicInterface)
: null;
final verbosity = Verbosity.parseArgument(messageVerbosity);
final errorPrinter = ErrorPrinter(verbosity, println: printMessage);
final errorDetector = ErrorDetector(previousErrorHandler: errorPrinter.call);
Uri mainUri = resolveInputUri(input);
if (packagesUri != null) {
mainUri = await convertToPackageUri(fileSystem, mainUri, packagesUri);
}
final BytecodeOptions bytecodeOptions =
BytecodeOptions(enableAsserts: enableAsserts)
..parseCommandLineFlags(bytecodeGeneratorOptions);
final CompilerOptions compilerOptions = CompilerOptions()
..sdkSummary = platformKernelUri
..fileSystem = fileSystem
..additionalDills = additionalDills
..packagesFileUri = packagesUri
..dynamicInterfaceSpecificationUri = dynamicInterfaceSpecificationUri
..explicitExperimentalFlags = parseExperimentalFlags(
parseExperimentalArguments(experimentalFlags),
onError: printMessage)
..onDiagnostic = (CfeDiagnosticMessage m) {
errorDetector(m);
}
..embedSourceText = bytecodeOptions.embedSourceText
..invocationModes = InvocationMode.parseArguments(cfeInvocationModes)
..verbosity = verbosity
..target = createFrontEndTarget(targetName,
trackWidgetCreation: trackWidgetCreation,
supportMirrors: false,
isClosureContextLoweringEnabled:
bytecodeOptions.isClosureContextLoweringEnabled);
if (compilerOptions.target == null) {
printMessage('Failed to create front-end target $targetName.');
return badUsageExitCode;
}
final results = await compileToKernel(KernelCompilationArguments(
source: mainUri,
options: compilerOptions,
requireMain: false,
includePlatform: false,
environmentDefines: Map.of(environmentDefines),
enableAsserts: enableAsserts));
errorPrinter.printCompilationMessages();
final Component? component = results.component;
if (errorDetector.hasCompilationErrors || component == null) {
return compileTimeErrorExitCode;
}
if (bytecodeOptions.showBytecodeSizeStatistics) {
BytecodeSizeStatistics.reset();
}
final io.IOSink sink = io.File(outputFileName).openWrite();
generateBytecode(component, sink,
libraries: component.libraries
.where((lib) => !results.loadedLibraries.contains(lib))
.toList(),
hierarchy: results.classHierarchy!,
coreTypes: results.coreTypes!,
options: bytecodeOptions,
target: compilerOptions.target!,
extraLoadedLibraries: results.loadedLibraries);
await sink.close();
if (bytecodeOptions.showBytecodeSizeStatistics) {
BytecodeSizeStatistics.dump();
}
if (depfile != null) {
await writeDepfile(
fileSystem,
results.compiledSources!,
depfileTarget ?? outputFileName,
depfile,
);
}
return successExitCode;
}