blob: f14e456c4696689dde08e9af7434c3c3a6f64541 [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;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.naming.ClassNameMinifier.ClassRenaming;
import com.android.tools.r8.naming.FieldNameMinifier.FieldRenaming;
import com.android.tools.r8.naming.MethodNameMinifier.MethodRenaming;
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
class MinifiedRenaming extends NamingLens {
private final AppView<?> appView;
private final Map<String, String> packageRenaming;
private final Map<DexItem, DexString> renaming = new IdentityHashMap<>();
MinifiedRenaming(
AppView<?> appView,
ClassRenaming classRenaming,
MethodRenaming methodRenaming,
FieldRenaming fieldRenaming) {
this.appView = appView;
this.packageRenaming = classRenaming.packageRenaming;
renaming.putAll(classRenaming.classRenaming);
renaming.putAll(methodRenaming.renaming);
renaming.putAll(methodRenaming.callSiteRenaming);
renaming.putAll(fieldRenaming.renaming);
}
@Override
public String lookupPackageName(String packageName) {
return packageRenaming.getOrDefault(packageName, packageName);
}
@Override
public DexString lookupDescriptor(DexType type) {
return renaming.getOrDefault(type, type.descriptor);
}
@Override
public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
if (attribute.getInnerName() == null) {
return null;
}
DexType innerType = attribute.getInner();
String inner = DescriptorUtils.descriptorToInternalName(innerType.descriptor.toString());
// At this point we assume the input was of the form: <OuterType>$<index><InnerName>
// Find the mapped type and if it remains the same return that, otherwise split at
// either the input separator ($, $$, or anything that starts with $) or $ (that we recover
// while minifying, e.g., empty separator, under bar, etc.).
String innerTypeMapped =
DescriptorUtils.descriptorToInternalName(lookupDescriptor(innerType).toString());
if (inner.equals(innerTypeMapped)) {
return attribute.getInnerName();
}
String separator = DescriptorUtils.computeInnerClassSeparator(
attribute.getOuter(), innerType, attribute.getInnerName());
if (separator == null) {
separator = String.valueOf(DescriptorUtils.INNER_CLASS_SEPARATOR);
}
int index = innerTypeMapped.lastIndexOf(separator);
if (index < 0) {
assert false : innerType + " -> " + innerTypeMapped;
String descriptor = lookupDescriptor(innerType).toString();
return options.itemFactory.createString(
DescriptorUtils.getUnqualifiedClassNameFromDescriptor(descriptor));
}
return options.itemFactory.createString(innerTypeMapped.substring(index + separator.length()));
}
@Override
public DexString lookupName(DexMethod method) {
return renaming.getOrDefault(method, method.name);
}
@Override
public DexString lookupMethodName(DexCallSite callSite) {
return renaming.getOrDefault(callSite, callSite.methodName);
}
@Override
public DexString lookupName(DexField field) {
return renaming.getOrDefault(field, field.name);
}
@Override
void forAllRenamedTypes(Consumer<DexType> consumer) {
DexReference.filterDexType(DexReference.filterDexReference(renaming.keySet().stream()))
.forEach(consumer);
}
@Override
<T extends DexItem> Map<String, T> getRenamedItems(
Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) {
return renaming.keySet().stream()
.filter(item -> (clazz.isInstance(item) && predicate.test(clazz.cast(item))))
.map(clazz::cast)
.collect(ImmutableMap.toImmutableMap(namer, i -> i));
}
/**
* Checks whether the target is precise enough to be translated,
*
* <p>We only track the renaming of actual definitions, Thus, if we encounter a method id that
* does not directly point at a definition, we won't find the actual renaming. To avoid
* dispatching on every lookup, we assume that the tree has been fully dispatched by {@link
* MemberRebindingAnalysis}.
*
* <p>Library methods are excluded from this check, as those are never renamed.
*/
@Override
public boolean checkTargetCanBeTranslated(DexMethod item) {
if (item.holder.isArrayType()) {
// Array methods are never renamed, so do not bother to check.
return true;
}
DexClass holder = appView.definitionFor(item.holder);
if (holder == null || holder.isNotProgramClass()) {
return true;
}
// We don't know which invoke type this method is used for, so checks that it has been
// rebound either way.
DexEncodedMethod staticTarget = appView.appInfo().lookupStaticTarget(item);
DexEncodedMethod directTarget = appView.appInfo().lookupDirectTarget(item);
DexEncodedMethod virtualTarget = appView.appInfo().lookupVirtualTarget(item.holder, item);
DexClass staticTargetHolder =
staticTarget != null ? appView.definitionFor(staticTarget.method.holder) : null;
DexClass directTargetHolder =
directTarget != null ? appView.definitionFor(directTarget.method.holder) : null;
DexClass virtualTargetHolder =
virtualTarget != null ? appView.definitionFor(virtualTarget.method.holder) : null;
return (directTarget == null && staticTarget == null && virtualTarget == null)
|| (virtualTarget != null && virtualTarget.method == item)
|| (directTarget != null && directTarget.method == item)
|| (staticTarget != null && staticTarget.method == item)
|| (directTargetHolder != null && directTargetHolder.isNotProgramClass())
|| (virtualTargetHolder != null && virtualTargetHolder.isNotProgramClass())
|| (staticTargetHolder != null && staticTargetHolder.isNotProgramClass())
|| appView.unneededVisibilityBridgeMethods().contains(item);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
renaming.forEach(
(item, str) -> {
if (item instanceof DexType) {
builder.append("[c] ");
} else if (item instanceof DexMethod) {
builder.append("[m] ");
} else if (item instanceof DexField) {
builder.append("[f] ");
}
builder.append(item.toSourceString());
builder.append(" -> ");
builder.append(str.toSourceString());
builder.append('\n');
});
return builder.toString();
}
}