|  | // 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(); | 
|  | } | 
|  | } |