| // 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.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 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; |
| |
| protected NonIdentityNamingLens(DexItemFactory dexItemFactory) { |
| this.dexItemFactory = dexItemFactory; |
| } |
| |
| 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); |
| } |
| } |
| |
| private static final class IdentityLens extends NamingLens { |
| |
| private IdentityLens() { |
| // Intentionally left empty. |
| } |
| |
| @Override |
| public DexString lookupDescriptor(DexType type) { |
| return type.descriptor; |
| } |
| |
| @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; |
| } |
| } |
| } |