blob: 7371902cd31eddc995c7379ddfa9637b9e67087a [file] [log] [blame]
// Copyright (c) 2016, 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.graph;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.ArrayUtils;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class DexAnnotationSet extends CachedHashValueDexItem {
public static final DexAnnotationSet[] EMPTY_ARRAY = {};
private static final int UNSORTED = 0;
private static final DexAnnotationSet THE_EMPTY_ANNOTATIONS_SET =
new DexAnnotationSet(DexAnnotation.EMPTY_ARRAY);
public final DexAnnotation[] annotations;
private int sorted = UNSORTED;
public DexAnnotationSet(DexAnnotation[] annotations) {
this.annotations = annotations;
}
public static DexType findDuplicateEntryType(DexAnnotation[] annotations) {
return findDuplicateEntryType(Arrays.asList(annotations));
}
public static DexType findDuplicateEntryType(List<DexAnnotation> annotations) {
Set<DexType> seenTypes = Sets.newIdentityHashSet();
for (DexAnnotation annotation : annotations) {
if (!seenTypes.add(annotation.annotation.type)) {
return annotation.annotation.type;
}
}
return null;
}
public static DexAnnotationSet empty() {
return THE_EMPTY_ANNOTATIONS_SET;
}
public void forEach(Consumer<DexAnnotation> consumer) {
for (DexAnnotation annotation : annotations) {
consumer.accept(annotation);
}
}
public Stream<DexAnnotation> stream() {
return Arrays.stream(annotations);
}
public int size() {
return annotations.length;
}
@Override
public int computeHashCode() {
return Arrays.hashCode(annotations);
}
@Override
public boolean computeEquals(Object other) {
if (other instanceof DexAnnotationSet) {
DexAnnotationSet o = (DexAnnotationSet) other;
return Arrays.equals(annotations, o.annotations);
}
return false;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
collectAll(indexedItems, annotations);
}
@Override
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
mixedItems.add(this);
collectAll(mixedItems, annotations);
}
public boolean isEmpty() {
return annotations.length == 0;
}
public void sort(NamingLens namingLens) {
if (sorted != UNSORTED) {
assert sorted == sortedHashCode();
return;
}
Arrays.sort(
annotations, (a, b) -> a.annotation.type.slowCompareTo(b.annotation.type, namingLens));
for (DexAnnotation annotation : annotations) {
annotation.annotation.sort();
}
sorted = hashCode();
}
public DexAnnotation getFirstMatching(DexType type) {
for (DexAnnotation annotation : annotations) {
if (annotation.annotation.type == type) {
return annotation;
}
}
return null;
}
public DexAnnotationSet getWithout(DexType annotationType) {
int index = 0;
for (DexAnnotation annotation : annotations) {
if (annotation.annotation.type == annotationType) {
DexAnnotation[] reducedArray = new DexAnnotation[annotations.length - 1];
System.arraycopy(annotations, 0, reducedArray, 0, index);
if (index < reducedArray.length) {
System.arraycopy(annotations, index + 1, reducedArray, index, reducedArray.length - index);
}
return new DexAnnotationSet(reducedArray);
}
++index;
}
return this;
}
private int sortedHashCode() {
int hashCode = hashCode();
return hashCode == UNSORTED ? 1 : hashCode;
}
public DexAnnotationSet getWithAddedOrReplaced(DexAnnotation newAnnotation) {
// Check existing annotation for replacement.
int index = 0;
for (DexAnnotation annotation : annotations) {
if (annotation.annotation.type == newAnnotation.annotation.type) {
DexAnnotation[] modifiedArray = annotations.clone();
modifiedArray[index] = newAnnotation;
return new DexAnnotationSet(modifiedArray);
}
++index;
}
// No existing annotation, append.
DexAnnotation[] extendedArray = new DexAnnotation[annotations.length + 1];
System.arraycopy(annotations, 0, extendedArray, 0, annotations.length);
extendedArray[annotations.length] = newAnnotation;
return new DexAnnotationSet(extendedArray);
}
public DexAnnotationSet keepIf(Predicate<DexAnnotation> filter) {
return rewrite(anno -> filter.test(anno) ? anno : null);
}
public DexAnnotationSet rewrite(Function<DexAnnotation, DexAnnotation> rewriter) {
if (isEmpty()) {
return this;
}
DexAnnotation[] rewritten = ArrayUtils.map(DexAnnotation[].class, annotations, rewriter);
if (rewritten == annotations) {
return this;
}
if (rewritten.length == 0) {
return DexAnnotationSet.empty();
}
return new DexAnnotationSet(rewritten);
}
@Override
public String toString() {
return Arrays.toString(annotations);
}
}