1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.naming; 5 6 import com.android.tools.r8.errors.CompilationError; 7 import com.android.tools.r8.graph.AppInfo; 8 import com.android.tools.r8.graph.DexClass; 9 import com.android.tools.r8.graph.DexEncodedMethod; 10 import com.android.tools.r8.graph.DexField; 11 import com.android.tools.r8.graph.DexItem; 12 import com.android.tools.r8.graph.DexMethod; 13 import com.android.tools.r8.graph.DexString; 14 import com.android.tools.r8.graph.DexType; 15 import com.android.tools.r8.optimize.MemberRebindingAnalysis; 16 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness; 17 import com.android.tools.r8.shaking.RootSetBuilder.RootSet; 18 import com.android.tools.r8.utils.InternalOptions; 19 import com.android.tools.r8.utils.Timing; 20 import com.google.common.collect.Iterables; 21 import java.util.IdentityHashMap; 22 import java.util.Map; 23 import java.util.function.Consumer; 24 25 public class Minifier { 26 27 private final AppInfoWithLiveness appInfo; 28 private final RootSet rootSet; 29 private final InternalOptions options; 30 Minifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options)31 public Minifier(AppInfoWithLiveness appInfo, RootSet rootSet, InternalOptions options) { 32 this.appInfo = appInfo; 33 this.rootSet = rootSet; 34 this.options = options; 35 } 36 run(Timing timing)37 public NamingLens run(Timing timing) { 38 assert !options.skipMinification; 39 if (!options.allowAccessModification) { 40 throw new CompilationError("Minification requires allowaccessmodification."); 41 } 42 timing.begin("MinifyClasses"); 43 Map<DexType, DexString> classRenaming = 44 new ClassNameMinifier( 45 appInfo, rootSet, options.packagePrefix, options.classObfuscationDictionary, 46 options.attributeRemoval.signature) 47 .computeRenaming(); 48 timing.end(); 49 timing.begin("MinifyMethods"); 50 Map<DexMethod, DexString> methodRenaming = 51 new MethodNameMinifier(appInfo, rootSet, options.obfuscationDictionary) 52 .computeRenaming(timing); 53 timing.end(); 54 timing.begin("MinifyFields"); 55 Map<DexField, DexString> fieldRenaming = 56 new FieldNameMinifier(appInfo, rootSet, options.obfuscationDictionary).computeRenaming(); 57 timing.end(); 58 return new MinifiedRenaming(classRenaming, methodRenaming, fieldRenaming, appInfo); 59 } 60 61 private static class MinifiedRenaming extends NamingLens { 62 63 private final AppInfo appInfo; 64 private final Map<DexItem, DexString> renaming = new IdentityHashMap<>(); 65 MinifiedRenaming(Map<DexType, DexString> classRenaming, Map<DexMethod, DexString> methodRenaming, Map<DexField, DexString> fieldRenaming, AppInfo appInfo)66 private MinifiedRenaming(Map<DexType, DexString> classRenaming, 67 Map<DexMethod, DexString> methodRenaming, Map<DexField, DexString> fieldRenaming, 68 AppInfo appInfo) { 69 this.appInfo = appInfo; 70 renaming.putAll(classRenaming); 71 renaming.putAll(methodRenaming); 72 renaming.putAll(fieldRenaming); 73 } 74 75 @Override lookupDescriptor(DexType type)76 public DexString lookupDescriptor(DexType type) { 77 return renaming.getOrDefault(type, type.descriptor); 78 } 79 80 @Override lookupName(DexMethod method)81 public DexString lookupName(DexMethod method) { 82 return renaming.getOrDefault(method, method.name); 83 } 84 85 @Override lookupName(DexField field)86 public DexString lookupName(DexField field) { 87 return renaming.getOrDefault(field, field.name); 88 } 89 90 @Override forAllRenamedTypes(Consumer<DexType> consumer)91 void forAllRenamedTypes(Consumer<DexType> consumer) { 92 Iterables.filter(renaming.keySet(), DexType.class).forEach(consumer); 93 } 94 95 /** 96 * Checks whether the target is precise enough to be translated, 97 * <p> 98 * We only track the renaming of actual definitions, Thus, if we encounter a method id that 99 * does not directly point at a definition, we won't find the actual renaming. To avoid 100 * dispatching on every lookup, we assume that the tree has been fully dispatched by 101 * {@link MemberRebindingAnalysis}. 102 * <p> 103 * Library methods are excluded from this check, as those are never renamed. 104 */ 105 @Override checkTargetCanBeTranslated(DexMethod item)106 public boolean checkTargetCanBeTranslated(DexMethod item) { 107 if (item.holder.isArrayType()) { 108 // Array methods are never renamed, so do not bother to check. 109 return true; 110 } 111 DexClass holder = appInfo.definitionFor(item.holder); 112 if (holder == null || holder.isLibraryClass()) { 113 return true; 114 } 115 // We don't know which invoke type this method is used for, so checks that it has been 116 // rebound either way. 117 DexEncodedMethod staticTarget = appInfo.lookupStaticTarget(item); 118 DexEncodedMethod directTarget = appInfo.lookupDirectTarget(item); 119 DexEncodedMethod virtualTarget = appInfo.lookupVirtualTarget(item.holder, item); 120 DexClass staticTargetHolder = 121 staticTarget != null ? appInfo.definitionFor(staticTarget.method.getHolder()) : null; 122 DexClass directTargetHolder = 123 directTarget != null ? appInfo.definitionFor(directTarget.method.getHolder()) : null; 124 DexClass virtualTargetHolder = 125 virtualTarget != null ? appInfo.definitionFor(virtualTarget.method.getHolder()) : null; 126 return directTarget == null && staticTarget == null && virtualTarget == null 127 || virtualTarget != null && virtualTarget.method == item 128 || directTarget != null && directTarget.method == item 129 || staticTarget != null && staticTarget.method == item 130 || directTargetHolder != null && directTargetHolder.isLibraryClass() 131 || virtualTargetHolder != null && virtualTargetHolder.isLibraryClass() 132 || staticTargetHolder != null && staticTargetHolder.isLibraryClass(); 133 } 134 } 135 } 136