blob: b6e94ee35dc4207ed8e1d30e000fd0e1a089ec6f [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.naming;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
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.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Implements a translation of the Dex graph from original names to new names produced by the {@link
* Minifier}.
*
* <p>The minifier does not actually rename classes and members but instead only produces a mapping
* from original ids to renamed ids. When writing the file, the graph has to be interpreted with
* that mapping in mind, i.e., it should be looked at only through this lens.
*
* <p>The translation relies on members being statically dispatched to actual definitions, as done
* by the {@link MemberRebindingAnalysis} optimization.
*/
public abstract class NamingLens {
public abstract String lookupPackageName(String packageName);
public abstract DexString lookupDescriptor(DexType type);
public abstract DexString lookupDescriptorForJavaTypeName(String typeName);
public DexString lookupClassDescriptor(DexType type) {
assert type.isClassType();
return internalLookupClassDescriptor(type);
}
protected abstract DexString internalLookupClassDescriptor(DexType type);
public abstract DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options);
public abstract DexString lookupName(DexMethod method);
public final DexString lookupMethodName(DexCallSite callSite, AppView<?> appView) {
if (!appView.appInfo().hasLiveness()) {
return callSite.methodName;
}
Set<DexEncodedMethod> lambdaImplementedMethods =
appView.appInfo().withLiveness().lookupLambdaImplementedMethods(callSite);
if (lambdaImplementedMethods.isEmpty()) {
return callSite.methodName;
}
DexMethod lambdaImplementedMethodReference =
lambdaImplementedMethods.iterator().next().getReference();
DexString renamedMethodName =
lookupMethod(lambdaImplementedMethodReference, appView.dexItemFactory()).getName();
// Verify that all lambda implemented methods are renamed consistently.
assert lambdaImplementedMethods.stream()
.map(DexEncodedMethod::getReference)
.map(reference -> lookupMethod(reference, appView.dexItemFactory()))
.map(DexMethod::getName)
.allMatch(name -> name == renamedMethodName);
return renamedMethodName;
}
public abstract DexString lookupName(DexField field);
public final DexString lookupName(DexReference reference, DexItemFactory dexItemFactory) {
if (reference.isDexType()) {
DexString renamed = lookupDescriptor(reference.asDexType());
return dexItemFactory.createString(DescriptorUtils.descriptorToJavaType(renamed.toString()));
}
if (reference.isDexMethod()) {
return lookupName(reference.asDexMethod());
}
assert reference.isDexField();
return lookupName(reference.asDexField());
}
public final DexField lookupField(DexField field, DexItemFactory dexItemFactory) {
return dexItemFactory.createField(
lookupType(field.holder, dexItemFactory),
lookupType(field.type, dexItemFactory),
lookupName(field));
}
public final DexMethod lookupMethod(DexMethod method, DexItemFactory dexItemFactory) {
return dexItemFactory.createMethod(
lookupType(method.holder, dexItemFactory),
lookupProto(method.proto, dexItemFactory),
lookupName(method));
}
private DexProto lookupProto(DexProto proto, DexItemFactory dexItemFactory) {
return dexItemFactory.createProto(
lookupType(proto.returnType, dexItemFactory),
Arrays.stream(proto.parameters.values)
.map(type -> lookupType(type, dexItemFactory))
.toArray(DexType[]::new));
}
public final DexType lookupType(DexType type, DexItemFactory dexItemFactory) {
if (type.isPrimitiveType() || type.isVoidType()) {
return type;
}
if (type.isArrayType()) {
DexType newBaseType = lookupType(type.toBaseType(dexItemFactory), dexItemFactory);
return type.replaceBaseType(newBaseType, dexItemFactory);
}
assert type.isClassType();
return dexItemFactory.createType(lookupClassDescriptor(type));
}
public boolean hasPrefixRewritingLogic() {
return false;
}
public DexString prefixRewrittenType(DexType type) {
return null;
}
public static NamingLens getIdentityLens() {
return new IdentityLens();
}
public final boolean isIdentityLens() {
return this instanceof IdentityLens;
}
public String lookupInternalName(DexType type) {
assert type.isClassType() || type.isArrayType();
return DescriptorUtils.descriptorToInternalName(lookupDescriptor(type).toString());
}
/**
* Checks whether the target will be translated properly by this lens.
*
* <p>Normally, this means that the target corresponds to an actual definition that has been
* renamed. For identity renamings, we are more relaxed, as no targets will be translated anyway.
*/
public abstract boolean verifyRenamingConsistentWithResolution(DexMethod item);
public final boolean verifyNoCollisions(
Iterable<DexProgramClass> classes, DexItemFactory dexItemFactory) {
Set<DexReference> references = Sets.newIdentityHashSet();
for (DexProgramClass clazz : classes) {
{
DexType newType = lookupType(clazz.type, dexItemFactory);
boolean referencesChanged = references.add(newType);
assert referencesChanged
: "Duplicate definition of type `" + newType.toSourceString() + "`";
}
for (DexEncodedField field : clazz.fields()) {
DexField newField = lookupField(field.getReference(), dexItemFactory);
boolean referencesChanged = references.add(newField);
assert referencesChanged
: "Duplicate definition of field `" + newField.toSourceString() + "`";
}
for (DexEncodedMethod method : clazz.methods()) {
DexMethod newMethod = lookupMethod(method.getReference(), dexItemFactory);
boolean referencesChanged = references.add(newMethod);
assert referencesChanged
: "Duplicate definition of method `" + newMethod.toSourceString() + "`";
}
}
return true;
}
public abstract static class NonIdentityNamingLens extends NamingLens {
private final DexItemFactory dexItemFactory;
private final Map<String, DexString> typeStringMapping;
protected NonIdentityNamingLens(
DexItemFactory dexItemFactory, Map<DexType, DexString> typeMapping) {
this.dexItemFactory = dexItemFactory;
typeStringMapping = new HashMap<>();
typeMapping.forEach((k, v) -> typeStringMapping.put(k.toSourceString(), v));
}
protected DexItemFactory dexItemFactory() {
return dexItemFactory;
}
@Override
public final DexString lookupDescriptor(DexType type) {
if (type.isPrimitiveType() || type.isVoidType() || type.isNullValueType()) {
return type.getDescriptor();
}
if (type.isArrayType()) {
DexType baseType = type.toBaseType(dexItemFactory);
DexString desc = lookupDescriptor(baseType);
return desc.toArrayDescriptor(type.getNumberOfLeadingSquareBrackets(), dexItemFactory);
}
assert type.isClassType();
return lookupClassDescriptor(type);
}
@Override
public DexString lookupDescriptorForJavaTypeName(String typeName) {
return typeStringMapping.get(typeName);
}
}
private static final class IdentityLens extends NamingLens {
private IdentityLens() {
// Intentionally left empty.
}
@Override
public DexString lookupDescriptor(DexType type) {
return type.descriptor;
}
@Override
public DexString lookupDescriptorForJavaTypeName(String typeName) {
return null;
}
@Override
protected DexString internalLookupClassDescriptor(DexType type) {
return type.descriptor;
}
@Override
public DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options) {
return attribute.getInnerName();
}
@Override
public DexString lookupName(DexMethod method) {
return method.name;
}
@Override
public DexString lookupName(DexField field) {
return field.name;
}
@Override
public String lookupPackageName(String packageName) {
return packageName;
}
@Override
public boolean verifyRenamingConsistentWithResolution(DexMethod item) {
return true;
}
}
}