|  | // 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, | 
|  | DiagnosticMessage, | 
|  | 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_strong.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_strong.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 CompilerOptions compilerOptions = CompilerOptions() | 
|  | ..sdkSummary = platformKernelUri | 
|  | ..fileSystem = fileSystem | 
|  | ..additionalDills = additionalDills | 
|  | ..packagesFileUri = packagesUri | 
|  | ..dynamicInterfaceSpecificationUri = dynamicInterfaceSpecificationUri | 
|  | ..explicitExperimentalFlags = parseExperimentalFlags( | 
|  | parseExperimentalArguments(experimentalFlags), | 
|  | onError: printMessage) | 
|  | ..onDiagnostic = (DiagnosticMessage m) { | 
|  | errorDetector(m); | 
|  | } | 
|  | ..embedSourceText = false | 
|  | ..invocationModes = InvocationMode.parseArguments(cfeInvocationModes) | 
|  | ..verbosity = verbosity; | 
|  |  | 
|  | compilerOptions.target = createFrontEndTarget(targetName, | 
|  | trackWidgetCreation: trackWidgetCreation, supportMirrors: false); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | final BytecodeOptions bytecodeOptions = | 
|  | BytecodeOptions(enableAsserts: enableAsserts) | 
|  | ..parseCommandLineFlags(bytecodeGeneratorOptions); | 
|  |  | 
|  | 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!); | 
|  | await sink.close(); | 
|  | if (bytecodeOptions.showBytecodeSizeStatistics) { | 
|  | BytecodeSizeStatistics.dump(); | 
|  | } | 
|  |  | 
|  | if (depfile != null) { | 
|  | await writeDepfile( | 
|  | fileSystem, | 
|  | results.compiledSources!, | 
|  | depfileTarget ?? outputFileName, | 
|  | depfile, | 
|  | ); | 
|  | } | 
|  |  | 
|  | return successExitCode; | 
|  | } |