782c4555b4
This change introduces flags to allow the use of dynamic calls now that they have their corresponding validation and runtime check. This will enable us to add end-to-end tests next. TEST=none yet - will be added in subsequent CL (see CL chain) Bug: b/448095881 Change-Id: I82448824e94dd940ce5346f79554d34847ecb610 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/498320 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Sigmund Cherem <sigmund@google.com>
383 lines
12 KiB
Dart
383 lines
12 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 'package:vm/transformations/prefix_library_uris.dart'
|
|
as prefix_library_uris;
|
|
|
|
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-creation-locations',
|
|
help:
|
|
'Run a kernel transformer to track creation locations for'
|
|
' classes annotated with @pragma(\'track-creation-locations\').',
|
|
defaultsTo: false,
|
|
aliases: [
|
|
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
|
|
// to the new flag.
|
|
'track-widget-creation',
|
|
],
|
|
)
|
|
..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,
|
|
)
|
|
..addOption(
|
|
'prefix-library-uris',
|
|
help: 'Slash-separated prefix to add to all library uris',
|
|
defaultsTo: '',
|
|
)
|
|
..addFlag(
|
|
'allow-dynamic-calls-in-dynamic-modules',
|
|
help: 'Allow dynamic calls in dynamic modules',
|
|
defaultsTo: false,
|
|
)
|
|
..addMultiOption(
|
|
'extra-selectors-allowed-in-dynamic-calls',
|
|
help:
|
|
'Selector names that weren\'t exposed by the host, but are allowed in '
|
|
'dynamic calls within the dynamic module',
|
|
);
|
|
|
|
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 trackCreationLocations = options['track-creation-locations'];
|
|
final List<String>? bytecodeGeneratorOptions = options['bytecode-options'];
|
|
final String libraryUrisPrefix = options['prefix-library-uris']!;
|
|
final bool allowDynamicCallsInDynamicModules =
|
|
options['allow-dynamic-calls-in-dynamic-modules'];
|
|
final List<String> dynamicCallsSelectorAllowList =
|
|
options['extra-selectors-allowed-in-dynamic-calls'];
|
|
|
|
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,
|
|
trackCreationLocations: trackCreationLocations,
|
|
bytecodeGeneratorOptions: bytecodeGeneratorOptions,
|
|
depfile: depfile,
|
|
depfileTarget: depfileTarget,
|
|
libraryUrisPrefix: libraryUrisPrefix,
|
|
allowDynamicCallsInDynamicModules: allowDynamicCallsInDynamicModules,
|
|
dynamicCallsSelectorAllowList: dynamicCallsSelectorAllowList,
|
|
);
|
|
}
|
|
|
|
/// 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 trackCreationLocations = false,
|
|
List<String>? bytecodeGeneratorOptions,
|
|
String? depfile,
|
|
String? depfileTarget,
|
|
required String libraryUrisPrefix,
|
|
bool allowDynamicCallsInDynamicModules = false,
|
|
List<String> dynamicCallsSelectorAllowList = const [],
|
|
}) 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> additionalDillModules = <Uri>[];
|
|
if (importDill != null) {
|
|
additionalDillModules.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
|
|
..additionalDillModules = additionalDillModules
|
|
..packagesFileUri = packagesUri
|
|
..dynamicInterfaceSpecificationUri = dynamicInterfaceSpecificationUri
|
|
..allowDynamicCallsInDynamicModules = allowDynamicCallsInDynamicModules
|
|
..dynamicCallsSelectorAllowList = dynamicCallsSelectorAllowList
|
|
..explicitExperimentalFlags = parseExperimentalFlags(
|
|
parseExperimentalArguments(experimentalFlags),
|
|
onError: printMessage,
|
|
)
|
|
..onDiagnostic = (CfeDiagnosticMessage m) {
|
|
errorDetector(m);
|
|
}
|
|
..embedSourceText = bytecodeOptions.embedSourceText
|
|
..invocationModes = InvocationMode.parseArguments(cfeInvocationModes)
|
|
..verbosity = verbosity
|
|
..target = createFrontEndTarget(
|
|
targetName,
|
|
trackCreationLocations: trackCreationLocations,
|
|
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();
|
|
|
|
Component? component = results.component;
|
|
if (errorDetector.hasCompilationErrors || component == null) {
|
|
return compileTimeErrorExitCode;
|
|
}
|
|
component = prefix_library_uris.prefixLibraryUris(
|
|
component,
|
|
results.loadedLibraries,
|
|
libraryUrisPrefix,
|
|
);
|
|
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;
|
|
}
|