blob: fd5dc1153e591d8f4f68d84a03b9b90e0b05a1c7 [file] [log] [blame]
// Copyright (c) 2019, 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.naming.signature;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.PredicateUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Function;
import java.util.function.Predicate;
// TODO(b/129925954): Reimplement this by using the internal encoding and transformation logic.
public class GenericSignatureRewriter {
private final AppView<?> appView;
private final NamingLens namingLens;
private final InternalOptions options;
private final Reporter reporter;
private final Predicate<DexType> isTypeMissing;
public GenericSignatureRewriter(AppView<?> appView, NamingLens namingLens) {
this.appView = appView;
this.namingLens = namingLens;
this.options = appView.options();
this.reporter = options.reporter;
isTypeMissing = PredicateUtils.isNull(appView.appInfo()::definitionForWithoutExistenceAssert);
}
public void run(Iterable<? extends DexProgramClass> classes, ExecutorService executorService)
throws ExecutionException {
// Rewrite signature annotations for applications that are minified or if we have liveness
// information, since we could have pruned types.
if (namingLens.isIdentityLens() && !appView.appInfo().hasLiveness()) {
return;
}
// Classes may not be the same as appInfo().classes() if applymapping is used on classpath
// arguments. If that is the case, the ProguardMapMinifier will pass in all classes that is
// either ProgramClass or has a mapping. This is then transitively called inside the
// ClassNameMinifier.
ThreadUtils.processItems(
classes,
clazz -> {
GenericSignatureTypeRewriter typeRewriter =
new GenericSignatureTypeRewriter(appView, clazz);
clazz.setAnnotations(
rewriteGenericSignatures(
clazz.annotations(),
signature -> {
ClassSignature classSignature =
GenericSignature.parseClassSignature(
clazz.toSourceString(),
signature,
clazz.origin,
appView.dexItemFactory(),
options.reporter);
if (classSignature.hasNoSignature()) {
return null;
}
return typeRewriter
.rewrite(classSignature)
.toRenamedString(namingLens, isTypeMissing);
}));
clazz.forEachField(
field ->
field.setAnnotations(
rewriteGenericSignatures(
field.annotations(),
signature -> {
FieldTypeSignature fieldSignature =
GenericSignature.parseFieldTypeSignature(
field.toSourceString(),
signature,
clazz.origin,
appView.dexItemFactory(),
options.reporter);
if (fieldSignature.hasNoSignature()) {
return null;
}
return typeRewriter
.rewrite(fieldSignature)
.toRenamedString(namingLens, isTypeMissing);
})));
clazz.forEachMethod(
method ->
method.setAnnotations(
rewriteGenericSignatures(
method.annotations(),
signature -> {
MethodTypeSignature methodSignature =
GenericSignature.parseMethodSignature(
method.toSourceString(),
signature,
clazz.origin,
appView.dexItemFactory(),
options.reporter);
if (methodSignature.hasNoSignature()) {
return null;
}
return typeRewriter
.rewrite(methodSignature)
.toRenamedString(namingLens, isTypeMissing);
})));
},
executorService);
}
private DexAnnotationSet rewriteGenericSignatures(
DexAnnotationSet annotations, Function<String, String> rewrite) {
// There can be no more than one signature annotation in an annotation set.
final int VALID = -1;
int invalidOrPrunedIndex = VALID;
DexAnnotation[] rewrittenAnnotations = null;
for (int i = 0; i < annotations.annotations.length && invalidOrPrunedIndex == VALID; i++) {
DexAnnotation annotation = annotations.annotations[i];
if (DexAnnotation.isSignatureAnnotation(annotation, appView.dexItemFactory())) {
if (rewrittenAnnotations == null) {
rewrittenAnnotations = new DexAnnotation[annotations.annotations.length];
System.arraycopy(annotations.annotations, 0, rewrittenAnnotations, 0, i);
}
String signature = DexAnnotation.getSignature(annotation);
String rewrittenSignature = rewrite.apply(signature);
if (rewrittenSignature != null) {
rewrittenAnnotations[i] =
DexAnnotation.createSignatureAnnotation(rewrittenSignature, appView.dexItemFactory());
} else {
invalidOrPrunedIndex = i;
}
} else if (rewrittenAnnotations != null) {
rewrittenAnnotations[i] = annotation;
}
}
// Return the rewritten signatures if it was valid and could be rewritten.
if (invalidOrPrunedIndex == VALID) {
return rewrittenAnnotations != null
? new DexAnnotationSet(rewrittenAnnotations)
: annotations;
}
// Remove invalid signature if found.
DexAnnotation[] prunedAnnotations =
new DexAnnotation[annotations.annotations.length - 1];
int dest = 0;
for (int i = 0; i < annotations.annotations.length; i++) {
if (i != invalidOrPrunedIndex) {
prunedAnnotations[dest++] = annotations.annotations[i];
}
}
assert dest == prunedAnnotations.length;
return new DexAnnotationSet(prunedAnnotations);
}
}