| // Copyright (c) 2012, 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. | 
 |  | 
 | library; | 
 |  | 
 | import 'dart:async' show Future; | 
 | import 'dart:convert' show jsonEncode; | 
 | import 'dart:typed_data'; | 
 |  | 
 | import 'package:compiler/src/universe/use.dart' show StaticUse; | 
 | // ignore: implementation_imports | 
 | import 'package:front_end/src/api_unstable/dart2js.dart' as fe; | 
 | import 'package:kernel/ast.dart' as ir; | 
 |  | 
 | import '../compiler_api.dart' as api; | 
 | import 'common.dart'; | 
 | import 'common/codegen.dart'; | 
 | import 'common/elements.dart' show ElementEnvironment; | 
 | import 'common/metrics.dart' show Metric; | 
 | import 'common/names.dart' show Selectors; | 
 | import 'common/tasks.dart' | 
 |     show CompilerTask, GenericTask, GenericTaskWithMetrics, Measurer; | 
 | import 'common/work.dart' show WorkItem; | 
 | import 'deferred_load/deferred_load.dart' show DeferredLoadTask; | 
 | import 'deferred_load/output_unit.dart' show OutputUnitData; | 
 | import 'deferred_load/program_split_constraints/nodes.dart' as psc | 
 |     show ConstraintData; | 
 | import 'deferred_load/program_split_constraints/parser.dart' as psc show Parser; | 
 | import 'diagnostics/diagnostic_listener.dart'; | 
 | import 'diagnostics/messages.dart' show Message; | 
 | import 'dump_info.dart' | 
 |     show | 
 |         DumpInfoJsAstRegistry, | 
 |         DumpInfoProgramData, | 
 |         DumpInfoStateData, | 
 |         DumpInfoTask; | 
 | import 'elements/entities.dart'; | 
 | import 'enqueue.dart' show Enqueuer; | 
 | import 'environment.dart'; | 
 | import 'inferrer/abstract_value_domain.dart'; | 
 | import 'inferrer/abstract_value_strategy.dart'; | 
 | import 'inferrer/computable.dart' show ComputableAbstractValueStrategy; | 
 | import 'inferrer/powersets/powersets.dart' show PowersetStrategy; | 
 | import 'inferrer/trivial.dart' show TrivialAbstractValueStrategy; | 
 | import 'inferrer/typemasks/masks.dart' show TypeMaskStrategy; | 
 | import 'inferrer/types.dart' | 
 |     show GlobalTypeInferenceResults, GlobalTypeInferenceTask; | 
 | import 'inferrer/wrapped.dart' show WrappedAbstractValueStrategy; | 
 | import 'io/source_information.dart'; | 
 | import 'js_backend/codegen_inputs.dart' show CodegenInputs; | 
 | import 'js_backend/enqueuer.dart'; | 
 | import 'js_backend/inferred_data.dart'; | 
 | import 'js_model/js_strategy.dart'; | 
 | import 'js_model/js_world.dart'; | 
 | import 'js_model/locals.dart'; | 
 | import 'kernel/front_end_adapter.dart' show CompilerFileSystem; | 
 | import 'kernel/kernel_strategy.dart'; | 
 | import 'kernel/kernel_world.dart'; | 
 | import 'null_compiler_output.dart' show NullCompilerOutput; | 
 | import 'options.dart' show CompilerOptions, CompilerStage; | 
 | import 'phase/load_kernel.dart' as load_kernel; | 
 | import 'resolution/enqueuer.dart'; | 
 | import 'serialization/serialization.dart'; | 
 | import 'serialization/task.dart'; | 
 | import 'serialization/strategies.dart'; | 
 | import 'source_file_provider.dart'; | 
 | import 'universe/selector.dart' show Selector; | 
 | import 'universe/codegen_world_builder.dart'; | 
 | import 'universe/resolution_world_builder.dart'; | 
 | import 'universe/world_impact.dart' show WorldImpact, WorldImpactBuilderImpl; | 
 |  | 
 | enum _ResolutionStatus { | 
 |   resolving, | 
 |   doneResolving, | 
 |   compiling, | 
 | } | 
 |  | 
 | /// Implementation of the compiler using a [api.CompilerInput] for supplying | 
 | /// the sources. | 
 | class Compiler { | 
 |   final Measurer measurer; | 
 |   final api.CompilerInput provider; | 
 |   final api.CompilerDiagnostics handler; | 
 |  | 
 |   late final KernelFrontendStrategy frontendStrategy; | 
 |   late final JsBackendStrategy backendStrategy; | 
 |   late final DiagnosticReporter _reporter; | 
 |   late final Map<Entity, WorldImpact> _impactCache; | 
 |   late final GenericTask userHandlerTask; | 
 |   late final GenericTask userProviderTask; | 
 |  | 
 |   /// Options provided from command-line arguments. | 
 |   final CompilerOptions options; | 
 |  | 
 |   // These internal flags are used to stop compilation after a specific phase. | 
 |   // Used only for debugging and testing purposes only. | 
 |   bool stopAfterClosedWorldForTesting = false; | 
 |   bool stopAfterGlobalTypeInferenceForTesting = false; | 
 |  | 
 |   /// Output provider from user of Compiler API. | 
 |   late final api.CompilerOutput _outputProvider; | 
 |  | 
 |   api.CompilerOutput get outputProvider => _outputProvider; | 
 |  | 
 |   late ir.Component componentForTesting; | 
 |   late JClosedWorld? backendClosedWorldForTesting; | 
 |   late ResolutionEnqueuer resolutionEnqueuerForTesting; | 
 |   late CodegenEnqueuer codegenEnqueuerForTesting; | 
 |   late DumpInfoStateData dumpInfoStateForTesting; | 
 |  | 
 |   ir.Component? untrimmedComponentForDumpInfo; | 
 |  | 
 |   DiagnosticReporter get reporter => _reporter; | 
 |   Map<Entity, WorldImpact> get impactCache => _impactCache; | 
 |  | 
 |   late final Environment environment; | 
 |   final DataReadMetrics dataReadMetrics = DataReadMetrics(); | 
 |  | 
 |   late final List<CompilerTask> tasks; | 
 |   late final GenericTask loadKernelTask; | 
 |   fe.InitializedCompilerState? initializedCompilerState; | 
 |   bool forceSerializationForTesting = false; | 
 |   late final GlobalTypeInferenceTask globalInference; | 
 |   late final CodegenWorldBuilder _codegenWorldBuilder; | 
 |  | 
 |   late AbstractValueStrategy abstractValueStrategy; | 
 |  | 
 |   late final GenericTask selfTask; | 
 |  | 
 |   late final GenericTask enqueueTask; | 
 |   late final DeferredLoadTask deferredLoadTask; | 
 |   late final DumpInfoTask dumpInfoTask; | 
 |   final DumpInfoJsAstRegistry dumpInfoRegistry; | 
 |   late final SerializationTask serializationTask; | 
 |  | 
 |   Progress progress = const Progress(); | 
 |  | 
 |   _ResolutionStatus? _resolutionStatus; | 
 |  | 
 |   CompilerStage get stage => options.stage; | 
 |  | 
 |   bool compilationFailed = false; | 
 |  | 
 |   psc.ConstraintData? programSplitConstraintsData; | 
 |  | 
 |   // Callback function used for testing resolution enqueuing. | 
 |   void Function()? onResolutionQueueEmptyForTesting; | 
 |  | 
 |   // Callback function used for testing codegen enqueuing. | 
 |   void Function()? onCodegenQueueEmptyForTesting; | 
 |  | 
 |   Compiler(this.provider, api.CompilerOutput outputProvider, this.handler, | 
 |       this.options) | 
 |       // NOTE: allocating measurer is done upfront to ensure the wallclock is | 
 |       // started before other computations. | 
 |       : measurer = Measurer(enableTaskMeasurements: options.verbose), | 
 |         dumpInfoRegistry = DumpInfoJsAstRegistry(options) { | 
 |     options.deriveOptions(); | 
 |     options.validate(); | 
 |     environment = Environment(options.environment); | 
 |  | 
 |     abstractValueStrategy = options.useTrivialAbstractValueDomain | 
 |         ? const TrivialAbstractValueStrategy() | 
 |         : const TypeMaskStrategy(); | 
 |     if (options.experimentalWrapped || options.testMode) { | 
 |       abstractValueStrategy = | 
 |           WrappedAbstractValueStrategy(abstractValueStrategy); | 
 |     } else if (options.experimentalPowersets) { | 
 |       abstractValueStrategy = PowersetStrategy(abstractValueStrategy); | 
 |     } | 
 |     if (options.debugGlobalInference) { | 
 |       abstractValueStrategy = | 
 |           ComputableAbstractValueStrategy(abstractValueStrategy); | 
 |     } | 
 |  | 
 |     CompilerTask kernelFrontEndTask; | 
 |     selfTask = GenericTaskWithMetrics('self', measurer, dataReadMetrics); | 
 |     _outputProvider = _CompilerOutput(this, outputProvider); | 
 |     _reporter = DiagnosticReporter(this); | 
 |     kernelFrontEndTask = GenericTask('Front end', measurer); | 
 |     frontendStrategy = | 
 |         KernelFrontendStrategy(kernelFrontEndTask, options, reporter); | 
 |     backendStrategy = createBackendStrategy(); | 
 |     _impactCache = <Entity, WorldImpact>{}; | 
 |  | 
 |     if (options.showInternalProgress) { | 
 |       progress = InteractiveProgress(); | 
 |     } | 
 |  | 
 |     tasks = [ | 
 |       // [enqueueTask] is created earlier because it contains the resolution | 
 |       // world objects needed by other tasks. | 
 |       enqueueTask = GenericTask('Enqueue', measurer), | 
 |       loadKernelTask = GenericTask('kernel loader', measurer), | 
 |       kernelFrontEndTask, | 
 |       globalInference = GlobalTypeInferenceTask(this), | 
 |       deferredLoadTask = frontendStrategy.createDeferredLoadTask(this), | 
 |       dumpInfoTask = DumpInfoTask(options, measurer, _outputProvider, reporter), | 
 |       selfTask, | 
 |       serializationTask = SerializationTask( | 
 |           options, reporter, provider, outputProvider, measurer), | 
 |       ...backendStrategy.tasks, | 
 |       userHandlerTask = GenericTask('Diagnostic handler', measurer), | 
 |       userProviderTask = GenericTask('Input provider', measurer) | 
 |     ]; | 
 |  | 
 |     initializedCompilerState = options.kernelInitializedCompilerState; | 
 |   } | 
 |  | 
 |   /// Creates the backend strategy. | 
 |   /// | 
 |   /// Override this to mock the backend strategy for testing. | 
 |   JsBackendStrategy createBackendStrategy() { | 
 |     return JsBackendStrategy(this); | 
 |   } | 
 |  | 
 |   ResolutionWorldBuilder? resolutionWorldBuilderForTesting; | 
 |  | 
 |   KClosedWorld? get frontendClosedWorldForTesting => | 
 |       resolutionWorldBuilderForTesting?.closedWorldForTesting; | 
 |  | 
 |   CodegenWorldBuilder get codegenWorldBuilder => _codegenWorldBuilder; | 
 |  | 
 |   CodegenWorld? codegenWorldForTesting; | 
 |  | 
 |   bool get disableTypeInference => | 
 |       options.disableTypeInference || compilationFailed; | 
 |  | 
 |   // Compiles the dart program as specified in [options]. | 
 |   // | 
 |   // The resulting future will complete with true if the compilation | 
 |   // succeeded. | 
 |   Future<bool> run() => selfTask.measureSubtask("run", () async { | 
 |         measurer.startWallClock(); | 
 |         var setupDuration = measurer.elapsedWallClock; | 
 |         try { | 
 |           await runInternal(); | 
 |         } catch (error, stackTrace) { | 
 |           await _reporter.onError(options.compilationTarget, error, stackTrace); | 
 |         } finally { | 
 |           measurer.stopWallClock(); | 
 |         } | 
 |         dataReadMetrics.addDataRead(provider); | 
 |         if (options.verbose) { | 
 |           var timings = StringBuffer(); | 
 |           computeTimings(setupDuration, timings); | 
 |           logVerbose('$timings'); | 
 |         } | 
 |         if (options.reportPrimaryMetrics || options.reportSecondaryMetrics) { | 
 |           var metrics = StringBuffer(); | 
 |           collectMetrics(metrics); | 
 |           logInfo('$metrics'); | 
 |         } | 
 |         return !compilationFailed; | 
 |       }); | 
 |  | 
 |   /// Dumps a list of unused [ir.Library]'s in the [KernelResult]. This *must* | 
 |   /// be called before [setMainAndTrimComponent], because that method will | 
 |   /// discard the unused [ir.Library]s. | 
 |   void dumpUnusedLibraries(ir.Component component, Set<Uri> libraries) { | 
 |     bool isUnused(ir.Library l) => !libraries.contains(l.importUri); | 
 |     String libraryString(ir.Library library) { | 
 |       return '${library.importUri}(${library.fileUri})'; | 
 |     } | 
 |  | 
 |     var unusedLibraries = | 
 |         component.libraries.where(isUnused).map(libraryString).toList(); | 
 |     unusedLibraries.sort(); | 
 |     var jsonLibraries = jsonEncode(unusedLibraries); | 
 |     outputProvider.createOutputSink(options.outputUri!.pathSegments.last, | 
 |         'unused.json', api.OutputType.dumpUnusedLibraries) | 
 |       ..add(jsonLibraries) | 
 |       ..close(); | 
 |     reporter.reportInfo( | 
 |         reporter.createMessage(noLocationSpannable, MessageKind.generic, { | 
 |       'text': "${unusedLibraries.length} unused libraries out of " | 
 |           "${component.libraries.length}. Dumping to JSON." | 
 |     })); | 
 |   } | 
 |  | 
 |   /// Trims a component down to only the provided library uris. | 
 |   ir.Component trimComponent( | 
 |       ir.Component component, Set<Uri> librariesToInclude) { | 
 |     var irLibraryMap = <Uri, ir.Library>{}; | 
 |     var irLibraries = <ir.Library>[]; | 
 |     for (var library in component.libraries) { | 
 |       irLibraryMap[library.importUri] = library; | 
 |     } | 
 |     for (var library in librariesToInclude) { | 
 |       irLibraries.add(irLibraryMap[library]!); | 
 |     } | 
 |     var mainMethod = component.mainMethodName; | 
 |     var componentMode = component.mode; | 
 |     final trimmedComponent = ir.Component( | 
 |         libraries: irLibraries, | 
 |         uriToSource: component.uriToSource, | 
 |         nameRoot: component.root); | 
 |     trimmedComponent.setMainMethodAndMode(mainMethod, true, componentMode); | 
 |     return trimmedComponent; | 
 |   } | 
 |  | 
 |   Future<void> runInternal() async { | 
 |     clearState(); | 
 |     var compilationTarget = options.compilationTarget; | 
 |     reporter.log('Compiling $compilationTarget (${options.buildId})'); | 
 |  | 
 |     if (options.readProgramSplit != null) { | 
 |       var constraintUri = options.readProgramSplit; | 
 |       var constraintParser = psc.Parser(); | 
 |       var programSplitJson = await CompilerFileSystem(provider) | 
 |           .entityForUri(constraintUri!) | 
 |           .readAsString(); | 
 |       programSplitConstraintsData = constraintParser.read(programSplitJson); | 
 |     } | 
 |  | 
 |     await selfTask.measureSubtask("compileFromKernel", () async { | 
 |       await runSequentialPhases(); | 
 |     }); | 
 |   } | 
 |  | 
 |   /// Clear the internal compiler state to prevent memory leaks when invoking | 
 |   /// the compiler multiple times (e.g. in batch mode). | 
 |   // TODO(ahe): implement a better mechanism where we can store | 
 |   // such caches in the compiler and get access to them through a | 
 |   // suitably maintained static reference to the current compiler. | 
 |   void clearState() { | 
 |     Selector.canonicalizedValues.clear(); | 
 |     StaticUse.clearCache(); | 
 |  | 
 |     // The selector objects held in static fields must remain canonical. | 
 |     for (Selector selector in Selectors.all) { | 
 |       Selector.canonicalizedValues | 
 |           .putIfAbsent(selector.hashCode, () => <Selector>[]) | 
 |           .add(selector); | 
 |     } | 
 |   } | 
 |  | 
 |   JClosedWorld? computeClosedWorld( | 
 |       ir.Component component, Uri rootLibraryUri, List<Uri> libraries) { | 
 |     frontendStrategy.registerLoadedLibraries(component, libraries); | 
 |     ResolutionEnqueuer resolutionEnqueuer = frontendStrategy | 
 |         .createResolutionEnqueuer(enqueueTask, this) | 
 |       ..onEmptyForTesting = onResolutionQueueEmptyForTesting; | 
 |     if (retainDataForTesting) { | 
 |       resolutionEnqueuerForTesting = resolutionEnqueuer; | 
 |       resolutionWorldBuilderForTesting = resolutionEnqueuer.worldBuilder; | 
 |     } | 
 |     frontendStrategy.onResolutionStart(); | 
 |     for (LibraryEntity library | 
 |         in frontendStrategy.elementEnvironment.libraries) { | 
 |       frontendStrategy.elementEnvironment.forEachClass(library, | 
 |           (ClassEntity cls) { | 
 |         // Register all classes eagerly to optimize closed world computation in | 
 |         // `ClassWorldBuilder.isInheritedInSubtypeOf`. | 
 |         resolutionEnqueuer.worldBuilder.registerClass(cls); | 
 |       }); | 
 |     } | 
 |     WorldImpactBuilderImpl mainImpact = WorldImpactBuilderImpl(); | 
 |     final mainFunction = frontendStrategy.computeMain(mainImpact); | 
 |  | 
 |     // In order to see if a library is deferred, we must compute the | 
 |     // compile-time constants that are metadata.  This means adding | 
 |     // something to the resolution queue.  So we cannot wait with | 
 |     // this until after the resolution queue is processed. | 
 |     deferredLoadTask.beforeResolution(rootLibraryUri, libraries); | 
 |  | 
 |     _resolutionStatus = _ResolutionStatus.resolving; | 
 |     resolutionEnqueuer.applyImpact(mainImpact); | 
 |     if (options.showInternalProgress) reporter.log('Computing closed world'); | 
 |  | 
 |     processQueue( | 
 |         frontendStrategy.elementEnvironment, resolutionEnqueuer, mainFunction, | 
 |         onProgress: showResolutionProgress); | 
 |     resolutionEnqueuer.logSummary(reporter.log); | 
 |  | 
 |     _reporter.reportSuppressedMessagesSummary(); | 
 |  | 
 |     if (compilationFailed) { | 
 |       return null; | 
 |     } | 
 |  | 
 |     checkQueue(resolutionEnqueuer); | 
 |  | 
 |     JClosedWorld? closedWorld = | 
 |         closeResolution(mainFunction!, resolutionEnqueuer.worldBuilder); | 
 |     return closedWorld; | 
 |   } | 
 |  | 
 |   Future<load_kernel.Output?> loadKernel() async { | 
 |     final input = load_kernel.Input(options, provider, reporter, | 
 |         initializedCompilerState, forceSerializationForTesting); | 
 |     load_kernel.Output? output = | 
 |         await loadKernelTask.measure(() async => load_kernel.run(input)); | 
 |     reporter.log("Kernel load complete"); | 
 |     return output; | 
 |   } | 
 |  | 
 |   Future<load_kernel.Output?> produceKernel() async { | 
 |     if (!stage.shouldReadClosedWorld) { | 
 |       load_kernel.Output? output = await loadKernel(); | 
 |       if (output == null) return null; | 
 |       if (compilationFailed) { | 
 |         // Some tests still use the component, even if the CFE failed. | 
 |         frontendStrategy.registerComponent(output.component); | 
 |         return null; | 
 |       } | 
 |       ir.Component component = output.component; | 
 |       if (retainDataForTesting) { | 
 |         componentForTesting = component; | 
 |       } | 
 |       if (options.features.newDumpInfo.isEnabled && stage.emitsDumpInfo) { | 
 |         untrimmedComponentForDumpInfo = component; | 
 |       } | 
 |       if (stage.shouldOnlyComputeDill) { | 
 |         Set<Uri> includedLibraries = output.libraries!.toSet(); | 
 |         if (options.shouldLoadFromDill) { | 
 |           if (options.dumpUnusedLibraries) { | 
 |             dumpUnusedLibraries(component, includedLibraries); | 
 |           } | 
 |           if (options.entryUri != null) { | 
 |             component = trimComponent(component, includedLibraries); | 
 |           } | 
 |         } | 
 |         serializationTask.serializeComponent(component, | 
 |             includeSourceBytes: false); | 
 |       } | 
 |       return output.withNewComponent(component); | 
 |     } else { | 
 |       ir.Component component = | 
 |           await serializationTask.deserializeComponentAndUpdateOptions(); | 
 |       if (retainDataForTesting) { | 
 |         componentForTesting = component; | 
 |       } | 
 |       return load_kernel.Output(component, null, null, null); | 
 |     } | 
 |   } | 
 |  | 
 |   bool shouldStopAfterLoadKernel(load_kernel.Output? output) => | 
 |       output == null || compilationFailed || stage.shouldOnlyComputeDill; | 
 |  | 
 |   GlobalTypeInferenceResults performGlobalTypeInference( | 
 |       JClosedWorld closedWorld) { | 
 |     FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction!; | 
 |     reporter.log('Performing global type inference'); | 
 |     GlobalLocalsMap globalLocalsMap = | 
 |         GlobalLocalsMap(closedWorld.closureDataLookup.getEnclosingMember); | 
 |     InferredDataBuilder inferredDataBuilder = | 
 |         InferredDataBuilderImpl(closedWorld.annotationsData); | 
 |     return globalInference.runGlobalTypeInference( | 
 |         mainFunction, closedWorld, globalLocalsMap, inferredDataBuilder); | 
 |   } | 
 |  | 
 |   int runCodegenEnqueuer( | 
 |       CodegenResults codegenResults, | 
 |       InferredData inferredData, | 
 |       SourceLookup sourceLookup, | 
 |       JClosedWorld closedWorld) { | 
 |     CodegenInputs codegenInputs = codegenResults.codegenInputs; | 
 |     CodegenEnqueuer codegenEnqueuer = backendStrategy.createCodegenEnqueuer( | 
 |         enqueueTask, | 
 |         closedWorld, | 
 |         inferredData, | 
 |         codegenInputs, | 
 |         codegenResults, | 
 |         sourceLookup) | 
 |       ..onEmptyForTesting = onCodegenQueueEmptyForTesting; | 
 |     if (retainDataForTesting) { | 
 |       codegenEnqueuerForTesting = codegenEnqueuer; | 
 |     } | 
 |     _codegenWorldBuilder = codegenEnqueuer.worldBuilder; | 
 |  | 
 |     reporter.log('Compiling methods'); | 
 |     FunctionEntity mainFunction = closedWorld.elementEnvironment.mainFunction!; | 
 |     processQueue(closedWorld.elementEnvironment, codegenEnqueuer, mainFunction, | 
 |         onProgress: showCodegenProgress); | 
 |     codegenEnqueuer.logSummary(reporter.log); | 
 |     CodegenWorld codegenWorld = codegenWorldBuilder.close(); | 
 |     if (retainDataForTesting) { | 
 |       codegenWorldForTesting = codegenWorld; | 
 |     } | 
 |     reporter.log('Emitting JavaScript'); | 
 |     int programSize = backendStrategy.assembleProgram( | 
 |         closedWorld, inferredData, codegenInputs, codegenWorld); | 
 |  | 
 |     backendStrategy.onCodegenEnd(codegenInputs); | 
 |  | 
 |     checkQueue(codegenEnqueuer); | 
 |     return programSize; | 
 |   } | 
 |  | 
 |   JClosedWorld closedWorldTestMode(JClosedWorld closedWorld) { | 
 |     SerializationIndices indices = SerializationIndices(testMode: true); | 
 |     final strategy = | 
 |         const BytesInMemorySerializationStrategy(useDataKinds: true); | 
 |     // TODO(natebiggs): Add when kernel offsets are consistent across | 
 |     //   serialization layer. | 
 |     // List<int> irData = strategy | 
 |     //     .serializeComponent(closedWorld.elementMap.programEnv.mainComponent); | 
 |     // final component = strategy.deserializeComponent(irData); | 
 |     List<int> closedWorldData = | 
 |         strategy.serializeClosedWorld(closedWorld, options, indices); | 
 |     final component = closedWorld.elementMap.programEnv.mainComponent; | 
 |     return strategy.deserializeClosedWorld(options, reporter, | 
 |         abstractValueStrategy, component, closedWorldData, indices); | 
 |   } | 
 |  | 
 |   GlobalTypeInferenceResults globalTypeInferenceResultsTestMode( | 
 |       GlobalTypeInferenceResults results) { | 
 |     SerializationIndices indices = SerializationIndices(testMode: true); | 
 |     final strategy = | 
 |         const BytesInMemorySerializationStrategy(useDataKinds: true); | 
 |     final closedWorld = results.closedWorld; | 
 |     final component = closedWorld.elementMap.programEnv.mainComponent; | 
 |     List<int> globalTypeInferenceResultsData = | 
 |         strategy.serializeGlobalTypeInferenceResults(results, options, indices); | 
 |     return strategy.deserializeGlobalTypeInferenceResults( | 
 |         options, | 
 |         reporter, | 
 |         environment, | 
 |         abstractValueStrategy, | 
 |         component, | 
 |         closedWorld, | 
 |         globalTypeInferenceResultsData, | 
 |         indices); | 
 |   } | 
 |  | 
 |   Future<JClosedWorld?> produceClosedWorld( | 
 |       load_kernel.Output output, SerializationIndices indices) async { | 
 |     ir.Component component = output.component; | 
 |     JClosedWorld? closedWorld; | 
 |     if (!stage.shouldReadClosedWorld) { | 
 |       Uri rootLibraryUri = output.rootLibraryUri!; | 
 |       List<Uri> libraries = output.libraries!; | 
 |       closedWorld = computeClosedWorld(component, rootLibraryUri, libraries); | 
 |       if (stage.shouldWriteClosedWorld && closedWorld != null) { | 
 |         serializationTask.serializeClosedWorld(closedWorld, indices); | 
 |         if (options.producesModifiedDill) { | 
 |           serializationTask.serializeComponent(component, | 
 |               includeSourceBytes: false); | 
 |         } | 
 |       } else if (options.testMode && closedWorld != null) { | 
 |         closedWorld = closedWorldTestMode(closedWorld); | 
 |         backendStrategy.registerJClosedWorld(closedWorld); | 
 |       } | 
 |     } else { | 
 |       closedWorld = await serializationTask.deserializeClosedWorld( | 
 |           abstractValueStrategy, component, useDeferredSourceReads, indices); | 
 |     } | 
 |     if (retainDataForTesting) { | 
 |       backendClosedWorldForTesting = closedWorld; | 
 |     } | 
 |     return closedWorld; | 
 |   } | 
 |  | 
 |   bool shouldStopAfterClosedWorld(JClosedWorld? closedWorld) => | 
 |       closedWorld == null || | 
 |       stage.shouldWriteClosedWorld || | 
 |       stage.emitsDeferredLoadIds || | 
 |       stopAfterClosedWorldForTesting; | 
 |  | 
 |   Future<GlobalTypeInferenceResults> produceGlobalTypeInferenceResults( | 
 |       JClosedWorld closedWorld, | 
 |       ir.Component component, | 
 |       SerializationIndices indices) async { | 
 |     GlobalTypeInferenceResults globalTypeInferenceResults; | 
 |     if (!stage.shouldReadGlobalInference) { | 
 |       globalTypeInferenceResults = performGlobalTypeInference(closedWorld); | 
 |       if (stage.shouldWriteGlobalInference) { | 
 |         serializationTask.serializeGlobalTypeInference( | 
 |             globalTypeInferenceResults, indices); | 
 |       } else if (options.testMode) { | 
 |         globalTypeInferenceResults = | 
 |             globalTypeInferenceResultsTestMode(globalTypeInferenceResults); | 
 |       } | 
 |     } else { | 
 |       globalTypeInferenceResults = | 
 |           await serializationTask.deserializeGlobalTypeInferenceResults( | 
 |               environment, | 
 |               abstractValueStrategy, | 
 |               closedWorld.elementMap.programEnv.mainComponent, | 
 |               closedWorld, | 
 |               useDeferredSourceReads, | 
 |               indices); | 
 |     } | 
 |     return globalTypeInferenceResults; | 
 |   } | 
 |  | 
 |   bool get shouldStopAfterGlobalTypeInference => | 
 |       stage.shouldWriteGlobalInference || | 
 |       stopAfterGlobalTypeInferenceForTesting; | 
 |  | 
 |   CodegenInputs initializeCodegen( | 
 |       GlobalTypeInferenceResults globalTypeInferenceResults) { | 
 |     backendStrategy | 
 |         .registerJClosedWorld(globalTypeInferenceResults.closedWorld); | 
 |     _resolutionStatus = _ResolutionStatus.compiling; | 
 |     return backendStrategy.onCodegenStart(globalTypeInferenceResults); | 
 |   } | 
 |  | 
 |   Future<CodegenResults> produceCodegenResults( | 
 |       GlobalTypeInferenceResults globalTypeInferenceResults, | 
 |       SourceLookup sourceLookup, | 
 |       SerializationIndices indices) async { | 
 |     CodegenInputs codegenInputs = initializeCodegen(globalTypeInferenceResults); | 
 |     CodegenResults codegenResults; | 
 |     if (stage.shouldReadCodegenShards && options.codegenShards != null) { | 
 |       codegenResults = await serializationTask.deserializeCodegen( | 
 |           backendStrategy, | 
 |           globalTypeInferenceResults.closedWorld, | 
 |           codegenInputs, | 
 |           useDeferredSourceReads, | 
 |           sourceLookup, | 
 |           indices); | 
 |     } else { | 
 |       codegenResults = OnDemandCodegenResults( | 
 |           codegenInputs, backendStrategy.functionCompiler); | 
 |       if (stage.shouldWriteCodegen) { | 
 |         serializationTask.serializeCodegen( | 
 |             backendStrategy, | 
 |             globalTypeInferenceResults.closedWorld.abstractValueDomain, | 
 |             codegenResults, | 
 |             indices); | 
 |       } | 
 |     } | 
 |     return codegenResults; | 
 |   } | 
 |  | 
 |   bool get shouldStopAfterCodegen => stage.shouldWriteCodegen; | 
 |  | 
 |   bool get useDeferredSourceReads => stage.shouldUseDeferredSourceReads; | 
 |  | 
 |   Future<void> runSequentialPhases() async { | 
 |     // Load kernel. | 
 |     final output = await produceKernel(); | 
 |     if (shouldStopAfterLoadKernel(output)) return; | 
 |  | 
 |     final indices = SerializationIndices(); | 
 |  | 
 |     // Compute closed world. | 
 |     JClosedWorld? closedWorld = await produceClosedWorld(output!, indices); | 
 |     if (shouldStopAfterClosedWorld(closedWorld)) return; | 
 |  | 
 |     // Run global analysis. | 
 |     GlobalTypeInferenceResults globalTypeInferenceResults = | 
 |         await produceGlobalTypeInferenceResults( | 
 |             closedWorld!, output.component, indices); | 
 |     if (shouldStopAfterGlobalTypeInference) return; | 
 |     closedWorld = globalTypeInferenceResults.closedWorld; | 
 |  | 
 |     // Allow the original references to these to be GCed and only hold | 
 |     // references to them if we are actually running the dump info task later. | 
 |     SerializationIndices? indicesForDumpInfo; | 
 |     GlobalTypeInferenceResults? globalTypeInferenceResultsForDumpInfo; | 
 |     AbstractValueDomain? abstractValueDomainForDumpInfo; | 
 |     OutputUnitData? outputUnitDataForDumpInfo; | 
 |     DataSinkWriter? sinkForDumpInfo; | 
 |     if (stage.emitsDumpInfo) { | 
 |       globalTypeInferenceResultsForDumpInfo = globalTypeInferenceResults; | 
 |       abstractValueDomainForDumpInfo = closedWorld.abstractValueDomain; | 
 |       outputUnitDataForDumpInfo = closedWorld.outputUnitData; | 
 |       indicesForDumpInfo = indices; | 
 |     } else if (stage.shouldWriteDumpInfoData) { | 
 |       sinkForDumpInfo = serializationTask.dataSinkWriterForDumpInfo( | 
 |           closedWorld.abstractValueDomain, indices); | 
 |       dumpInfoRegistry.registerDataSinkWriter(sinkForDumpInfo); | 
 |     } | 
 |  | 
 |     // Run codegen. | 
 |     final sourceLookup = SourceLookup(output.component); | 
 |     CodegenResults codegenResults = await produceCodegenResults( | 
 |         globalTypeInferenceResults, sourceLookup, indices); | 
 |     if (shouldStopAfterCodegen) return; | 
 |     final inferredData = globalTypeInferenceResults.inferredData; | 
 |  | 
 |     if (stage.shouldReadDumpInfoData) { | 
 |       final dumpInfoData = | 
 |           await serializationTask.deserializeDumpInfoProgramData( | 
 |               backendStrategy, | 
 |               abstractValueDomainForDumpInfo!, | 
 |               outputUnitDataForDumpInfo!, | 
 |               indicesForDumpInfo!); | 
 |       await runDumpInfo( | 
 |           codegenResults, globalTypeInferenceResultsForDumpInfo!, dumpInfoData); | 
 |     } else { | 
 |       // Link. | 
 |       final programSize = runCodegenEnqueuer( | 
 |           codegenResults, inferredData, sourceLookup, closedWorld); | 
 |       if (stage.emitsDumpInfo || stage.shouldWriteDumpInfoData) { | 
 |         final dumpInfoData = DumpInfoProgramData.fromEmitterResults( | 
 |             backendStrategy.emitterTask, | 
 |             dumpInfoRegistry, | 
 |             codegenResults, | 
 |             programSize); | 
 |         dumpInfoRegistry.close(); | 
 |         if (stage.shouldWriteDumpInfoData) { | 
 |           serializationTask.serializeDumpInfoProgramData(sinkForDumpInfo!, | 
 |               backendStrategy, dumpInfoData, dumpInfoRegistry); | 
 |         } else { | 
 |           await runDumpInfo(codegenResults, | 
 |               globalTypeInferenceResultsForDumpInfo!, dumpInfoData); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   Future<void> runDumpInfo( | 
 |       CodegenResults codegenResults, | 
 |       GlobalTypeInferenceResults globalTypeInferenceResults, | 
 |       DumpInfoProgramData dumpInfoProgramData) async { | 
 |     JClosedWorld closedWorld = globalTypeInferenceResults.closedWorld; | 
 |  | 
 |     DumpInfoStateData dumpInfoState; | 
 |     dumpInfoTask.registerDumpInfoProgramData(dumpInfoProgramData); | 
 |     if (options.features.newDumpInfo.isEnabled) { | 
 |       untrimmedComponentForDumpInfo ??= (await produceKernel())!.component; | 
 |       dumpInfoState = await dumpInfoTask.dumpInfoNew( | 
 |           untrimmedComponentForDumpInfo!, | 
 |           closedWorld, | 
 |           globalTypeInferenceResults, | 
 |           codegenResults, | 
 |           backendStrategy); | 
 |     } else { | 
 |       dumpInfoState = await dumpInfoTask.dumpInfo(closedWorld, | 
 |           globalTypeInferenceResults, codegenResults, backendStrategy); | 
 |     } | 
 |     if (retainDataForTesting) { | 
 |       dumpInfoStateForTesting = dumpInfoState; | 
 |     } | 
 |   } | 
 |  | 
 |   /// Perform the steps needed to fully end the resolution phase. | 
 |   JClosedWorld? closeResolution(FunctionEntity mainFunction, | 
 |       ResolutionWorldBuilder resolutionWorldBuilder) { | 
 |     _resolutionStatus = _ResolutionStatus.doneResolving; | 
 |  | 
 |     KClosedWorld kClosedWorld = resolutionWorldBuilder.closeWorld(reporter); | 
 |     OutputUnitData result = deferredLoadTask.run(mainFunction, kClosedWorld); | 
 |     if (stage.emitsDeferredLoadIds) return null; | 
 |  | 
 |     // Impact data is no longer needed. | 
 |     if (!retainDataForTesting) { | 
 |       _impactCache.clear(); | 
 |     } | 
 |     JClosedWorld jClosedWorld = | 
 |         backendStrategy.createJClosedWorld(kClosedWorld, result); | 
 |     return jClosedWorld; | 
 |   } | 
 |  | 
 |   /// Empty the [enqueuer] queue. | 
 |   void emptyQueue(Enqueuer enqueuer, | 
 |       {void Function(Enqueuer enqueuer)? onProgress}) { | 
 |     selfTask.measureSubtask("emptyQueue", () { | 
 |       enqueuer.forEach((WorkItem work) { | 
 |         if (onProgress != null) { | 
 |           onProgress(enqueuer); | 
 |         } | 
 |         reporter.withCurrentElement( | 
 |             work.element, | 
 |             () => selfTask.measureSubtask("applyImpact", () { | 
 |                   enqueuer.applyImpact( | 
 |                       selfTask.measureSubtask("work.run", () => work.run())); | 
 |                 })); | 
 |       }); | 
 |     }); | 
 |   } | 
 |  | 
 |   void processQueue(ElementEnvironment elementEnvironment, Enqueuer enqueuer, | 
 |       FunctionEntity? mainMethod, | 
 |       {void Function(Enqueuer enqueuer)? onProgress}) { | 
 |     selfTask.measureSubtask("processQueue", () { | 
 |       enqueuer.open( | 
 |           mainMethod, | 
 |           elementEnvironment.libraries | 
 |               .map((LibraryEntity library) => library.canonicalUri)); | 
 |       progress.startPhase(); | 
 |       emptyQueue(enqueuer, onProgress: onProgress); | 
 |       enqueuer.queueIsClosed = true; | 
 |       enqueuer.close(); | 
 |       assert(compilationFailed || | 
 |           enqueuer.checkNoEnqueuedInvokedInstanceMethods(elementEnvironment)); | 
 |     }); | 
 |   } | 
 |  | 
 |   /// Perform various checks of the queue. This includes checking that the | 
 |   /// queues are empty (nothing was added after we stopped processing the | 
 |   /// queues). | 
 |   void checkQueue(Enqueuer enqueuer) { | 
 |     enqueuer.checkQueueIsEmpty(); | 
 |   } | 
 |  | 
 |   void showResolutionProgress(Enqueuer enqueuer) { | 
 |     assert(_resolutionStatus == _ResolutionStatus.resolving, | 
 |         'Unexpected phase: $_resolutionStatus'); | 
 |     progress.showProgress( | 
 |         'Resolved ', enqueuer.processedEntities.length, ' elements.'); | 
 |   } | 
 |  | 
 |   void showCodegenProgress(Enqueuer enqueuer) { | 
 |     progress.showProgress( | 
 |         'Compiled ', enqueuer.processedEntities.length, ' methods.'); | 
 |   } | 
 |  | 
 |   void reportDiagnostic(DiagnosticMessage message, | 
 |       List<DiagnosticMessage> infos, api.Diagnostic kind) { | 
 |     _reportDiagnosticMessage(message, kind); | 
 |     for (DiagnosticMessage info in infos) { | 
 |       _reportDiagnosticMessage(info, api.Diagnostic.context); | 
 |     } | 
 |   } | 
 |  | 
 |   void _reportDiagnosticMessage( | 
 |       DiagnosticMessage diagnosticMessage, api.Diagnostic kind) { | 
 |     var span = diagnosticMessage.sourceSpan; | 
 |     var message = diagnosticMessage.message; | 
 |     // If the message came from the CFE use the message code as the text | 
 |     // so that tests can determine the cause of the message. | 
 |     final messageText = | 
 |         diagnosticMessage is DiagnosticCfeMessage && options.testMode | 
 |             ? diagnosticMessage.messageCode | 
 |             : '$message'; | 
 |     if (span.isUnknown) { | 
 |       callUserHandler(message, null, null, null, messageText, kind); | 
 |     } else { | 
 |       callUserHandler( | 
 |           message, span.uri, span.begin, span.end, messageText, kind); | 
 |     } | 
 |   } | 
 |  | 
 |   void callUserHandler(Message? message, Uri? uri, int? begin, int? end, | 
 |       String text, api.Diagnostic kind) { | 
 |     try { | 
 |       userHandlerTask.measure(() { | 
 |         handler.report(message, uri, begin, end, text, kind); | 
 |       }); | 
 |     } catch (ex, s) { | 
 |       reportCrashInUserCode('Uncaught exception in diagnostic handler', ex, s); | 
 |       rethrow; | 
 |     } | 
 |   } | 
 |  | 
 |   Future<api.Input<Uint8List>> callUserProvider( | 
 |       Uri uri, api.InputKind inputKind) { | 
 |     try { | 
 |       return userProviderTask | 
 |           .measureIo(() => provider.readFromUri(uri, inputKind: inputKind)); | 
 |     } catch (ex, s) { | 
 |       reportCrashInUserCode('Uncaught exception in input provider', ex, s); | 
 |       rethrow; | 
 |     } | 
 |   } | 
 |  | 
 |   void reportCrashInUserCode( | 
 |       String message, Object exception, StackTrace stackTrace) { | 
 |     reporter.onCrashInUserCode(message, exception, stackTrace); | 
 |   } | 
 |  | 
 |   /// Messages for which compile-time errors are reported but compilation | 
 |   /// continues regardless. | 
 |   static const List<MessageKind> benignErrors = <MessageKind>[ | 
 |     MessageKind.invalidMetadata, | 
 |     MessageKind.invalidMetadataGeneric, | 
 |   ]; | 
 |  | 
 |   bool markCompilationAsFailed(DiagnosticMessage message, api.Diagnostic kind) { | 
 |     if (options.testMode) { | 
 |       // When in test mode, i.e. on the build-bot, we always stop compilation. | 
 |       return true; | 
 |     } | 
 |     if (reporter.options.fatalWarnings) { | 
 |       return true; | 
 |     } | 
 |     return !benignErrors.contains(message.message.kind); | 
 |   } | 
 |  | 
 |   void fatalDiagnosticReported(DiagnosticMessage message, | 
 |       List<DiagnosticMessage> infos, api.Diagnostic kind) { | 
 |     if (markCompilationAsFailed(message, kind)) { | 
 |       compilationFailed = true; | 
 |     } | 
 |   } | 
 |  | 
 |   /// Compute a [SourceSpan] from spannable using the [currentElement] as | 
 |   /// context. | 
 |   SourceSpan spanFromSpannable(Spannable spannable, Entity? currentElement) { | 
 |     SourceSpan span; | 
 |     if (_resolutionStatus == _ResolutionStatus.compiling) { | 
 |       span = backendStrategy.spanFromSpannable(spannable, currentElement); | 
 |     } else { | 
 |       span = frontendStrategy.spanFromSpannable(spannable, currentElement); | 
 |     } | 
 |     return span; | 
 |   } | 
 |  | 
 |   /// Helper for determining whether [element] is declared within 'user code'. | 
 |   bool inUserCode(Entity? element) { | 
 |     return element == null || _uriFromElement(element) != null; | 
 |   } | 
 |  | 
 |   /// Return a canonical URI for the source of [element]. | 
 |   /// | 
 |   /// For a package library with canonical URI 'package:foo/bar/baz.dart' the | 
 |   /// return URI is 'package:foo'. For non-package libraries the returned URI is | 
 |   /// the canonical URI of the library itself. | 
 |   Uri? getCanonicalUri(Entity element) { | 
 |     final libraryUri = _uriFromElement(element); | 
 |     if (libraryUri == null) return null; | 
 |     if (libraryUri.isScheme('package')) { | 
 |       int slashPos = libraryUri.path.indexOf('/'); | 
 |       if (slashPos != -1) { | 
 |         String packageName = libraryUri.path.substring(0, slashPos); | 
 |         return Uri(scheme: 'package', path: packageName); | 
 |       } | 
 |     } | 
 |     return libraryUri; | 
 |   } | 
 |  | 
 |   Uri? _uriFromElement(Entity element) { | 
 |     if (element is LibraryEntity) { | 
 |       return element.canonicalUri; | 
 |     } else if (element is ClassEntity) { | 
 |       return element.library.canonicalUri; | 
 |     } else if (element is MemberEntity) { | 
 |       return element.library.canonicalUri; | 
 |     } | 
 |     return null; | 
 |   } | 
 |  | 
 |   void logInfo(String message) { | 
 |     callUserHandler(null, null, null, null, message, api.Diagnostic.info); | 
 |   } | 
 |  | 
 |   void logVerbose(String message) { | 
 |     callUserHandler( | 
 |         null, null, null, null, message, api.Diagnostic.verboseInfo); | 
 |   } | 
 |  | 
 |   String _formatMs(int ms) { | 
 |     return '${(ms / 1000).toStringAsFixed(3)}s'; | 
 |   } | 
 |  | 
 |   void computeTimings(Duration setupDuration, StringBuffer timings) { | 
 |     timings.writeln("Timings:"); | 
 |     var totalDuration = measurer.elapsedWallClock; | 
 |     var asyncDuration = measurer.elapsedAsyncWallClock; | 
 |     var cumulatedDuration = Duration.zero; | 
 |     var timingData = <_TimingData>[]; | 
 |     for (final task in tasks) { | 
 |       var running = task.isRunning ? "*" : " "; | 
 |       var duration = task.duration; | 
 |       if (duration != Duration.zero) { | 
 |         cumulatedDuration += duration; | 
 |         var milliseconds = duration.inMilliseconds; | 
 |         timingData.add(_TimingData('   $running${task.name}:', milliseconds, | 
 |             milliseconds * 100 / totalDuration.inMilliseconds)); | 
 |         for (String subtask in task.subtasks) { | 
 |           var subtime = task.getSubtaskTime(subtask); | 
 |           var running = task.getSubtaskIsRunning(subtask) ? "*" : " "; | 
 |           timingData.add(_TimingData('   $running${task.name} > $subtask:', | 
 |               subtime, subtime * 100 / totalDuration.inMilliseconds)); | 
 |         } | 
 |       } | 
 |     } | 
 |     int longestDescription = timingData | 
 |         .map((d) => d.description.length) | 
 |         .fold(0, (a, b) => a < b ? b : a); | 
 |     for (var data in timingData) { | 
 |       var ms = _formatMs(data.milliseconds); | 
 |       var padding = | 
 |           " " * (longestDescription + 10 - data.description.length - ms.length); | 
 |       var percentPadding = data.percent < 10 ? " " : ""; | 
 |       timings.writeln('${data.description}$padding $ms ' | 
 |           '$percentPadding(${data.percent.toStringAsFixed(1)}%)'); | 
 |     } | 
 |     var unaccountedDuration = | 
 |         totalDuration - cumulatedDuration - setupDuration - asyncDuration; | 
 |     var percent = | 
 |         unaccountedDuration.inMilliseconds * 100 / totalDuration.inMilliseconds; | 
 |     timings.write( | 
 |         '    Total compile-time ${_formatMs(totalDuration.inMilliseconds)};' | 
 |         ' setup ${_formatMs(setupDuration.inMilliseconds)};' | 
 |         ' async ${_formatMs(asyncDuration.inMilliseconds)};' | 
 |         ' unaccounted ${_formatMs(unaccountedDuration.inMilliseconds)}' | 
 |         ' (${percent.toStringAsFixed(2)}%)'); | 
 |   } | 
 |  | 
 |   void collectMetrics(StringBuffer buffer) { | 
 |     buffer.writeln('Metrics:'); | 
 |     for (final task in tasks) { | 
 |       var metrics = task.metrics; | 
 |       var namespace = metrics.namespace; | 
 |       if (namespace == '') { | 
 |         namespace = | 
 |             task.name.toLowerCase().replaceAll(RegExp(r'[^a-z0-9]+'), '_'); | 
 |       } | 
 |       void report(Metric metric) { | 
 |         buffer.writeln('  $namespace.${metric.name}: ${metric.formatValue()}'); | 
 |       } | 
 |  | 
 |       for (final metric in metrics.primary) { | 
 |         report(metric); | 
 |       } | 
 |       if (options.reportSecondaryMetrics) { | 
 |         for (final metric in metrics.secondary) { | 
 |           report(metric); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | class _CompilerOutput implements api.CompilerOutput { | 
 |   final Compiler _compiler; | 
 |   final api.CompilerOutput _userOutput; | 
 |  | 
 |   _CompilerOutput(this._compiler, api.CompilerOutput? output) | 
 |       : _userOutput = output ?? const NullCompilerOutput(); | 
 |  | 
 |   @override | 
 |   api.OutputSink createOutputSink( | 
 |       String name, String extension, api.OutputType type) { | 
 |     if (_compiler.compilationFailed) { | 
 |       // Ensure that we don't emit output when the compilation has failed. | 
 |       return const NullCompilerOutput().createOutputSink(name, extension, type); | 
 |     } | 
 |     return _userOutput.createOutputSink(name, extension, type); | 
 |   } | 
 |  | 
 |   @override | 
 |   api.BinaryOutputSink createBinarySink(Uri uri) { | 
 |     return _userOutput.createBinarySink(uri); | 
 |   } | 
 | } | 
 |  | 
 | class _TimingData { | 
 |   final String description; | 
 |   final int milliseconds; | 
 |   final double percent; | 
 |  | 
 |   _TimingData(this.description, this.milliseconds, this.percent); | 
 | } | 
 |  | 
 | /// Interface for showing progress during compilation. | 
 | class Progress { | 
 |   const Progress(); | 
 |  | 
 |   /// Starts a new phase for which to show progress. | 
 |   void startPhase() {} | 
 |  | 
 |   /// Shows progress of the current phase if needed. The shown message is | 
 |   /// computed as '$prefix$count$suffix'. | 
 |   void showProgress(String prefix, int count, String suffix) {} | 
 | } | 
 |  | 
 | /// Progress implementations that prints progress to the [DiagnosticReporter] | 
 | /// with 500ms intervals. | 
 | class ProgressImpl implements Progress { | 
 |   final DiagnosticReporter _reporter; | 
 |   final Stopwatch _stopwatch = Stopwatch()..start(); | 
 |  | 
 |   ProgressImpl(this._reporter); | 
 |  | 
 |   @override | 
 |   void showProgress(String prefix, int count, String suffix) { | 
 |     if (_stopwatch.elapsedMilliseconds > 500) { | 
 |       _reporter.log('$prefix$count$suffix'); | 
 |       _stopwatch.reset(); | 
 |     } | 
 |   } | 
 |  | 
 |   @override | 
 |   void startPhase() { | 
 |     _stopwatch.reset(); | 
 |   } | 
 | } | 
 |  | 
 | /// Progress implementations that prints progress to the [DiagnosticReporter] | 
 | /// with 500ms intervals using escape sequences to keep the progress data on a | 
 | /// single line. | 
 | class InteractiveProgress implements Progress { | 
 |   final Stopwatch _stopwatchPhase = Stopwatch()..start(); | 
 |   final Stopwatch _stopwatchInterval = Stopwatch()..start(); | 
 |  | 
 |   @override | 
 |   void startPhase() { | 
 |     print(''); | 
 |     _stopwatchPhase.reset(); | 
 |     _stopwatchInterval.reset(); | 
 |   } | 
 |  | 
 |   @override | 
 |   void showProgress(String prefix, int count, String suffix) { | 
 |     if (_stopwatchInterval.elapsedMilliseconds > 500) { | 
 |       var time = _stopwatchPhase.elapsedMilliseconds / 1000; | 
 |       var rate = count / _stopwatchPhase.elapsedMilliseconds; | 
 |       var s = StringBuffer('\x1b[1A\x1b[K') // go up and clear the line. | 
 |         ..write('\x1b[48;5;40m\x1b[30m==>\x1b[0m $prefix') | 
 |         ..write(count) | 
 |         ..write('$suffix Elapsed time: ') | 
 |         ..write(time.toStringAsFixed(2)) | 
 |         ..write(' s. Rate: ') | 
 |         ..write(rate.toStringAsFixed(2)) | 
 |         ..write(' units/ms'); | 
 |       print('$s'); | 
 |       _stopwatchInterval.reset(); | 
 |     } | 
 |   } | 
 | } |