| // Copyright (c) 2025, the R8 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. | |
| package com.android.tools.r8.shaking; | |
| import static com.android.tools.r8.utils.MapUtils.ignoreKey; | |
| import com.android.tools.r8.graph.DexAnnotation; | |
| import com.android.tools.r8.graph.DexAnnotation.AnnotatedKind; | |
| import com.android.tools.r8.graph.DexClass; | |
| import com.android.tools.r8.graph.DexDefinition; | |
| import com.android.tools.r8.graph.DexProgramClass; | |
| import com.android.tools.r8.graph.ProgramDefinition; | |
| import com.android.tools.r8.utils.MapUtils; | |
| import com.android.tools.r8.utils.ObjectUtils; | |
| import com.google.common.collect.Sets; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.IdentityHashMap; | |
| import java.util.Map; | |
| import java.util.Set; | |
| public class EnqueuerDeferredAnnotationTracing { | |
| private final Enqueuer enqueuer; | |
| /** | |
| * A map from annotation classes to annotations that need to be processed should the classes ever | |
| * become live. | |
| * | |
| * <p>Concurrent instance since the concurrent processing of deferred annotations may update this. | |
| */ | |
| private final Map<DexProgramClass, Set<DeferredAnnotation>> deferredAnnotationsByAnnotationClass = | |
| new IdentityHashMap<>(); | |
| /** | |
| * A map from annotated items to the annotations that need to be processed if/when the keep info | |
| * of the annotated item changes. | |
| * | |
| * <p>Concurrent instance since the concurrent processing of deferred annotations may update this. | |
| */ | |
| private final Map<DexDefinition, Set<DeferredAnnotation>> deferredAnnotationsByAnnotatedItem = | |
| new IdentityHashMap<>(); | |
| EnqueuerDeferredAnnotationTracing(Enqueuer enqueuer) { | |
| this.enqueuer = enqueuer; | |
| } | |
| public void enqueueAnnotationForProcessingWhenAnnotationTypeBecomesLive( | |
| ProgramDefinition annotatedItem, | |
| DexAnnotation annotation, | |
| DexProgramClass annotationClass, | |
| AnnotatedKind kind) { | |
| assert annotationClass.isAnnotation(); | |
| deferredAnnotationsByAnnotationClass | |
| .computeIfAbsent(annotationClass, ignoreKey(HashSet::new)) | |
| .add(new DeferredAnnotation(annotatedItem, annotation, annotationClass, kind)); | |
| } | |
| public void enqueueAnnotationForProcessingWhenKeepInfoChanges( | |
| ProgramDefinition annotatedItem, | |
| DexAnnotation annotation, | |
| DexClass annotationClass, | |
| AnnotatedKind kind) { | |
| deferredAnnotationsByAnnotatedItem | |
| .computeIfAbsent(annotatedItem.getDefinition(), ignoreKey(Sets::newHashSet)) | |
| .add(new DeferredAnnotation(annotatedItem, annotation, annotationClass, kind)); | |
| } | |
| // Called by the Enqueuer when an item has its keep info changed. | |
| public void notifyKeepInfoChanged(ProgramDefinition definition) { | |
| processDeferredAnnotations(definition.getDefinition(), deferredAnnotationsByAnnotatedItem); | |
| } | |
| // Called by the Enqueuer when an annotation class becomes live. | |
| public void notifyNewlyLiveAnnotation(DexProgramClass newlyLiveAnnotationClass) { | |
| assert newlyLiveAnnotationClass.isAnnotation(); | |
| processDeferredAnnotations(newlyLiveAnnotationClass, deferredAnnotationsByAnnotationClass); | |
| } | |
| private <T> void processDeferredAnnotations( | |
| T key, Map<T, Set<DeferredAnnotation>> deferredAnnotations) { | |
| Set<DeferredAnnotation> annotations = | |
| MapUtils.removeOrDefault(deferredAnnotations, key, Collections.emptySet()); | |
| for (var annotation : annotations) { | |
| enqueuer.processAnnotation( | |
| annotation.annotatedItem, | |
| annotation.annotation, | |
| annotation.kind, | |
| annotation.annotationClass); | |
| } | |
| } | |
| private static class DeferredAnnotation { | |
| private final ProgramDefinition annotatedItem; | |
| private final DexAnnotation annotation; | |
| private final DexClass annotationClass; | |
| private final AnnotatedKind kind; | |
| DeferredAnnotation( | |
| ProgramDefinition annotatedItem, | |
| DexAnnotation annotation, | |
| DexClass annotationClass, | |
| AnnotatedKind kind) { | |
| this.annotatedItem = annotatedItem; | |
| this.annotation = annotation; | |
| this.annotationClass = annotationClass; | |
| this.kind = kind; | |
| } | |
| @Override | |
| public boolean equals(Object obj) { | |
| if (obj == null) { | |
| return false; | |
| } | |
| if (!(obj instanceof DeferredAnnotation)) { | |
| return false; | |
| } | |
| DeferredAnnotation other = (DeferredAnnotation) obj; | |
| if (ObjectUtils.identical(annotation, other.annotation) | |
| && annotatedItem.getDefinition() == other.annotatedItem.getDefinition()) { | |
| assert annotatedItem.getDefinition() == other.annotatedItem.getDefinition(); | |
| assert annotationClass == other.annotationClass; | |
| assert kind == other.kind; | |
| return true; | |
| } | |
| return false; | |
| } | |
| @Override | |
| public int hashCode() { | |
| return (31 * (31 + System.identityHashCode(annotation))) | |
| + annotatedItem.getDefinition().hashCode(); | |
| } | |
| } | |
| } |