@@ -195,6 +195,77 @@ class LibraryContext {
195195 }
196196 }
197197
198+ /// Returns a previously linked bundle entry for [cycle] if present and
199+ /// reusable; otherwise returns `null` . Always returns the last known
200+ /// [LibraryManifest] s from the stored entry (if any) so they can be reused
201+ /// to preserve item versions during relinking.
202+ _LinkedBundleEntryResult _getLinkedBundleEntry ({
203+ required LibraryCycle cycle,
204+ required OperationPerformanceImpl performance,
205+ }) {
206+ return performance.run ('getLinkedBundleEntry' , (performance) {
207+ var entry = performance.run ('bundleProvider.get' , (performance) {
208+ return linkedBundleProvider.get (
209+ key: cycle.linkedKey,
210+ performance: performance,
211+ );
212+ });
213+
214+ // Nothing cached at all.
215+ if (entry == null ) {
216+ return _LinkedBundleEntryResult (entry: null , libraryManifests: {});
217+ }
218+
219+ // If we don't track fine dependencies, any hit is good enough.
220+ // The key already depends on the transitive API signature.
221+ if (! withFineDependencies) {
222+ return _LinkedBundleEntryResult (entry: entry, libraryManifests: {});
223+ }
224+
225+ // If anything changed in the API signature, relink the cycle.
226+ // But keep previous manifests to reuse item IDs.
227+ if (entry.nonTransitiveApiSignature != cycle.nonTransitiveApiSignature) {
228+ return _LinkedBundleEntryResult (
229+ entry: null ,
230+ libraryManifests: entry.libraryManifests,
231+ );
232+ }
233+
234+ // Already validated for this transitive state? Skip.
235+ if (entry.isValidated (cycle.apiSignature)) {
236+ return _LinkedBundleEntryResult (
237+ entry: entry,
238+ libraryManifests: entry.libraryManifests,
239+ );
240+ }
241+
242+ var failure = performance.run ('checkRequirements' , (performance) {
243+ return entry.requirements.isSatisfied (
244+ elementFactory: elementFactory,
245+ libraryManifests: elementFactory.libraryManifests,
246+ );
247+ });
248+
249+ eventsController? .add (
250+ CheckLinkedBundleRequirements (cycle: cycle, failure: failure),
251+ );
252+
253+ if (failure != null ) {
254+ return _LinkedBundleEntryResult (
255+ entry: null ,
256+ libraryManifests: entry.libraryManifests,
257+ );
258+ }
259+
260+ entry.addValidated (cycle.apiSignature);
261+
262+ return _LinkedBundleEntryResult (
263+ entry: entry,
264+ libraryManifests: entry.libraryManifests,
265+ );
266+ });
267+ }
268+
198269 /// Recursively load the linked bundle for [cycle] , link if not available.
199270 ///
200271 /// Uses the same [performance] during recursion, so has single aggregate
@@ -220,46 +291,11 @@ class LibraryContext {
220291 }
221292 }
222293
223- var bundleEntry = performance.run ('bundleProvider.get' , (performance) {
224- return linkedBundleProvider.get (
225- key: cycle.linkedKey,
226- performance: performance,
227- );
228- });
229-
230- var inputLibraryManifests = < Uri , LibraryManifest > {};
231- if (withFineDependencies && bundleEntry != null ) {
232- var isSatisfied = performance.run ('libraryContext(isSatisfied)' , (
233- performance,
234- ) {
235- inputLibraryManifests = bundleEntry! .libraryManifests;
236- // If anything change in the API signature, relink the cycle.
237- // But use previous manifests to reuse item versions.
238- if (bundleEntry.apiSignature != cycle.nonTransitiveApiSignature) {
239- return false ;
240- } else {
241- var requirements = bundleEntry.requirements;
242- var failure = requirements.isSatisfied (
243- elementFactory: elementFactory,
244- libraryManifests: elementFactory.libraryManifests,
245- );
246- if (failure != null ) {
247- eventsController? .add (
248- CannotReuseLinkedBundle (
249- elementFactory: elementFactory,
250- cycle: cycle,
251- failure: failure,
252- ),
253- );
254- return false ;
255- }
256- }
257- return true ;
258- });
259- if (! isSatisfied) {
260- bundleEntry = null ;
261- }
262- }
294+ var bundleLookup = _getLinkedBundleEntry (
295+ cycle: cycle,
296+ performance: performance,
297+ );
298+ var bundleEntry = bundleLookup.entry;
263299
264300 if (bundleEntry == null ) {
265301 testData? .linkedCycles.add (
@@ -287,7 +323,7 @@ class LibraryContext {
287323 newLibraryManifests = LibraryManifestBuilder (
288324 elementFactory: elementFactory,
289325 inputLibraries: cycle.libraries,
290- inputManifests: inputLibraryManifests ,
326+ inputManifests: bundleLookup.libraryManifests ,
291327 ).computeManifests (performance: performance);
292328 elementFactory.libraryManifests.addAll (newLibraryManifests);
293329 });
@@ -301,11 +337,13 @@ class LibraryContext {
301337 assert (requirements.assertSerialization ());
302338
303339 bundleEntry = LinkedBundleEntry (
304- apiSignature : cycle.nonTransitiveApiSignature,
340+ nonTransitiveApiSignature : cycle.nonTransitiveApiSignature,
305341 libraryManifests: newLibraryManifests,
306342 requirements: requirements,
307343 linkedBytes: linkedBytes,
308344 );
345+ bundleEntry.addValidated (cycle.apiSignature);
346+
309347 performance.run ('bundleProvider.put' , (performance) {
310348 linkedBundleProvider.put (
311349 key: cycle.linkedKey,
@@ -333,7 +371,7 @@ class LibraryContext {
333371 linkedBytes = linkResult.resolutionBytes;
334372
335373 bundleEntry = LinkedBundleEntry (
336- apiSignature : cycle.nonTransitiveApiSignature,
374+ nonTransitiveApiSignature : cycle.nonTransitiveApiSignature,
337375 libraryManifests: {},
338376 requirements: RequirementsManifest (),
339377 linkedBytes: linkedBytes,
@@ -366,7 +404,7 @@ class LibraryContext {
366404 performance.getDataInt ('bytesGet' ).add (linkedBytes.length);
367405 performance.getDataInt ('libraryLoadCount' ).add (cycle.libraries.length);
368406 // TODO(scheglov): Take / clear parsed units in files.
369- eventsController? .add (ReuseLinkLibraryCycleBundle (cycle: cycle));
407+ eventsController? .add (ReuseLinkedBundle (cycle: cycle));
370408 var bundleReader = performance.run ('bundleReader' , (performance) {
371409 return BundleReader (
372410 elementFactory: elementFactory,
@@ -436,7 +474,7 @@ class LibraryCycleTestData {
436474/// The entry in [LinkedBundleProvider] .
437475class LinkedBundleEntry {
438476 /// See [LibraryCycle.nonTransitiveApiSignature] .
439- final String apiSignature ;
477+ final String nonTransitiveApiSignature ;
440478
441479 /// The manifests of libraries in [linkedBytes] .
442480 ///
@@ -451,15 +489,26 @@ class LinkedBundleEntry {
451489 /// Without fine-grained dependencies, the requirements are empty.
452490 final RequirementsManifest requirements;
453491
492+ /// Last transitive API signature for which [requirements] were verified.
493+ String ? _validatedApiSignature;
494+
454495 /// The serialized libraries, for [BundleReader] .
455496 final Uint8List linkedBytes;
456497
457498 LinkedBundleEntry ({
458- required this .apiSignature ,
499+ required this .nonTransitiveApiSignature ,
459500 required this .libraryManifests,
460501 required this .requirements,
461502 required this .linkedBytes,
462503 });
504+
505+ void addValidated (String apiSignature) {
506+ _validatedApiSignature = apiSignature;
507+ }
508+
509+ bool isValidated (String apiSignature) {
510+ return _validatedApiSignature == apiSignature;
511+ }
463512}
464513
465514/// The cache of serialized libraries.
@@ -502,7 +551,7 @@ class LinkedBundleProvider {
502551
503552 performance.getDataInt ('bytesLength' ).add (bytes.length);
504553 var reader = SummaryDataReader (bytes);
505- var apiSignature = reader.readStringUtf8 ();
554+ var nonTransitiveApiSignature = reader.readStringUtf8 ();
506555 var libraryManifests = reader.readMap (
507556 readKey: () => reader.readUri (),
508557 readValue: () => LibraryManifest .read (reader),
@@ -511,7 +560,7 @@ class LinkedBundleProvider {
511560 var linkedBytes = reader.readUint8List ();
512561
513562 var result = LinkedBundleEntry (
514- apiSignature : apiSignature ,
563+ nonTransitiveApiSignature : nonTransitiveApiSignature ,
515564 libraryManifests: libraryManifests,
516565 requirements: requirements,
517566 linkedBytes: linkedBytes,
@@ -534,7 +583,7 @@ class LinkedBundleProvider {
534583 }) {
535584 var sink = BufferedSink ();
536585
537- sink.writeStringUtf8 (entry.apiSignature );
586+ sink.writeStringUtf8 (entry.nonTransitiveApiSignature );
538587 sink.writeMap (
539588 entry.libraryManifests,
540589 writeKey: (uri) => sink.writeUri (uri),
@@ -552,3 +601,13 @@ class LinkedBundleProvider {
552601 }
553602 }
554603}
604+
605+ class _LinkedBundleEntryResult {
606+ final LinkedBundleEntry ? entry;
607+ final Map <Uri , LibraryManifest > libraryManifests;
608+
609+ _LinkedBundleEntryResult ({
610+ required this .entry,
611+ required this .libraryManifests,
612+ });
613+ }
0 commit comments