|  | // 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:typed_data'; | 
|  |  | 
|  | import 'package:build_integration/file_system/multi_root.dart' | 
|  | show MultiRootFileSystem; | 
|  | import 'package:front_end/src/api_prototype/macros.dart' as macros | 
|  | show isMacroLibraryUri; | 
|  | import 'package:front_end/src/api_prototype/standard_file_system.dart' | 
|  | show StandardFileSystem; | 
|  | import 'package:front_end/src/api_unstable/vm.dart' | 
|  | show | 
|  | CompilerOptions, | 
|  | CompilerResult, | 
|  | DiagnosticMessage, | 
|  | kernelForProgram, | 
|  | NnbdMode, | 
|  | Severity; | 
|  | import 'package:kernel/ast.dart'; | 
|  | import 'package:kernel/class_hierarchy.dart'; | 
|  | import 'package:kernel/core_types.dart'; | 
|  | import 'package:kernel/kernel.dart' show writeComponentToText; | 
|  | import 'package:kernel/library_index.dart'; | 
|  | import 'package:kernel/verifier.dart'; | 
|  | import 'package:vm/kernel_front_end.dart' show writeDepfile; | 
|  | import 'package:vm/transformations/mixin_deduplication.dart' | 
|  | as mixin_deduplication show transformComponent; | 
|  | import 'package:vm/transformations/to_string_transformer.dart' | 
|  | as to_string_transformer; | 
|  | import 'package:vm/transformations/type_flow/transformer.dart' as globalTypeFlow | 
|  | show transformComponent; | 
|  | import 'package:vm/transformations/unreachable_code_elimination.dart' | 
|  | as unreachable_code_elimination; | 
|  | import 'package:wasm_builder/wasm_builder.dart' show Serializer; | 
|  |  | 
|  | import 'compiler_options.dart' as compiler; | 
|  | import 'constant_evaluator.dart'; | 
|  | import 'deferred_loading.dart' as deferred_loading; | 
|  | import 'js/runtime_generator.dart' as js; | 
|  | import 'record_class_generator.dart'; | 
|  | import 'records.dart'; | 
|  | import 'target.dart' as wasm show Mode; | 
|  | import 'target.dart' hide Mode; | 
|  | import 'translator.dart'; | 
|  |  | 
|  | sealed class CompilationResult {} | 
|  |  | 
|  | class CompilationSuccess extends CompilationResult { | 
|  | final Map<String, ({Uint8List moduleBytes, String? sourceMap})> wasmModules; | 
|  | final String jsRuntime; | 
|  |  | 
|  | CompilationSuccess(this.wasmModules, this.jsRuntime); | 
|  | } | 
|  |  | 
|  | class CompilationError extends CompilationResult {} | 
|  |  | 
|  | /// The CFE has crashed with an exception. | 
|  | /// | 
|  | /// This is a CFE bug and should be reported by users. | 
|  | class CFECrashError extends CompilationError { | 
|  | final Object error; | 
|  | final StackTrace stackTrace; | 
|  |  | 
|  | CFECrashError(this.error, this.stackTrace); | 
|  | } | 
|  |  | 
|  | /// Compiling the Dart program resulted in compile-time errors. | 
|  | /// | 
|  | /// This is a bug in the dart program (e.g. syntax errors, static type errors, | 
|  | /// ...) that's being compiled.  Users have to address those errors in their | 
|  | /// code for it to compile successfully. | 
|  | /// | 
|  | /// The errors are already printed via the `handleDiagnosticMessage` callback. | 
|  | /// (We print them as soon as they are reported by CFE. i.e. we stream errors | 
|  | /// instead of accumulating/batching all of them and reporting at the end.) | 
|  | class CFECompileTimeErrors extends CompilationError { | 
|  | CFECompileTimeErrors(); | 
|  | } | 
|  |  | 
|  | /// Compile a Dart file into a Wasm module. | 
|  | /// | 
|  | /// Returns `null` if an error occurred during compilation. The | 
|  | /// [handleDiagnosticMessage] callback will have received an error message | 
|  | /// describing the error. | 
|  | /// | 
|  | /// When generating source maps, `sourceMapUrlGenerator` argument should be | 
|  | /// provided which takes the module name and gives the URL of the source map. | 
|  | /// This value will be added to the Wasm module in `sourceMappingURL` section. | 
|  | /// When this argument is null the code generator does not generate source | 
|  | /// mappings. | 
|  | Future<CompilationResult> compileToModule( | 
|  | compiler.WasmCompilerOptions options, | 
|  | Uri Function(String moduleName)? sourceMapUrlGenerator, | 
|  | void Function(DiagnosticMessage) handleDiagnosticMessage) async { | 
|  | var hadCompileTimeError = false; | 
|  | void diagnosticMessageHandler(DiagnosticMessage message) { | 
|  | if (message.severity == Severity.error) { | 
|  | hadCompileTimeError = true; | 
|  | } | 
|  | handleDiagnosticMessage(message); | 
|  | } | 
|  |  | 
|  | final wasm.Mode mode; | 
|  | if (options.translatorOptions.jsCompatibility) { | 
|  | mode = wasm.Mode.jsCompatibility; | 
|  | } else { | 
|  | mode = wasm.Mode.regular; | 
|  | } | 
|  | final WasmTarget target = WasmTarget( | 
|  | enableExperimentalFfi: options.translatorOptions.enableExperimentalFfi, | 
|  | enableExperimentalWasmInterop: | 
|  | options.translatorOptions.enableExperimentalWasmInterop, | 
|  | removeAsserts: !options.translatorOptions.enableAsserts, | 
|  | mode: mode); | 
|  | CompilerOptions compilerOptions = CompilerOptions() | 
|  | ..target = target | 
|  | // This is a dummy directory that always exists. This option should be | 
|  | // unused as we pass platform.dill or libraries.json, though currently the | 
|  | // CFE mandates this option to be there (but doesn't use it). | 
|  | // => Remove this once CFE no longer mandates this (or remove option in CFE | 
|  | // entirely). | 
|  | ..sdkRoot = Uri.file('.') | 
|  | ..librariesSpecificationUri = options.librariesSpecPath | 
|  | ..packagesFileUri = options.packagesPath | 
|  | ..environmentDefines = { | 
|  | 'dart.tool.dart2wasm': 'true', | 
|  | ...options.environment, | 
|  | } | 
|  | ..explicitExperimentalFlags = options.feExperimentalFlags | 
|  | ..verbose = false | 
|  | ..onDiagnostic = diagnosticMessageHandler | 
|  | ..nnbdMode = NnbdMode.Strong; | 
|  | if (options.multiRootScheme != null) { | 
|  | compilerOptions.fileSystem = MultiRootFileSystem( | 
|  | options.multiRootScheme!, | 
|  | options.multiRoots.isEmpty ? [Uri.base] : options.multiRoots, | 
|  | StandardFileSystem.instance); | 
|  | } | 
|  |  | 
|  | if (options.platformPath != null) { | 
|  | compilerOptions.sdkSummary = options.platformPath; | 
|  | } else { | 
|  | compilerOptions.compileSdk = true; | 
|  | } | 
|  |  | 
|  | CompilerResult? compilerResult; | 
|  | try { | 
|  | compilerResult = await kernelForProgram(options.mainUri, compilerOptions); | 
|  | } catch (e, s) { | 
|  | return CFECrashError(e, s); | 
|  | } | 
|  | if (hadCompileTimeError) return CFECompileTimeErrors(); | 
|  | assert(compilerResult != null); | 
|  |  | 
|  | Component component = compilerResult!.component!; | 
|  | CoreTypes coreTypes = compilerResult.coreTypes!; | 
|  | ClassHierarchy classHierarchy = compilerResult.classHierarchy!; | 
|  | LibraryIndex libraryIndex = LibraryIndex(component, [ | 
|  | "dart:_boxed_bool", | 
|  | "dart:_boxed_double", | 
|  | "dart:_boxed_int", | 
|  | "dart:_compact_hash", | 
|  | "dart:_internal", | 
|  | "dart:_js_helper", | 
|  | "dart:_js_types", | 
|  | "dart:_list", | 
|  | "dart:_string", | 
|  | "dart:_wasm", | 
|  | "dart:async", | 
|  | "dart:collection", | 
|  | "dart:core", | 
|  | "dart:ffi", | 
|  | "dart:typed_data", | 
|  | ]); | 
|  |  | 
|  | if (options.dumpKernelAfterCfe != null) { | 
|  | writeComponentToText(component, path: options.dumpKernelAfterCfe!); | 
|  | } | 
|  |  | 
|  | if (options.deleteToStringPackageUri.isNotEmpty) { | 
|  | to_string_transformer.transformComponent( | 
|  | component, options.deleteToStringPackageUri); | 
|  | } | 
|  |  | 
|  | if (options.translatorOptions.enableMultiModuleStressTestMode) { | 
|  | deferred_loading.transformComponentForTestMode( | 
|  | component, classHierarchy, coreTypes, target); | 
|  | } | 
|  |  | 
|  | ConstantEvaluator constantEvaluator = ConstantEvaluator( | 
|  | options, target, component, coreTypes, classHierarchy, libraryIndex); | 
|  | unreachable_code_elimination.transformComponent(target, component, | 
|  | constantEvaluator, options.translatorOptions.enableAsserts); | 
|  |  | 
|  | js.RuntimeFinalizer jsRuntimeFinalizer = | 
|  | js.createRuntimeFinalizer(component, coreTypes, classHierarchy); | 
|  |  | 
|  | final Map<RecordShape, Class> recordClasses = | 
|  | generateRecordClasses(component, coreTypes); | 
|  | target.recordClasses = recordClasses; | 
|  |  | 
|  | if (options.dumpKernelBeforeTfa != null) { | 
|  | writeComponentToText(component, path: options.dumpKernelBeforeTfa!); | 
|  | } | 
|  |  | 
|  | mixin_deduplication.transformComponent(component); | 
|  |  | 
|  | // Patch `dart:_internal`s `mainTearOff` getter. | 
|  | final internalLib = component.libraries | 
|  | .singleWhere((lib) => lib.importUri.toString() == 'dart:_internal'); | 
|  | final mainTearOff = internalLib.procedures | 
|  | .singleWhere((procedure) => procedure.name.text == 'mainTearOff'); | 
|  | mainTearOff.isExternal = false; | 
|  | mainTearOff.function.body = ReturnStatement( | 
|  | ConstantExpression(StaticTearOffConstant(component.mainMethod!))); | 
|  |  | 
|  | // Keep the flags in-sync with | 
|  | // pkg/vm/test/transformations/type_flow/transformer_test.dart | 
|  | globalTypeFlow.transformComponent(target, coreTypes, component, | 
|  | useRapidTypeAnalysis: false); | 
|  |  | 
|  | if (options.dumpKernelAfterTfa != null) { | 
|  | writeComponentToText(component, | 
|  | path: options.dumpKernelAfterTfa!, showMetadata: true); | 
|  | } | 
|  |  | 
|  | assert(() { | 
|  | verifyComponent( | 
|  | target, VerificationStage.afterGlobalTransformations, component); | 
|  | return true; | 
|  | }()); | 
|  |  | 
|  | final moduleOutputData = deferred_loading.modulesForComponent( | 
|  | component, options, target, coreTypes); | 
|  |  | 
|  | var translator = Translator(component, coreTypes, libraryIndex, recordClasses, | 
|  | moduleOutputData, options.translatorOptions); | 
|  |  | 
|  | String? depFile = options.depFile; | 
|  | if (depFile != null) { | 
|  | writeDepfile( | 
|  | compilerOptions.fileSystem, | 
|  | // TODO(https://dartbug.com/55246): track macro deps when available. | 
|  | component.uriToSource.keys | 
|  | .where((uri) => !macros.isMacroLibraryUri(uri)), | 
|  | options.outputFile, | 
|  | depFile); | 
|  | } | 
|  |  | 
|  | final generateSourceMaps = options.translatorOptions.generateSourceMaps; | 
|  | final modules = translator.translate(sourceMapUrlGenerator); | 
|  | final wasmModules = <String, ({Uint8List moduleBytes, String? sourceMap})>{}; | 
|  | modules.forEach((moduleOutput, module) { | 
|  | final serializer = Serializer(); | 
|  | module.serialize(serializer); | 
|  | final wasmModuleSerialized = serializer.data; | 
|  |  | 
|  | final sourceMap = | 
|  | generateSourceMaps ? serializer.sourceMapSerializer.serialize() : null; | 
|  | wasmModules[moduleOutput.moduleName] = | 
|  | (moduleBytes: wasmModuleSerialized, sourceMap: sourceMap); | 
|  | }); | 
|  |  | 
|  | String jsRuntime = jsRuntimeFinalizer.generate( | 
|  | translator.functions.translatedProcedures, | 
|  | translator.internalizedStringsForJSRuntime, | 
|  | mode); | 
|  |  | 
|  | return CompilationSuccess(wasmModules, jsRuntime); | 
|  | } |