Merge "Avoid <= in version checks as it is then easy to miss MR releases."
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 4025180..ee48da2 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.3.9-dev";
+ public static final String LABEL = "1.3.10-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLense.java b/src/main/java/com/android/tools/r8/graph/GraphLense.java
index 8de4a6e..84ceac3 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLense.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLense.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -78,7 +79,8 @@
if (typeMap.isEmpty() && methodMap.isEmpty() && fieldMap.isEmpty()) {
return previousLense;
}
- return new NestedGraphLense(typeMap, methodMap, fieldMap, previousLense, dexItemFactory);
+ return new NestedGraphLense(
+ typeMap, methodMap, fieldMap, null, null, previousLense, dexItemFactory);
}
}
@@ -87,6 +89,10 @@
return new Builder();
}
+ public abstract DexField getOriginalFieldSignature(DexField field);
+
+ public abstract DexMethod getOriginalMethodSignature(DexMethod method);
+
public abstract DexType lookupType(DexType type);
// This overload can be used when the graph lense is known to be context insensitive.
@@ -156,6 +162,16 @@
private static class IdentityGraphLense extends GraphLense {
@Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return field;
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ return method;
+ }
+
+ @Override
public DexType lookupType(DexType type) {
return type;
}
@@ -197,16 +213,47 @@
protected final Map<DexMethod, DexMethod> methodMap;
protected final Map<DexField, DexField> fieldMap;
- public NestedGraphLense(Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap,
- Map<DexField, DexField> fieldMap, GraphLense previousLense, DexItemFactory dexItemFactory) {
+ // Maps that store the original signature of fields and methods that have been affected by
+ // vertical class merging. Needed to generate a correct Proguard map in the end.
+ private final BiMap<DexField, DexField> originalFieldSignatures;
+ private final BiMap<DexMethod, DexMethod> originalMethodSignatures;
+
+ public NestedGraphLense(
+ Map<DexType, DexType> typeMap,
+ Map<DexMethod, DexMethod> methodMap,
+ Map<DexField, DexField> fieldMap,
+ BiMap<DexField, DexField> originalFieldSignatures,
+ BiMap<DexMethod, DexMethod> originalMethodSignatures,
+ GraphLense previousLense,
+ DexItemFactory dexItemFactory) {
this.typeMap = typeMap.isEmpty() ? null : typeMap;
this.methodMap = methodMap;
this.fieldMap = fieldMap;
+ this.originalFieldSignatures = originalFieldSignatures;
+ this.originalMethodSignatures = originalMethodSignatures;
this.previousLense = previousLense;
this.dexItemFactory = dexItemFactory;
}
@Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ DexField originalField =
+ originalFieldSignatures != null
+ ? originalFieldSignatures.getOrDefault(field, field)
+ : field;
+ return previousLense.getOriginalFieldSignature(originalField);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ DexMethod originalMethod =
+ originalMethodSignatures != null
+ ? originalMethodSignatures.getOrDefault(method, method)
+ : method;
+ return previousLense.getOriginalMethodSignature(originalMethod);
+ }
+
+ @Override
public DexType lookupType(DexType type) {
if (type.isArrayType()) {
synchronized (this) {
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 5b908af..c5d366a 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -25,7 +25,10 @@
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -150,7 +153,12 @@
}
public void write(Writer writer) throws IOException {
- for (ClassNamingForNameMapper naming : classNameMappings.values()) {
+ // Sort classes by their original name such that the generated Proguard map is deterministic
+ // (and easy to navigate manually).
+ List<ClassNamingForNameMapper> classNamingForNameMappers =
+ new ArrayList<>(classNameMappings.values());
+ classNamingForNameMappers.sort(Comparator.comparing(x -> x.originalName));
+ for (ClassNamingForNameMapper naming : classNamingForNameMappers) {
naming.write(writer);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
index 492bcd0..6ba18ac 100644
--- a/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/BridgeMethodAnalysis.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.optimize;
+import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -88,6 +89,17 @@
}
@Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ return previousLense.getOriginalFieldSignature(field);
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ // TODO(b/79143143): implement this when re-enable bridge analysis.
+ throw new Unimplemented("BridgeLense.getOriginalMethodSignature() not implemented");
+ }
+
+ @Override
public DexType lookupType(DexType type) {
return previousLense.lookupType(type);
}
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
index 4ee0cee..98a0da4 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingLense.java
@@ -38,7 +38,8 @@
Map<DexMethod, DexMethod> methodMap,
Map<DexField, DexField> fieldMap,
GraphLense previousLense) {
- super(ImmutableMap.of(), methodMap, fieldMap, previousLense, appInfo.dexItemFactory);
+ super(
+ ImmutableMap.of(), methodMap, fieldMap, null, null, previousLense, appInfo.dexItemFactory);
this.appInfo = appInfo;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
index 7a9c08f..ad7effd 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLense.java
@@ -25,6 +25,8 @@
ImmutableMap.of(),
ImmutableMap.of(),
ImmutableMap.of(),
+ null,
+ null,
appView.getGraphLense(),
appView.getAppInfo().dexItemFactory);
this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 565856c..f263bd6 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class InlineRule extends ProguardConfigurationRule {
@@ -12,20 +14,27 @@
ALWAYS, FORCE, NEVER
}
- public static class Builder extends ProguardConfigurationRule.Builder {
+ public static class Builder extends ProguardConfigurationRule.Builder<InlineRule, Builder> {
private Builder() {
+ super();
}
Type type;
+ @Override
+ public Builder self() {
+ return this;
+ }
+
public Builder setType(Type type) {
this.type = type;
return this;
}
+ @Override
public InlineRule build() {
- return new InlineRule(classAnnotation, classAccessFlags,
+ return new InlineRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
inheritanceClassName, inheritanceIsExtends, memberRules, type);
}
@@ -34,6 +43,9 @@
private final Type type;
private InlineRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -45,13 +57,14 @@
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules,
Type type) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
this.type = type;
}
- public static InlineRule.Builder builder() {
- return new InlineRule.Builder();
+ public static Builder builder() {
+ return new Builder();
}
public Type getType() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
index 39d0335..4c1f9ef 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -3,22 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardClassSpecification.Builder {
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ProguardAssumeNoSideEffectRule, Builder> {
- private Builder() {}
+ private Builder() {
+ super();
+ }
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardAssumeNoSideEffectRule build() {
- return new ProguardAssumeNoSideEffectRule(classAnnotation, classAccessFlags,
- negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
- inheritanceClassName, inheritanceIsExtends, memberRules);
+ return new ProguardAssumeNoSideEffectRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardAssumeNoSideEffectRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -29,8 +43,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
/**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
index 0dafb11..8b8f4dd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -3,21 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardClassSpecification.Builder {
- private Builder() {}
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ProguardAssumeValuesRule, Builder> {
+ private Builder() {
+ super();
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardAssumeValuesRule build() {
- return new ProguardAssumeValuesRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
- classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
- inheritanceIsExtends, memberRules);
+ return new ProguardAssumeValuesRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardAssumeValuesRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -28,8 +43,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
/**
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
index 2461732..664cb25 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -3,23 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardConfigurationRule.Builder {
+ public static class Builder extends
+ ProguardConfigurationRule.Builder<ProguardCheckDiscardRule, Builder> {
private Builder() {
+ super();
}
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardCheckDiscardRule build() {
- return new ProguardCheckDiscardRule(classAnnotation, classAccessFlags,
- negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
- inheritanceClassName, inheritanceIsExtends, memberRules);
+ return new ProguardCheckDiscardRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardCheckDiscardRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -30,8 +43,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
index 8de361e..5d610a8 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -3,6 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.position.TextPosition;
+import com.android.tools.r8.position.TextRange;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.util.LinkedList;
@@ -11,8 +15,13 @@
public abstract class ProguardClassSpecification {
- public static class Builder {
+ public abstract static class
+ Builder<C extends ProguardClassSpecification, B extends Builder<C, B>> {
+ protected Origin origin;
+ protected Position start;
+ protected Position end;
+ protected String source;
protected ProguardTypeMatcher classAnnotation;
protected ProguardAccessFlags classAccessFlags = new ProguardAccessFlags();
protected ProguardAccessFlags negatedClassAccessFlags = new ProguardAccessFlags();
@@ -25,6 +34,46 @@
protected List<ProguardMemberRule> memberRules = new LinkedList<>();
protected Builder() {
+ this(Origin.unknown(), Position.UNKNOWN);
+ }
+
+ protected Builder(Origin origin, Position start) {
+ this.origin = origin;
+ this.start = start;
+ }
+
+ public abstract C build();
+
+ public abstract B self();
+
+ public B setOrigin(Origin origin) {
+ this.origin = origin;
+ return self();
+ }
+
+ public B setStart(Position start) {
+ this.start = start;
+ return self();
+ }
+
+ public B setEnd(Position end) {
+ this.end = end;
+ return self();
+ }
+
+ public B setSource(String source) {
+ this.source = source;
+ return self();
+ }
+
+ public Position getPosition() {
+ if (start == null) {
+ return Position.UNKNOWN;
+ }
+ if (end == null || !((start instanceof TextPosition) && (end instanceof TextPosition))) {
+ return start;
+ }
+ return new TextRange((TextPosition) start, (TextPosition) end);
}
public List<ProguardMemberRule> getMemberRules() {
@@ -117,6 +166,9 @@
}
}
+ private final Origin origin;
+ private final Position position;
+ private final String source;
private final ProguardTypeMatcher classAnnotation;
private final ProguardAccessFlags classAccessFlags;
private final ProguardAccessFlags negatedClassAccessFlags;
@@ -129,6 +181,9 @@
private final List<ProguardMemberRule> memberRules;
protected ProguardClassSpecification(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -139,6 +194,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
+ this.origin = origin;
+ this.position = position;
+ this.source =source;
this.classAnnotation = classAnnotation;
this.classAccessFlags = classAccessFlags;
this.negatedClassAccessFlags = negatedClassAccessFlags;
@@ -152,6 +210,18 @@
this.memberRules = memberRules;
}
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ public Position getPosition() {
+ return position;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
public List<ProguardMemberRule> getMemberRules() {
return memberRules;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 5b22456..e070db9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -228,7 +228,7 @@
} else if (acceptString("keepparameternames")) {
configurationBuilder.setKeepParameterNames(true, origin, getPosition(optionStart));
} else if (acceptString("checkdiscard")) {
- ProguardCheckDiscardRule rule = parseCheckDiscardRule();
+ ProguardCheckDiscardRule rule = parseCheckDiscardRule(optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("keepdirectories")) {
// TODO(74279367): Report an error until it's fully supported.
@@ -237,10 +237,10 @@
}
parsePathFilter(configurationBuilder::addKeepDirectories);
} else if (acceptString("keep")) {
- ProguardKeepRule rule = parseKeepRule();
+ ProguardKeepRule rule = parseKeepRule(optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("whyareyoukeeping")) {
- ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule();
+ ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule(optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("dontoptimize")) {
configurationBuilder.disableOptimization();
@@ -316,10 +316,10 @@
} else if (acceptString("applymapping")) {
configurationBuilder.setApplyMappingFile(parseFileName());
} else if (acceptString("assumenosideeffects")) {
- ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule();
+ ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule(optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("assumevalues")) {
- ProguardAssumeValuesRule rule = parseAssumeValuesRule();
+ ProguardAssumeValuesRule rule = parseAssumeValuesRule(optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("include")) {
// Collect the parsed configuration until the include.
@@ -348,16 +348,16 @@
} else if (acceptString("packageobfuscationdictionary")) {
configurationBuilder.setPackageObfuscationDictionary(parseFileName());
} else if (acceptString("alwaysinline")) {
- InlineRule rule = parseInlineRule(Type.ALWAYS);
+ InlineRule rule = parseInlineRule(Type.ALWAYS, optionStart);
configurationBuilder.addRule(rule);
} else if (allowTestOptions && acceptString("forceinline")) {
- InlineRule rule = parseInlineRule(Type.FORCE);
+ InlineRule rule = parseInlineRule(Type.FORCE, optionStart);
configurationBuilder.addRule(rule);
// Insert a matching -checkdiscard rule to ensure force inlining happens.
ProguardCheckDiscardRule ruled = rule.asProguardCheckDiscardRule();
configurationBuilder.addRule(ruled);
} else if (allowTestOptions && acceptString("neverinline")) {
- InlineRule rule = parseInlineRule(Type.NEVER);
+ InlineRule rule = parseInlineRule(Type.NEVER, optionStart);
configurationBuilder.addRule(rule);
} else if (acceptString("useuniqueclassmembernames")) {
configurationBuilder.setUseUniqueClassMemberNames(true);
@@ -376,7 +376,7 @@
}
parsePathFilter(configurationBuilder::addAdaptResourceFilecontents);
} else if (acceptString("identifiernamestring")) {
- configurationBuilder.addRule(parseIdentifierNameStringRule());
+ configurationBuilder.addRule(parseIdentifierNameStringRule(optionStart));
} else if (acceptString("if")) {
configurationBuilder.addRule(parseIfRule(optionStart));
} else {
@@ -545,9 +545,11 @@
}
}
- private ProguardKeepRule parseKeepRule()
+ private ProguardKeepRule parseKeepRule(Position start)
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
+ ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseRuleTypeAndModifiers(keepRuleBuilder);
parseClassSpec(keepRuleBuilder, false);
if (keepRuleBuilder.getMemberRules().isEmpty()) {
@@ -560,13 +562,21 @@
defaultRuleBuilder.setArguments(Collections.emptyList());
keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
}
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
return keepRuleBuilder.build();
}
- private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule()
+ private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule(Position start)
throws ProguardRuleParserException {
- ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder();
+ ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
return keepRuleBuilder.build();
}
@@ -577,37 +587,56 @@
return keepRuleBuilder.build();
}
- private ProguardCheckDiscardRule parseCheckDiscardRule()
+ private ProguardCheckDiscardRule parseCheckDiscardRule(Position start)
throws ProguardRuleParserException {
- ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder();
+ ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
return keepRuleBuilder.build();
}
- private InlineRule parseInlineRule(InlineRule.Type type)
+ private InlineRule parseInlineRule(InlineRule.Type type, Position start)
throws ProguardRuleParserException {
- InlineRule.Builder keepRuleBuilder = InlineRule.builder().setType(type);
+ InlineRule.Builder keepRuleBuilder = InlineRule.builder()
+ .setOrigin(origin)
+ .setStart(start)
+ .setType(type);
parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
return keepRuleBuilder.build();
}
- private ProguardIdentifierNameStringRule parseIdentifierNameStringRule()
+ private ProguardIdentifierNameStringRule parseIdentifierNameStringRule(Position start)
throws ProguardRuleParserException {
ProguardIdentifierNameStringRule.Builder keepRuleBuilder =
- ProguardIdentifierNameStringRule.builder();
+ ProguardIdentifierNameStringRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseClassSpec(keepRuleBuilder, false);
+ Position end = getPosition();
+ keepRuleBuilder.setSource(getSourceSnippet(contents, start, end));
+ keepRuleBuilder.setEnd(end);
return keepRuleBuilder.build();
}
private ProguardIfRule parseIfRule(TextPosition optionStart)
throws ProguardRuleParserException {
- ProguardIfRule.Builder ifRuleBuilder = ProguardIfRule.builder();
+ ProguardIfRule.Builder ifRuleBuilder = ProguardIfRule.builder()
+ .setOrigin(origin)
+ .setStart(optionStart);
parseClassSpec(ifRuleBuilder, false);
// Required a subsequent keep rule.
skipWhitespace();
+ Position keepStart = getPosition();
if (acceptString("-keep")) {
- ProguardKeepRule subsequentRule = parseKeepRule();
+ ProguardKeepRule subsequentRule = parseKeepRule(keepStart);
ifRuleBuilder.setSubsequentRule(subsequentRule);
ProguardIfRule ifRule = ifRuleBuilder.build();
verifyAndLinkBackReferences(ifRule.getWildcards());
@@ -636,8 +665,11 @@
}
}
- private void parseClassSpec(
- ProguardConfigurationRule.Builder builder, boolean allowValueSpecification)
+ private
+ <C extends ProguardClassSpecification, B extends ProguardClassSpecification.Builder<C, B>>
+ void parseClassSpec(
+ ProguardClassSpecification.Builder<C, B> builder,
+ boolean allowValueSpecification)
throws ProguardRuleParserException {
parseClassFlagsAndAnnotations(builder);
parseClassType(builder);
@@ -646,8 +678,7 @@
parseMemberRules(builder, allowValueSpecification);
}
- private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder)
- throws ProguardRuleParserException {
+ private void parseRuleTypeAndModifiers(ProguardKeepRule.Builder builder) {
if (acceptString("names")) {
builder.setType(ProguardKeepRuleType.KEEP);
builder.getModifiersBuilder().setAllowsShrinking(true);
@@ -800,12 +831,15 @@
ClassOrType.CLASS, dexItemFactory));
}
- private void parseMemberRules(ProguardClassSpecification.Builder classSpecificationBuilder,
+ private
+ <C extends ProguardClassSpecification, B extends ProguardClassSpecification.Builder<C, B>>
+ void parseMemberRules(
+ ProguardClassSpecification.Builder<C, B> classSpecificationBuilder,
boolean allowValueSpecification)
throws ProguardRuleParserException {
skipWhitespace();
if (!eof() && acceptChar('{')) {
- ProguardMemberRule rule = null;
+ ProguardMemberRule rule;
while ((rule = parseMemberRule(allowValueSpecification)) != null) {
classSpecificationBuilder.getMemberRules().add(rule);
}
@@ -1086,16 +1120,27 @@
return fileFilter;
}
- private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule()
+ private ProguardAssumeNoSideEffectRule parseAssumeNoSideEffectsRule(Position start)
throws ProguardRuleParserException {
- ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder();
+ ProguardAssumeNoSideEffectRule.Builder builder = ProguardAssumeNoSideEffectRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseClassSpec(builder, true);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
return builder.build();
}
- private ProguardAssumeValuesRule parseAssumeValuesRule() throws ProguardRuleParserException {
- ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder();
+ private ProguardAssumeValuesRule parseAssumeValuesRule(Position start)
+ throws ProguardRuleParserException {
+ ProguardAssumeValuesRule.Builder builder = ProguardAssumeValuesRule.builder()
+ .setOrigin(origin)
+ .setStart(start);
parseClassSpec(builder, true);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
return builder.build();
}
@@ -1549,7 +1594,24 @@
private int getColumn() {
return position - lineStartPosition + 1 /* column starts at 1 */;
}
- }
+
+ private String getSourceSnippet(String source, Position start, Position end) {
+ return start instanceof TextPosition && end instanceof TextPosition
+ ? getTextSourceSnippet(source, (TextPosition) start, (TextPosition) end)
+ : null;
+ }
+ }
+
+ private String getTextSourceSnippet(String source, TextPosition start, TextPosition end) {
+ long length = end.getOffset() - start.getOffset();
+ if (start.getOffset() < 0 || end.getOffset() < 0
+ || start.getOffset() >= source.length() || end.getOffset() > source.length()
+ || length <= 0) {
+ return null;
+ } else {
+ return source.substring((int) start.getOffset(), (int) end.getOffset());
+ }
+ }
static class IdentifierPatternWithWildcards {
final String pattern;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
index e4c5691..f7282ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Iterables;
import java.util.Collections;
@@ -11,6 +13,9 @@
public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
ProguardConfigurationRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -21,8 +26,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
abstract String typeString();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
index 07ffd85..8ff7670 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -3,22 +3,35 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardIdentifierNameStringRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardConfigurationRule.Builder {
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ProguardIdentifierNameStringRule, Builder> {
private Builder() {
+ super();
}
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardIdentifierNameStringRule build() {
- return new ProguardIdentifierNameStringRule(classAnnotation, classAccessFlags,
- negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
- inheritanceClassName, inheritanceIsExtends, memberRules);
+ return new ProguardIdentifierNameStringRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardIdentifierNameStringRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -29,8 +42,9 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
public static Builder builder() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index 47f26ed..54a3f2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,18 +3,29 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import com.google.common.collect.Iterables;
import java.util.List;
import java.util.stream.Collectors;
-public class ProguardIfRule extends ProguardKeepRule {
+public class ProguardIfRule extends ProguardKeepRuleBase {
final ProguardKeepRule subsequentRule;
- public static class Builder extends ProguardKeepRule.Builder {
+ public static class Builder extends ProguardKeepRuleBase.Builder<ProguardIfRule, Builder> {
ProguardKeepRule subsequentRule = null;
+ protected Builder() {
+ super();
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+
public void setSubsequentRule(ProguardKeepRule rule) {
subsequentRule = rule;
}
@@ -22,13 +33,17 @@
@Override
public ProguardIfRule build() {
assert subsequentRule != null : "Option -if without a subsequent rule.";
- return new ProguardIfRule(classAnnotation, classAccessFlags,
+ return new ProguardIfRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
inheritanceClassName, inheritanceIsExtends, memberRules, subsequentRule);
}
}
- private ProguardIfRule(ProguardTypeMatcher classAnnotation,
+ private ProguardIfRule(
+ Origin origin,
+ Position position,
+ String source,
+ ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags, boolean classTypeNegated,
ProguardClassType classType, ProguardClassNameList classNames,
@@ -36,8 +51,9 @@
ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules,
ProguardKeepRule subsequentRule) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules,
ProguardKeepRuleType.CONDITIONAL, ProguardKeepRuleModifiers.builder().build());
this.subsequentRule = subsequentRule;
}
@@ -51,9 +67,11 @@
return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
}
- @Override
protected ProguardIfRule materialize() {
return new ProguardIfRule(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ null,
getClassAnnotation(),
getClassAccessFlags(),
getNegatedClassAccessFlags(),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
index 66fdd50..767ecf7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -3,23 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardConfigurationRule.Builder {
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ProguardKeepPackageNamesRule, Builder> {
private Builder() {
+ super();
}
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardKeepPackageNamesRule build() {
- return new ProguardKeepPackageNamesRule(classAnnotation, classAccessFlags,
- negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
- inheritanceClassName, inheritanceIsExtends, memberRules);
+ return new ProguardKeepPackageNamesRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardKeepPackageNamesRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -30,12 +43,13 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
- public static ProguardKeepPackageNamesRule.Builder builder() {
- return new ProguardKeepPackageNamesRule.Builder();
+ public static Builder builder() {
+ return new Builder();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
index 6651dae..3c1fb2d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -3,39 +3,37 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-public class ProguardKeepRule extends ProguardConfigurationRule {
+public class ProguardKeepRule extends ProguardKeepRuleBase {
- public static class Builder extends ProguardClassSpecification.Builder {
+ public static class Builder extends ProguardKeepRuleBase.Builder<ProguardKeepRule, Builder> {
- private ProguardKeepRuleType type;
- private final ProguardKeepRuleModifiers.Builder modifiersBuilder =
- ProguardKeepRuleModifiers.builder();
-
- protected Builder() {}
-
- public void setType(ProguardKeepRuleType type) {
- this.type = type;
+ protected Builder() {
+ super();
}
- public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
- return modifiersBuilder;
+ @Override
+ public Builder self() {
+ return this;
}
+ @Override
public ProguardKeepRule build() {
- return new ProguardKeepRule(classAnnotation, classAccessFlags, negatedClassAccessFlags,
- classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
- inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
+ return new ProguardKeepRule(origin, getPosition(), source, classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules, type, modifiersBuilder.build());
}
}
- private final ProguardKeepRuleType type;
- private final ProguardKeepRuleModifiers modifiers;
-
protected ProguardKeepRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -48,10 +46,9 @@
List<ProguardMemberRule> memberRules,
ProguardKeepRuleType type,
ProguardKeepRuleModifiers modifiers) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
- this.type = type;
- this.modifiers = modifiers;
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules, type, modifiers);
}
/**
@@ -61,16 +58,11 @@
return new Builder();
}
- public ProguardKeepRuleType getType() {
- return type;
- }
-
- public ProguardKeepRuleModifiers getModifiers() {
- return modifiers;
- }
-
protected ProguardKeepRule materialize() {
return new ProguardKeepRule(
+ Origin.unknown(),
+ Position.UNKNOWN,
+ null,
getClassAnnotation(),
getClassAccessFlags(),
getNegatedClassAccessFlags(),
@@ -93,25 +85,9 @@
return false;
}
ProguardKeepRule that = (ProguardKeepRule) o;
-
- if (type != that.type) {
- return false;
- }
- if (!modifiers.equals(that.modifiers)) {
- return false;
- }
return super.equals(that);
}
- @Override
- public int hashCode() {
- // Used multiplier 3 to avoid too much overflow when computing hashCode.
- int result = type.hashCode();
- result = 3 * result + modifiers.hashCode();
- result = 3 * result + super.hashCode();
- return result;
- }
-
static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
if (item == null) {
return;
@@ -128,16 +104,6 @@
}
}
- @Override
- String typeString() {
- return type.toString();
- }
-
- @Override
- String modifierString() {
- return modifiers.toString();
- }
-
public static ProguardKeepRule defaultKeepAllRule(
Consumer<ProguardKeepRuleModifiers.Builder> modifiers) {
ProguardKeepRule.Builder builder = ProguardKeepRule.builder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
new file mode 100644
index 0000000..f67634e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleBase.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2016, 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.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class ProguardKeepRuleBase extends ProguardConfigurationRule {
+
+ public static abstract class Builder<C extends ProguardKeepRuleBase, B extends Builder<C, B>>
+ extends ProguardConfigurationRule.Builder<C, B> {
+
+ protected ProguardKeepRuleType type;
+ protected final ProguardKeepRuleModifiers.Builder modifiersBuilder =
+ ProguardKeepRuleModifiers.builder();
+
+ protected Builder() {
+ super();
+ }
+
+ public void setType(ProguardKeepRuleType type) {
+ this.type = type;
+ }
+
+ public ProguardKeepRuleModifiers.Builder getModifiersBuilder() {
+ return modifiersBuilder;
+ }
+ }
+
+ private final ProguardKeepRuleType type;
+ private final ProguardKeepRuleModifiers modifiers;
+
+ protected ProguardKeepRuleBase(
+ Origin origin,
+ Position position,
+ String source,
+ ProguardTypeMatcher classAnnotation,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules,
+ ProguardKeepRuleType type,
+ ProguardKeepRuleModifiers modifiers) {
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
+ this.type = type;
+ this.modifiers = modifiers;
+ }
+
+ public ProguardKeepRuleType getType() {
+ return type;
+ }
+
+ public ProguardKeepRuleModifiers getModifiers() {
+ return modifiers;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ProguardKeepRuleBase)) {
+ return false;
+ }
+ ProguardKeepRuleBase that = (ProguardKeepRuleBase) o;
+
+ if (type != that.type) {
+ return false;
+ }
+ if (!modifiers.equals(that.modifiers)) {
+ return false;
+ }
+ return super.equals(that);
+ }
+
+ @Override
+ public int hashCode() {
+ // Used multiplier 3 to avoid too much overflow when computing hashCode.
+ int result = type.hashCode();
+ result = 3 * result + modifiers.hashCode();
+ result = 3 * result + super.hashCode();
+ return result;
+ }
+
+ static void appendNonEmpty(StringBuilder builder, String pre, Object item, String post) {
+ if (item == null) {
+ return;
+ }
+ String text = item.toString();
+ if (!text.isEmpty()) {
+ if (pre != null) {
+ builder.append(pre);
+ }
+ builder.append(text);
+ if (post != null) {
+ builder.append(post);
+ }
+ }
+ }
+
+ @Override
+ String typeString() {
+ return type.toString();
+ }
+
+ @Override
+ String modifierString() {
+ return modifiers.toString();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
index 73b5538..87200fb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -3,23 +3,36 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
import java.util.List;
public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
- public static class Builder extends ProguardConfigurationRule.Builder {
+ public static class Builder
+ extends ProguardConfigurationRule.Builder<ProguardWhyAreYouKeepingRule, Builder> {
private Builder() {
+ super();
}
+ @Override
+ public Builder self() {
+ return this;
+ }
+
+ @Override
public ProguardWhyAreYouKeepingRule build() {
- return new ProguardWhyAreYouKeepingRule(classAnnotation, classAccessFlags,
- negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
- inheritanceClassName, inheritanceIsExtends, memberRules);
+ return new ProguardWhyAreYouKeepingRule(origin, getPosition(), source, classAnnotation,
+ classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType, classNames,
+ inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
}
private ProguardWhyAreYouKeepingRule(
+ Origin origin,
+ Position position,
+ String source,
ProguardTypeMatcher classAnnotation,
ProguardAccessFlags classAccessFlags,
ProguardAccessFlags negatedClassAccessFlags,
@@ -30,12 +43,13 @@
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
List<ProguardMemberRule> memberRules) {
- super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
- classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ super(origin, position, source, classAnnotation, classAccessFlags, negatedClassAccessFlags,
+ classTypeNegated, classType, classNames, inheritanceAnnotation, inheritanceClassName,
+ inheritanceIsExtends, memberRules);
}
- public static ProguardWhyAreYouKeepingRule.Builder builder() {
- return new ProguardWhyAreYouKeepingRule.Builder();
+ public static Builder builder() {
+ return new Builder();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index a19c0d5..9973121 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -773,6 +773,7 @@
directMethod.isClassInitializer() ? Rename.NEVER : Rename.IF_NEEDED);
add(directMethods, resultingDirectMethod, MethodSignatureEquivalence.get());
deferredRenamings.map(directMethod.method, resultingDirectMethod.method);
+ deferredRenamings.recordMove(directMethod.method, resultingDirectMethod.method);
if (!directMethod.isStaticMethod()) {
blockRedirectionOfSuperCalls(resultingDirectMethod.method);
@@ -802,6 +803,7 @@
DexEncodedMethod resultingVirtualMethod =
renameMethod(virtualMethod, availableMethodSignatures, Rename.NEVER);
deferredRenamings.map(virtualMethod.method, resultingVirtualMethod.method);
+ deferredRenamings.recordMove(virtualMethod.method, resultingVirtualMethod.method);
add(virtualMethods, resultingVirtualMethod, MethodSignatureEquivalence.get());
continue;
}
@@ -856,6 +858,7 @@
}
deferredRenamings.map(virtualMethod.method, shadowedBy.method);
+ deferredRenamings.recordMove(virtualMethod.method, resultingDirectMethod.method);
}
if (abortMerge) {
@@ -1155,6 +1158,7 @@
DexEncodedMethod result = method.toTypeSubstitutedMethod(newSignature);
result.markForceInline();
deferredRenamings.map(method.method, result.method);
+ deferredRenamings.recordMove(method.method, result.method);
// Renamed constructors turn into ordinary private functions. They can be private, as
// they are only references from their direct subclass, which they were merged into.
result.accessFlags.unsetConstructor();
@@ -1510,6 +1514,16 @@
}
@Override
+ public DexField getOriginalFieldSignature(DexField field) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public DexMethod getOriginalMethodSignature(DexMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
public DexType lookupType(DexType type) {
return type == source ? target : mergedClasses.getOrDefault(type, type);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
index 5974e60..e074c25 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLense.java
@@ -14,6 +14,9 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.graph.GraphLense.NestedGraphLense;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
@@ -57,8 +60,17 @@
Map<DexMethod, DexMethod> methodMap,
Set<DexMethod> mergedMethods,
Map<DexType, Map<DexMethod, GraphLenseLookupResult>> contextualVirtualToDirectMethodMaps,
+ BiMap<DexField, DexField> originalFieldSignatures,
+ BiMap<DexMethod, DexMethod> originalMethodSignatures,
GraphLense previousLense) {
- super(ImmutableMap.of(), methodMap, fieldMap, previousLense, appInfo.dexItemFactory);
+ super(
+ ImmutableMap.of(),
+ methodMap,
+ fieldMap,
+ originalFieldSignatures,
+ originalMethodSignatures,
+ previousLense,
+ appInfo.dexItemFactory);
this.appInfo = appInfo;
this.mergedMethods = mergedMethods;
this.contextualVirtualToDirectMethodMaps = contextualVirtualToDirectMethodMaps;
@@ -138,12 +150,14 @@
public static class Builder {
private final AppInfo appInfo;
- protected final Map<DexField, DexField> fieldMap = new HashMap<>();
+ protected final BiMap<DexField, DexField> fieldMap = HashBiMap.create();
protected final Map<DexMethod, DexMethod> methodMap = new HashMap<>();
private final ImmutableSet.Builder<DexMethod> mergedMethodsBuilder = ImmutableSet.builder();
private final Map<DexType, Map<DexMethod, GraphLenseLookupResult>>
contextualVirtualToDirectMethodMaps = new HashMap<>();
+ private final Map<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create();
+
private Builder(AppInfo appInfo) {
this.appInfo = appInfo;
}
@@ -157,39 +171,91 @@
&& contextualVirtualToDirectMethodMaps.isEmpty()) {
return previousLense;
}
+ Map<DexProto, DexProto> cache = new HashMap<>();
+ BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse();
return new VerticalClassMergerGraphLense(
appInfo,
fieldMap,
methodMap,
getMergedMethodSignaturesAfterClassMerging(
- mergedMethodsBuilder.build(), mergedClasses, dexItemFactory),
+ mergedMethodsBuilder.build(), mergedClasses, dexItemFactory, cache),
contextualVirtualToDirectMethodMaps,
+ getOriginalFieldSignaturesAfterClassMerging(
+ originalFieldSignatures, mergedClasses, dexItemFactory),
+ getOriginalMethodSignaturesAfterClassMerging(
+ originalMethodSignatures, mergedClasses, dexItemFactory, cache),
previousLense);
}
// After we have recorded that a method "a.b.c.Foo;->m(A, B, C)V" was merged into another class,
// it could be that the class B was merged into its subclass B'. In that case we update the
// signature to "a.b.c.Foo;->m(A, B', C)V".
- private Set<DexMethod> getMergedMethodSignaturesAfterClassMerging(
+ private static Set<DexMethod> getMergedMethodSignaturesAfterClassMerging(
Set<DexMethod> mergedMethods,
Map<DexType, DexType> mergedClasses,
- DexItemFactory dexItemFactory) {
+ DexItemFactory dexItemFactory,
+ Map<DexProto, DexProto> cache) {
ImmutableSet.Builder<DexMethod> result = ImmutableSet.builder();
- Map<DexProto, DexProto> cache = new HashMap<>();
for (DexMethod signature : mergedMethods) {
- DexType newHolder = mergedClasses.getOrDefault(signature.holder, signature.holder);
- DexProto newProto =
- dexItemFactory.applyClassMappingToProto(
- signature.proto, type -> mergedClasses.getOrDefault(type, type), cache);
- if (signature.holder.equals(newHolder) && signature.proto.equals(newProto)) {
- result.add(signature);
- } else {
- result.add(dexItemFactory.createMethod(newHolder, newProto, signature.name));
- }
+ result.add(
+ getMethodSignatureAfterClassMerging(signature, mergedClasses, dexItemFactory, cache));
}
return result.build();
}
+ private static BiMap<DexField, DexField> getOriginalFieldSignaturesAfterClassMerging(
+ Map<DexField, DexField> originalFieldSignatures,
+ Map<DexType, DexType> mergedClasses,
+ DexItemFactory dexItemFactory) {
+ ImmutableBiMap.Builder<DexField, DexField> result = ImmutableBiMap.builder();
+ for (Map.Entry<DexField, DexField> entry : originalFieldSignatures.entrySet()) {
+ result.put(
+ getFieldSignatureAfterClassMerging(entry.getKey(), mergedClasses, dexItemFactory),
+ entry.getValue());
+ }
+ return result.build();
+ }
+
+ private static BiMap<DexMethod, DexMethod> getOriginalMethodSignaturesAfterClassMerging(
+ Map<DexMethod, DexMethod> originalMethodSignatures,
+ Map<DexType, DexType> mergedClasses,
+ DexItemFactory dexItemFactory,
+ Map<DexProto, DexProto> cache) {
+ ImmutableBiMap.Builder<DexMethod, DexMethod> result = ImmutableBiMap.builder();
+ for (Map.Entry<DexMethod, DexMethod> entry : originalMethodSignatures.entrySet()) {
+ result.put(
+ getMethodSignatureAfterClassMerging(
+ entry.getKey(), mergedClasses, dexItemFactory, cache),
+ entry.getValue());
+ }
+ return result.build();
+ }
+
+ private static DexField getFieldSignatureAfterClassMerging(
+ DexField signature, Map<DexType, DexType> mergedClasses, DexItemFactory dexItemFactory) {
+ DexType newClass = mergedClasses.getOrDefault(signature.clazz, signature.clazz);
+ DexType newType = mergedClasses.getOrDefault(signature.type, signature.type);
+ if (signature.clazz.equals(newClass) && signature.type.equals(newType)) {
+ return signature;
+ }
+ return dexItemFactory.createField(newClass, newType, signature.name);
+ }
+
+ private static DexMethod getMethodSignatureAfterClassMerging(
+ DexMethod signature,
+ Map<DexType, DexType> mergedClasses,
+ DexItemFactory dexItemFactory,
+ Map<DexProto, DexProto> cache) {
+ DexType newHolder = mergedClasses.getOrDefault(signature.holder, signature.holder);
+ DexProto newProto =
+ dexItemFactory.applyClassMappingToProto(
+ signature.proto, type -> mergedClasses.getOrDefault(type, type), cache);
+ if (signature.holder.equals(newHolder) && signature.proto.equals(newProto)) {
+ return signature;
+ }
+ return dexItemFactory.createMethod(newHolder, newProto, signature.name);
+ }
+
public boolean hasMappingForSignatureInContext(DexType context, DexMethod signature) {
Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
contextualVirtualToDirectMethodMaps.get(context);
@@ -211,6 +277,10 @@
methodMap.put(from, to);
}
+ public void recordMove(DexMethod from, DexMethod to) {
+ originalMethodSignatures.put(to, from);
+ }
+
public void mapVirtualMethodToDirectInType(
DexMethod from, GraphLenseLookupResult to, DexType type) {
Map<DexMethod, GraphLenseLookupResult> virtualToDirectMethodMap =
@@ -222,6 +292,7 @@
fieldMap.putAll(builder.fieldMap);
methodMap.putAll(builder.methodMap);
mergedMethodsBuilder.addAll(builder.mergedMethodsBuilder.build());
+ originalMethodSignatures.putAll(builder.originalMethodSignatures);
for (DexType context : builder.contextualVirtualToDirectMethodMaps.keySet()) {
Map<DexMethod, GraphLenseLookupResult> current =
contextualVirtualToDirectMethodMaps.get(context);
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index b4e6ae2..d177b36 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -153,14 +153,9 @@
addRule(clazz, feature, 0);
}
- public void addNonClassMapping(String name, String feature) throws FeatureMappingException {
- if (parseNonClassRules.containsKey(name)) {
- throw new FeatureMappingException(
- "Non-code files with the same name present in multiple feature splits. " +
- "File '" + name + "' present in both '" + feature + "' and '" +
- parseNonClassRules.get(name) + "'.");
- }
- parseNonClassRules.put(name, feature);
+ public void addNonClassMapping(String name, String feature) {
+ // If a non-class file is present in multiple features put the resource in the base.
+ parseNonClassRules.put(name, parseNonClassRules.containsKey(name) ? baseName : feature);
}
FeatureClassMapping(List<String> lines) throws FeatureMappingException {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index bc8bd8c..5848396 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -39,8 +39,6 @@
import java.util.zip.ZipOutputStream;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.internal.runners.statements.ExpectException;
-import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
public class DexSplitterTests {
@@ -206,19 +204,6 @@
}
}
- private void validateNonClassOutput(Path base, Path feature) throws IOException {
- byte[] contents = Files.readAllBytes(Paths.get(TEXT_FILE));
- byte[] contents2 = new byte[contents.length * 2];
- System.arraycopy(contents, 0, contents2, 0, contents.length);
- System.arraycopy(contents, 0, contents2, contents.length, contents.length);
- Path baseTextFile = base.resolve("dexsplitsample/TextFile.txt");
- Path featureTextFile = feature.resolve("dexsplitsample/TextFile2.txt");
- assert Files.exists(baseTextFile);
- assert Files.exists(featureTextFile);
- assert Arrays.equals(Files.readAllBytes(baseTextFile), contents);
- assert Arrays.equals(Files.readAllBytes(featureTextFile), contents2);
- }
-
private Path createSplitSpec() throws FileNotFoundException, UnsupportedEncodingException {
Path splitSpec = temp.getRoot().toPath().resolve("split_spec");
try (PrintWriter out = new PrintWriter(splitSpec.toFile(), "UTF-8")) {
@@ -392,12 +377,13 @@
ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip));
String name = "dexsplitsample/TextFile.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE));
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
name = "dexsplitsample/TextFile2.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ inputZipStream.write(fileBytes);
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
inputZipStream.close();
Path output = temp.newFolder().toPath().resolve("output");
@@ -407,14 +393,14 @@
ZipOutputStream baseStream = new ZipOutputStream(Files.newOutputStream(baseJar));
name = "dexsplitsample/TextFile.txt";
baseStream.putNextEntry(new ZipEntry(name));
- baseStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ baseStream.write(fileBytes);
baseStream.closeEntry();
baseStream.close();
ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar));
name = "dexsplitsample/TextFile2.txt";
featureStream.putNextEntry(new ZipEntry(name));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ featureStream.write(fileBytes);
+ featureStream.write(fileBytes);
featureStream.closeEntry();
featureStream.close();
Options options = new Options();
@@ -426,33 +412,43 @@
DexSplitter.run(options);
Path baseDir = output.resolve("base");
Path featureDir = output.resolve("feature1");
- validateNonClassOutput(baseDir, featureDir);
+ byte[] contents = fileBytes;
+ byte[] contents2 = new byte[contents.length * 2];
+ System.arraycopy(contents, 0, contents2, 0, contents.length);
+ System.arraycopy(contents, 0, contents2, contents.length, contents.length);
+ Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt");
+ Path featureTextFile = featureDir.resolve("dexsplitsample/TextFile2.txt");
+ assert Files.exists(baseTextFile);
+ assert Files.exists(featureTextFile);
+ assert Arrays.equals(Files.readAllBytes(baseTextFile), contents);
+ assert Arrays.equals(Files.readAllBytes(featureTextFile), contents2);
}
- @Test(expected = FeatureMappingException.class)
+ @Test
public void splitDuplicateNonClassFiles()
throws IOException, CompilationFailedException, FeatureMappingException {
Path inputZip = temp.getRoot().toPath().resolve("input-zip-with-non-class-files.jar");
ZipOutputStream inputZipStream = new ZipOutputStream(Files.newOutputStream(inputZip));
String name = "dexsplitsample/TextFile.txt";
inputZipStream.putNextEntry(new ZipEntry(name));
- inputZipStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ byte[] fileBytes = Files.readAllBytes(Paths.get(TEXT_FILE));
+ inputZipStream.write(fileBytes);
inputZipStream.closeEntry();
inputZipStream.close();
Path output = temp.newFolder().toPath().resolve("output");
Files.createDirectory(output);
Path featureJar = temp.getRoot().toPath().resolve("feature1.jar");
Path feature2Jar = temp.getRoot().toPath().resolve("feature2.jar");
- ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(feature2Jar));
+ ZipOutputStream featureStream = new ZipOutputStream(Files.newOutputStream(featureJar));
name = "dexsplitsample/TextFile.txt";
featureStream.putNextEntry(new ZipEntry(name));
- featureStream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ featureStream.write(fileBytes);
featureStream.closeEntry();
featureStream.close();
- ZipOutputStream feature2Stream = new ZipOutputStream(Files.newOutputStream(featureJar));
+ ZipOutputStream feature2Stream = new ZipOutputStream(Files.newOutputStream(feature2Jar));
name = "dexsplitsample/TextFile.txt";
feature2Stream.putNextEntry(new ZipEntry(name));
- feature2Stream.write(Files.readAllBytes(Paths.get(TEXT_FILE)));
+ feature2Stream.write(fileBytes);
feature2Stream.closeEntry();
feature2Stream.close();
Options options = new Options();
@@ -462,5 +458,15 @@
options.addFeatureJar(featureJar.toString());
options.setSplitNonClassResources(true);
DexSplitter.run(options);
+ Path baseDir = output.resolve("base");
+ Path feature1Dir = output.resolve("feature1");
+ Path feature2Dir = output.resolve("feature2");
+ Path baseTextFile = baseDir.resolve("dexsplitsample/TextFile.txt");
+ Path feature1TextFile = feature1Dir.resolve("dexsplitsample/TextFile2.txt");
+ Path feature2TextFile = feature2Dir.resolve("dexsplitsample/TextFile2.txt");
+ assert !Files.exists(feature1TextFile);
+ assert !Files.exists(feature2TextFile);
+ assert Files.exists(baseTextFile);
+ assert Arrays.equals(Files.readAllBytes(baseTextFile), fileBytes);
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 748866e..7325be3 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -41,6 +41,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
+import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
@@ -1813,6 +1814,66 @@
testNotSupported("-adaptresourcefilecontents");
}
+ private void checkRulesSourceSnippet(List<String> sourceRules) {
+ checkRulesSourceSnippet(sourceRules, sourceRules, false);
+ }
+
+ private void checkRulesSourceSnippet(
+ List<String> sourceRules, List<String> expected, boolean trim) {
+ reset();
+ parser.parse(createConfigurationForTesting(sourceRules));
+ verifyParserEndsCleanly();
+ List<ProguardConfigurationRule> rules = parser.getConfig().getRules();
+ assertEquals(expected.size(), rules.size());
+ for (int i = 0; i < expected.size(); i++) {
+ assertEquals(trim ? expected.get(i).trim() : expected.get(i), rules.get(i).getSource());
+ }
+ }
+
+ @Test
+ public void accurateSourceSnippet() {
+ String rule1 = String.join(System.lineSeparator(), ImmutableList.of("-keep class A { *; }"));
+ String rule2 =
+ String.join(System.lineSeparator(), ImmutableList.of("-keep class A ", "{ *; ", "}"));
+ String rule3 =
+ String.join(
+ System.lineSeparator(), ImmutableList.of("-checkdiscard class A ", "{ *; ", "}"));
+
+ checkRulesSourceSnippet(ImmutableList.of(rule1));
+ checkRulesSourceSnippet(ImmutableList.of(rule2));
+ checkRulesSourceSnippet(ImmutableList.of(rule3));
+ checkRulesSourceSnippet(ImmutableList.of(rule1, rule2, rule3));
+ }
+
+ @Test
+ public void accurateSourceSnippet_withWhitespace() {
+ Iterable<String> nonEmptyWhiteSpace =
+ whiteSpace.stream().filter(space -> !space.equals("")).collect(Collectors.toList());
+ for (String space : nonEmptyWhiteSpace) {
+ String rule1 =
+ String.join(System.lineSeparator(), ImmutableList.of(" -keep class A { *; } "))
+ .replaceAll(" {2}", space);
+ String rule2 =
+ String.join(
+ System.lineSeparator(), ImmutableList.of("-keep class A ", "{ *; ", "}", ""))
+ .replaceAll(" {2}", space);
+
+ checkRulesSourceSnippet(ImmutableList.of(rule1), ImmutableList.of(rule1), true);
+ checkRulesSourceSnippet(
+ ImmutableList.of("#Test comment ", "", rule1), ImmutableList.of(rule1), true);
+ checkRulesSourceSnippet(
+ ImmutableList.of("#Test comment ", "", rule1, "", "#Test comment ", ""),
+ ImmutableList.of(rule1),
+ true);
+ checkRulesSourceSnippet(ImmutableList.of(rule2), ImmutableList.of(rule2), true);
+ checkRulesSourceSnippet(ImmutableList.of(rule1, rule2), ImmutableList.of(rule1, rule2), true);
+ checkRulesSourceSnippet(
+ ImmutableList.of(
+ "#Test comment ", "", rule1, " ", "#Test comment ", "", rule2, "#Test comment ", ""),
+ ImmutableList.of(rule1, rule2),
+ true);
+ }
+ }
private ProguardConfiguration parseAndVerifyParserEndsCleanly(List<String> config) {
parser.parse(createConfigurationForTesting(config));