| // 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 static com.android.tools.r8.utils.DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR; |
| import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType; |
| |
| 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.DexItem; |
| 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.ImmutableMap; |
| import com.google.common.collect.Sets; |
| import java.util.Arrays; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| |
| /** |
| * 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 DexString lookupSimpleName(DexType type, DexItemFactory factory) { |
| String descriptor = lookupDescriptor(type).toString(); |
| int lastSeparator = descriptor.lastIndexOf(DESCRIPTOR_PACKAGE_SEPARATOR); |
| String simpleName = |
| descriptor.substring(lastSeparator > 0 ? lastSeparator + 1 : 1, descriptor.length() - 1); |
| // TODO(b/120811478): Replace by DescriptorUtils.getSimpleClassNameFromDescriptor once fixed. |
| return factory.createString(simpleName); |
| } |
| |
| public abstract DexString lookupInnerName(InnerClassAttribute attribute, InternalOptions options); |
| |
| public abstract DexString lookupName(DexMethod method); |
| |
| public abstract DexString lookupMethodName(DexCallSite callSite); |
| |
| 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(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.clazz, 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(lookupDescriptor(type)); |
| } |
| |
| 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()); |
| } |
| |
| abstract void forAllRenamedTypes(Consumer<DexType> consumer); |
| |
| abstract <T extends DexItem> Map<String, T> getRenamedItems( |
| Class<T> clazz, Predicate<T> predicate, Function<T, String> namer); |
| |
| /** |
| * Checks whether the target will be translated properly by this lense. |
| * <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 checkTargetCanBeTranslated(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.field, dexItemFactory); |
| boolean referencesChanged = references.add(newField); |
| assert referencesChanged |
| : "Duplicate definition of field `" + newField.toSourceString() + "`"; |
| } |
| |
| for (DexEncodedMethod method : clazz.methods()) { |
| DexMethod newMethod = lookupMethod(method.method, dexItemFactory); |
| boolean referencesChanged = references.add(newMethod); |
| assert referencesChanged |
| : "Duplicate definition of method `" + newMethod.toSourceString() + "`"; |
| } |
| } |
| return true; |
| } |
| |
| private static class IdentityLens extends NamingLens { |
| |
| private IdentityLens() { |
| // Intentionally left empty. |
| } |
| |
| @Override |
| public DexString lookupDescriptor(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 lookupMethodName(DexCallSite callSite) { |
| return callSite.methodName; |
| } |
| |
| @Override |
| public DexString lookupName(DexField field) { |
| return field.name; |
| } |
| |
| @Override |
| public String lookupPackageName(String packageName) { |
| return packageName; |
| } |
| |
| @Override |
| void forAllRenamedTypes(Consumer<DexType> consumer) { |
| // Intentionally left empty. |
| } |
| |
| @Override |
| <T extends DexItem> Map<String, T> getRenamedItems( |
| Class<T> clazz, Predicate<T> predicate, Function<T, String> namer) { |
| return ImmutableMap.of(); |
| } |
| |
| @Override |
| public boolean checkTargetCanBeTranslated(DexMethod item) { |
| return true; |
| } |
| } |
| } |