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.utils; 5 6 import com.android.tools.r8.dex.Constants; 7 import com.android.tools.r8.errors.CompilationError; 8 import com.android.tools.r8.graph.DexEncodedMethod; 9 import com.android.tools.r8.graph.DexItemFactory; 10 import com.android.tools.r8.ir.conversion.CallGraph; 11 import com.android.tools.r8.shaking.ProguardConfigurationRule; 12 import com.android.tools.r8.shaking.ProguardTypeMatcher; 13 import com.google.common.collect.ImmutableList; 14 import com.google.common.collect.ImmutableSet; 15 import java.nio.file.Path; 16 import java.util.List; 17 import java.util.function.BiFunction; 18 import java.util.function.Function; 19 20 public class InternalOptions { 21 22 public final DexItemFactory itemFactory; 23 InternalOptions()24 public InternalOptions() { 25 itemFactory = new DexItemFactory(); 26 } 27 InternalOptions(DexItemFactory factory)28 public InternalOptions(DexItemFactory factory) { 29 assert factory != null; 30 itemFactory = factory; 31 } 32 33 public final int NOT_SPECIFIED = -1; 34 35 public boolean printTimes = false; 36 // Skipping optimizations. 37 public boolean skipDebugInfoOpt = false; 38 public boolean skipDebugLineNumberOpt = false; 39 public boolean skipClassMerging = true; 40 41 // Number of threads to use while processing the dex files. 42 public int numberOfThreads = NOT_SPECIFIED; 43 // Print smali disassembly. 44 public boolean useSmaliSyntax = false; 45 // Verbose output. 46 public boolean verbose = false; 47 // Silencing output. 48 public boolean quiet = false; 49 50 public List<String> methodsFilter = ImmutableList.of(); 51 public int minApiLevel = Constants.DEFAULT_ANDROID_API; 52 public List<String> logArgumentsFilter = ImmutableList.of(); 53 54 // Defines interface method rewriter behavior. 55 public OffOrAuto interfaceMethodDesugaring = OffOrAuto.Off; 56 // Defines try-with-resources rewriter behavior. 57 public OffOrAuto tryWithResourcesDesugaring = OffOrAuto.Off; 58 59 // Application writing mode. 60 public OutputMode outputMode = OutputMode.Indexed; 61 62 public boolean useTreeShaking = true; 63 public boolean printUsage = false; 64 public Path printUsageFile = null; 65 66 public boolean printCfg = false; 67 public String printCfgFile; 68 public boolean printSeeds; 69 public Path seedsFile; 70 public boolean printMapping; 71 public Path printMappingFile; 72 public boolean printMainDexList; 73 public Path printMainDexListFile; 74 public boolean ignoreMissingClasses = false; 75 public boolean skipMinification = false; 76 public String packagePrefix = ""; 77 public boolean allowAccessModification = true; 78 public boolean inlineAccessors = true; 79 public boolean removeSwitchMaps = true; 80 public boolean disableAssertions = true; 81 public final OutlineOptions outline = new OutlineOptions(); 82 public boolean debugKeepRules = false; 83 public final AttributeRemovalOptions attributeRemoval = new AttributeRemovalOptions(); 84 public boolean allowParameterName = false; 85 86 public boolean debug = false; 87 public final TestingOptions testing = new TestingOptions(); 88 89 // TODO(zerny): These stateful dictionaries do not belong here. 90 public List<String> classObfuscationDictionary = ImmutableList.of(); 91 public List<String> obfuscationDictionary = ImmutableList.of(); 92 93 public ImmutableList<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of(); 94 public boolean minimalMainDex; 95 public ImmutableList<ProguardConfigurationRule> keepRules = ImmutableList.of(); 96 public ImmutableSet<ProguardTypeMatcher> dontWarnPatterns = ImmutableSet.of(); 97 98 public String warningInvalidParameterAnnotations = null; 99 printWarnings()100 public boolean printWarnings() { 101 boolean printed = false; 102 if (warningInvalidParameterAnnotations != null) { 103 System.out.println("Warning: " + warningInvalidParameterAnnotations); 104 printed = true; 105 } 106 return printed; 107 } 108 hasMethodsFilter()109 public boolean hasMethodsFilter() { 110 return methodsFilter.size() > 0; 111 } 112 methodMatchesFilter(DexEncodedMethod method)113 public boolean methodMatchesFilter(DexEncodedMethod method) { 114 // Not specifying a filter matches all methods. 115 if (!hasMethodsFilter()) { 116 return true; 117 } 118 // Currently the filter is simple string equality on the qualified name. 119 String qualifiedName = method.qualifiedName(); 120 return methodsFilter.indexOf(qualifiedName) >= 0; 121 } 122 methodMatchesLogArgumentsFilter(DexEncodedMethod method)123 public boolean methodMatchesLogArgumentsFilter(DexEncodedMethod method) { 124 // Not specifying a filter matches no methods. 125 if (logArgumentsFilter.size() == 0) { 126 return false; 127 } 128 // Currently the filter is simple string equality on the qualified name. 129 String qualifiedName = method.qualifiedName(); 130 return logArgumentsFilter.indexOf(qualifiedName) >= 0; 131 } 132 133 public static class OutlineOptions { 134 135 public boolean enabled = true; 136 public static final String className = "r8.GeneratedOutlineSupport"; 137 public String methodPrefix = "outline"; 138 public int minSize = 3; 139 public int maxSize = 99; 140 public int threshold = 20; 141 } 142 143 public static class TestingOptions { 144 public Function<List<DexEncodedMethod>, List<DexEncodedMethod>> irOrdering; 145 } 146 147 public static class AttributeRemovalOptions { 148 149 public static final String INNER_CLASSES = "InnerClasses"; 150 public static final String ENCLOSING_METHOD = "EnclosingMethod"; 151 public static final String SIGNATURE = "Signature"; 152 public static final String EXCEPTIONS = "Exceptions"; 153 public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension"; 154 public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; 155 public static final String RUNTIME_INVISBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; 156 public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = 157 "RuntimeVisibleParameterAnnotations"; 158 public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = 159 "RuntimeInvisibleParameterAnnotations"; 160 public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; 161 public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = 162 "RuntimeInvisibleTypeAnnotations"; 163 public static final String ANNOTATION_DEFAULT = "AnnotationDefault"; 164 165 public boolean innerClasses = false; 166 public boolean enclosingMethod = false; 167 public boolean signature = false; 168 public boolean exceptions = false; 169 public boolean sourceDebugExtension = false; 170 public boolean runtimeVisibleAnnotations = false; 171 public boolean runtimeInvisibleAnnotations = false; 172 public boolean runtimeVisibleParameterAnnotations = false; 173 public boolean runtimeInvisibleParamterAnnotations = false; 174 public boolean runtimeVisibleTypeAnnotations = false; 175 public boolean runtimeInvisibleTypeAnnotations = false; 176 public boolean annotationDefault = false; 177 AttributeRemovalOptions()178 private AttributeRemovalOptions() { 179 180 } 181 filterOnlySignatures()182 public static AttributeRemovalOptions filterOnlySignatures() { 183 AttributeRemovalOptions result = new AttributeRemovalOptions(); 184 result.applyPattern("*"); 185 result.signature = false; 186 return result; 187 } 188 189 /** 190 * Implements ProGuards attribute matching rules. 191 * 192 * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>. 193 */ update(boolean previous, String text, String[] patterns)194 private boolean update(boolean previous, String text, String[] patterns) { 195 for (String pattern : patterns) { 196 if (previous) { 197 return true; 198 } 199 if (pattern.charAt(0) == '!') { 200 if (matches(pattern, 1, text, 0)) { 201 break; 202 } 203 } else { 204 previous = matches(pattern, 0, text, 0); 205 } 206 } 207 return previous; 208 } 209 matches(String pattern, int patternPos, String text, int textPos)210 private boolean matches(String pattern, int patternPos, String text, int textPos) { 211 while (patternPos < pattern.length()) { 212 char next = pattern.charAt(patternPos++); 213 if (next == '*') { 214 while (textPos < text.length()) { 215 if (matches(pattern, patternPos, text, textPos++)) { 216 return true; 217 } 218 } 219 return patternPos >= pattern.length(); 220 } else { 221 if (textPos >= text.length() || text.charAt(textPos) != next) { 222 return false; 223 } 224 textPos++; 225 } 226 } 227 return textPos == text.length(); 228 } 229 applyPattern(String pattern)230 public void applyPattern(String pattern) { 231 String[] patterns = pattern.split(","); 232 innerClasses = update(innerClasses, INNER_CLASSES, patterns); 233 enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns); 234 signature = update(signature, SIGNATURE, patterns); 235 exceptions = update(exceptions, EXCEPTIONS, patterns); 236 sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns); 237 runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS, 238 patterns); 239 runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations, 240 RUNTIME_INVISBLE_ANNOTATIONS, patterns); 241 runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations, 242 RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns); 243 runtimeInvisibleParamterAnnotations = update(runtimeInvisibleParamterAnnotations, 244 RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns); 245 runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations, 246 RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns); 247 runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations, 248 RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns); 249 annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns); 250 } 251 ensureValid(boolean isMinifying)252 public void ensureValid(boolean isMinifying) { 253 if (innerClasses && !enclosingMethod) { 254 throw new CompilationError("Attribute InnerClasses requires EnclosingMethod attribute. " 255 + "Check -keepattributes directive."); 256 } else if (!innerClasses && enclosingMethod) { 257 throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. " 258 + "Check -keepattributes directive."); 259 } else if (signature && !innerClasses) { 260 throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check " 261 + "-keepattributes directive."); 262 } else if (signature && isMinifying) { 263 // TODO(38188583): Allow this once we can minify signatures. 264 throw new CompilationError("Attribute Signature cannot be kept when minifying. " 265 + "Check -keepattributes directive."); 266 } 267 } 268 } 269 canUseInvokePolymorphic()270 public boolean canUseInvokePolymorphic() { 271 return minApiLevel >= Constants.ANDROID_O_API; 272 } 273 canUseInvokeCustom()274 public boolean canUseInvokeCustom() { 275 return minApiLevel >= Constants.ANDROID_O_API; 276 } 277 canUseDefaultAndStaticInterfaceMethods()278 public boolean canUseDefaultAndStaticInterfaceMethods() { 279 return minApiLevel >= Constants.ANDROID_N_API; 280 } 281 canUseLongCompareAndObjectsNonNull()282 public boolean canUseLongCompareAndObjectsNonNull() { 283 return minApiLevel >= Constants.ANDROID_K_API; 284 } 285 canUseSuppressedExceptions()286 public boolean canUseSuppressedExceptions() { 287 return minApiLevel >= Constants.ANDROID_K_API; 288 } 289 canUsePrivateInterfaceMethods()290 public boolean canUsePrivateInterfaceMethods() { 291 return minApiLevel >= Constants.ANDROID_N_API; 292 } 293 294 // APIs for accessing parameter names annotations are not available before Android O, thus does 295 // not emit them to avoid wasting space in Dex files because runtimes before Android O will ignore 296 // them. canUseParameterNameAnnotations()297 public boolean canUseParameterNameAnnotations() { 298 return minApiLevel >= Constants.ANDROID_O_API; 299 } 300 } 301