blob: 3106c0c2f92ed37f49099fdcbe2e5d11925e5465 [file] [log] [blame]
// Copyright (c) 2017, 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 com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexAnnotationSetRefList;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Predicate;
public class AnnotationRemover {
private final AppInfoWithLiveness appInfo;
private final AttributeRemovalOptions keep;
public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
this(appInfo, options.attributeRemoval);
}
public AnnotationRemover(AppInfoWithLiveness appInfo,
AttributeRemovalOptions keep) {
this.appInfo = appInfo;
this.keep = keep;
}
/**
* Used to filter annotations on classes, methods and fields.
*/
private boolean filterAnnotations(DexAnnotation annotation) {
switch (annotation.visibility) {
case DexAnnotation.VISIBILITY_SYSTEM:
// EnclosingClass and InnerClass are used in Dalvik to represent the InnerClasses
// attribute section from class files.
DexItemFactory factory = appInfo.dexItemFactory;
if (keep.innerClasses
&& (DexAnnotation.isInnerClassesAnnotation(annotation, factory) ||
DexAnnotation.isEnclosingClassAnnotation(annotation, factory))) {
return true;
}
if (keep.enclosingMethod
&& DexAnnotation.isEnclosingMethodAnnotation(annotation, factory)) {
return true;
}
if (keep.exceptions && DexAnnotation.isThrowingAnnotation(annotation, factory)) {
return true;
}
if (keep.signature && DexAnnotation.isSignatureAnnotation(annotation, factory)) {
return true;
}
if (keep.sourceDebugExtension
&& DexAnnotation.isSourceDebugExtension(annotation, factory)) {
return true;
}
return false;
case DexAnnotation.VISIBILITY_RUNTIME:
if (!keep.runtimeVisibleAnnotations) {
return false;
}
break;
case DexAnnotation.VISIBILITY_BUILD:
if (!keep.runtimeInvisibleAnnotations) {
return false;
}
break;
default:
throw new Unreachable("Unexpected annotation visiility.");
}
return appInfo.liveTypes.contains(annotation.annotation.type);
}
/**
* Used to filter annotations on parameters.
*/
private boolean filterParameterAnnotations(DexAnnotation annotation) {
switch (annotation.visibility) {
case DexAnnotation.VISIBILITY_SYSTEM:
return false;
case DexAnnotation.VISIBILITY_RUNTIME:
if (!keep.runtimeVisibleParameterAnnotations) {
return false;
}
break;
case DexAnnotation.VISIBILITY_BUILD:
if (!keep.runtimeInvisibleParamterAnnotations) {
return false;
}
break;
default:
throw new Unreachable("Unexpected annotation visibility.");
}
return appInfo.liveTypes.contains(annotation.annotation.type);
}
public void run() {
keep.ensureValid();
for (DexProgramClass clazz : appInfo.classes()) {
clazz.annotations = stripAnnotations(clazz.annotations, this::filterAnnotations);
clazz.forEachMethod(this::processMethod);
clazz.forEachField(this::processField);
}
}
private void processMethod(DexEncodedMethod method) {
method.annotations = stripAnnotations(method.annotations, this::filterAnnotations);
method.parameterAnnotations = stripAnnotations(method.parameterAnnotations,
this::filterParameterAnnotations);
}
private void processField(DexEncodedField field) {
field.annotations = stripAnnotations(field.annotations, this::filterAnnotations);
}
private DexAnnotationSetRefList stripAnnotations(DexAnnotationSetRefList annotations,
Predicate<DexAnnotation> filter) {
DexAnnotationSet[] filtered = null;
for (int i = 0; i < annotations.values.length; i++) {
DexAnnotationSet updated = stripAnnotations(annotations.values[i], filter);
if (updated != annotations.values[i]) {
if (filtered == null) {
filtered = annotations.values.clone();
filtered[i] = updated;
}
}
}
if (filtered == null) {
return annotations;
} else {
if (Arrays.stream(filtered).allMatch(DexAnnotationSet::isEmpty)) {
return DexAnnotationSetRefList.empty();
}
return new DexAnnotationSetRefList(filtered);
}
}
private DexAnnotationSet stripAnnotations(DexAnnotationSet annotations,
Predicate<DexAnnotation> filter) {
ArrayList<DexAnnotation> filtered = null;
for (int i = 0; i < annotations.annotations.length; i++) {
DexAnnotation annotation = annotations.annotations[i];
if (filter.test(annotation)) {
if (filtered != null) {
filtered.add(annotation);
}
} else {
if (filtered == null) {
filtered = new ArrayList<>(annotations.annotations.length);
for (int j = 0; j < i; j++) {
filtered.add(annotations.annotations[j]);
}
}
}
}
if (filtered == null) {
return annotations;
} else if (filtered.isEmpty()) {
return DexAnnotationSet.empty();
} else {
return new DexAnnotationSet(filtered.toArray(new DexAnnotation[filtered.size()]));
}
}
}