blob: 1945306484762417e08a0d889c5c3be6039e6e84 [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 java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Predicate;
public class AnnotationRemover {
private final AppInfoWithLiveness appInfo;
private final ProguardKeepAttributes keep;
private final InternalOptions options;
private final ProguardConfiguration.Builder compatibility;
public AnnotationRemover(AppInfoWithLiveness appInfo,
ProguardConfiguration.Builder compatibility, InternalOptions options) {
this.appInfo = appInfo;
this.keep = options.proguardConfiguration.getKeepAttributes();
this.compatibility = compatibility;
this.options = options;
}
/**
* Used to filter annotations on classes, methods and fields.
*/
private boolean filterAnnotations(DexAnnotation annotation) {
switch (annotation.visibility) {
case DexAnnotation.VISIBILITY_SYSTEM:
DexItemFactory factory = appInfo.dexItemFactory;
// InnerClass and EnclosingMember are represented in class attributes, not annotations.
assert !DexAnnotation.isInnerClassAnnotation(annotation, factory);
assert !DexAnnotation.isMemberClassesAnnotation(annotation, factory);
assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, factory);
assert !DexAnnotation.isEnclosingClassAnnotation(annotation, factory);
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.runtimeInvisibleParameterAnnotations) {
return false;
}
break;
default:
throw new Unreachable("Unexpected annotation visibility.");
}
return appInfo.liveTypes.contains(annotation.annotation.type);
}
public void run() {
keep.ensureValid(options.forceProguardCompatibility, compatibility);
for (DexProgramClass clazz : appInfo.classes()) {
stripAttributes(clazz);
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 void stripAttributes(DexProgramClass clazz) {
if (!keep.enclosingMethod && clazz.getEnclosingMethod() != null) {
clazz.clearEnclosingMethod();
}
if (!keep.innerClasses && !clazz.getInnerClasses().isEmpty()) {
clazz.clearInnerClasses();
}
}
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()]));
}
}
}