blob: 163c60b156e4c32b23b0d684c7b3baac5163e7a3 [file] [log] [blame]
// 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.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ProguardConfigurationUtils {
private static Origin proguardCompatOrigin =
new Origin(Origin.root()) {
@Override
public String part() {
return "<PROGUARD_COMPATIBILITY_RULE>";
}
};
public static ProguardKeepRule buildMethodHandleKeepRule(DexClass clazz) {
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
builder.setType(ProguardKeepRuleType.KEEP);
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.setClassType(
clazz.isInterface() ? ProguardClassType.INTERFACE : ProguardClassType.CLASS);
builder.setClassNames(
ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
return builder.build();
}
public static ProguardKeepRule buildDefaultInitializerKeepRule(DexClass clazz) {
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
builder.setType(ProguardKeepRuleType.KEEP);
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
builder.setClassType(ProguardClassType.CLASS);
builder.setClassNames(
ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
if (clazz.hasDefaultInitializer()) {
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
memberRuleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
memberRuleBuilder.setArguments(ImmutableList.of());
builder.getMemberRules().add(memberRuleBuilder.build());
}
return builder.build();
}
public static ProguardKeepRule buildFieldKeepRule(
DexClass clazz, DexEncodedField field, boolean keepClass) {
assert clazz.type == field.field.getHolder();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
if (keepClass) {
builder.setType(ProguardKeepRuleType.KEEP);
} else {
builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
}
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
if (clazz.isInterface()) {
builder.setClassType(ProguardClassType.INTERFACE);
} else {
builder.setClassType(ProguardClassType.CLASS);
}
builder.setClassNames(
ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
memberRuleBuilder.getAccessFlags().setFlags(field.accessFlags);
memberRuleBuilder.setName(
IdentifierPatternWithWildcards.withoutWildcards(field.field.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.field.type));
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
}
public static ProguardKeepRule buildMethodKeepRule(DexClass clazz, DexEncodedMethod method) {
// TODO(b/122295241): These generated rules should be linked into the graph, eg, the method
// using identified reflection should be the source keeping the target alive.
assert clazz.type == method.method.getHolder();
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
builder.setOrigin(proguardCompatOrigin);
builder.setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
builder.getModifiersBuilder().setAllowsObfuscation(true);
builder.getModifiersBuilder().setAllowsOptimization(true);
builder.getClassAccessFlags().setVisibility(clazz.accessFlags);
if (clazz.isInterface()) {
builder.setClassType(ProguardClassType.INTERFACE);
} else {
builder.setClassType(ProguardClassType.CLASS);
}
builder.setClassNames(
ProguardClassNameList.singletonList(ProguardTypeMatcher.create(clazz.type)));
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
memberRuleBuilder.getAccessFlags().setFlags(method.accessFlags);
memberRuleBuilder.setName(
IdentifierPatternWithWildcards.withoutWildcards(method.method.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.method.proto.returnType));
List<ProguardTypeMatcher> arguments = Arrays.stream(method.method.proto.parameters.values)
.map(ProguardTypeMatcher::create)
.collect(Collectors.toList());
memberRuleBuilder.setArguments(arguments);
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
}
public static ProguardIdentifierNameStringRule buildIdentifierNameStringRule(DexReference item) {
assert item.isDexField() || item.isDexMethod();
ProguardIdentifierNameStringRule.Builder builder = ProguardIdentifierNameStringRule.builder();
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
DexType holderType;
if (item.isDexField()) {
DexField field = item.asDexField();
holderType = field.getHolder();
memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
memberRuleBuilder.setName(
IdentifierPatternWithWildcards.withoutWildcards(field.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
} else {
DexMethod method = item.asDexMethod();
holderType = method.getHolder();
memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
memberRuleBuilder.setName(
IdentifierPatternWithWildcards.withoutWildcards(method.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(method.proto.returnType));
List<ProguardTypeMatcher> arguments = Arrays.stream(method.proto.parameters.values)
.map(ProguardTypeMatcher::create)
.collect(Collectors.toList());
memberRuleBuilder.setArguments(arguments);
}
if (holderType.isInterface()) {
builder.setClassType(ProguardClassType.INTERFACE);
} else {
builder.setClassType(ProguardClassType.CLASS);
}
builder.setClassNames(
ProguardClassNameList.singletonList(ProguardTypeMatcher.create(holderType)));
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
}
public static ProguardAssumeValuesRule buildAssumeValuesForApiLevel(
DexItemFactory factory, AndroidApiLevel apiLevel) {
Origin synthesizedFromApiLevel =
new Origin(Origin.root()) {
@Override
public String part() {
return "<SYNTHESIZED_FROM_API_LEVEL_" + apiLevel.getLevel() + ">";
}
};
ProguardAccessFlags publicStaticFinalFlags = new ProguardAccessFlags();
publicStaticFinalFlags.setPublic();
publicStaticFinalFlags.setStatic();
publicStaticFinalFlags.setFinal();
return ProguardAssumeValuesRule
.builder()
.setOrigin(synthesizedFromApiLevel)
.setClassType(ProguardClassType.CLASS)
.setClassNames(
ProguardClassNameList.singletonList(
ProguardTypeMatcher.create(factory.createType("Landroid/os/Build$VERSION;"))))
.setMemberRules(ImmutableList.of(
ProguardMemberRule.builder()
.setAccessFlags(publicStaticFinalFlags)
.setRuleType(ProguardMemberType.FIELD)
.setTypeMatcher(ProguardTypeMatcher.create(factory.intType))
.setName(IdentifierPatternWithWildcards.withoutWildcards("SDK_INT"))
.setReturnValue(
new ProguardMemberRuleReturnValue(
new LongInterval(apiLevel.getLevel(), Integer.MAX_VALUE)))
.build()
))
.build();
}
/**
* Check if an explicit rule matching the field
* public static final int android.os.Build$VERSION.SDK_INT
* is present.
*/
public static boolean hasExplicitAssumeValuesRuleForMinSdk(
DexItemFactory factory, List<ProguardConfigurationRule> rules) {
for (ProguardConfigurationRule rule : rules) {
if (!(rule instanceof ProguardAssumeValuesRule)) {
continue;
}
if (rule.getClassType() != ProguardClassType.CLASS) {
continue;
}
if (rule.hasInheritanceClassName()
&& !rule.getInheritanceClassName().matches(factory.objectType)) {
continue;
}
if (!rule.getClassNames().matches(factory.createType("Landroid/os/Build$VERSION;"))) {
continue;
}
for (ProguardMemberRule memberRule : rule.getMemberRules()) {
if (memberRule.getRuleType() == ProguardMemberType.ALL
|| memberRule.getRuleType() == ProguardMemberType.ALL_FIELDS) {
return true;
}
if (memberRule.getRuleType() != ProguardMemberType.FIELD) {
continue;
}
if (memberRule.getAccessFlags().isProtected()
|| memberRule.getAccessFlags().isPrivate()
|| memberRule.getAccessFlags().isAbstract()
|| memberRule.getAccessFlags().isTransient()
|| memberRule.getAccessFlags().isVolatile()) {
continue;
}
if (memberRule.getNegatedAccessFlags().isPublic()
|| memberRule.getNegatedAccessFlags().isStatic()
|| memberRule.getNegatedAccessFlags().isFinal()) {
continue;
}
if (!memberRule.getType().matches(factory.intType)) {
continue;
}
if (!memberRule.getName().matches("SDK_INT")) {
continue;
}
return true;
}
}
return false;
}
}