9e8fdef17c
Closes https://github.com/dart-lang/sdk/pull/63011 GitOrigin-RevId: 926534c2f5d3cd1e9629cf25f6b028ab34c00e64 Change-Id: I88460a060faabeae8f611e88f23b2eb5fc99f21a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/491702 Reviewed-by: Daco Harkes <dacoharkes@google.com> Reviewed-by: Jens Johansen <jensj@google.com> Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
1325 lines
41 KiB
Dart
1325 lines
41 KiB
Dart
// Copyright (c) 2017, 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.
|
|
|
|
/// Defines the VM-specific translation of Dart source code to kernel binaries.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert' as convert show json;
|
|
import 'dart:io' show File, IOSink;
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:args/args.dart' show ArgParser, ArgResults;
|
|
import 'package:build_integration/file_system/multi_root.dart'
|
|
show MultiRootFileSystem, MultiRootFileSystemEntity;
|
|
import 'package:crypto/crypto.dart';
|
|
import 'package:front_end/src/api_unstable/vm.dart'
|
|
show
|
|
CompilerContext,
|
|
CompilerOptions,
|
|
CompilerResult,
|
|
InvocationMode,
|
|
CfeDiagnosticMessage,
|
|
DiagnosticMessageHandler,
|
|
FileSystem,
|
|
FileSystemEntity,
|
|
ProcessedOptions,
|
|
CfeSeverity,
|
|
StandardFileSystem,
|
|
Verbosity,
|
|
getMessageUri,
|
|
kernelForModule,
|
|
kernelForProgram,
|
|
parseExperimentalArguments,
|
|
parseExperimentalFlags,
|
|
printDiagnosticMessage,
|
|
resolveInputUri;
|
|
import 'package:front_end/src/api_prototype/dynamic_module_validator.dart'
|
|
show DynamicInterfaceYamlFile;
|
|
import 'package:kernel/ast.dart' show Component, Library;
|
|
import 'package:kernel/binary/ast_to_binary.dart' show BinaryPrinter;
|
|
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
|
import 'package:kernel/core_types.dart' show CoreTypes;
|
|
import 'package:kernel/kernel.dart' show loadComponentFromBinary;
|
|
import 'package:kernel/target/targets.dart' show Target, TargetFlags, getTarget;
|
|
import 'package:package_config/package_config.dart' show loadPackageConfigUri;
|
|
|
|
import 'http_filesystem.dart' show HttpAwareFileSystem;
|
|
import 'modular/target/install.dart' show installAdditionalTargets;
|
|
import 'modular/transformations/call_site_annotator.dart'
|
|
as call_site_annotator;
|
|
import 'native_assets/synthesizer.dart';
|
|
import 'target_os.dart';
|
|
import 'transformations/deferred_loading.dart' as deferred_loading;
|
|
import 'transformations/devirtualization.dart'
|
|
as devirtualization
|
|
show transformComponent;
|
|
import 'transformations/dynamic_interface_annotator.dart'
|
|
as dynamic_interface_annotator
|
|
show annotateComponent;
|
|
import 'transformations/mixin_deduplication.dart'
|
|
as mixin_deduplication
|
|
show transformComponent;
|
|
import 'transformations/no_dynamic_invocations_annotator.dart'
|
|
as no_dynamic_invocations_annotator
|
|
show transformComponent;
|
|
import 'transformations/obfuscation_prohibitions_annotator.dart'
|
|
as obfuscationProhibitions;
|
|
import 'transformations/record_use/record_use.dart' as record_use;
|
|
import 'transformations/to_string_transformer.dart' as to_string_transformer;
|
|
import 'transformations/type_flow/transformer.dart'
|
|
as globalTypeFlow
|
|
show transformComponent;
|
|
import 'transformations/unreachable_code_elimination.dart'
|
|
as unreachable_code_elimination;
|
|
import 'transformations/vm_constant_evaluator.dart' as vm_constant_evaluator;
|
|
|
|
/// Declare options consumed by [runCompiler].
|
|
void declareCompilerOptions(ArgParser args) {
|
|
args.addOption(
|
|
'platform',
|
|
help: 'Path to vm_platform.dill file',
|
|
defaultsTo: null,
|
|
);
|
|
args.addOption(
|
|
'packages',
|
|
help: 'Path to .dart_tool/package_config.json file',
|
|
defaultsTo: null,
|
|
);
|
|
args.addOption(
|
|
'output',
|
|
abbr: 'o',
|
|
help: 'Path to resulting dill file',
|
|
defaultsTo: null,
|
|
);
|
|
args.addFlag(
|
|
'aot',
|
|
help:
|
|
'Produce kernel file for AOT compilation (enables global transformations).',
|
|
defaultsTo: false,
|
|
);
|
|
args.addFlag(
|
|
'support-mirrors',
|
|
help:
|
|
'Whether dart:mirrors is supported. By default dart:mirrors is '
|
|
'supported when --aot and --minimal-kernel are not used.',
|
|
defaultsTo: null,
|
|
);
|
|
args.addFlag('compact-async', help: 'Obsolete, ignored.', hide: true);
|
|
args.addOption('depfile', help: 'Path to output Ninja depfile');
|
|
args.addOption(
|
|
'depfile-target',
|
|
help: 'Override the target in the generated depfile',
|
|
hide: true,
|
|
);
|
|
args.addOption(
|
|
'from-dill',
|
|
help: 'Read existing dill file instead of compiling from sources',
|
|
defaultsTo: null,
|
|
);
|
|
args.addFlag(
|
|
'link-platform',
|
|
help: 'Include platform into resulting kernel file.',
|
|
defaultsTo: true,
|
|
);
|
|
args.addFlag(
|
|
'minimal-kernel',
|
|
help: 'Produce minimal tree-shaken kernel file.',
|
|
defaultsTo: false,
|
|
);
|
|
args.addFlag(
|
|
'embed-sources',
|
|
help: 'Embed source files in the generated kernel component',
|
|
defaultsTo: true,
|
|
);
|
|
args.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.',
|
|
);
|
|
args.addOption(
|
|
'filesystem-scheme',
|
|
help: 'The URI scheme for the multi-root virtual filesystem.',
|
|
);
|
|
args.addMultiOption(
|
|
'source',
|
|
help: 'List additional source files to include into compilation.',
|
|
defaultsTo: const <String>[],
|
|
);
|
|
args.addOption(
|
|
'native-assets',
|
|
help: 'Provide the native-assets mapping for @Native external functions.',
|
|
);
|
|
args.addOption(
|
|
'recorded-usages-file',
|
|
help: 'The path to store the recorded usages.',
|
|
);
|
|
args.addOption(
|
|
'target',
|
|
help: 'Target model that determines what core libraries are available',
|
|
allowed: <String>['vm', 'flutter', 'flutter_runner', 'dart_runner'],
|
|
defaultsTo: 'vm',
|
|
);
|
|
args.addFlag(
|
|
'tfa',
|
|
help:
|
|
'Enable global type flow analysis and related transformations in AOT mode.',
|
|
defaultsTo: true,
|
|
);
|
|
args.addOption(
|
|
'target-os',
|
|
help: 'Compile for a specific target operating system when in AOT mode.',
|
|
allowed: TargetOS.names,
|
|
);
|
|
args.addFlag(
|
|
'rta',
|
|
help: 'Use rapid type analysis for faster compilation in AOT mode.',
|
|
defaultsTo: true,
|
|
);
|
|
args.addFlag(
|
|
'tree-shake-write-only-fields',
|
|
help: 'Enable tree shaking of fields which are only written in AOT mode.',
|
|
defaultsTo: true,
|
|
);
|
|
args.addFlag(
|
|
'protobuf-tree-shaker-v2',
|
|
help: 'Enable protobuf tree shaker v2 in AOT mode.',
|
|
defaultsTo: false,
|
|
);
|
|
args.addFlag(
|
|
'protobuf-tree-shaker-mixins',
|
|
help: 'Include protobuf messages with mixins in the tree shaker pass.',
|
|
defaultsTo: false,
|
|
);
|
|
args.addMultiOption(
|
|
'define',
|
|
abbr: 'D',
|
|
help: 'The values for the environment constants (e.g. -Dkey=value).',
|
|
);
|
|
args.addFlag(
|
|
'enable-asserts',
|
|
help: 'Whether asserts will be enabled.',
|
|
defaultsTo: false,
|
|
);
|
|
args.addFlag(
|
|
'sound-null-safety',
|
|
help: 'Respect the nullability of types at runtime.',
|
|
defaultsTo: true,
|
|
hide: true,
|
|
);
|
|
args.addFlag(
|
|
'split-output-by-packages',
|
|
help: 'Split resulting kernel file into multiple files (one per package).',
|
|
defaultsTo: false,
|
|
);
|
|
args.addOption(
|
|
'component-name',
|
|
help: 'Name of the Fuchsia component',
|
|
defaultsTo: null,
|
|
);
|
|
args.addOption(
|
|
'data-dir',
|
|
help: 'Name of the subdirectory of //data for output files',
|
|
);
|
|
args.addOption(
|
|
'dynamic-interface',
|
|
help: 'Path to dynamic module interface yaml file.',
|
|
);
|
|
args.addOption(
|
|
'dump-detailed-dynamic-interface',
|
|
help: 'Path to output detailed dynamic interface.',
|
|
);
|
|
args.addOption('manifest', help: 'Path to output Fuchsia package manifest');
|
|
args.addMultiOption(
|
|
'enable-experiment',
|
|
help: 'Comma separated list of experimental features to enable.',
|
|
);
|
|
args.addFlag(
|
|
'help',
|
|
abbr: 'h',
|
|
negatable: false,
|
|
help: 'Print this help message.',
|
|
);
|
|
args.addFlag(
|
|
'track-creation-locations',
|
|
help: 'Run a kernel transformer to track creation locations for widgets.',
|
|
defaultsTo: false,
|
|
aliases: [
|
|
// TODO(http://dartbug.com/63225): Remove this once flutter is migrated
|
|
// to the new flag.
|
|
'track-widget-creation',
|
|
],
|
|
);
|
|
args.addMultiOption(
|
|
'delete-tostring-package-uri',
|
|
help:
|
|
'Replaces implementations of `toString` with `super.toString()` for '
|
|
'specified package',
|
|
valueHelp: 'dart:ui',
|
|
defaultsTo: const <String>[],
|
|
);
|
|
args.addMultiOption(
|
|
'keep-class-names-implementing',
|
|
help:
|
|
'Prevents obfuscation of the class names of any class implementing '
|
|
'the given class.',
|
|
defaultsTo: const <String>[],
|
|
);
|
|
args.addOption(
|
|
'invocation-modes',
|
|
help: 'Provides information to the front end about how it is invoked.',
|
|
defaultsTo: '',
|
|
);
|
|
args.addOption(
|
|
'verbosity',
|
|
help:
|
|
'Sets the verbosity level used for filtering messages during '
|
|
'compilation.',
|
|
defaultsTo: Verbosity.defaultValue,
|
|
);
|
|
}
|
|
|
|
/// Create ArgParser and populate it with options consumed by [runCompiler].
|
|
ArgParser createCompilerArgParser() {
|
|
final ArgParser argParser = new ArgParser(allowTrailingOptions: true);
|
|
declareCompilerOptions(argParser);
|
|
return argParser;
|
|
}
|
|
|
|
const int successExitCode = 0;
|
|
const int badUsageExitCode = 1;
|
|
const int compileTimeErrorExitCode = 254;
|
|
|
|
/// Run kernel compiler tool with given [options] and [usage]
|
|
/// and return exit code.
|
|
Future<int> runCompiler(ArgResults options, String usage) async {
|
|
final String? platformKernel = options['platform'];
|
|
|
|
if (options['help']) {
|
|
print(usage);
|
|
return successExitCode;
|
|
}
|
|
|
|
final String? nativeAssetsPath = options['native-assets'];
|
|
final String? recordedUsagesFile = options['recorded-usages-file'];
|
|
final bool splitOutputByPackages = options['split-output-by-packages'];
|
|
final String? input = options.rest.singleOrNull;
|
|
if ((input == null && (nativeAssetsPath == null || splitOutputByPackages)) ||
|
|
(platformKernel == null)) {
|
|
print(usage);
|
|
return badUsageExitCode;
|
|
}
|
|
|
|
final String outputFileName = options['output'] ?? "$input.dill";
|
|
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 String? fromDillFile = options['from-dill'];
|
|
final List<String>? fileSystemRoots = options['filesystem-root'];
|
|
final String? targetOS = options['target-os'];
|
|
final bool aot = options['aot'];
|
|
final bool tfa = options['tfa'];
|
|
final bool rta = options['rta'];
|
|
final bool linkPlatform = options['link-platform'];
|
|
final bool embedSources = options['embed-sources'];
|
|
final bool enableAsserts = options['enable-asserts'];
|
|
final bool useProtobufTreeShakerV2 = options['protobuf-tree-shaker-v2'];
|
|
final bool protobufTreeShakerMixins = options['protobuf-tree-shaker-mixins'];
|
|
final String? manifestFilename = options['manifest'];
|
|
final String? dataDir = options['component-name'] ?? options['data-dir'];
|
|
final bool? supportMirrors = options['support-mirrors'];
|
|
|
|
final bool minimalKernel = options['minimal-kernel'];
|
|
final bool treeShakeWriteOnlyFields = options['tree-shake-write-only-fields'];
|
|
final List<String>? experimentalFlags = options['enable-experiment'];
|
|
final Map<String, String> environmentDefines = {};
|
|
final List<String> sources = options['source'];
|
|
|
|
if (!parseCommandLineDefines(options['define'], environmentDefines, usage)) {
|
|
return badUsageExitCode;
|
|
}
|
|
|
|
final bool soundNullSafety = options['sound-null-safety'];
|
|
if (!soundNullSafety) {
|
|
print('Error: --no-sound-null-safety is not supported.');
|
|
return badUsageExitCode;
|
|
}
|
|
|
|
if (aot) {
|
|
if (!linkPlatform) {
|
|
print('Error: --no-link-platform option cannot be used with --aot');
|
|
return badUsageExitCode;
|
|
}
|
|
if (splitOutputByPackages) {
|
|
print(
|
|
'Error: --split-output-by-packages option cannot be used with --aot',
|
|
);
|
|
return badUsageExitCode;
|
|
}
|
|
}
|
|
|
|
if (supportMirrors == true) {
|
|
if (aot) {
|
|
print('Error: --support-mirrors option cannot be used with --aot');
|
|
return badUsageExitCode;
|
|
}
|
|
if (minimalKernel) {
|
|
print(
|
|
'Error: --support-mirrors option cannot be used with '
|
|
'--minimal-kernel',
|
|
);
|
|
return badUsageExitCode;
|
|
}
|
|
}
|
|
|
|
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 (aot || linkPlatform) {
|
|
additionalDills.add(platformKernelUri);
|
|
}
|
|
|
|
final verbosity = Verbosity.parseArgument(options['verbosity']);
|
|
final errorPrinter = new ErrorPrinter(verbosity);
|
|
final errorDetector = new ErrorDetector(
|
|
previousErrorHandler: errorPrinter.call,
|
|
);
|
|
|
|
final Uri? nativeAssetsUri = nativeAssetsPath == null
|
|
? null
|
|
: resolveInputUri(nativeAssetsPath);
|
|
|
|
final Uri? recordedUsagesUri = recordedUsagesFile == null
|
|
? null
|
|
: resolveInputUri(recordedUsagesFile);
|
|
|
|
final String? dynamicInterfaceFilePath = options['dynamic-interface'];
|
|
final Uri? dynamicInterfaceUri = dynamicInterfaceFilePath == null
|
|
? null
|
|
: resolveInputUri(dynamicInterfaceFilePath);
|
|
final String? dumpDetailedDynamicInterface =
|
|
options['dump-detailed-dynamic-interface'];
|
|
|
|
Uri? mainUri;
|
|
if (input != null) {
|
|
mainUri = resolveInputUri(input);
|
|
if (packagesUri != null) {
|
|
mainUri = await convertToPackageUri(fileSystem, mainUri, packagesUri);
|
|
}
|
|
}
|
|
|
|
final List<Uri> additionalSources = sources.map(resolveInputUri).toList();
|
|
|
|
final CompilerOptions compilerOptions = new CompilerOptions()
|
|
..sdkSummary = platformKernelUri
|
|
..fileSystem = fileSystem
|
|
..additionalDills = additionalDills
|
|
..packagesFileUri = packagesUri
|
|
..explicitExperimentalFlags = parseExperimentalFlags(
|
|
parseExperimentalArguments(experimentalFlags),
|
|
onError: print,
|
|
)
|
|
..onDiagnostic = (CfeDiagnosticMessage m) {
|
|
errorDetector(m);
|
|
}
|
|
..embedSourceText = embedSources
|
|
..invocationModes = InvocationMode.parseArguments(
|
|
options['invocation-modes'],
|
|
)
|
|
..verbosity = verbosity;
|
|
|
|
compilerOptions.target = createFrontEndTarget(
|
|
targetName,
|
|
trackCreationLocations: options['track-creation-locations'],
|
|
supportMirrors: supportMirrors ?? !(aot || minimalKernel),
|
|
constKeepLocalsIndicator: !(aot || minimalKernel),
|
|
);
|
|
if (compilerOptions.target == null) {
|
|
print('Failed to create front-end target $targetName.');
|
|
return badUsageExitCode;
|
|
}
|
|
|
|
final results = await compileToKernel(
|
|
KernelCompilationArguments(
|
|
source: mainUri,
|
|
options: compilerOptions,
|
|
additionalSources: additionalSources,
|
|
nativeAssets: nativeAssetsUri,
|
|
recordedUsages: recordedUsagesUri,
|
|
includePlatform: additionalDills.isNotEmpty,
|
|
deleteToStringPackageUris: options['delete-tostring-package-uri'],
|
|
keepClassNamesImplementing: options['keep-class-names-implementing'],
|
|
dynamicInterface: dynamicInterfaceUri,
|
|
dumpDetailedDynamicInterface: dumpDetailedDynamicInterface,
|
|
aot: aot,
|
|
useGlobalTypeFlowAnalysis: tfa,
|
|
useRapidTypeAnalysis: rta,
|
|
environmentDefines: environmentDefines,
|
|
enableAsserts: enableAsserts,
|
|
useProtobufTreeShakerV2: useProtobufTreeShakerV2,
|
|
protobufTreeShakerMixins: protobufTreeShakerMixins,
|
|
minimalKernel: minimalKernel,
|
|
treeShakeWriteOnlyFields: treeShakeWriteOnlyFields,
|
|
targetOS: targetOS,
|
|
fromDillFile: fromDillFile,
|
|
),
|
|
);
|
|
|
|
errorPrinter.printCompilationMessages();
|
|
|
|
final Component? component = results.component;
|
|
final Library? nativeAssetsLibrary = results.nativeAssetsLibrary;
|
|
if (errorDetector.hasCompilationErrors ||
|
|
(component == null && nativeAssetsLibrary == null)) {
|
|
return compileTimeErrorExitCode;
|
|
}
|
|
|
|
final IOSink sink = new File(outputFileName).openWrite();
|
|
if (component != null) {
|
|
final BinaryPrinter printer = new BinaryPrinter(
|
|
sink,
|
|
libraryFilter: (lib) => !results.loadedLibraries.contains(lib),
|
|
);
|
|
if (aot && nativeAssetsLibrary != null) {
|
|
// If Dart component in AOT, write the vm:native-assets library _inside_
|
|
// the Dart component.
|
|
// TODO(https://dartbug.com/50152): Support AOT dill concatenation.
|
|
component.libraries.add(nativeAssetsLibrary);
|
|
nativeAssetsLibrary.parent = component;
|
|
}
|
|
printer.writeComponentFile(component);
|
|
}
|
|
if ((nativeAssetsLibrary != null && (!aot || component == null))) {
|
|
// If no Dart component, write as separate dill.
|
|
// If Dart component in JIT, write as concatenated dill, to not mess with
|
|
// the incremental compiler.
|
|
final BinaryPrinter printer = new BinaryPrinter(sink);
|
|
printer.writeComponentFile(Component(libraries: [nativeAssetsLibrary]));
|
|
}
|
|
await sink.close();
|
|
|
|
if (depfile != null) {
|
|
final usedPackageConfig = results.usedPackageConfig;
|
|
await writeDepfile(
|
|
fileSystem,
|
|
[
|
|
if (usedPackageConfig != null) usedPackageConfig,
|
|
...results.compiledSources!,
|
|
],
|
|
depfileTarget ?? outputFileName,
|
|
depfile,
|
|
);
|
|
}
|
|
|
|
if (splitOutputByPackages) {
|
|
await writeOutputSplitByPackages(
|
|
mainUri!,
|
|
compilerOptions,
|
|
results,
|
|
outputFileName,
|
|
);
|
|
}
|
|
|
|
if (manifestFilename != null) {
|
|
await createFarManifest(outputFileName, dataDir, manifestFilename);
|
|
}
|
|
|
|
return successExitCode;
|
|
}
|
|
|
|
/// Results of [compileToKernel]: generated kernel [Component] and
|
|
/// collection of compiled sources.
|
|
class KernelCompilationResults {
|
|
final Component? component;
|
|
|
|
final Library? nativeAssetsLibrary;
|
|
|
|
/// Set of libraries loaded from .dill, with or without the SDK depending on
|
|
/// the compilation settings.
|
|
final Set<Library> loadedLibraries;
|
|
final ClassHierarchy? classHierarchy;
|
|
final CoreTypes? coreTypes;
|
|
final Iterable<Uri>? compiledSources;
|
|
final Uri? usedPackageConfig;
|
|
|
|
KernelCompilationResults(
|
|
this.component,
|
|
this.loadedLibraries,
|
|
this.classHierarchy,
|
|
this.coreTypes,
|
|
this.compiledSources,
|
|
) : nativeAssetsLibrary = null,
|
|
usedPackageConfig = null;
|
|
|
|
KernelCompilationResults.named({
|
|
this.component,
|
|
this.loadedLibraries = const {},
|
|
this.classHierarchy,
|
|
this.coreTypes,
|
|
this.compiledSources,
|
|
this.nativeAssetsLibrary,
|
|
this.usedPackageConfig,
|
|
});
|
|
}
|
|
|
|
// Arguments for [compileToKernel].
|
|
class KernelCompilationArguments {
|
|
final Uri? source;
|
|
final CompilerOptions? options;
|
|
final List<Uri> additionalSources;
|
|
final Uri? nativeAssets;
|
|
final Uri? recordedUsages;
|
|
final bool requireMain;
|
|
final bool includePlatform;
|
|
final List<String> deleteToStringPackageUris;
|
|
final List<String> keepClassNamesImplementing;
|
|
final bool aot;
|
|
final Uri? dynamicInterface;
|
|
final String? dumpDetailedDynamicInterface;
|
|
final Map<String, String> environmentDefines; // Should be mutable.
|
|
final bool enableAsserts;
|
|
final bool useGlobalTypeFlowAnalysis;
|
|
final bool useRapidTypeAnalysis;
|
|
final bool treeShakeWriteOnlyFields;
|
|
final bool useProtobufTreeShakerV2;
|
|
final bool protobufTreeShakerMixins;
|
|
final bool minimalKernel;
|
|
final String? targetOS;
|
|
final String? fromDillFile;
|
|
|
|
KernelCompilationArguments({
|
|
this.source,
|
|
this.options,
|
|
this.additionalSources = const <Uri>[],
|
|
this.nativeAssets,
|
|
this.recordedUsages,
|
|
this.requireMain = true,
|
|
this.includePlatform = false,
|
|
this.deleteToStringPackageUris = const <String>[],
|
|
this.keepClassNamesImplementing = const <String>[],
|
|
this.aot = false,
|
|
this.dynamicInterface,
|
|
this.dumpDetailedDynamicInterface,
|
|
Map<String, String>? environmentDefines,
|
|
this.enableAsserts = true,
|
|
this.useGlobalTypeFlowAnalysis = false,
|
|
this.useRapidTypeAnalysis = true,
|
|
this.treeShakeWriteOnlyFields = false,
|
|
this.useProtobufTreeShakerV2 = false,
|
|
this.protobufTreeShakerMixins = false,
|
|
this.minimalKernel = false,
|
|
this.targetOS,
|
|
this.fromDillFile,
|
|
}) : environmentDefines = environmentDefines ?? {};
|
|
}
|
|
|
|
/// Generates a kernel representation of the program whose main library is in
|
|
/// the given [args.source]. Intended for whole program (non-modular) compilation.
|
|
///
|
|
/// VM-specific replacement of [kernelForProgram].
|
|
///
|
|
/// Either [arg.source], or [args.nativeAssets], or both must be non-null.
|
|
Future<KernelCompilationResults> compileToKernel(
|
|
KernelCompilationArguments args,
|
|
) async {
|
|
final options = args.options!;
|
|
|
|
// Replace error handler to detect if there are compilation errors.
|
|
final errorDetector = new ErrorDetector(
|
|
previousErrorHandler: options.onDiagnostic,
|
|
);
|
|
options.onDiagnostic = errorDetector.call;
|
|
|
|
final nativeAssetsLibrary =
|
|
await NativeAssetsSynthesizer.synthesizeLibraryFromYamlFile(
|
|
args.nativeAssets,
|
|
errorDetector,
|
|
);
|
|
if (args.source == null) {
|
|
return KernelCompilationResults.named(
|
|
nativeAssetsLibrary: nativeAssetsLibrary,
|
|
);
|
|
}
|
|
|
|
final target = options.target!;
|
|
options.environmentDefines = target.updateEnvironmentDefines(
|
|
args.environmentDefines,
|
|
);
|
|
|
|
List<Uri> additionalSources = args.additionalSources;
|
|
final dynamicInterface = args.dynamicInterface;
|
|
if (dynamicInterface != null) {
|
|
final fileUri = await asFileUri(args.options!.fileSystem, dynamicInterface);
|
|
final contents = File(fileUri.toFilePath()).readAsStringSync();
|
|
final dynamicInterfaceYamlFile = DynamicInterfaceYamlFile(contents);
|
|
additionalSources = [
|
|
...additionalSources,
|
|
...dynamicInterfaceYamlFile.getUserLibraryUris(dynamicInterface),
|
|
];
|
|
}
|
|
|
|
CompilerResult? compilerResult;
|
|
final fromDillFile = args.fromDillFile;
|
|
Uri? usedPackageConfig;
|
|
if (fromDillFile != null) {
|
|
compilerResult = await loadKernel(
|
|
options.fileSystem,
|
|
resolveInputUri(fromDillFile),
|
|
);
|
|
} else {
|
|
final processedOptions = new ProcessedOptions(
|
|
options: options,
|
|
inputs: [args.source!, ...additionalSources],
|
|
);
|
|
compilerResult = await CompilerContext.runWithOptions(processedOptions, (
|
|
CompilerContext context,
|
|
) async {
|
|
return args.requireMain
|
|
? await kernelForProgram(
|
|
args.source!,
|
|
options,
|
|
additionalSources: additionalSources,
|
|
)
|
|
: await kernelForModule([
|
|
args.source!,
|
|
...additionalSources,
|
|
], options);
|
|
});
|
|
usedPackageConfig = await processedOptions.resolvePackagesFileUri();
|
|
}
|
|
final Component? component = compilerResult?.component;
|
|
|
|
Iterable<Uri>? compiledSources = component?.uriToSource.keys;
|
|
|
|
Set<Library> loadedLibraries = createLoadedLibrariesSet(
|
|
compilerResult?.loadedComponents,
|
|
compilerResult?.sdkComponent,
|
|
includePlatform: args.includePlatform,
|
|
);
|
|
|
|
if (args.deleteToStringPackageUris.isNotEmpty && component != null) {
|
|
to_string_transformer.transformComponent(
|
|
component,
|
|
args.deleteToStringPackageUris,
|
|
);
|
|
}
|
|
|
|
// Run global transformations only if component is correct.
|
|
if (component != null) {
|
|
if (args.aot || args.minimalKernel) {
|
|
await runGlobalTransformations(target, component, errorDetector, args);
|
|
|
|
if (args.minimalKernel) {
|
|
// compiledSources is component.uriToSource.keys.
|
|
// Make a copy of compiledSources to detach it from
|
|
// component.uriToSource which is cleared below.
|
|
compiledSources = compiledSources!.toList();
|
|
|
|
component.metadata.clear();
|
|
component.uriToSource.clear();
|
|
}
|
|
} else if (dynamicInterface != null) {
|
|
// Even though the dynamic_interface_annotator is a global transformation,
|
|
// we apply it here to partially test dynamic modules with JIT when
|
|
// loading full kernel snapshots. We only use this to insert all
|
|
// dynamically-callable pragmas needed to perform runtime checks around
|
|
// dynamic calls.
|
|
//
|
|
// Note: this is not sufficient to support JIT in the general case. Here
|
|
// we don't handle how JIT uses a different code-path for loading the
|
|
// platform libraries, we also don't support loading code from sources to
|
|
// compile in front-end server, or incremental updates used in hot-reload,
|
|
// and we don't support concatenating dill files (since the global
|
|
// transformation will not see relations established when combining
|
|
// libraries). To properly support JIT we will need a modular strategy in
|
|
// the long run.
|
|
await _runDynamicInterfaceAnnotator(
|
|
component,
|
|
new CoreTypes(component),
|
|
args,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Restore error handler (in case 'options' are reused).
|
|
options.onDiagnostic = errorDetector.previousErrorHandler;
|
|
|
|
return KernelCompilationResults.named(
|
|
component: component,
|
|
nativeAssetsLibrary: nativeAssetsLibrary,
|
|
loadedLibraries: loadedLibraries,
|
|
classHierarchy: compilerResult?.classHierarchy,
|
|
coreTypes: compilerResult?.coreTypes,
|
|
compiledSources: compiledSources,
|
|
usedPackageConfig: usedPackageConfig,
|
|
);
|
|
}
|
|
|
|
Set<Library> createLoadedLibrariesSet(
|
|
List<Component>? loadedComponents,
|
|
Component? sdkComponent, {
|
|
bool includePlatform = false,
|
|
}) {
|
|
final Set<Library> loadedLibraries = {};
|
|
if (loadedComponents != null) {
|
|
for (Component c in loadedComponents) {
|
|
for (Library lib in c.libraries) {
|
|
loadedLibraries.add(lib);
|
|
}
|
|
}
|
|
}
|
|
if (sdkComponent != null) {
|
|
if (includePlatform) {
|
|
for (Library lib in sdkComponent.libraries) {
|
|
loadedLibraries.remove(lib);
|
|
}
|
|
} else {
|
|
for (Library lib in sdkComponent.libraries) {
|
|
loadedLibraries.add(lib);
|
|
}
|
|
}
|
|
}
|
|
return loadedLibraries;
|
|
}
|
|
|
|
Future runGlobalTransformations(
|
|
Target target,
|
|
Component component,
|
|
ErrorDetector errorDetector,
|
|
KernelCompilationArguments args,
|
|
) async {
|
|
assert(!target.flags.supportMirrors);
|
|
if (errorDetector.hasCompilationErrors) return;
|
|
|
|
final coreTypes = new CoreTypes(component);
|
|
|
|
// The dynamic_interface_annotator transformation annotates AST nodes with
|
|
// pragmas and should precede other transformations looking at pragmas
|
|
// (such as mixin_deduplication and TFA).
|
|
if (args.dynamicInterface != null) {
|
|
await _runDynamicInterfaceAnnotator(component, coreTypes, args);
|
|
}
|
|
|
|
// TODO(alexmarkov,cstefantsova): Consider doing canonicalization of
|
|
// identical mixin applications when creating mixin applications in frontend,
|
|
// so all backends (and all transformation passes from the very beginning)
|
|
// can benefit from mixin de-duplication.
|
|
// At least, in addition to VM/AOT case we should run this transformation
|
|
// when building a platform dill file for VM/JIT case.
|
|
mixin_deduplication.transformComponent(component, coreTypes, target);
|
|
|
|
// Perform unreachable code elimination, which should be performed before
|
|
// type flow analysis so TFA won't take unreachable code into account.
|
|
final targetOS = args.targetOS;
|
|
final os = targetOS != null ? TargetOS.fromString(targetOS)! : null;
|
|
final evaluator = vm_constant_evaluator.VMConstantEvaluator.create(
|
|
target,
|
|
component,
|
|
os,
|
|
enableAsserts: args.enableAsserts,
|
|
environmentDefines: args.environmentDefines,
|
|
coreTypes: coreTypes,
|
|
);
|
|
unreachable_code_elimination.transformComponent(
|
|
target,
|
|
component,
|
|
evaluator,
|
|
args.enableAsserts,
|
|
);
|
|
|
|
if (args.useGlobalTypeFlowAnalysis) {
|
|
globalTypeFlow.transformComponent(
|
|
target,
|
|
coreTypes,
|
|
component,
|
|
treeShakeSignatures: !args.minimalKernel,
|
|
treeShakeWriteOnlyFields: args.treeShakeWriteOnlyFields,
|
|
treeShakeProtobufs: args.useProtobufTreeShakerV2,
|
|
treeShakeProtobufMixins: args.protobufTreeShakerMixins,
|
|
useRapidTypeAnalysis: args.useRapidTypeAnalysis,
|
|
);
|
|
} else {
|
|
devirtualization.transformComponent(coreTypes, component);
|
|
no_dynamic_invocations_annotator.transformComponent(component);
|
|
}
|
|
|
|
// TODO(35069): avoid recomputing CSA by reading it from the platform files.
|
|
void ignoreAmbiguousSupertypes(cls, a, b) {}
|
|
final hierarchy = new ClassHierarchy(
|
|
component,
|
|
coreTypes,
|
|
onAmbiguousSupertypes: ignoreAmbiguousSupertypes,
|
|
);
|
|
call_site_annotator.transformLibraries(
|
|
component,
|
|
component.libraries,
|
|
coreTypes,
|
|
hierarchy,
|
|
);
|
|
|
|
// We don't know yet whether gen_snapshot will want to do obfuscation, but if
|
|
// it does it will need the obfuscation prohibitions.
|
|
obfuscationProhibitions.transformComponent(
|
|
component,
|
|
coreTypes,
|
|
target,
|
|
hierarchy,
|
|
args.keepClassNamesImplementing,
|
|
);
|
|
|
|
deferred_loading.transformComponent(component, coreTypes, target);
|
|
|
|
final recordedUsagesFile = args.recordedUsages;
|
|
if (recordedUsagesFile != null) {
|
|
assert(args.source != null);
|
|
record_use.transformComponent(component, recordedUsagesFile);
|
|
}
|
|
}
|
|
|
|
Future _runDynamicInterfaceAnnotator(
|
|
Component component,
|
|
CoreTypes coreTypes,
|
|
KernelCompilationArguments args,
|
|
) async {
|
|
final dynamicInterface = args.dynamicInterface!;
|
|
final fileUri = await asFileUri(args.options!.fileSystem, dynamicInterface);
|
|
final dumpDetailedDynamicInterface = args.dumpDetailedDynamicInterface;
|
|
Map<String, List<Map<String, String>>>? detailedDynamicInterfaceJson =
|
|
(dumpDetailedDynamicInterface != null) ? {} : null;
|
|
dynamic_interface_annotator.annotateComponent(
|
|
File(fileUri.toFilePath()).readAsStringSync(),
|
|
dynamicInterface,
|
|
component,
|
|
coreTypes,
|
|
detailedDynamicInterfaceJson: detailedDynamicInterfaceJson,
|
|
);
|
|
if (dumpDetailedDynamicInterface != null) {
|
|
File(
|
|
dumpDetailedDynamicInterface,
|
|
).writeAsStringSync(convert.json.encode(detailedDynamicInterfaceJson));
|
|
}
|
|
}
|
|
|
|
/// Runs given [action] with [CompilerContext]. This is needed to
|
|
/// be able to report compile-time errors.
|
|
Future<T> runWithFrontEndCompilerContext<T>(
|
|
Uri source,
|
|
CompilerOptions compilerOptions,
|
|
Component component,
|
|
Future<T> action(),
|
|
) async {
|
|
final processedOptions = new ProcessedOptions(
|
|
options: compilerOptions,
|
|
inputs: [source],
|
|
);
|
|
|
|
// Run within the context, so we have uri source tokens...
|
|
return await CompilerContext.runWithOptions(processedOptions, (
|
|
CompilerContext context,
|
|
) async {
|
|
// To make the fileUri/fileOffset -> line/column mapping, we need to
|
|
// pre-fill the map.
|
|
context.uriToSource.addAll(component.uriToSource);
|
|
|
|
return action();
|
|
});
|
|
}
|
|
|
|
class ErrorDetector {
|
|
final DiagnosticMessageHandler? previousErrorHandler;
|
|
bool hasCompilationErrors = false;
|
|
|
|
ErrorDetector({this.previousErrorHandler});
|
|
|
|
void call(CfeDiagnosticMessage message) {
|
|
if (message.severity == CfeSeverity.error) {
|
|
hasCompilationErrors = true;
|
|
}
|
|
|
|
previousErrorHandler?.call(message);
|
|
}
|
|
}
|
|
|
|
class ErrorPrinter {
|
|
final Verbosity verbosity;
|
|
final DiagnosticMessageHandler? previousErrorHandler;
|
|
final Map<Uri?, List<CfeDiagnosticMessage>> compilationMessages =
|
|
<Uri?, List<CfeDiagnosticMessage>>{};
|
|
final void Function(String) println;
|
|
|
|
ErrorPrinter(
|
|
this.verbosity, {
|
|
this.previousErrorHandler,
|
|
this.println = print,
|
|
});
|
|
|
|
void call(CfeDiagnosticMessage message) {
|
|
final sourceUri = getMessageUri(message);
|
|
(compilationMessages[sourceUri] ??= <CfeDiagnosticMessage>[]).add(message);
|
|
previousErrorHandler?.call(message);
|
|
}
|
|
|
|
void printCompilationMessages() {
|
|
final sortedUris = compilationMessages.keys.toList()
|
|
..sort((a, b) {
|
|
// Sort messages without a corresponding uri before the location based
|
|
// messages, since these related to the whole compilation.
|
|
if (a != null && b != null) {
|
|
return '$a'.compareTo('$b');
|
|
} else if (a != null) {
|
|
return 1;
|
|
} else if (b != null) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
});
|
|
for (final Uri? sourceUri in sortedUris) {
|
|
for (final CfeDiagnosticMessage message
|
|
in compilationMessages[sourceUri]!) {
|
|
if (Verbosity.shouldPrint(verbosity, message)) {
|
|
printDiagnosticMessage(message, println);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool parseCommandLineDefines(
|
|
List<String> dFlags,
|
|
Map<String, String> environmentDefines,
|
|
String usage,
|
|
) {
|
|
for (final String dflag in dFlags) {
|
|
final equalsSignIndex = dflag.indexOf('=');
|
|
if (equalsSignIndex < 0) {
|
|
// Ignored.
|
|
} else if (equalsSignIndex > 0) {
|
|
final key = dflag.substring(0, equalsSignIndex);
|
|
final value = dflag.substring(equalsSignIndex + 1);
|
|
environmentDefines[key] = value;
|
|
} else {
|
|
print('The environment constant options must have a key (was: "$dflag")');
|
|
print(usage);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Create front-end target with given name.
|
|
Target? createFrontEndTarget(
|
|
String targetName, {
|
|
bool trackCreationLocations = false,
|
|
bool supportMirrors = true,
|
|
bool includeUnsupportedPlatformLibraryStubs = false,
|
|
bool? constKeepLocalsIndicator,
|
|
bool isClosureContextLoweringEnabled = false,
|
|
}) {
|
|
// Make sure VM-specific targets are available.
|
|
installAdditionalTargets();
|
|
|
|
final TargetFlags targetFlags = new TargetFlags(
|
|
trackCreationLocations: trackCreationLocations,
|
|
supportMirrors: supportMirrors,
|
|
includeUnsupportedPlatformLibraryStubs:
|
|
includeUnsupportedPlatformLibraryStubs,
|
|
constKeepLocalsIndicator: constKeepLocalsIndicator,
|
|
isClosureContextLoweringEnabled: isClosureContextLoweringEnabled,
|
|
);
|
|
return getTarget(targetName, targetFlags);
|
|
}
|
|
|
|
/// Create a front-end file system.
|
|
///
|
|
/// If requested, create a virtual multi-root file system and/or an http aware
|
|
/// file system.
|
|
FileSystem createFrontEndFileSystem(
|
|
String? multiRootFileSystemScheme,
|
|
List<String>? multiRootFileSystemRoots, {
|
|
bool allowHttp = false,
|
|
}) {
|
|
FileSystem fileSystem = StandardFileSystem.instance;
|
|
if (allowHttp) {
|
|
fileSystem = HttpAwareFileSystem(fileSystem);
|
|
}
|
|
if (multiRootFileSystemRoots != null &&
|
|
multiRootFileSystemRoots.isNotEmpty &&
|
|
multiRootFileSystemScheme != null) {
|
|
final rootUris = <Uri>[];
|
|
for (String root in multiRootFileSystemRoots) {
|
|
rootUris.add(resolveInputUri(root));
|
|
}
|
|
fileSystem = new MultiRootFileSystem(
|
|
multiRootFileSystemScheme,
|
|
rootUris,
|
|
fileSystem,
|
|
);
|
|
}
|
|
return fileSystem;
|
|
}
|
|
|
|
/// Convert a URI which may use virtual file system schema to a real file URI.
|
|
Future<Uri> asFileUri(FileSystem fileSystem, Uri uri) async {
|
|
FileSystemEntity fse = fileSystem.entityForUri(uri);
|
|
if (fse is MultiRootFileSystemEntity) {
|
|
fse = await fse.delegate;
|
|
}
|
|
return fse.uri;
|
|
}
|
|
|
|
/// Convert URI to a package URI if it is inside one of the packages.
|
|
/// TODO(alexmarkov) Remove this conversion after Fuchsia build rules are fixed.
|
|
Future<Uri> convertToPackageUri(
|
|
FileSystem fileSystem,
|
|
Uri uri,
|
|
Uri packagesUri,
|
|
) async {
|
|
if (uri.scheme == 'package') {
|
|
return uri;
|
|
}
|
|
// Convert virtual URI to a real file URI.
|
|
final Uri fileUri = await asFileUri(fileSystem, uri);
|
|
try {
|
|
final packageConfig = await loadPackageConfigUri(
|
|
await asFileUri(fileSystem, packagesUri),
|
|
);
|
|
return packageConfig.toPackageUri(fileUri) ?? uri;
|
|
} catch (_) {
|
|
// Can't read packages file - silently give up.
|
|
return uri;
|
|
}
|
|
}
|
|
|
|
/// Write a separate kernel binary for each package. The name of the
|
|
/// output kernel binary is '[outputFileName]-$package.dilp'.
|
|
/// The list of package names is written into a file '[outputFileName]-packages'.
|
|
Future writeOutputSplitByPackages(
|
|
Uri source,
|
|
CompilerOptions compilerOptions,
|
|
KernelCompilationResults compilationResults,
|
|
String outputFileName,
|
|
) async {
|
|
final packages = <String>[];
|
|
final Component component = compilationResults.component!;
|
|
await runWithFrontEndCompilerContext(
|
|
source,
|
|
compilerOptions,
|
|
component,
|
|
() async {
|
|
// When loading a kernel file list, flutter_runner and dart_runner expect
|
|
// 'main' to be last.
|
|
await forEachPackage(compilationResults, (
|
|
String package,
|
|
List<Library> libraries,
|
|
) async {
|
|
packages.add(package);
|
|
final String filename = '$outputFileName-$package.dilp';
|
|
final IOSink sink = new File(filename).openWrite();
|
|
|
|
final BinaryPrinter printer = new BinaryPrinter(
|
|
sink,
|
|
libraryFilter: (lib) =>
|
|
packageFor(lib, compilationResults.loadedLibraries) == package,
|
|
);
|
|
printer.writeComponentFile(component);
|
|
|
|
await sink.close();
|
|
}, mainFirst: false);
|
|
},
|
|
);
|
|
|
|
final IOSink packagesList = new File('$outputFileName-packages').openWrite();
|
|
for (String package in packages) {
|
|
packagesList.writeln(package);
|
|
}
|
|
await packagesList.close();
|
|
}
|
|
|
|
String? packageFor(Library lib, Set<Library> loadedLibraries) {
|
|
// Core libraries are not written into any package kernel binaries.
|
|
if (loadedLibraries.contains(lib)) return null;
|
|
|
|
// Packages are written into their own kernel binaries.
|
|
Uri uri = lib.importUri;
|
|
if (uri.scheme == 'package') return uri.pathSegments.first;
|
|
|
|
// Everything else (e.g., file: or data: imports) is lumped into the main
|
|
// kernel binary.
|
|
return 'main';
|
|
}
|
|
|
|
/// Sort the libraries etc in the component. Helps packages to produce identical
|
|
/// output when their parts are imported in different orders in different
|
|
/// contexts.
|
|
void sortComponent(Component component) {
|
|
component.libraries.sort((Library a, Library b) {
|
|
return a.importUri.toString().compareTo(b.importUri.toString());
|
|
});
|
|
component.computeCanonicalNames();
|
|
for (Library lib in component.libraries) {
|
|
lib.additionalExports.sort();
|
|
}
|
|
}
|
|
|
|
Future<void> forEachPackage(
|
|
KernelCompilationResults results,
|
|
Future<void> action(String package, List<Library> libraries), {
|
|
required bool mainFirst,
|
|
}) async {
|
|
final Component component = results.component!;
|
|
final Set<Library> loadedLibraries = results.loadedLibraries;
|
|
sortComponent(component);
|
|
|
|
final Map<String, List<Library>> packages = <String, List<Library>>{};
|
|
packages['main'] = <Library>[]; // Always create 'main'.
|
|
for (Library lib in component.libraries) {
|
|
final String? package = packageFor(lib, loadedLibraries);
|
|
// Ignore external libraries.
|
|
if (package == null) {
|
|
continue;
|
|
}
|
|
packages.putIfAbsent(package, () => <Library>[]).add(lib);
|
|
}
|
|
|
|
final mainLibraries = packages.remove('main')!;
|
|
if (mainFirst) {
|
|
await action('main', mainLibraries);
|
|
}
|
|
|
|
final mainMethod = component.mainMethod;
|
|
final problemsAsJson = component.problemsAsJson;
|
|
component.setMainMethodAndMode(null, true);
|
|
component.problemsAsJson = null;
|
|
for (String package in packages.keys) {
|
|
await action(package, packages[package]!);
|
|
}
|
|
component.setMainMethodAndMode(mainMethod?.reference, true);
|
|
component.problemsAsJson = problemsAsJson;
|
|
|
|
if (!mainFirst) {
|
|
await action('main', mainLibraries);
|
|
}
|
|
}
|
|
|
|
String _escapePath(String path) {
|
|
return path.replaceAll('\\', '\\\\').replaceAll(' ', '\\ ');
|
|
}
|
|
|
|
/// Create ninja dependencies file, as described in
|
|
/// https://ninja-build.org/manual.html#_depfile
|
|
Future<void> writeDepfile(
|
|
FileSystem fileSystem,
|
|
Iterable<Uri> compiledSources,
|
|
String output,
|
|
String depfile,
|
|
) async {
|
|
final IOSink file = new File(depfile).openWrite();
|
|
file.write(_escapePath(output));
|
|
file.write(':');
|
|
|
|
// TODO(https://dartbug.com/55246): track macro deps when available.
|
|
for (Uri dep in compiledSources) {
|
|
// Skip corelib dependencies.
|
|
if (dep.scheme == 'org-dartlang-sdk') continue;
|
|
Uri uri = await asFileUri(fileSystem, dep);
|
|
file.write(' ');
|
|
file.write(_escapePath(uri.toFilePath()));
|
|
}
|
|
file.write('\n');
|
|
await file.close();
|
|
}
|
|
|
|
Future<void> createFarManifest(
|
|
String output,
|
|
String? dataDir,
|
|
String packageManifestFilename,
|
|
) async {
|
|
List<String> packages = await File('$output-packages').readAsLines();
|
|
|
|
// Make sure the 'main' package is the last (convention with package loader).
|
|
packages.remove('main');
|
|
packages.add('main');
|
|
|
|
final IOSink packageManifest = File(packageManifestFilename).openWrite();
|
|
|
|
final String kernelListFilename = '$packageManifestFilename.dilplist';
|
|
final IOSink kernelList = File(kernelListFilename).openWrite();
|
|
for (String package in packages) {
|
|
final String filenameInPackage = '$package.dilp';
|
|
final String filenameInBuild = '$output-$package.dilp';
|
|
packageManifest.write(
|
|
'data/$dataDir/$filenameInPackage=$filenameInBuild\n',
|
|
);
|
|
kernelList.write('$filenameInPackage\n');
|
|
}
|
|
await kernelList.close();
|
|
|
|
final String frameworkVersionFilename =
|
|
'$packageManifestFilename.frameworkversion';
|
|
final IOSink frameworkVersion = File(frameworkVersionFilename).openWrite();
|
|
for (String package in [
|
|
'collection',
|
|
'flutter',
|
|
'meta',
|
|
'typed_data',
|
|
'vector_math',
|
|
]) {
|
|
Digest? digest;
|
|
if (packages.contains(package)) {
|
|
final filenameInBuild = '$output-$package.dilp';
|
|
final bytes = await File(filenameInBuild).readAsBytes();
|
|
digest = sha256.convert(bytes);
|
|
}
|
|
frameworkVersion.write('$package=$digest\n');
|
|
}
|
|
await frameworkVersion.close();
|
|
|
|
packageManifest.write('data/$dataDir/app.dilplist=$kernelListFilename\n');
|
|
packageManifest.write(
|
|
'data/$dataDir/app.frameworkversion=$frameworkVersionFilename\n',
|
|
);
|
|
await packageManifest.close();
|
|
}
|
|
|
|
class CompilerResultLoadedFromKernel implements CompilerResult {
|
|
final Component component;
|
|
final Component sdkComponent = Component();
|
|
|
|
CompilerResultLoadedFromKernel(this.component);
|
|
|
|
@override
|
|
Uint8List? get summary => null;
|
|
|
|
@override
|
|
List<Component> get loadedComponents => const <Component>[];
|
|
|
|
@override
|
|
CoreTypes? get coreTypes => null;
|
|
|
|
@override
|
|
ClassHierarchy? get classHierarchy => null;
|
|
}
|
|
|
|
Future<CompilerResult> loadKernel(
|
|
FileSystem fileSystem,
|
|
Uri dillFileUri,
|
|
) async {
|
|
final component = loadComponentFromBinary(
|
|
(await asFileUri(fileSystem, dillFileUri)).toFilePath(),
|
|
);
|
|
return CompilerResultLoadedFromKernel(component);
|
|
}
|
|
|
|
// Used by kernel_front_end_test.dart
|
|
main() {}
|