blob: aafe94b3d0344cd8515bdf0329a5a4495483446d [file] [log] [blame]
// 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();
}
}
}