| // 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.shaking; |
| |
| import com.android.tools.r8.errors.CompilationError; |
| import com.google.common.collect.ImmutableList; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class ProguardKeepAttributes { |
| |
| public static final String SOURCE_FILE = "SourceFile"; |
| public static final String SOURCE_DIR = "SourceDir"; |
| public static final String INNER_CLASSES = "InnerClasses"; |
| public static final String ENCLOSING_METHOD = "EnclosingMethod"; |
| public static final String SIGNATURE = "Signature"; |
| public static final String EXCEPTIONS = "Exceptions"; |
| public static final String SOURCE_DEBUG_EXTENSION = "SourceDebugExtension"; |
| public static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations"; |
| public static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations"; |
| public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = |
| "RuntimeVisibleParameterAnnotations"; |
| public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = |
| "RuntimeInvisibleParameterAnnotations"; |
| public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations"; |
| public static final String RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = |
| "RuntimeInvisibleTypeAnnotations"; |
| public static final String ANNOTATION_DEFAULT = "AnnotationDefault"; |
| |
| public static final List<String> KEEP_ALL = ImmutableList.of("*"); |
| |
| public boolean sourceFile = false; |
| public boolean sourceDir = false; |
| public boolean innerClasses = false; |
| public boolean enclosingMethod = false; |
| public boolean signature = false; |
| public boolean exceptions = false; |
| public boolean sourceDebugExtension = false; |
| public boolean runtimeVisibleAnnotations = false; |
| public boolean runtimeInvisibleAnnotations = false; |
| public boolean runtimeVisibleParameterAnnotations = false; |
| public boolean runtimeInvisibleParameterAnnotations = false; |
| public boolean runtimeVisibleTypeAnnotations = false; |
| public boolean runtimeInvisibleTypeAnnotations = false; |
| public boolean annotationDefault = false; |
| |
| private ProguardKeepAttributes() { |
| } |
| |
| public static ProguardKeepAttributes filterOnlySignatures() { |
| ProguardKeepAttributes result = new ProguardKeepAttributes(); |
| result.applyPatterns(KEEP_ALL); |
| result.signature = false; |
| return result; |
| } |
| |
| /** |
| * Implements ProGuards attribute matching rules. |
| * |
| * @see <a href="https://www.guardsquare.com/en/proguard/manual/attributes">ProGuard manual</a>. |
| */ |
| private boolean update(boolean previous, String text, List<String> patterns) { |
| for (String pattern : patterns) { |
| if (previous) { |
| return true; |
| } |
| if (pattern.charAt(0) == '!') { |
| if (matches(pattern, 1, text, 0)) { |
| break; |
| } |
| } else { |
| previous = matches(pattern, 0, text, 0); |
| } |
| } |
| return previous; |
| } |
| |
| private boolean matches(String pattern, int patternPos, String text, int textPos) { |
| while (patternPos < pattern.length()) { |
| char next = pattern.charAt(patternPos++); |
| if (next == '*') { |
| while (textPos < text.length()) { |
| if (matches(pattern, patternPos, text, textPos++)) { |
| return true; |
| } |
| } |
| return patternPos >= pattern.length(); |
| } else { |
| if (textPos >= text.length() || text.charAt(textPos) != next) { |
| return false; |
| } |
| textPos++; |
| } |
| } |
| return textPos == text.length(); |
| } |
| |
| public static ProguardKeepAttributes fromPatterns(List<String> patterns) { |
| ProguardKeepAttributes keepAttributes = new ProguardKeepAttributes(); |
| keepAttributes.applyPatterns(patterns); |
| return keepAttributes; |
| } |
| |
| public void applyPatterns(List<String> patterns) { |
| sourceFile = update(sourceFile, SOURCE_FILE, patterns); |
| sourceDir = update(sourceDir, SOURCE_DIR, patterns); |
| innerClasses = update(innerClasses, INNER_CLASSES, patterns); |
| enclosingMethod = update(enclosingMethod, ENCLOSING_METHOD, patterns); |
| signature = update(signature, SIGNATURE, patterns); |
| exceptions = update(exceptions, EXCEPTIONS, patterns); |
| sourceDebugExtension = update(sourceDebugExtension, SOURCE_DEBUG_EXTENSION, patterns); |
| runtimeVisibleAnnotations = update(runtimeVisibleAnnotations, RUNTIME_VISIBLE_ANNOTATIONS, |
| patterns); |
| runtimeInvisibleAnnotations = update(runtimeInvisibleAnnotations, |
| RUNTIME_INVISIBLE_ANNOTATIONS, patterns); |
| runtimeVisibleParameterAnnotations = update(runtimeVisibleParameterAnnotations, |
| RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, patterns); |
| runtimeInvisibleParameterAnnotations = update(runtimeInvisibleParameterAnnotations, |
| RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, patterns); |
| runtimeVisibleTypeAnnotations = update(runtimeVisibleTypeAnnotations, |
| RUNTIME_VISIBLE_TYPE_ANNOTATIONS, patterns); |
| runtimeInvisibleTypeAnnotations = update(runtimeInvisibleTypeAnnotations, |
| RUNTIME_INVISIBLE_TYPE_ANNOTATIONS, patterns); |
| annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns); |
| } |
| |
| public void ensureValid( |
| boolean forceProguardCompatibility, ProguardConfiguration.Builder compatibility) { |
| if (forceProguardCompatibility && innerClasses != enclosingMethod) { |
| // If only one is true set both to true in Proguard compatibility mode. |
| enclosingMethod = true; |
| innerClasses = true; |
| compatibility.addKeepAttributePatterns( |
| ImmutableList.of( |
| ProguardKeepAttributes.INNER_CLASSES, |
| ProguardKeepAttributes.ENCLOSING_METHOD)); |
| } |
| if (innerClasses && !enclosingMethod) { |
| throw new CompilationError("Attribute InnerClasses requires EnclosingMethod attribute. " |
| + "Check -keepattributes directive."); |
| } else if (!innerClasses && enclosingMethod) { |
| throw new CompilationError("Attribute EnclosingMethod requires InnerClasses attribute. " |
| + "Check -keepattributes directive."); |
| } else if (signature && !innerClasses) { |
| throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check " |
| + "-keepattributes directive."); |
| } |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof ProguardKeepAttributes)) { |
| return false; |
| } |
| ProguardKeepAttributes other = (ProguardKeepAttributes) o; |
| return this.sourceFile == other.sourceFile |
| && this.sourceDir == other.sourceDir |
| && this.innerClasses == other.innerClasses |
| && this.enclosingMethod == other.enclosingMethod |
| && this.signature == other.signature |
| && this.exceptions == other.exceptions |
| && this.sourceDebugExtension == other.sourceDebugExtension |
| && this.runtimeVisibleAnnotations == other.runtimeVisibleAnnotations |
| && this.runtimeInvisibleAnnotations == other.runtimeInvisibleAnnotations |
| && this.runtimeVisibleParameterAnnotations == other.runtimeVisibleParameterAnnotations |
| && this.runtimeInvisibleParameterAnnotations == other.runtimeInvisibleParameterAnnotations |
| && this.runtimeVisibleTypeAnnotations == other.runtimeVisibleTypeAnnotations |
| && this.runtimeInvisibleTypeAnnotations == other.runtimeInvisibleTypeAnnotations |
| && this.annotationDefault == other.annotationDefault; |
| } |
| |
| @Override |
| public int hashCode() { |
| return (this.sourceFile ? 1 : 0) |
| + (this.sourceDir ? 1 << 1 : 0) |
| + (this.innerClasses ? 1 << 2 : 0) |
| + (this.enclosingMethod ? 1 << 3 : 0) |
| + (this.signature ? 1 << 4 : 0) |
| + (this.exceptions ? 1 << 5 : 0) |
| + (this.sourceDebugExtension ? 1 << 6 : 0) |
| + (this.runtimeVisibleAnnotations ? 1 << 7 : 0) |
| + (this.runtimeInvisibleAnnotations ? 1 << 8 : 0) |
| + (this.runtimeVisibleParameterAnnotations ? 1 << 9 : 0) |
| + (this.runtimeInvisibleParameterAnnotations ? 1 << 10 : 0) |
| + (this.runtimeVisibleTypeAnnotations ? 1 << 11 : 0) |
| + (this.runtimeInvisibleTypeAnnotations ? 1 << 12 : 0) |
| + (this.annotationDefault ? 1 << 13 : 0); |
| } |
| |
| public boolean isEmpty() { |
| return !sourceFile |
| && !sourceDir |
| && !innerClasses |
| && !enclosingMethod |
| && !signature |
| && !exceptions |
| && !sourceDebugExtension |
| && !runtimeVisibleAnnotations |
| && !runtimeInvisibleAnnotations |
| && !runtimeVisibleParameterAnnotations |
| && !runtimeInvisibleParameterAnnotations |
| && !runtimeVisibleTypeAnnotations |
| && !runtimeInvisibleTypeAnnotations |
| && !annotationDefault; |
| } |
| |
| public StringBuilder append(StringBuilder builder) { |
| List<String> attributes = new ArrayList<>(); |
| if (sourceFile) { |
| attributes.add(SOURCE_FILE); |
| } |
| if (sourceDir) { |
| attributes.add(SOURCE_DIR); |
| } |
| if (innerClasses) { |
| attributes.add(INNER_CLASSES); |
| } |
| if (enclosingMethod) { |
| attributes.add(ENCLOSING_METHOD); |
| } |
| if (signature) { |
| attributes.add(SIGNATURE); |
| } |
| if (exceptions) { |
| attributes.add(EXCEPTIONS); |
| } |
| if (sourceDebugExtension) { |
| attributes.add(SOURCE_DEBUG_EXTENSION); |
| } |
| if (runtimeVisibleAnnotations) { |
| attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS); |
| } |
| if (runtimeInvisibleAnnotations) { |
| attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS); |
| } |
| if (runtimeVisibleParameterAnnotations) { |
| attributes.add(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS); |
| } |
| if (runtimeInvisibleParameterAnnotations) { |
| attributes.add(RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); |
| } |
| if (runtimeVisibleTypeAnnotations) { |
| attributes.add(RUNTIME_VISIBLE_TYPE_ANNOTATIONS); |
| } |
| if (runtimeInvisibleTypeAnnotations) { |
| attributes.add(RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); |
| } |
| if (annotationDefault) { |
| attributes.add(ANNOTATION_DEFAULT); |
| } |
| |
| if (attributes.size() > 0) { |
| builder.append("-keepattributes "); |
| builder.append(String.join(",", attributes)); |
| } |
| |
| return builder; |
| } |
| |
| @Override |
| public String toString() { |
| return append(new StringBuilder()).toString(); |
| } |
| } |