diff --git a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart index 5d55107c2b2..f7733515939 100644 --- a/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart +++ b/pkg/front_end/lib/src/api_prototype/incremental_kernel_generator.dart @@ -165,6 +165,7 @@ class IncrementalCompilerResult { final Component component; final ClassHierarchy classHierarchy; final CoreTypes coreTypes; + final List loadedComponents; final Set? neededDillLibraries; IncrementalCompilerResult( @@ -172,5 +173,6 @@ class IncrementalCompilerResult { required this.classHierarchy, required this.coreTypes, this.neededDillLibraries, + this.loadedComponents = const [], }); } diff --git a/pkg/front_end/lib/src/base/incremental_compiler.dart b/pkg/front_end/lib/src/base/incremental_compiler.dart index 9563f15e24c..8f8f68181a3 100644 --- a/pkg/front_end/lib/src/base/incremental_compiler.dart +++ b/pkg/front_end/lib/src/base/incremental_compiler.dart @@ -11,6 +11,7 @@ import 'package:_fe_analyzer_shared/src/parser/experimental_features.dart' import 'package:_fe_analyzer_shared/src/scanner/abstract_scanner.dart' show ScannerConfiguration; import 'package:front_end/src/base/name_space.dart'; +import 'package:front_end/src/base/processed_options.dart'; import 'package:front_end/src/codes/diagnostic.dart' as diag; import 'package:front_end/src/type_inference/inference_results.dart'; import 'package:front_end/src/type_inference/object_access_target.dart'; @@ -617,6 +618,7 @@ class IncrementalCompiler implements IncrementalKernelGenerator { classHierarchy: currentKernelTarget.loader.hierarchy, coreTypes: currentKernelTarget.loader.coreTypes, neededDillLibraries: neededDillLibraries, + loadedComponents: data.loadedModules ?? const [], ); }); } @@ -1821,6 +1823,13 @@ class IncrementalCompiler implements IncrementalKernelGenerator { byteCount: bytesLength, ); } + List? loadedModules = data.loadedModules; + if (loadedModules != null) { + // Coverage-ignore-block(suite): Not run. + for (Component module in loadedModules) { + _dillLoadedData!.loader.appendLibraries(module); + } + } _ticker.logMs("Appended libraries"); } @@ -2730,6 +2739,7 @@ class InitializeFromComponentError { class IncrementalCompilerData { Component? component = null; List? initializationBytes = null; + List? loadedModules = null; } class ReusageResult { @@ -2858,14 +2868,24 @@ class _InitializationFromSdkSummary extends _InitializationStrategy { IncrementalSerializer? incrementalSerializer, RecorderForTesting? recorderForTesting, ) async { - Uint8List? summaryBytes = await context.options.loadSdkSummaryBytes(); - return _prepareSummary( + ProcessedOptions options = context.options; + Uint8List? summaryBytes = await options.loadSdkSummaryBytes(); + int bytesLength = _prepareSummary( dillLoadedData, summaryBytes, uriTranslator, context, data, ); + + if (options.hasAdditionalDillModules) { + List loadedModules = await options.loadAdditionalDillModules( + data.component?.root, + ); + data.loadedModules = loadedModules; + } + + return bytesLength; } // Coverage-ignore(suite): Not run. diff --git a/pkg/front_end/test/incremental_suite.dart b/pkg/front_end/test/incremental_suite.dart index 482adc44ecb..db4e9fcc47d 100644 --- a/pkg/front_end/test/incremental_suite.dart +++ b/pkg/front_end/test/incremental_suite.dart @@ -1081,6 +1081,16 @@ class NewWorldTest { ); if (subtestResult != null) return subtestResult; + // Load modules via additionalDillModules. + subtestResult = await _loadModulesViaAdditionalDillModules( + world, + worldTestData, + newWorldTestData, + prevFormattedErrors, + prevFormattedWarnings, + ); + if (subtestResult != null) return subtestResult; + // Dummy tree nodes can (currently) leak though the parent pointer. // To avoid that (here) (for leak testing) we'll null them out. for (TreeNode treeNode in dummyTreeNodes) { @@ -2394,6 +2404,98 @@ class NewWorldTest { return newWorldTestData; } + Future?> _loadModulesViaAdditionalDillModules( + World world, + WorldSpecificTestData worldTestData, + NewWorldTestData newWorldTestData, + Set prevFormattedErrors, + Set prevFormattedWarnings, + ) async { + // Load modules via additionalDillModules. + if (!world.noFullComponent && world.modules != null) { + // Do compile from scratch and compare. + worldTestData.clearPrevErrorsEtc(); + CompilerOptions options = _createOptionsForWorld( + newWorldTestData, + worldTestData, + world, + // The DDC target saves stuff in the target that causes leaks + // in this config where we've loaded a new sdk etc, so we create + // a new target to avoid it. + createNewTarget: true, + ); + + List additionalDillModules = []; + int moduleNum = 0; + for (String moduleName in world.modules!) { + moduleNum++; + final Uri moduleUri = newWorldTestData.base.resolve( + "module_$moduleNum.dill", + ); + newWorldTestData.fs + .entityForUri(moduleUri) + .writeAsBytesSync(newWorldTestData.moduleData![moduleName]!); + additionalDillModules.add(moduleUri); + } + + options.additionalDillModules = additionalDillModules; + + TestIncrementalCompiler? compilerFromScratch = + new TestIncrementalCompiler( + options, + worldTestData.entries.first, + null, + world.outlineOnly, + ); + Stopwatch stopwatch = new Stopwatch()..start(); + IncrementalCompilerResult? compilerResultLocal = await compilerFromScratch + .computeDelta( + entryPoints: worldTestData.entries, + simulateTransformer: world.simulateTransformer, + ); + Component? componentLocal = compilerResultLocal.component; + compilerResultLocal = null; + compilerFromScratch = null; + Result? result = _performErrorAndWarningCheck( + world, + data, + worldTestData, + ); + if (result != null) return result; + util.throwOnEmptyMixinBodies(componentLocal); + await util.throwOnInsufficientUriToSource(componentLocal); + print("Compile took ${stopwatch.elapsedMilliseconds} ms"); + + List thisWholeComponent = util.postProcess(componentLocal); + String component4String = _componentToStringSdkFiltered( + componentLocal, + printErrors: world.printErrorsInExpect + ? worldTestData.formattedErrors + : null, + ); + print("*****\n\ncomponent (4):\n$component4String\n\n\n"); + if (world.compareWithFromScratch) { + checkIsEqual( + newWorldTestData.newestWholeComponentData!, + thisWholeComponent, + ); + } + _checkErrorsAndWarnings( + prevFormattedErrors, + worldTestData.formattedErrors, + prevFormattedWarnings, + worldTestData.formattedWarnings, + ); + + componentLocal = null; + + // For whatever reason the above introduces a "temporary leak" that + // awaiting something here removes. + await null; + } + return null; + } + void _loadWorldModules( World world, NewWorldTestData newWorldTestData,