com.android.build.gradle.shrinker.ProguardFlagsKeepRules Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-core Show documentation
Show all versions of gradle-core Show documentation
Core library to build Android Gradle plugin.
The newest version!
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.shrinker;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.gradle.shrinker.parser.MethodSpecification;
import com.android.build.gradle.shrinker.parser.AnnotationSpecification;
import com.android.build.gradle.shrinker.parser.ClassSpecification;
import com.android.build.gradle.shrinker.parser.FieldSpecification;
import com.android.build.gradle.shrinker.parser.Flags;
import com.android.build.gradle.shrinker.parser.InheritanceSpecification;
import com.android.build.gradle.shrinker.parser.Matcher;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Implementation of {@link KeepRules} that uses {@link Flags} obtained from parsing a ProGuard
* config file.
*/
public class ProguardFlagsKeepRules implements KeepRules {
private final Flags mFlags;
private final ShrinkerLogger mShrinkerLogger;
public ProguardFlagsKeepRules(Flags flags, ShrinkerLogger shrinkerLogger) {
mFlags = flags;
mShrinkerLogger = shrinkerLogger;
}
@Override
public Map getSymbolsToKeep(T klass, ShrinkerGraph graph) {
Map result = Maps.newHashMap();
for (ClassSpecification spec : mFlags.getKeepClassSpecs()) {
if (matchesClass(klass, spec, graph)) {
result.put(klass, DependencyType.REQUIRED_CLASS_STRUCTURE);
result.put(
graph.getMemberReference(graph.getClassName(klass), "", "()V"),
DependencyType.REQUIRED_CLASS_STRUCTURE);
for (T member : findMatchingMembers(klass, spec, graph)) {
result.put(member, DependencyType.REQUIRED_CLASS_STRUCTURE);
}
}
}
for (ClassSpecification spec : mFlags.getKeepClassMembersSpecs()) {
if (matchesClass(klass, spec, graph)) {
for (T member : findMatchingMembers(klass, spec, graph)) {
result.put(member, DependencyType.IF_CLASS_KEPT);
graph.addDependency(klass, member, DependencyType.CLASS_IS_KEPT);
}
}
}
for (ClassSpecification spec : mFlags.getKeepClassesWithMembersSpecs()) {
if (matchesClass(klass, spec, graph)) {
for (T t : handleKeepClassesWithMembers(spec, klass, graph)) {
result.put(t, DependencyType.REQUIRED_CLASS_STRUCTURE);
}
}
}
return result;
}
private static List handleKeepClassesWithMembers(
ClassSpecification classSpec,
T klass,
ShrinkerGraph graph) {
List result = Lists.newArrayList();
for (MethodSpecification methodSpec : classSpec.getMethodSpecifications()) {
boolean found = false;
for (T method : graph.getMethods(klass)) {
if (matchesMethod(method, methodSpec, graph)) {
found = true;
result.add(method);
}
}
if (!found) {
return Collections.emptyList();
}
}
for (FieldSpecification fieldSpec : classSpec.getFieldSpecifications()) {
boolean found = false;
for (T field : graph.getFields(klass)) {
if (matchesField(field, fieldSpec, graph)) {
found = true;
result.add(field);
}
}
if (!found) {
return Collections.emptyList();
}
}
// If we're here, then all member specs have matched something.
result.add(klass);
return result;
}
private static List findMatchingMembers(
T klass,
ClassSpecification spec,
ShrinkerGraph graph) {
List result = Lists.newArrayList();
for (T method : graph.getMethods(klass)) {
for (MethodSpecification methodSpec : spec.getMethodSpecifications()) {
if (matchesMethod(method, methodSpec, graph)) {
result.add(method);
}
}
}
for (T field : graph.getFields(klass)) {
for (FieldSpecification fieldSpecification : spec.getFieldSpecifications()) {
if (matchesField(field, fieldSpecification, graph)) {
result.add(field);
}
}
}
return result;
}
private static boolean matchesField(
T field,
FieldSpecification spec,
ShrinkerGraph graph) {
return matches(spec.getName(), graph.getMemberName(field))
&& matches(spec.getModifier(), graph.getModifiers(field))
&& matches(spec.getTypeSignature(), graph.getMemberDescriptor(field))
&& matchesAnnotations(field, spec.getAnnotations(), graph);
}
private static boolean matchesMethod(
T method,
MethodSpecification spec,
ShrinkerGraph graph) {
String nameAndDescriptor =
graph.getMemberName(method) + ":" + graph.getMemberDescriptor(method);
return matches(spec.getName(), nameAndDescriptor)
&& matches(spec.getModifiers(), graph.getModifiers(method))
&& matchesAnnotations(method, spec.getAnnotations(), graph);
}
private boolean matchesClass(
T klass,
ClassSpecification spec,
ShrinkerGraph graph) {
int classModifiers = graph.getModifiers(klass);
return matches(spec.getName(), graph.getClassName(klass))
&& matches(spec.getClassType(), classModifiers)
&& matches(spec.getModifier(), classModifiers)
&& matchesAnnotations(klass, spec.getAnnotation(), graph)
&& matchesInheritance(klass, spec.getInheritance(), graph);
}
private static boolean matches(@Nullable Matcher matcher, @NonNull U value) {
return matcher == null || matcher.matches(value);
}
private static boolean matchesAnnotations(
@NonNull T classOrMember,
@Nullable AnnotationSpecification annotation,
@NonNull ShrinkerGraph graph) {
if (annotation == null) {
return true;
}
for (String annotationName : graph.getAnnotations(classOrMember)) {
if (annotation.getName().matches(annotationName)) {
return true;
}
}
return false;
}
private boolean matchesInheritance(
@NonNull T klass,
@Nullable InheritanceSpecification spec,
@NonNull ShrinkerGraph graph) {
if (spec == null) {
return true;
}
FluentIterable superTypes =
TypeHierarchyTraverser.superclassesAndInterfaces(graph, mShrinkerLogger)
.preOrderTraversal(klass)
.skip(1); // Skip the class itself.
for (T superType : superTypes) {
String name = graph.getClassName(superType);
if (spec.getNameSpec().matches(name)) {
return true;
}
}
return false;
}
}