Merge "Regression test for bug on 6.0.1 dex2oat compiler."
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 063d14d..df3d81b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -358,9 +358,12 @@
}
application = application.asDirect().rewrittenWithLense(graphLense);
appInfo = appInfo.withLiveness().rewrittenWithLense(application.asDirect(), graphLense);
- // Collect switch maps and ordinals maps.
- appInfo = new SwitchMapCollector(appInfo.withLiveness(), options).run();
- appInfo = new EnumOrdinalMapCollector(appInfo.withLiveness(), options).run();
+ // TODO(mathiasr): Remove this check when CF->IR construction is complete.
+ if (!options.skipIR) {
+ // Collect switch maps and ordinals maps.
+ appInfo = new SwitchMapCollector(appInfo.withLiveness(), options).run();
+ appInfo = new EnumOrdinalMapCollector(appInfo.withLiveness(), options).run();
+ }
// TODO(b/79143143): re-enable once fixed.
// graphLense = new BridgeMethodAnalysis(graphLense, appInfo.withLiveness()).run();
diff --git a/src/main/java/com/android/tools/r8/ResourceShrinker.java b/src/main/java/com/android/tools/r8/ResourceShrinker.java
index d2969d6..7fee6fe 100644
--- a/src/main/java/com/android/tools/r8/ResourceShrinker.java
+++ b/src/main/java/com/android/tools/r8/ResourceShrinker.java
@@ -153,8 +153,9 @@
}
for (DexEncodedField field : classDef.staticFields()) {
- if (field.staticValue != null) {
- processFieldValue(field.staticValue);
+ DexValue staticValue = field.getStaticValue();
+ if (staticValue != null) {
+ processFieldValue(staticValue);
}
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 509a91a..b116e65 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 = "v1.2.12-dev";
+ public static final String LABEL = "v1.2.14-dev";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 70ce7c8..300335a 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -412,14 +412,16 @@
public void print(CfSwitch cfSwitch) {
indent();
- builder.append(cfSwitch.getKind() == Kind.LOOKUP ? "lookup" : "table").append("switch");
+ Kind kind = cfSwitch.getKind();
+ builder.append(kind == Kind.LOOKUP ? "lookup" : "table").append("switch");
IntList keys = cfSwitch.getKeys();
List<CfLabel> targets = cfSwitch.getTargets();
- for (int i = 0; i < keys.size(); i++) {
+ for (int i = 0; i < targets.size(); i++) {
indent();
+ int key = kind == Kind.LOOKUP ? keys.getInt(i) : (keys.getInt(0) + i);
builder
.append(" ")
- .append(keys.getInt(i))
+ .append(key)
.append(": ")
.append(getLabel(targets.get(i)));
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
index f27064c..1dc72d4 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfSwitch.java
@@ -20,12 +20,13 @@
private final int[] keys;
private final List<CfLabel> targets;
- public CfSwitch(CfLabel defaultTarget, int[] keys, List<CfLabel> targets) {
- // TODO(zerny): Support emitting table switches.
- this.kind = Kind.LOOKUP;
+ public CfSwitch(Kind kind, CfLabel defaultTarget, int[] keys, List<CfLabel> targets) {
+ this.kind = kind;
this.defaultTarget = defaultTarget;
this.keys = keys;
this.targets = targets;
+ assert kind != Kind.LOOKUP || keys.length == targets.size();
+ assert kind != Kind.TABLE || keys.length == 1;
}
public Kind getKind() {
@@ -50,7 +51,16 @@
for (int i = 0; i < targets.size(); i++) {
labels[i] = targets.get(i).getLabel();
}
- visitor.visitLookupSwitchInsn(defaultTarget.getLabel(), keys, labels);
+ switch (kind) {
+ case LOOKUP:
+ visitor.visitLookupSwitchInsn(defaultTarget.getLabel(), keys, labels);
+ break;
+ case TABLE: {
+ int min = keys[0];
+ int max = min + labels.length - 1;
+ visitor.visitTableSwitchInsn(min, max, defaultTarget.getLabel(), labels);
+ }
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 958deb4..4524505 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -576,8 +576,6 @@
if (accessFlags.isStatic()) {
if (staticValues != null && i < staticValues.length) {
staticValue = staticValues[i];
- } else {
- staticValue = DexValue.defaultForType(field.type, dexItemFactory);
}
}
fields[i] = new DexEncodedField(field, accessFlags, fieldAnnotations, staticValue);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
index 336eac5..e6b4a2c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotationSetRefList.java
@@ -12,6 +12,7 @@
private static final DexAnnotationSetRefList theEmptyTypeList = new DexAnnotationSetRefList();
public final DexAnnotationSet[] values;
+ private final int missingParameterAnnotations;
public static DexAnnotationSetRefList empty() {
return theEmptyTypeList;
@@ -19,11 +20,17 @@
private DexAnnotationSetRefList() {
this.values = new DexAnnotationSet[0];
+ this.missingParameterAnnotations = 0;
}
public DexAnnotationSetRefList(DexAnnotationSet[] values) {
+ this(values, 0);
+ }
+
+ public DexAnnotationSetRefList(DexAnnotationSet[] values, int missingParameterAnnotations) {
assert values != null && values.length > 0;
this.values = values;
+ this.missingParameterAnnotations = missingParameterAnnotations;
}
@Override
@@ -57,4 +64,8 @@
public boolean isEmpty() {
return values.length == 0;
}
+
+ public int getMissingParameterAnnotations() {
+ return missingParameterAnnotations;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index e9b9255..7b09cab 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -369,7 +369,7 @@
public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
return Arrays.stream(staticFields())
- .anyMatch(field -> !field.staticValue.mayTriggerAllocation());
+ .anyMatch(field -> !field.getStaticValue().mayTriggerAllocation());
}
public List<InnerClassAttribute> getInnerClasses() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index bdfadfc..a28dc82 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -15,14 +15,13 @@
public final DexField field;
public final FieldAccessFlags accessFlags;
public DexAnnotationSet annotations;
- public DexValue staticValue;
+ private DexValue staticValue;
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
DexAnnotationSet annotations,
DexValue staticValue) {
- assert !accessFlags.isStatic() || staticValue != null;
this.field = field;
this.accessFlags = accessFlags;
this.annotations = annotations;
@@ -33,8 +32,8 @@
public void collectIndexedItems(IndexedItemCollection indexedItems) {
field.collectIndexedItems(indexedItems);
annotations.collectIndexedItems(indexedItems);
- if (staticValue != null) {
- staticValue.collectIndexedItems(indexedItems);
+ if (accessFlags.isStatic()) {
+ getStaticValue().collectIndexedItems(indexedItems);
}
}
@@ -67,6 +66,22 @@
return !annotations.isEmpty();
}
+ public boolean hasExplicitStaticValue() {
+ assert accessFlags.isStatic();
+ return staticValue != null;
+ }
+
+ public void setStaticValue(DexValue staticValue) {
+ assert accessFlags.isStatic();
+ assert staticValue != null;
+ this.staticValue = staticValue;
+ }
+
+ public DexValue getStaticValue() {
+ assert accessFlags.isStatic();
+ return staticValue == null ? DexValue.defaultForType(field.type) : staticValue;
+ }
+
// Returns a const instructions if this field is a compile time final const.
public Instruction valueAsConstInstruction(AppInfo appInfo, Value dest) {
// The only way to figure out whether the DexValue contains the final value
@@ -74,7 +89,7 @@
if (accessFlags.isStatic() && accessFlags.isPublic() && accessFlags.isFinal()) {
DexClass clazz = appInfo.definitionFor(field.getHolder());
assert clazz != null : "Class for the field must be present";
- return staticValue.asConstInstruction(clazz.hasClassInitializer(), dest);
+ return getStaticValue().asConstInstruction(clazz.hasClassInitializer(), dest);
}
return null;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
index 9644558..0fac5e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexLibraryClass.java
@@ -49,7 +49,7 @@
// Set all static field values to unknown. We don't want to use the value from the library
// at compile time, as it can be different at runtime.
for (DexEncodedField staticField : staticFields) {
- staticField.staticValue = DexValue.UNKNOWN;
+ staticField.setStaticValue(DexValue.UNKNOWN);
}
assert kind == Kind.CF : "Invalid kind " + kind + " for library-path class " + type;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 90db1d2..1473191 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -275,9 +275,10 @@
List<DexValue> values = new ArrayList<>(fields.length);
for (int i = 0; i < fields.length; i++) {
DexEncodedField field = fields[i];
- assert field.staticValue != null;
- values.add(field.staticValue);
- if (!field.staticValue.isDefault(field.field.type, factory)) {
+ DexValue staticValue = field.getStaticValue();
+ assert staticValue != null;
+ values.add(staticValue);
+ if (!staticValue.isDefault(field.field.type)) {
length = i + 1;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index f1a0f4d..342ea2a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -96,35 +96,29 @@
@Override
public abstract String toString();
- public static DexValue defaultForType(DexType type, DexItemFactory factory) {
- if (type == factory.booleanType) {
- return DexValueBoolean.DEFAULT;
+ public static DexValue defaultForType(DexType type) {
+ switch (type.toShorty()) {
+ case 'Z':
+ return DexValueBoolean.DEFAULT;
+ case 'B':
+ return DexValueByte.DEFAULT;
+ case 'C':
+ return DexValueChar.DEFAULT;
+ case 'S':
+ return DexValueShort.DEFAULT;
+ case 'I':
+ return DexValueInt.DEFAULT;
+ case 'J':
+ return DexValueLong.DEFAULT;
+ case 'F':
+ return DexValueFloat.DEFAULT;
+ case 'D':
+ return DexValueDouble.DEFAULT;
+ case 'L':
+ return DexValueNull.NULL;
+ default:
+ throw new Unreachable("No default value for unexpected type " + type);
}
- if (type == factory.byteType) {
- return DexValueByte.DEFAULT;
- }
- if (type == factory.charType) {
- return DexValueChar.DEFAULT;
- }
- if (type == factory.shortType) {
- return DexValueShort.DEFAULT;
- }
- if (type == factory.intType) {
- return DexValueInt.DEFAULT;
- }
- if (type == factory.longType) {
- return DexValueLong.DEFAULT;
- }
- if (type == factory.floatType) {
- return DexValueFloat.DEFAULT;
- }
- if (type == factory.doubleType) {
- return DexValueDouble.DEFAULT;
- }
- if (type.isArrayType() || type.isClassType()) {
- return DexValueNull.NULL;
- }
- throw new Unreachable("No default value for unexpected type " + type);
}
public abstract Object getBoxedValue();
@@ -134,8 +128,8 @@
return null;
}
- public boolean isDefault(DexType type, DexItemFactory factory) {
- return this == defaultForType(type, factory);
+ public boolean isDefault(DexType type) {
+ return this == defaultForType(type);
}
/**
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 28709bd..bef2f57 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -49,7 +49,6 @@
import com.android.tools.r8.cf.code.CfUnop;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
@@ -105,6 +104,8 @@
// Hidden ASM "synthetic attribute" bit we need to clear.
private static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
+ // Descriptor used by ASM for missing annotations.
+ public static final String SYNTHETIC_ANNOTATION = "Ljava/lang/Synthetic;";
private final JarApplicationReader application;
private final Consumer<DexClass> classConsumer;
@@ -404,7 +405,7 @@
private DexValue getStaticValue(Object value, DexType type) {
if (value == null) {
- return DexValue.defaultForType(type, parent.application.getFactory());
+ return null;
}
DexItemFactory factory = parent.application.getFactory();
if (type == factory.booleanType) {
@@ -521,7 +522,7 @@
// with that descriptor. If javac is fixed, the ASM workaround will not be hit and we will
// never see this non-existing annotation descriptor. ASM uses the same check to make
// sure to undo their workaround for the javac bug in their MethodWriter.
- if (desc.equals("Ljava/lang/Synthetic;")) {
+ if (desc.equals(SYNTHETIC_ANNOTATION)) {
// We can iterate through all the parameters twice. Once for visible and once for
// invisible parameter annotations. We only record the number of fake parameter
// annotations once.
@@ -595,7 +596,7 @@
for (int i = 0; i < parameterAnnotations.size(); i++) {
sets[i] = createAnnotationSet(parameterAnnotations.get(i));
}
- parameterAnnotationSets = new DexAnnotationSetRefList(sets);
+ parameterAnnotationSets = new DexAnnotationSetRefList(sets, fakeParameterAnnotations);
}
InternalOptions internalOptions = parent.application.options;
if (parameterNames != null && internalOptions.canUseParameterNameAnnotations()) {
@@ -1216,8 +1217,12 @@
@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
- // TODO(mathiasr): Support emitting table switches in CfSwitch.
- throw new Unimplemented("Table switches not supported in CF backend");
+ assert max == min + labels.length - 1;
+ ArrayList<CfLabel> targets = new ArrayList<>(labels.length);
+ for (Label label : labels) {
+ targets.add(getLabel(label));
+ }
+ instructions.add(new CfSwitch(CfSwitch.Kind.TABLE, getLabel(dflt), new int[] {min}, targets));
}
@Override
@@ -1226,7 +1231,7 @@
for (Label label : labels) {
targets.add(getLabel(label));
}
- instructions.add(new CfSwitch(getLabel(dflt), keys, targets));
+ instructions.add(new CfSwitch(CfSwitch.Kind.LOOKUP, getLabel(dflt), keys, targets));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Switch.java b/src/main/java/com/android/tools/r8/ir/code/Switch.java
index b8f90e6..50e59f1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Switch.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Switch.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.code.CfLabel;
import com.android.tools.r8.cf.code.CfSwitch;
+import com.android.tools.r8.cf.code.CfSwitch.Kind;
import com.android.tools.r8.code.Nop;
import com.android.tools.r8.code.PackedSwitch;
import com.android.tools.r8.code.PackedSwitchPayload;
@@ -294,6 +295,6 @@
for (int index : targetBlockIndices) {
labels.add(builder.getLabel(successors.get(index)));
}
- builder.add(new CfSwitch(builder.getLabel(fallthroughBlock()), keys, labels));
+ builder.add(new CfSwitch(Kind.LOOKUP, builder.getLabel(fallthroughBlock()), keys, labels));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 8fca809..5ea5d0e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -113,8 +113,10 @@
DexItemFactory factory = factory();
if (a.isArrayType()) {
- // Arrays are only adaptable to java.lang.Object.
- return b == factory.objectType;
+ // Arrays are only adaptable to java.lang.Object or other arrays, note that we
+ // don't check element type inheritance in the second case since we assume the
+ // input code is verifiable.
+ return b == factory.objectType || b.isArrayType();
}
if (a.isPrimitiveType()) {
@@ -342,8 +344,10 @@
}
}
- if (fromType.isArrayType() && toType == factory().objectType) {
+ if (fromType.isArrayType() && (toType == factory().objectType || toType.isArrayType())) {
// If `fromType` is an array and `toType` is java.lang.Object, no cast is needed.
+ // If both `fromType` and `toType` are arrays, no cast is needed since we assume
+ // the input code is verifiable.
return register;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 7073215..85a3ac6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1086,47 +1086,47 @@
if (put.inValue().isConstant()) {
if (put.inValue().isConstNumber()) {
assert put.inValue().isZero();
- encodedField.staticValue = DexValueNull.NULL;
+ encodedField.setStaticValue(DexValueNull.NULL);
} else {
ConstString cnst = put.inValue().getConstInstruction().asConstString();
- encodedField.staticValue = new DexValueString(cnst.getValue());
+ encodedField.setStaticValue(new DexValueString(cnst.getValue()));
}
} else {
InvokeVirtual invoke = put.inValue().definition.asInvokeVirtual();
String name = method.method.getHolder().toSourceString();
if (invoke.getInvokedMethod() == dexItemFactory.classMethods.getSimpleName) {
String simpleName = name.substring(name.lastIndexOf('.') + 1);
- encodedField.staticValue =
- new DexValueString(dexItemFactory.createString(simpleName));
+ encodedField.setStaticValue(
+ new DexValueString(dexItemFactory.createString(simpleName)));
} else {
assert invoke.getInvokedMethod() == dexItemFactory.classMethods.getName;
- encodedField.staticValue = new DexValueString(dexItemFactory.createString(name));
+ encodedField.setStaticValue(new DexValueString(dexItemFactory.createString(name)));
}
}
} else if (field.type.isClassType() || field.type.isArrayType()) {
if (put.inValue().isZero()) {
- encodedField.staticValue = DexValueNull.NULL;
+ encodedField.setStaticValue(DexValueNull.NULL);
} else {
throw new Unreachable("Unexpected default value for field type " + field.type + ".");
}
} else {
ConstNumber cnst = put.inValue().getConstInstruction().asConstNumber();
if (field.type == dexItemFactory.booleanType) {
- encodedField.staticValue = DexValueBoolean.create(cnst.getBooleanValue());
+ encodedField.setStaticValue(DexValueBoolean.create(cnst.getBooleanValue()));
} else if (field.type == dexItemFactory.byteType) {
- encodedField.staticValue = DexValueByte.create((byte) cnst.getIntValue());
+ encodedField.setStaticValue(DexValueByte.create((byte) cnst.getIntValue()));
} else if (field.type == dexItemFactory.shortType) {
- encodedField.staticValue = DexValueShort.create((short) cnst.getIntValue());
+ encodedField.setStaticValue(DexValueShort.create((short) cnst.getIntValue()));
} else if (field.type == dexItemFactory.intType) {
- encodedField.staticValue = DexValueInt.create(cnst.getIntValue());
+ encodedField.setStaticValue(DexValueInt.create(cnst.getIntValue()));
} else if (field.type == dexItemFactory.longType) {
- encodedField.staticValue = DexValueLong.create(cnst.getLongValue());
+ encodedField.setStaticValue(DexValueLong.create(cnst.getLongValue()));
} else if (field.type == dexItemFactory.floatType) {
- encodedField.staticValue = DexValueFloat.create(cnst.getFloatValue());
+ encodedField.setStaticValue(DexValueFloat.create(cnst.getFloatValue()));
} else if (field.type == dexItemFactory.doubleType) {
- encodedField.staticValue = DexValueDouble.create(cnst.getDoubleValue());
+ encodedField.setStaticValue(DexValueDouble.create(cnst.getDoubleValue()));
} else if (field.type == dexItemFactory.charType) {
- encodedField.staticValue = DexValueChar.create((char) cnst.getIntValue());
+ encodedField.setStaticValue(DexValueChar.create((char) cnst.getIntValue()));
} else {
throw new Unreachable("Unexpected field type " + field.type + ".");
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 65afb32..cdfa2b1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -78,7 +78,7 @@
DexEncodedField staticField = appInfo.lookupStaticTarget(field.clazz, field);
if (staticField != null) {
Value value = code.createValue(valueType, instruction.getLocalInfo());
- replacement = staticField.staticValue.asConstInstruction(false, value);
+ replacement = staticField.getStaticValue().asConstInstruction(false, value);
} else {
throw new CompilationError(field.clazz.toSourceString() + "." + field.name.toString() +
" used in assumevalues rule does not exist.");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 2734ff4..7b3eef0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.ConstInstruction;
+import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.DebugLocalsChange;
import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
@@ -266,10 +266,9 @@
// Mapping from register number to const number instructions for this basic block.
// Used to remove redundant const instructions that reloads the same constant into
// the same register.
- Map<Integer, ConstInstruction> registerToConstant = new HashMap<>();
+ Map<Integer, ConstNumber> registerToNumber = new HashMap<>();
MoveEliminator moveEliminator = new MoveEliminator(allocator);
ListIterator<Instruction> iterator = block.getInstructions().listIterator();
- boolean mayNoLongerThrow = false;
while (iterator.hasNext()) {
Instruction current = iterator.next();
if (moveEliminator.shouldBeEliminated(current)) {
@@ -277,30 +276,28 @@
} else if (current.outValue() != null && current.outValue().needsRegister()) {
Value outValue = current.outValue();
int instructionNumber = current.getNumber();
- if (!outValue.hasLocalInfo() && (current.isConstNumber() || current.isConstString())) {
- if (constantSpilledAtDefinition(current.asConstInstruction(), allocator)) {
+ if (outValue.isConstant() && current.isConstNumber()) {
+ if (constantSpilledAtDefinition(current.asConstNumber(), allocator)) {
// Remove constant instructions that are spilled at their definition and are
// therefore unused.
iterator.remove();
- mayNoLongerThrow |= current.instructionTypeCanThrow();
+ continue;
+ }
+ int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
+ ConstNumber numberInRegister = registerToNumber.get(outRegister);
+ if (numberInRegister != null
+ && numberInRegister.identicalNonValueNonPositionParts(current)) {
+ // This instruction is not needed, the same constant is already in this register.
+ // We don't consider the positions of the two (non-throwing) instructions.
+ iterator.remove();
} else {
- int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
- ConstInstruction constantInRegister = registerToConstant.get(outRegister);
- if (constantInRegister != null
- && constantInRegister.identicalNonValueNonPositionParts(current)) {
- // This instruction is not needed, the same constant is already in this register.
- // We don't consider the positions of the two (non-throwing) instructions.
- iterator.remove();
- mayNoLongerThrow |= current.instructionTypeCanThrow();
+ // Insert the current constant in the mapping. Make sure to clobber the second
+ // register if wide and register-1 if that defines a wide value.
+ registerToNumber.put(outRegister, current.asConstNumber());
+ if (current.outType().isWide()) {
+ registerToNumber.remove(outRegister + 1);
} else {
- // Insert the current constant in the mapping. Make sure to clobber the second
- // register if wide and register-1 if that defines a wide value.
- registerToConstant.put(outRegister, current.asConstNumber());
- if (current.outType().isWide()) {
- registerToConstant.remove(outRegister + 1);
- } else {
- removeWideConstantCovering(registerToConstant, outRegister);
- }
+ removeWideConstantCovering(registerToNumber, outRegister);
}
}
} else {
@@ -308,40 +305,32 @@
// from the mapping.
int outRegister = allocator.getRegisterForValue(outValue, instructionNumber);
for (int i = 0; i < outValue.requiredRegisters(); i++) {
- registerToConstant.remove(outRegister + i);
+ registerToNumber.remove(outRegister + i);
}
// Check if the first register written is the second part of a wide value. If so
// the wide value is no longer active.
- removeWideConstantCovering(registerToConstant, outRegister);
+ removeWideConstantCovering(registerToNumber, outRegister);
}
}
}
-
- if (mayNoLongerThrow && block.hasCatchHandlers() && !block.canThrow()) {
- block.clearCatchHandlers();
- }
}
}
private static void removeWideConstantCovering(
- Map<Integer, ConstInstruction> registerToConstant, int register) {
- ConstInstruction constant = registerToConstant.get(register - 1);
- if (constant != null && constant.outType().isWide()) {
- registerToConstant.remove(register - 1);
+ Map<Integer, ConstNumber> registerToNumber, int register) {
+ ConstNumber number = registerToNumber.get(register - 1);
+ if (number != null && number.outType().isWide()) {
+ registerToNumber.remove(register - 1);
}
}
private static boolean constantSpilledAtDefinition(
- ConstInstruction constInstruction, LinearScanRegisterAllocator allocator) {
- assert constInstruction.isConstNumber() || constInstruction.isConstString();
- if (constInstruction.outValue().isFixedRegisterValue()) {
+ ConstNumber constNumber, LinearScanRegisterAllocator allocator) {
+ if (constNumber.outValue().isFixedRegisterValue()) {
return false;
}
LiveIntervals definitionIntervals =
- constInstruction
- .outValue()
- .getLiveIntervals()
- .getSplitCovering(constInstruction.getNumber());
+ constNumber.outValue().getLiveIntervals().getSplitCovering(constNumber.getNumber());
return definitionIntervals.isSpilledAndRematerializable(allocator);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index e5c19d9..769c281 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -672,7 +672,7 @@
// const number instructions are for values that can be rematerialized instead of
// spilled.
assert instruction.getNumber() == -1;
- assert instruction.isMove() || instruction.isConstNumber() || instruction.isConstString();
+ assert instruction.isMove() || instruction.isConstNumber();
assert !instruction.isDebugInstruction();
return true;
}
@@ -1927,11 +1927,8 @@
newActive.add(splitChild);
// If the constant is split before its first actual use, mark the constant as being
// spilled. That will allows us to remove it afterwards if it is rematerializable.
- Value intervalsValue = intervals.getValue();
- boolean isRematerializableConstantValue =
- intervalsValue.isConstNumber() || intervalsValue.isConstString();
- if (isRematerializableConstantValue
- && intervals.getStart() == intervalsValue.definition.getNumber()
+ if (intervals.getValue().isConstNumber()
+ && intervals.getStart() == intervals.getValue().definition.getNumber()
&& intervals.getUses().size() == 1) {
intervals.setSpilled(true);
}
@@ -1941,7 +1938,9 @@
LiveIntervals splitOfSplit = splitChild.splitBefore(splitChild.getFirstUse());
splitOfSplit.setRegister(intervals.getRegister());
inactive.add(splitOfSplit);
- } else if (isRematerializableConstantValue) {
+ } else if (intervals.getValue().isConstNumber()) {
+ // TODO(ager): Do this for all constants. Currently we only rematerialize const
+ // number and therefore we only do it for numbers at this point.
splitRangesForSpilledConstant(splitChild, registerNumber);
} else if (intervals.isArgumentInterval()) {
splitRangesForSpilledArgument(splitChild);
@@ -1972,7 +1971,6 @@
// register for as long as possible to avoid further moves.
assert spilled.isSpilled();
assert !spilled.getValue().isConstNumber();
- assert !spilled.getValue().isConstString();
assert !spilled.isLinked() || spilled.isArgumentInterval();
boolean isSpillingToArgumentRegister =
(spilled.isArgumentInterval() || registerNumber < numberOfArgumentRegisters);
@@ -2005,7 +2003,7 @@
// spill we are running low on registers and this constant should get out of the way
// as much as possible.
assert spilled.isSpilled();
- assert spilled.getValue().isConstNumber() || spilled.getValue().isConstString();
+ assert spilled.getValue().isConstNumber();
assert !spilled.isLinked() || spilled.isArgumentInterval();
// Do not split range if constant is reused by one of the eleven following instruction.
int maxGapSize = 11 * INSTRUCTION_NUMBER_DELTA;
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index d7c6588..7e398e6 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -99,8 +99,8 @@
if (value.isArgument()) {
return true;
}
- boolean isRematerializableConstantValue = value.isConstNumber() || value.isConstString();
- if (!isRematerializableConstantValue) {
+ // TODO(ager): rematerialize const string as well.
+ if (!value.isConstNumber()) {
return false;
}
// If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index b1e5e4f..d1fe04c 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.FixedRegisterValue;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
@@ -141,12 +140,10 @@
instruction = new Move(to, from);
} else {
assert move.definition.isOutConstant();
- Value to = new FixedRegisterValue(move.definition.outType(), move.dst);
ConstInstruction definition = move.definition.getOutConstantConstInstruction();
if (definition.isConstNumber()) {
+ Value to = new FixedRegisterValue(move.definition.outType(), move.dst);
instruction = new ConstNumber(to, definition.asConstNumber().getRawValue());
- } else if (definition.isConstString()) {
- instruction = new ConstString(to, definition.asConstString().getValue());
} else {
throw new Unreachable("Unexpected definition");
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index a7b3f77..e49ad85 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexAnnotationSetRefList;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
@@ -32,6 +33,7 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.DexValue.UnknownDexValue;
import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.JarClassFileReader;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -217,10 +219,10 @@
}
private Object getStaticValue(DexEncodedField field) {
- if (!field.accessFlags.isStatic() || field.staticValue == null) {
+ if (!field.accessFlags.isStatic() || !field.hasExplicitStaticValue()) {
return null;
}
- return field.staticValue.asAsmEncodedObject();
+ return field.getStaticValue().asAsmEncodedObject();
}
private void writeField(DexEncodedField field, ClassWriter writer) {
@@ -250,18 +252,31 @@
}
}
writeAnnotations(visitor::visitAnnotation, method.annotations.annotations);
- for (int i = 0; i < method.parameterAnnotations.values.length; i++) {
- final int iFinal = i;
- writeAnnotations(
- (d, vis) -> visitor.visitParameterAnnotation(iFinal, d, vis),
- method.parameterAnnotations.values[i].annotations);
- }
+ writeParameterAnnotations(visitor, method.parameterAnnotations);
if (!method.accessFlags.isAbstract() && !method.accessFlags.isNative()) {
writeCode(method.getCode(), visitor);
}
visitor.visitEnd();
}
+ private void writeParameterAnnotations(
+ MethodVisitor visitor, DexAnnotationSetRefList parameterAnnotations) {
+ int missingParameterAnnotations = parameterAnnotations.getMissingParameterAnnotations();
+ for (int i = 0; i < missingParameterAnnotations; i++) {
+ AnnotationVisitor av =
+ visitor.visitParameterAnnotation(i, JarClassFileReader.SYNTHETIC_ANNOTATION, false);
+ if (av != null) {
+ av.visitEnd();
+ }
+ }
+ for (int i = 0; i < parameterAnnotations.values.length; i++) {
+ int parameterIndex = i + missingParameterAnnotations;
+ writeAnnotations(
+ (d, vis) -> visitor.visitParameterAnnotation(parameterIndex, d, vis),
+ parameterAnnotations.values[i].annotations);
+ }
+ }
+
private interface AnnotationConsumer {
AnnotationVisitor visit(String desc, boolean visible);
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index f766061..2fd66d0 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.ProguardClassFilter;
@@ -62,13 +63,17 @@
}
private void adaptClassStringsInField(DexEncodedField encodedField) {
- if (!(encodedField.staticValue instanceof DexValueString)) {
+ if (!encodedField.accessFlags.isStatic()) {
return;
}
- DexString original = ((DexValueString) encodedField.staticValue).getValue();
+ DexValue staticValue = encodedField.getStaticValue();
+ if (!(staticValue instanceof DexValueString)) {
+ return;
+ }
+ DexString original = ((DexValueString) staticValue).getValue();
DexString renamed = getRenamedStringLiteral(original);
if (renamed != original) {
- encodedField.staticValue = new DexValueString(renamed);
+ encodedField.setStaticValue(new DexValueString(renamed));
}
}
@@ -123,12 +128,16 @@
}
private void replaceIdentifierNameStringInField(DexEncodedField encodedField) {
- if (!(encodedField.staticValue instanceof DexValueString)) {
+ if (!encodedField.accessFlags.isStatic()) {
return;
}
- DexString original = ((DexValueString) encodedField.staticValue).getValue();
+ DexValue staticValue = encodedField.getStaticValue();
+ if (!(staticValue instanceof DexValueString)) {
+ return;
+ }
+ DexString original = ((DexValueString) staticValue).getValue();
if (original instanceof DexItemBasedString) {
- encodedField.staticValue = new DexValueString(materialize((DexItemBasedString) original));
+ encodedField.setStaticValue(new DexValueString(materialize((DexItemBasedString) original)));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index f6e087b..57e8514 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstString;
@@ -61,13 +62,17 @@
if (!identifierNameStrings.contains(encodedField.field)) {
return;
}
- if (!(encodedField.staticValue instanceof DexValueString)) {
+ if (!encodedField.accessFlags.isStatic()) {
return;
}
- DexString original = ((DexValueString) encodedField.staticValue).getValue();
+ DexValue staticValue = encodedField.getStaticValue();
+ if (!(staticValue instanceof DexValueString)) {
+ return;
+ }
+ DexString original = ((DexValueString) staticValue).getValue();
DexItemBasedString itemBasedString = inferMemberOrTypeFromNameString(appInfo, original);
if (itemBasedString != null) {
- encodedField.staticValue = new DexValueString(itemBasedString);
+ encodedField.setStaticValue(new DexValueString(itemBasedString));
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
index ab8342d..8d4a14b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardAlwaysInlineRule extends ProguardConfigurationRule {
@@ -29,7 +29,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
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 736c778..39d0335 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardAssumeNoSideEffectRule extends ProguardConfigurationRule {
@@ -28,7 +28,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(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 97cb5c3..0dafb11 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardAssumeValuesRule extends ProguardConfigurationRule {
public static class Builder extends ProguardClassSpecification.Builder {
@@ -27,7 +27,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(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 da5bb26..2461732 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
@@ -29,7 +29,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
index 0a71f43..fd1d305 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
public abstract class ProguardClassNameList {
@@ -74,6 +75,10 @@
public abstract boolean matches(DexType type);
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of();
+ }
+
public abstract void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer);
private static class EmptyClassNameList extends ProguardClassNameList {
@@ -135,6 +140,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return className.getWildcards();
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
consumer.accept(className);
}
@@ -180,6 +190,14 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return classNames.stream()
+ .map(ProguardTypeMatcher::getWildcards)
+ .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+ .collect(Collectors.toList());
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.forEach(consumer);
}
@@ -230,6 +248,14 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return classNames.keySet().stream()
+ .map(ProguardTypeMatcher::getWildcards)
+ .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+ .collect(Collectors.toList());
+ }
+
+ @Override
public void forEachTypeMatcher(Consumer<ProguardTypeMatcher> consumer) {
classNames.object2BooleanEntrySet().forEach(entry -> consumer.accept(entry.getKey()));
}
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 cb81639..653f0cf 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.utils.StringUtils;
-import java.util.Collections;
-import java.util.LinkedHashSet;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Objects;
-import java.util.Set;
public abstract class ProguardClassSpecification {
@@ -22,16 +22,16 @@
protected ProguardTypeMatcher inheritanceAnnotation;
protected ProguardTypeMatcher inheritanceClassName;
protected boolean inheritanceIsExtends = false;
- protected Set<ProguardMemberRule> memberRules = new LinkedHashSet<>();
+ protected List<ProguardMemberRule> memberRules = new LinkedList<>();
protected Builder() {
}
- public Set<ProguardMemberRule> getMemberRules() {
+ public List<ProguardMemberRule> getMemberRules() {
return memberRules;
}
- public void setMemberRules(Set<ProguardMemberRule> memberRules) {
+ public void setMemberRules(List<ProguardMemberRule> memberRules) {
this.memberRules = memberRules;
}
@@ -113,7 +113,7 @@
protected void matchAllSpecification() {
setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
- setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
+ setMemberRules(ImmutableList.of(ProguardMemberRule.defaultKeepAllRule()));
}
}
@@ -126,7 +126,7 @@
private final ProguardTypeMatcher inheritanceAnnotation;
private final ProguardTypeMatcher inheritanceClassName;
private final boolean inheritanceIsExtends;
- private final Set<ProguardMemberRule> memberRules;
+ private final List<ProguardMemberRule> memberRules;
protected ProguardClassSpecification(
ProguardTypeMatcher classAnnotation,
@@ -138,7 +138,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
this.classAnnotation = classAnnotation;
this.classAccessFlags = classAccessFlags;
this.negatedClassAccessFlags = negatedClassAccessFlags;
@@ -152,7 +152,7 @@
this.memberRules = memberRules;
}
- public Set<ProguardMemberRule> getMemberRules() {
+ 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 9b89374..568ba54 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -16,7 +18,6 @@
import com.android.tools.r8.shaking.ProguardConfiguration.Builder;
import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.LongInterval;
@@ -33,9 +34,12 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ProguardConfigurationParser {
@@ -511,7 +515,8 @@
// If there are no member rules, a default rule for the parameterless constructor
// applies. So we add that here.
ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
- defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+ defaultRuleBuilder.setName(
+ IdentifierPatternWithWildcards.withoutWildcards(Constants.INSTANCE_INITIALIZER_NAME));
defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
defaultRuleBuilder.setArguments(Collections.emptyList());
keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
@@ -565,10 +570,33 @@
if (acceptString("-keep")) {
ProguardKeepRule subsequentRule = parseKeepRule();
ifRuleBuilder.setSubsequentRule(subsequentRule);
- return ifRuleBuilder.build();
+ ProguardIfRule ifRule = ifRuleBuilder.build();
+ verifyWildcardRange(ifRule.getWildcards());
+ return ifRule;
}
throw reporter.fatalError(new StringDiagnostic(
- "Option -if without a subsequent keep rule.", origin, getPosition(optionStart)));
+ "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart)));
+ }
+
+ private void verifyWildcardRange(Iterable<String> wildcards) {
+ Pattern backReference = Pattern.compile("<(\\d+)>");
+ int i = 1;
+ Iterator<String> iterator = wildcards.iterator();
+ while (iterator.hasNext()) {
+ String wildcard = iterator.next();
+ Matcher m = backReference.matcher(wildcard);
+ if (m.matches()) {
+ int n = Integer.parseInt(m.group(1));
+ if (i <= n) {
+ throw reporter.fatalError(new StringDiagnostic(
+ "Wildcard <" + n + "> is invalid.", origin, getPosition()));
+ }
+ } else {
+ // Increase the index of wildcards for non-back-reference only
+ // to not allow one back-reference to point to another back-reference
+ i++;
+ }
+ }
}
private void parseClassSpec(
@@ -639,14 +667,16 @@
skipWhitespace();
int startPosition = position;
if (acceptChar('@')) {
- String className = parseClassName();
+ IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+ String className = identifierPatternWithWildcards.pattern;
if (className.equals("interface")) {
// Not an annotation after all but a class type. Move position back to start
// so this can be dealt with as a class type instead.
position = startPosition;
return null;
}
- return ProguardTypeMatcher.create(className, ClassOrType.CLASS, dexItemFactory);
+ return ProguardTypeMatcher.create(
+ identifierPatternWithWildcards, ClassOrType.CLASS, dexItemFactory);
}
return null;
}
@@ -815,13 +845,14 @@
ruleBuilder.setRuleType(ProguardMemberType.ALL_FIELDS);
} else if (acceptString("<init>")) {
ruleBuilder.setRuleType(ProguardMemberType.INIT);
- ruleBuilder.setName("<init>");
+ ruleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
ruleBuilder.setArguments(parseArgumentList());
} else {
- String first = acceptIdentifierWithBackreference(IdentifierType.ANY);
+ IdentifierPatternWithWildcards first =
+ acceptIdentifierWithBackreference(IdentifierType.ANY);
if (first != null) {
skipWhitespace();
- if (first.equals("*") && hasNextChar(';')) {
+ if (first.pattern.equals("*") && hasNextChar(';')) {
ruleBuilder.setRuleType(ProguardMemberType.ALL);
} else {
if (hasNextChar('(')) {
@@ -829,7 +860,8 @@
ruleBuilder.setName(first);
ruleBuilder.setArguments(parseArgumentList());
} else {
- String second = acceptIdentifierWithBackreference(IdentifierType.ANY);
+ IdentifierPatternWithWildcards second =
+ acceptIdentifierWithBackreference(IdentifierType.ANY);
if (second != null) {
skipWhitespace();
if (hasNextChar('(')) {
@@ -880,7 +912,7 @@
.getTypeMatcher()).type;
DexType fieldClass =
dexItemFactory.createType(
- DescriptorUtils.javaTypeToDescriptor(
+ javaTypeToDescriptor(
qualifiedFieldNameOrInteger.substring(0, lastDotIndex)));
DexString fieldName =
dexItemFactory.createString(
@@ -918,13 +950,16 @@
return arguments;
}
if (acceptString("...")) {
- arguments
- .add(ProguardTypeMatcher.create("...", ClassOrType.TYPE, dexItemFactory));
+ arguments.add(ProguardTypeMatcher.create(
+ IdentifierPatternWithWildcards.withoutWildcards("..."),
+ ClassOrType.TYPE,
+ dexItemFactory));
} else {
- for (String name = parseClassName(); name != null; name =
- acceptChar(',') ? parseClassName() : null) {
- arguments
- .add(ProguardTypeMatcher.create(name, ClassOrType.TYPE, dexItemFactory));
+ for (IdentifierPatternWithWildcards identifierPatternWithWildcards = parseClassName();
+ identifierPatternWithWildcards != null;
+ identifierPatternWithWildcards = acceptChar(',') ? parseClassName() : null) {
+ arguments.add(ProguardTypeMatcher.create(
+ identifierPatternWithWildcards, ClassOrType.TYPE, dexItemFactory));
skipWhitespace();
}
}
@@ -1126,13 +1161,17 @@
return acceptString(CLASS_NAME_PREDICATE);
}
- private String acceptIdentifierWithBackreference(IdentifierType kind) {
+ private IdentifierPatternWithWildcards acceptIdentifierWithBackreference(IdentifierType kind) {
+ ImmutableList.Builder<String> wildcardsCollector = ImmutableList.builder();
+ StringBuilder currentAsterisks = null;
StringBuilder currentBackreference = null;
skipWhitespace();
int start = position;
int end = position;
while (!eof(end)) {
int current = contents.codePointAt(end);
+ // Should not be both in asterisk collecting state and back reference collecting state.
+ assert currentAsterisks == null || currentBackreference == null;
if (currentBackreference != null) {
if (current == '>') {
try {
@@ -1146,29 +1185,55 @@
"Wildcard <" + currentBackreference.toString() + "> is invalid.",
origin, getPosition()));
}
+ wildcardsCollector.add("<" + currentBackreference.toString() + ">");
currentBackreference = null;
+ end += Character.charCount(current);
+ continue;
} else if (('0' <= current && current <= '9')
- // Only collect integer literal for the backreference.
+ // Only collect integer literal for the back reference.
|| (current == '-' && currentBackreference.length() == 0)) {
currentBackreference.append((char) current);
+ end += Character.charCount(current);
+ continue;
} else if (kind == IdentifierType.CLASS_NAME) {
throw reporter.fatalError(new StringDiagnostic(
"Use of generics not allowed for java type.", origin, getPosition()));
} else {
// If not parsing a class name allow identifiers including <'s by canceling the
- // collection of the backreference.
+ // collection of the back reference.
currentBackreference = null;
}
+ } else if (currentAsterisks != null) {
+ if (current == '*') {
+ currentAsterisks.append((char) current);
+ end += Character.charCount(current);
+ continue;
+ } else {
+ wildcardsCollector.add(currentAsterisks.toString());
+ currentAsterisks = null;
+ }
+ }
+ // From now on, neither in asterisk collecting state nor back reference collecting state.
+ assert currentAsterisks == null && currentBackreference == null;
+ if (current == '*') {
+ currentAsterisks = new StringBuilder();
+ currentAsterisks.append((char) current);
+ end += Character.charCount(current);
+ } else if (current == '?' || current == '%') {
+ wildcardsCollector.add(String.valueOf((char) current));
end += Character.charCount(current);
} else if (CLASS_NAME_PREDICATE.test(current) || current == '>') {
end += Character.charCount(current);
} else if (current == '<') {
currentBackreference = new StringBuilder();
- ++end;
+ end += Character.charCount(current);
} else {
break;
}
}
+ if (currentAsterisks != null) {
+ wildcardsCollector.add(currentAsterisks.toString());
+ }
if (kind == IdentifierType.CLASS_NAME && currentBackreference != null) {
// Proguard 6 reports this error message, so try to be compatible.
throw reporter.fatalError(
@@ -1178,7 +1243,9 @@
return null;
}
position = end;
- return contents.substring(start, end);
+ return new IdentifierPatternWithWildcards(
+ contents.substring(start, end),
+ wildcardsCollector.build());
}
private String acceptFieldNameOrIntegerForReturn() {
@@ -1292,8 +1359,9 @@
return name == null ? "" : name;
}
- private String parseClassName() throws ProguardRuleParserException {
- String name = acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
+ private IdentifierPatternWithWildcards parseClassName() throws ProguardRuleParserException {
+ IdentifierPatternWithWildcards name =
+ acceptIdentifierWithBackreference(IdentifierType.CLASS_NAME);
if (name == null) {
throw parseError("Class name expected");
}
@@ -1408,4 +1476,22 @@
return position - lineStartPosition + 1 /* column starts at 1 */;
}
}
+
+ static class IdentifierPatternWithWildcards {
+ final String pattern;
+ final List<String> wildcards;
+
+ IdentifierPatternWithWildcards(String pattern, List<String> wildcards) {
+ this.pattern = pattern;
+ this.wildcards = wildcards;
+ }
+
+ static IdentifierPatternWithWildcards withoutWildcards(String pattern) {
+ return new IdentifierPatternWithWildcards(pattern, ImmutableList.of());
+ }
+
+ boolean isMatchAllNames() {
+ return pattern.equals("*");
+ }
+ }
}
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 af1948c..9791def 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -4,7 +4,11 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.utils.StringUtils;
-import java.util.Set;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
public abstract class ProguardConfigurationRule extends ProguardClassSpecification {
ProguardConfigurationRule(
@@ -17,7 +21,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
@@ -32,6 +36,25 @@
return false;
}
+ protected Iterable<String> getWildcards() {
+ ProguardTypeMatcher classAnnotation = getClassAnnotation();
+ ProguardTypeMatcher inheritanceAnnotation = getInheritanceAnnotation();
+ ProguardTypeMatcher inheritanceClassName = getInheritanceClassName();
+ List<ProguardMemberRule> memberRules = getMemberRules();
+ return Iterables.concat(
+ classAnnotation != null ? classAnnotation.getWildcards() : ImmutableList.of(),
+ getClassNames().getWildcards(),
+ inheritanceAnnotation != null ? inheritanceAnnotation.getWildcards() : ImmutableList.of(),
+ inheritanceClassName != null ? inheritanceClassName.getWildcards() : ImmutableList.of(),
+ memberRules != null
+ ? memberRules.stream()
+ .map(ProguardMemberRule::getWildcards)
+ .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+ .collect(Collectors.toList())
+ : ImmutableList.of()
+ );
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardConfigurationRule)) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 5852cd9..400422f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;
@@ -30,7 +31,7 @@
if (clazz.hasDefaultInitializer()) {
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.INIT);
- memberRuleBuilder.setName("<init>");
+ memberRuleBuilder.setName(IdentifierPatternWithWildcards.withoutWildcards("<init>"));
memberRuleBuilder.setArguments(ImmutableList.of());
builder.getMemberRules().add(memberRuleBuilder.build());
}
@@ -54,7 +55,8 @@
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
memberRuleBuilder.getAccessFlags().setFlags(field.accessFlags);
- memberRuleBuilder.setName(field.field.name.toString());
+ memberRuleBuilder.setName(
+ IdentifierPatternWithWildcards.withoutWildcards(field.field.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.field.type));
builder.getMemberRules().add(memberRuleBuilder.build());
return builder.build();
@@ -77,7 +79,8 @@
ProguardMemberRule.Builder memberRuleBuilder = ProguardMemberRule.builder();
memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
memberRuleBuilder.getAccessFlags().setFlags(method.accessFlags);
- memberRuleBuilder.setName(method.method.name.toString());
+ 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)
@@ -96,13 +99,15 @@
DexField field = (DexField) item;
holderType = field.getHolder();
memberRuleBuilder.setRuleType(ProguardMemberType.FIELD);
- memberRuleBuilder.setName(field.name.toString());
+ memberRuleBuilder.setName(
+ IdentifierPatternWithWildcards.withoutWildcards(field.name.toString()));
memberRuleBuilder.setTypeMatcher(ProguardTypeMatcher.create(field.type));
} else {
DexMethod method = (DexMethod) item;
holderType = method.getHolder();
memberRuleBuilder.setRuleType(ProguardMemberType.METHOD);
- memberRuleBuilder.setName(method.name.toString());
+ 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)
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 f0d7c63..07ffd85 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIdentifierNameStringRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardIdentifierNameStringRule extends ProguardConfigurationRule {
@@ -28,7 +28,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
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 9659954..67a0572 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import com.google.common.collect.Iterables;
+import java.util.List;
public class ProguardIfRule extends ProguardKeepRule {
@@ -32,7 +33,7 @@
ProguardClassType classType, ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName, boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules,
+ List<ProguardMemberRule> memberRules,
ProguardKeepRule subsequentRule) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules,
@@ -45,6 +46,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return Iterables.concat(super.getWildcards(), subsequentRule.getWildcards());
+ }
+
+ @Override
public boolean equals(Object o) {
if (!(o instanceof ProguardIfRule)) {
return false;
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 c4731c6..66fdd50 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
@@ -29,7 +29,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
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 d3d6705..8756f1a 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
import java.util.function.Consumer;
public class ProguardKeepRule extends ProguardConfigurationRule {
@@ -11,8 +11,8 @@
public static class Builder extends ProguardClassSpecification.Builder {
private ProguardKeepRuleType type;
- private final ProguardKeepRuleModifiers.Builder modifiersBuilder
- = ProguardKeepRuleModifiers.builder();
+ private final ProguardKeepRuleModifiers.Builder modifiersBuilder =
+ ProguardKeepRuleModifiers.builder();
protected Builder() {}
@@ -44,7 +44,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules,
+ List<ProguardMemberRule> memberRules,
ProguardKeepRuleType type,
ProguardKeepRuleModifiers modifiers) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
index 5e1293f..1e089a9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRule.java
@@ -7,9 +7,14 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
public class ProguardMemberRule {
@@ -19,8 +24,8 @@
private ProguardAccessFlags accessFlags = new ProguardAccessFlags();
private ProguardAccessFlags negatedAccessFlags = new ProguardAccessFlags();
private ProguardMemberType ruleType;
- private ProguardNameMatcher name;
private ProguardTypeMatcher type;
+ private ProguardNameMatcher name;
private List<ProguardTypeMatcher> arguments;
private ProguardMemberRuleReturnValue returnValue;
@@ -50,10 +55,6 @@
this.ruleType = ruleType;
}
- public void setName(String name) {
- this.name = ProguardNameMatcher.create(name);
- }
-
public ProguardTypeMatcher getTypeMatcher() {
return type;
}
@@ -62,6 +63,10 @@
this.type = type;
}
+ public void setName(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+ this.name = ProguardNameMatcher.create(identifierPatternWithWildcards);
+ }
+
public void setArguments(List<ProguardTypeMatcher> arguments) {
this.arguments = arguments;
}
@@ -76,8 +81,8 @@
public ProguardMemberRule build() {
assert isValid();
- return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, name,
- type, arguments, returnValue);
+ return new ProguardMemberRule(annotation, accessFlags, negatedAccessFlags, ruleType, type,
+ name, arguments, returnValue);
}
}
@@ -85,8 +90,8 @@
private final ProguardAccessFlags accessFlags;
private final ProguardAccessFlags negatedAccessFlags;
private final ProguardMemberType ruleType;
- private final ProguardNameMatcher name;
private final ProguardTypeMatcher type;
+ private final ProguardNameMatcher name;
private final List<ProguardTypeMatcher> arguments;
private final ProguardMemberRuleReturnValue returnValue;
@@ -95,17 +100,17 @@
ProguardAccessFlags accessFlags,
ProguardAccessFlags negatedAccessFlags,
ProguardMemberType ruleType,
- ProguardNameMatcher name,
ProguardTypeMatcher type,
+ ProguardNameMatcher name,
List<ProguardTypeMatcher> arguments,
ProguardMemberRuleReturnValue returnValue) {
this.annotation = annotation;
this.accessFlags = accessFlags;
this.negatedAccessFlags = negatedAccessFlags;
this.ruleType = ruleType;
- this.name = name;
this.type = type;
- this.arguments = arguments != null ? ImmutableList.copyOf(arguments) : null;
+ this.name = name;
+ this.arguments = arguments != null ? Collections.unmodifiableList(arguments) : null;
this.returnValue = returnValue;
}
@@ -132,14 +137,14 @@
return ruleType;
}
- public ProguardNameMatcher getName() {
- return name;
- }
-
public ProguardTypeMatcher getType() {
return type;
}
+ public ProguardNameMatcher getName() {
+ return name;
+ }
+
public List<ProguardTypeMatcher> getArguments() {
return arguments;
}
@@ -261,6 +266,20 @@
return false;
}
+ Iterable<String> getWildcards() {
+ return Iterables.concat(
+ annotation != null ? annotation.getWildcards() : ImmutableList.of(),
+ type != null ? type.getWildcards() : ImmutableList.of(),
+ name != null ? name.getWildcards() : ImmutableList.of(),
+ arguments != null
+ ? arguments.stream()
+ .map(ProguardTypeMatcher::getWildcards)
+ .flatMap(it -> StreamSupport.stream(it.spliterator(), false))
+ .collect(Collectors.toList())
+ : ImmutableList.of()
+ );
+ }
+
@Override
public boolean equals(Object o) {
if (!(o instanceof ProguardMemberRule)) {
@@ -296,8 +315,8 @@
result = 31 * result + accessFlags.hashCode();
result = 31 * result + negatedAccessFlags.hashCode();
result = 31 * result + (ruleType != null ? ruleType.hashCode() : 0);
- result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (type != null ? type.hashCode() : 0);
+ result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (arguments != null ? arguments.hashCode() : 0);
return result;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
index 9286d0a..b8f583d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardNameMatcher.java
@@ -3,21 +3,25 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+
public abstract class ProguardNameMatcher {
private static final ProguardNameMatcher MATCH_ALL_NAMES = new MatchAllNames();
private ProguardNameMatcher() {
-
}
- public static ProguardNameMatcher create(String pattern) {
- if (pattern.equals("*")) {
+ public static ProguardNameMatcher create(
+ IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+ if (identifierPatternWithWildcards.isMatchAllNames()) {
return MATCH_ALL_NAMES;
- } else if (pattern.contains("*") || pattern.contains("?")) {
- return new MatchNamePattern(pattern);
+ } else if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+ return new MatchSpecificName(identifierPatternWithWildcards.pattern);
} else {
- return new MatchSpecificName(pattern);
+ return new MatchNamePattern(identifierPatternWithWildcards);
}
}
@@ -63,6 +67,10 @@
public abstract boolean matches(String name);
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of();
+ }
+
private static class MatchAllNames extends ProguardNameMatcher {
@Override
@@ -71,6 +79,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of("*");
+ }
+
+ @Override
public String toString() {
return "*";
}
@@ -79,9 +92,11 @@
private static class MatchNamePattern extends ProguardNameMatcher {
private final String pattern;
+ private final List<String> wildcards;
- MatchNamePattern(String pattern) {
- this.pattern = pattern;
+ MatchNamePattern(IdentifierPatternWithWildcards identifierPatternWithWildcards) {
+ this.pattern = identifierPatternWithWildcards.pattern;
+ this.wildcards = identifierPatternWithWildcards.wildcards;
}
@Override
@@ -90,6 +105,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return wildcards;
+ }
+
+ @Override
public String toString() {
return pattern;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 809278f..51d11bb 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -3,9 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor;
+
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
public abstract class ProguardTypeMatcher {
@@ -25,6 +29,10 @@
public abstract boolean matches(DexType type);
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of();
+ }
+
@Override
public abstract String toString();
@@ -32,12 +40,14 @@
return false;
}
- public static ProguardTypeMatcher create(String pattern, ClassOrType kind,
+ public static ProguardTypeMatcher create(
+ IdentifierPatternWithWildcards identifierPatternWithWildcards,
+ ClassOrType kind,
DexItemFactory dexItemFactory) {
- if (pattern == null) {
+ if (identifierPatternWithWildcards == null || identifierPatternWithWildcards.pattern == null) {
return null;
}
- switch (pattern) {
+ switch (identifierPatternWithWildcards.pattern) {
case MATCH_ALL_PATTERN:
return MatchAllTypes.MATCH_ALL_TYPES;
case MATCH_ANY_ARG_SEQUENCE_PATTERN:
@@ -49,11 +59,11 @@
case MATCH_BASIC_PATTERN:
return MatchBasicTypes.MATCH_BASIC_TYPES;
default:
- if (!pattern.contains("*") && !pattern.contains("%") && !pattern.contains("?")) {
- return new MatchSpecificType(
- dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(pattern)));
+ if (identifierPatternWithWildcards.wildcards.isEmpty()) {
+ return new MatchSpecificType(dexItemFactory.createType(
+ javaTypeToDescriptor(identifierPatternWithWildcards.pattern)));
}
- return new MatchTypePattern(pattern, kind);
+ return new MatchTypePattern(identifierPatternWithWildcards, kind);
}
}
@@ -85,6 +95,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of(MATCH_ALL_PATTERN);
+ }
+
+ @Override
public String toString() {
return MATCH_ALL_PATTERN;
}
@@ -150,6 +165,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of(pattern);
+ }
+
+ @Override
public String toString() {
return pattern;
}
@@ -175,6 +195,11 @@
}
@Override
+ protected Iterable<String> getWildcards() {
+ return ImmutableList.of(MATCH_BASIC_PATTERN);
+ }
+
+ @Override
public String toString() {
return MATCH_BASIC_PATTERN;
}
@@ -230,10 +255,13 @@
private static class MatchTypePattern extends ProguardTypeMatcher {
private final String pattern;
+ private final List<String> wildcards;
private final ClassOrType kind;
- private MatchTypePattern(String pattern, ClassOrType kind) {
- this.pattern = pattern;
+ private MatchTypePattern(
+ IdentifierPatternWithWildcards identifierPatternWithWildcards, ClassOrType kind) {
+ this.pattern = identifierPatternWithWildcards.pattern;
+ this.wildcards = identifierPatternWithWildcards.wildcards;
this.kind = kind;
}
@@ -244,6 +272,11 @@
return matchClassOrTypeNameImpl(pattern, 0, typeName, 0, kind);
}
+ @Override
+ protected Iterable<String> getWildcards() {
+ return wildcards;
+ }
+
private static boolean matchClassOrTypeNameImpl(
String pattern, int patternIndex, String className, int nameIndex, ClassOrType kind) {
for (int i = patternIndex; i < pattern.length(); i++) {
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 ef83926..73b5538 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import java.util.Set;
+import java.util.List;
public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
@@ -29,7 +29,7 @@
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
- Set<ProguardMemberRule> memberRules) {
+ List<ProguardMemberRule> memberRules) {
super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index 587d947..46cd555 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -143,6 +143,92 @@
}
}
+ static class B78901754 {
+ public static class A {
+ public final String msg;
+
+ public A(String msg) {
+ this.msg = msg;
+ }
+ }
+
+ public static class B extends A {
+ public B(String msg) {
+ super(msg);
+ }
+ }
+
+ public interface IAA {
+ A[] foo(A[] p);
+ }
+
+ public interface IAB {
+ A[] foo(B[] p);
+ }
+
+ public interface IBA {
+ B[] foo(A[] p);
+ }
+
+ public interface IBB {
+ B[] foo(B[] p);
+ }
+
+ public static A[] fooAA(A[] p) {
+ return new A[]{new A("fooAA")};
+ }
+
+ public static A[] fooBA(B[] p) {
+ return new A[]{new A("fooBA")};
+ }
+
+ public static B[] fooAB(A[] p) {
+ return new B[]{new B("fooAB")};
+ }
+
+ public static B[] fooBB(B[] p) {
+ return new B[]{new B("fooBB")};
+ }
+
+ public static void testAA(IAA i) {
+ System.out.println(i.foo(null)[0].msg);
+ }
+
+ public static void testAB(IAB i) {
+ System.out.println(i.foo(null)[0].msg);
+ }
+
+ public static void testBA(IBA i) {
+ System.out.println(i.foo(null)[0].msg);
+ }
+
+ public static void testBB(IBB i) {
+ System.out.println(i.foo(null)[0].msg);
+ }
+
+ public static void test() {
+ testAA(B78901754::fooAA);
+ testAA(B78901754::fooAB);
+ // testAA(B78901754::fooBA); javac error: incompatible types: A[] cannot be converted to B[]
+ // testAA(B78901754::fooBB); javac error: incompatible types: A[] cannot be converted to B[]
+
+ testAB(B78901754::fooAA);
+ testAB(B78901754::fooAB);
+ testAB(B78901754::fooBA);
+ testAB(B78901754::fooBB);
+
+ // testBA(B78901754::fooAA); javac error: A[] cannot be converted to B[]
+ testBA(B78901754::fooAB);
+ // testBA(B78901754::fooBA); javac error: incompatible types: A[] cannot be converted to B[]
+ // testBA(B78901754::fooBB); javac error: incompatible types: A[] cannot be converted to B[]
+
+ // testBB(B78901754::fooAA); javac error: A[] cannot be converted to B[]
+ testBB(B78901754::fooAB);
+ // testBB(B78901754::fooBA); javac error: A[] cannot be converted to B[]
+ testBB(B78901754::fooBB);
+ }
+ }
+
interface B38257037_I1 {
default Number getNumber() {
return new Integer(1);
@@ -420,5 +506,6 @@
B38308515.test();
B38302860.test();
B62168701.test();
+ B78901754.test();
}
}
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
index 2ec147b..18c3436 100644
--- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -53,6 +53,8 @@
makeTest("returns.Returns"),
makeTest("staticfield.StaticField"),
makeTest("stringbuilding.StringBuilding"),
+ makeTest("switches.Switches"),
+ makeTest("sync.Sync"),
makeTest("throwing.Throwing"),
makeTest("trivial.Trivial"),
makeTest("trycatch.TryCatch"),
@@ -65,25 +67,20 @@
makeTest("regress_37658666.Regress", CfFrontendExamplesTest::compareRegress37658666),
makeTest("regress_37875803.Regress"),
makeTest("regress_37955340.Regress"),
- // TODO(mathiasr): This test fails because we remove ASM's java.lang.Synthetic annotations.
- // makeTest("regress_62300145.Regress"),
+ makeTest("regress_62300145.Regress"),
makeTest("regress_64881691.Regress"),
makeTest("regress_65104300.Regress"),
makeTest("regress_70703087.Test"),
makeTest("regress_70736958.Test"),
makeTest("regress_70737019.Test"),
makeTest("regress_72361252.Test"),
+ makeTest("memberrebinding2.Memberrebinding"),
makeTest("memberrebinding3.Memberrebinding"),
makeTest("minification.Minification"),
makeTest("enclosingmethod.Main"),
makeTest("enclosingmethod_proguarded.Main"),
- makeTest("interfaceinlining.Main")
- // TODO(mathiasr): These fail because we add a zero initializer to an field with no initializer.
- // makeTest("sync.Sync"),
- // makeTest("memberrebinding2.Memberrebinding"),
- // TODO(mathiasr): Support emitting table switches in CfSwitch.
- // makeTest("switches.Switches"),
- // makeTest("switchmaps.Switches"),
+ makeTest("interfaceinlining.Main"),
+ makeTest("switchmaps.Switches")
);
private static Object[] makeTest(String className) {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index f276c46..c7fcdcc 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1209,6 +1209,11 @@
return result.stdout;
}
+ public static ProcessResult runProguardRaw(Path inJar, Path outJar, List<Path> config, Path map)
+ throws IOException {
+ return runProguardRaw(getProguardScript(), inJar, outJar, config, map);
+ }
+
public static ProcessResult runProguardRaw(Path inJar, Path outJar, Path config, Path map)
throws IOException {
return runProguardRaw(getProguardScript(), inJar, outJar, ImmutableList.of(config), map);
@@ -1230,6 +1235,11 @@
}
public static ProcessResult runProguard6Raw(
+ Path inJar, Path outJar, List<Path> config, Path map) throws IOException {
+ return runProguardRaw(getProguard6Script(), inJar, outJar, config, map);
+ }
+
+ public static ProcessResult runProguard6Raw(
Path inJar, Path outJar, Path lib, Path config, Path map) throws IOException {
return runProguardRaw(getProguard6Script(), inJar, outJar, lib, ImmutableList.of(config), map);
}
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
index d371243..45e3c96 100644
--- a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -4,36 +4,195 @@
package com.android.tools.r8.debug;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.Pair;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.stream.Collectors;
import org.apache.harmony.jpda.tests.framework.jdwp.Value;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+@RunWith(Parameterized.class)
public class ContinuousSteppingTest extends DebugTestBase {
- private static DebugTestConfig javaD8Config;
- private static DebugTestConfig kotlinD8Config;
+ private static final String MAIN_METHOD_NAME = "main";
+
+ // A list of self-contained jars to process (which do not depend on other jar files).
+ private static final List<Pair<Path, Predicate<Version>>> LIST_OF_JARS = new ConfigListBuilder()
+ .add(DebugTestBase.DEBUGGEE_JAR, ContinuousSteppingTest::allVersions)
+ .add(DebugTestBase.DEBUGGEE_JAVA8_JAR, ContinuousSteppingTest::allVersions)
+ .add(KotlinD8Config.DEBUGGEE_KOTLIN_JAR, ContinuousSteppingTest::allVersions)
+ .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_N_BUILD_DIR)),
+ ContinuousSteppingTest::fromAndroidN)
+ .addAll(findAllJarsIn(Paths.get(ToolHelper.EXAMPLES_ANDROID_O_BUILD_DIR)),
+ ContinuousSteppingTest::fromAndroidO)
+ .build();
+
+ private static final Map<Path, DebugTestConfig> compiledJarConfig = new HashMap<>();
+
+ private final String mainClass;
+ private final Path jarPath;
+
+ private static class ConfigListBuilder {
+
+ private final Builder<Pair<Path, Predicate<Version>>> builder = ImmutableList.builder();
+
+ public ConfigListBuilder add(Path path, Predicate<Version> predicate) {
+ builder.add(new Pair<>(path, predicate));
+ return this;
+ }
+
+ public ConfigListBuilder addAll(List<Path> paths, Predicate<Version> predicate) {
+ for (Path path : paths) {
+ add(path, predicate);
+ }
+ return this;
+ }
+
+ public List<Pair<Path, Predicate<Version>>> build() {
+ return builder.build();
+ }
+ }
+
+ public static boolean allVersions(Version dexVmVersion) {
+ return true;
+ }
+
+ public static boolean fromAndroidN(Version dexVmVersion) {
+ return dexVmVersion.isAtLeast(Version.V7_0_0);
+ }
+
+ public static boolean fromAndroidO(Version dexVmVersion) {
+ return dexVmVersion.isAtLeast(Version.DEFAULT);
+ }
+
+ private static List<Path> findAllJarsIn(Path root) {
+ try {
+ return Files.walk(root)
+ .filter(p -> p.toFile().getPath().endsWith(FileUtils.JAR_EXTENSION))
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ return Collections.emptyList();
+ }
+ }
@BeforeClass
public static void setup() {
- javaD8Config = new D8DebugTestResourcesConfig(temp);
- kotlinD8Config = new KotlinD8Config(temp);
+ LIST_OF_JARS.forEach(pair -> {
+ if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
+ Path jarPath = pair.getFirst();
+ DebugTestConfig config = new D8DebugTestConfig().compileAndAdd(temp, jarPath);
+ compiledJarConfig.put(jarPath, config);
+ }
+ });
+ }
+
+ @Parameters(name = "{0} from {1}")
+ public static Collection<Object[]> getData() throws IOException {
+ List<Object[]> testCases = new ArrayList<>();
+ for (Pair<Path, Predicate<Version>> pair : LIST_OF_JARS) {
+ if (pair.getSecond().test(ToolHelper.getDexVm().getVersion())) {
+ Path jarPath = pair.getFirst();
+ List<String> mainClasses = getAllMainClassesFromJar(jarPath);
+ for (String className : mainClasses) {
+ testCases.add(new Object[]{className, jarPath});
+ }
+ }
+ }
+ return testCases;
+ }
+
+ public ContinuousSteppingTest(String mainClass, Path jarPath) {
+ this.mainClass = mainClass;
+ this.jarPath = jarPath;
}
@Test
- public void testArithmetic() throws Throwable {
- runContinuousTest("Arithmetic", javaD8Config);
+ public void testContinuousSingleStep() throws Throwable {
+ assert compiledJarConfig.containsKey(jarPath);
+ DebugTestConfig config = compiledJarConfig.get(jarPath);
+ assert config != null;
+ runContinuousTest(mainClass, config);
}
- @Test
- public void testLocals() throws Throwable {
- runContinuousTest("Locals", javaD8Config);
+ // Returns a list of classes with a "public static void main(String[])" method in the given jar
+ // file.
+ private static List<String> getAllMainClassesFromJar(Path pathToJar) throws IOException {
+ JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(pathToJar,
+ StandardOpenOption.READ));
+ final URL url = pathToJar.toUri().toURL();
+ assert pathToJar.toFile().exists();
+ assert pathToJar.toFile().isFile();
+ List<String> mainClasses = new ArrayList<>();
+ ClassLoader loader = new URLClassLoader(new URL[]{url},
+ Thread.currentThread().getContextClassLoader());
+
+ try {
+ JarEntry entry;
+ while ((entry = jarInputStream.getNextJarEntry()) != null) {
+ String entryName = entry.getName();
+ if (entryName.endsWith(FileUtils.CLASS_EXTENSION)) {
+ String className =
+ entryName.substring(0, entryName.length() - FileUtils.CLASS_EXTENSION.length());
+ className = className.replace(DescriptorUtils.DESCRIPTOR_PACKAGE_SEPARATOR,
+ DescriptorUtils.JAVA_PACKAGE_SEPARATOR);
+ try {
+ Class<?> cls = loader.loadClass(className);
+ if (cls != null) {
+ long mainMethodsCount = Arrays.stream(cls.getMethods())
+ .filter(ContinuousSteppingTest::isMainMethod)
+ .count();
+ if (mainMethodsCount == 1) {
+ // Add class to the list
+ mainClasses.add(className);
+ }
+ }
+ } catch (Throwable e) {
+ System.out.println(
+ "Could not load class " + className + " from " + pathToJar.toFile().getPath());
+ return Collections.emptyList();
+ }
+ }
+ }
+ } finally {
+ jarInputStream.close();
+ }
+ return mainClasses;
}
- @Test
- public void testKotlinInline() throws Throwable {
- runContinuousTest("KotlinInline", kotlinD8Config);
+ private static boolean isMainMethod(Method m) {
+ return Modifier.isStatic(m.getModifiers())
+ && m.getReturnType() == void.class
+ && m.getName().equals(MAIN_METHOD_NAME)
+ && m.getParameterCount() == 1
+ && m.getParameterTypes()[0] == String[].class;
}
private void runContinuousTest(String debuggeeClassName, DebugTestConfig config)
@@ -41,7 +200,7 @@
runDebugTest(
config,
debuggeeClassName,
- breakpoint(debuggeeClassName, "main"),
+ breakpoint(debuggeeClassName, MAIN_METHOD_NAME),
run(),
stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, debuggeeState -> {
// Fetch local variables.
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
index fa5f5ca..d828a5e 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinD8Config.java
@@ -17,7 +17,7 @@
*/
class KotlinD8Config extends D8DebugTestConfig {
- private static final Path DEBUGGEE_KOTLIN_JAR =
+ public static final Path DEBUGGEE_KOTLIN_JAR =
Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
private static AndroidApp compiledResources = null;
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index fa39257..f27c88a 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -303,9 +303,10 @@
private static int countRenamedClassIdentifier(
DexInspector inspector, DexEncodedField[] fields) {
return Arrays.stream(fields)
- .filter(encodedField -> encodedField.staticValue instanceof DexValueString)
+ .filter(encodedField -> encodedField.getStaticValue() instanceof DexValueString)
.reduce(0, (cnt, encodedField) -> {
- String cnstString = ((DexValueString) encodedField.staticValue).getValue().toString();
+ String cnstString =
+ ((DexValueString) encodedField.getStaticValue()).getValue().toString();
if (isValidJavaType(cnstString)) {
ClassSubject classSubject = inspector.clazz(cnstString);
if (classSubject.isRenamed()
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
index 51772fe..087b5f8 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/StaticValuesTest.java
@@ -100,47 +100,47 @@
inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
DexValue value;
- assertTrue(inspector.clazz("Test").field("boolean", "booleanField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("boolean", "booleanField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("boolean", "booleanField").getStaticValue();
assertTrue(value instanceof DexValueBoolean);
assertEquals(true, ((DexValueBoolean) value).getValue());
- assertTrue(inspector.clazz("Test").field("byte", "byteField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("byte", "byteField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("byte", "byteField").getStaticValue();
assertTrue(value instanceof DexValueByte);
assertEquals(1, ((DexValueByte) value).getValue());
- assertTrue(inspector.clazz("Test").field("short", "shortField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("short", "shortField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("short", "shortField").getStaticValue();
assertTrue(value instanceof DexValueShort);
assertEquals(2, ((DexValueShort) value).getValue());
- assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("int", "intField").getStaticValue();
assertTrue(value instanceof DexValueInt);
assertEquals(3, ((DexValueInt) value).getValue());
- assertTrue(inspector.clazz("Test").field("long", "longField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("long", "longField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("long", "longField").getStaticValue();
assertTrue(value instanceof DexValueLong);
assertEquals(4, ((DexValueLong) value).getValue());
- assertTrue(inspector.clazz("Test").field("float", "floatField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("float", "floatField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("float", "floatField").getStaticValue();
assertTrue(value instanceof DexValueFloat);
assertEquals(5.0f, ((DexValueFloat) value).getValue(), 0.0);
- assertTrue(inspector.clazz("Test").field("double", "doubleField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("double", "doubleField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("double", "doubleField").getStaticValue();
assertTrue(value instanceof DexValueDouble);
assertEquals(6.0f, ((DexValueDouble) value).getValue(), 0.0);
- assertTrue(inspector.clazz("Test").field("char", "charField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("char", "charField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("char", "charField").getStaticValue();
assertTrue(value instanceof DexValueChar);
assertEquals(0x30 + 7, ((DexValueChar) value).getValue());
- assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
assertTrue(value instanceof DexValueString);
assertEquals(("8"), ((DexValueString) value).getValue().toString());
@@ -317,12 +317,12 @@
inspector.clazz("Test").clinit().getMethod().getCode().asDexCode().isEmptyVoidMethod());
DexValue value;
- assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("int", "intField").getStaticValue();
assertTrue(value instanceof DexValueInt);
assertEquals(3, ((DexValueInt) value).getValue());
- assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
assertTrue(value instanceof DexValueString);
assertEquals(("7"), ((DexValueString) value).getValue().toString());
@@ -388,12 +388,12 @@
assertTrue(inspector.clazz("Test").clinit().isPresent());
DexValue value;
- assertTrue(inspector.clazz("Test").field("int", "intField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("int", "intField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("int", "intField").getStaticValue();
assertTrue(value instanceof DexValueInt);
assertEquals(3, ((DexValueInt) value).getValue());
- assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasStaticValue());
+ assertTrue(inspector.clazz("Test").field("java.lang.String", "stringField").hasExplicitStaticValue());
value = inspector.clazz("Test").field("java.lang.String", "stringField").getStaticValue();
assertTrue(value instanceof DexValueString);
assertEquals(("7"), ((DexValueString) value).getValue().toString());
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 89a7291..2dfe42e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.position.TextRange;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
import com.android.tools.r8.utils.DexInspector;
@@ -46,7 +47,6 @@
import java.util.List;
import java.util.function.Function;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
class EmptyMainClassForProguardTests {
@@ -205,7 +205,8 @@
assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
ProguardMemberRule memberRule = rule.getMemberRules().iterator().next();
assertTrue(memberRule.getAccessFlags().isProtected());
- assertEquals(ProguardNameMatcher.create("getContents"), memberRule.getName());
+ assertEquals(ProguardNameMatcher.create(
+ IdentifierPatternWithWildcards.withoutWildcards("getContents")), memberRule.getName());
assertEquals("java.lang.Object[][]", memberRule.getType().toString());
assertEquals(ProguardMemberType.METHOD, memberRule.getRuleType());
assertEquals(0, memberRule.getArguments().size());
@@ -1410,10 +1411,12 @@
assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType());
assertEquals("**$D<2>", if0.subsequentRule.getClassNames().toString());
// TODO(b/73800755): Test <2> matches with expected wildcard: ** after '$R'.
+
+ verifyWithProguard6(proguardConfig);
}
@Test
- public void parse_if_nthWildcard_notNumber() throws Exception {
+ public void parse_if_nthWildcard_notNumber_literalN() throws Exception {
Path proguardConfig = writeTextToTempFile(
"-if class **$R**",
"-keep class **D<n>"
@@ -1424,7 +1427,6 @@
parser.parse(proguardConfig);
fail();
} catch (AbortException e) {
- System.out.println(handler.errors.get(0));
checkDiagnostic(handler.errors, proguardConfig, 2, 13,
"Use of generics not allowed for java type");
}
@@ -1432,6 +1434,76 @@
}
@Test
+ public void parse_if_nthWildcard_notNumber_asterisk_inClassName() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **D<*>"
+ );
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostic(handler.errors, proguardConfig, 2, 13,
+ "Use of generics not allowed for java type");
+ }
+ verifyFailWithProguard6(proguardConfig, "Use of generics not allowed for java type");
+ }
+
+ @Test
+ public void parse_if_nthWildcard_notNumber_asterisk_inMemberName() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **D<2> {",
+ " int id<*>;",
+ "}"
+ );
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ verifyParserEndsCleanly();
+
+ verifyWithProguard6(proguardConfig);
+ }
+
+ @Test
+ public void parse_if_nestedAngularBrackets_inMemberName() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **D<2> {",
+ " int id<<*>>;",
+ "}"
+ );
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ verifyParserEndsCleanly();
+
+ verifyWithProguard6(proguardConfig);
+ }
+
+ @Test
+ public void parse_if_nestedAngularBrackets_outOfRange() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **D<2> {",
+ " int id<<4>>;", // There are 3 previous referable wildcards in this rule.
+ "}"
+ );
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostic(handler.errors, proguardConfig, 4, 2,
+ "Wildcard", "<4>", "invalid");
+ }
+ verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
+ }
+
+ @Test
public void parse_if_nthWildcard_outOfRange_tooSmall() throws Exception {
Path proguardConfig = writeTextToTempFile(
"-if class **$R**",
@@ -1446,9 +1518,9 @@
checkDiagnostic(handler.errors, proguardConfig, 2, 13,
"Wildcard", "<0>", "invalid");
}
+ verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (0,");
}
- @Ignore("b/73800755: verify the range of <n>")
@Test
public void parse_if_nthWildcard_outOfRange_tooBig() throws Exception {
Path proguardConfig = writeTextToTempFile(
@@ -1461,12 +1533,12 @@
parser.parse(proguardConfig);
fail();
} catch (AbortException e) {
- checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+ checkDiagnostic(handler.errors, proguardConfig, 3, 1,
"Wildcard", "<4>", "invalid");
}
+ verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (4,");
}
- @Ignore("b/73800755: verify the range of <n>")
@Test
public void parse_if_nthWildcard_outOfRange_inIf() throws Exception {
Path proguardConfig = writeTextToTempFile(
@@ -1479,12 +1551,12 @@
parser.parse(proguardConfig);
fail();
} catch (AbortException e) {
- checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+ checkDiagnostic(handler.errors, proguardConfig, 3, 1,
"Wildcard", "<2>", "invalid");
}
+ verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (2,");
}
- @Ignore("b/73800755: verify the range of <n>")
@Test
public void parse_if_nthWildcard_not_referable() throws Exception {
Path proguardConfig = writeTextToTempFile(
@@ -1501,9 +1573,10 @@
parser.parse(proguardConfig);
fail();
} catch (AbortException e) {
- checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+ checkDiagnostic(handler.errors, proguardConfig, 6, 2,
"Wildcard", "<3>", "invalid");
}
+ verifyFailWithProguard6(proguardConfig, "Invalid reference to wildcard (3,");
}
@Test
@@ -1519,8 +1592,9 @@
fail();
} catch (AbortException e) {
checkDiagnostic(handler.errors, proguardConfig, 1, 1,
- "without", "subsequent", "keep");
+ "Expecting", "'-keep'", "after", "'-if'");
}
+ verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
}
@Test
@@ -1535,8 +1609,9 @@
fail();
} catch (AbortException e) {
checkDiagnostic(handler.errors, proguardConfig, 1, 1,
- "without", "subsequent", "keep");
+ "Expecting", "'-keep'", "after", "'-if'");
}
+ verifyFailWithProguard6(proguardConfig, "Expecting '-keep' option after '-if' option");
}
@Test
@@ -1690,9 +1765,35 @@
);
Path proguardedJar =
File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
- ToolHelper
- .runProguard(jarTestClasses(ImmutableList.of(classToKeepForTest)),
- proguardedJar, ImmutableList.of(proguardConfig, additionalProguardConfig), null);
+ ProcessResult result = ToolHelper.runProguardRaw(
+ jarTestClasses(ImmutableList.of(classToKeepForTest)),
+ proguardedJar,
+ ImmutableList.of(proguardConfig, additionalProguardConfig),
+ null);
+ assertEquals(0, result.exitCode);
+ DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
+ assertEquals(1, proguardInspector.allClasses().size());
+ }
+ }
+
+ private void verifyWithProguard6(Path proguardConfig) throws Exception {
+ if (isRunProguard()) {
+ // Add a keep rule for the test class as Proguard will fail if the resulting output jar is
+ // empty
+ Class classToKeepForTest = EmptyMainClassForProguardTests.class;
+ Path additionalProguardConfig = writeTextToTempFile(
+ "-keep class " + classToKeepForTest.getCanonicalName() + " {",
+ " public static void main(java.lang.String[]);",
+ "}"
+ );
+ Path proguardedJar =
+ File.createTempFile("proguarded", FileUtils.JAR_EXTENSION, temp.getRoot()).toPath();
+ ProcessResult result = ToolHelper.runProguard6Raw(
+ jarTestClasses(ImmutableList.of(classToKeepForTest)),
+ proguardedJar,
+ ImmutableList.of(proguardConfig, additionalProguardConfig),
+ null);
+ assertEquals(0, result.exitCode);
DexInspector proguardInspector = new DexInspector(readJar(proguardedJar));
assertEquals(1, proguardInspector.allClasses().size());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
index d8c43c9..4a734a4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.shaking.ProguardTypeMatcher.ClassOrType;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
@@ -22,7 +23,8 @@
private static final DexItemFactory dexItemFactory = new DexItemFactory();
private static boolean matchTypeName(String typeName, String pattern) {
- return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
+ return ProguardTypeMatcher.create(
+ toIdentifierPatternWithWildCards(pattern), ClassOrType.TYPE, dexItemFactory)
.matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
}
@@ -37,8 +39,8 @@
for (String pattern : patterns) {
boolean isNegated = pattern.startsWith("!");
String actualPattern = isNegated ? pattern.substring(1) : pattern;
- listBuilder.addClassName(isNegated,
- ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+ listBuilder.addClassName(isNegated, ProguardTypeMatcher.create(
+ toIdentifierPatternWithWildCards(actualPattern), ClassOrType.CLASS, dexItemFactory));
}
builder.addPattern(listBuilder.build());
}
@@ -142,5 +144,36 @@
assertFalse(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject"));
assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject1"));
assertTrue(ProguardNameMatcher.matchFieldOrMethodName("getObject?", "getObject5"));
- }
+ }
+
+ private static IdentifierPatternWithWildcards toIdentifierPatternWithWildCards(String pattern) {
+ ImmutableList.Builder<String> builder = ImmutableList.builder();
+ String allPattern = "";
+ String backReference = "";
+ for (int i = 0; i < pattern.length(); i++) {
+ char patternChar = pattern.charAt(i);
+ if (patternChar == '?' || patternChar == '%') {
+ builder.add(String.valueOf(patternChar));
+ } else if (patternChar == '*') {
+ allPattern += patternChar;
+ } else if (patternChar == '<') {
+ backReference += patternChar;
+ } else if (patternChar == '>') {
+ backReference += patternChar;
+ builder.add(backReference);
+ backReference = "";
+ } else {
+ if (allPattern.length() > 0) {
+ builder.add(allPattern);
+ allPattern = "";
+ } else if (backReference.length() > 0) {
+ backReference += patternChar;
+ }
+ }
+ }
+ if (allPattern.length() > 0) {
+ builder.add(allPattern);
+ }
+ return new IdentifierPatternWithWildcards(pattern, builder.build());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index abbe3d3..1151648 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -50,7 +50,6 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import org.junit.Test;
public class ForceProguardCompatibilityTest extends TestBase {
@@ -157,7 +156,7 @@
assertEquals(1, classNames.size());
assertEquals(testClass.getCanonicalName(),
classNames.asSpecificDexTypes().get(0).toSourceString());
- Set<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
+ List<ProguardMemberRule> memberRules = configuration.getRules().get(0).getMemberRules();
assertEquals(1, memberRules.size());
assertEquals(ProguardMemberType.INIT, memberRules.iterator().next().getRuleType());
} else {
@@ -279,7 +278,7 @@
assertEquals(ProguardKeepRuleType.KEEP, rule.getType());
assertTrue(rule.getModifiers().allowsObfuscation);
assertTrue(rule.getModifiers().allowsOptimization);
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -295,7 +294,7 @@
}
});
Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -388,7 +387,7 @@
assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
assertTrue(rule.getModifiers().allowsObfuscation);
assertTrue(rule.getModifiers().allowsOptimization);
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -403,7 +402,7 @@
}
});
Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -504,7 +503,7 @@
assertEquals(ProguardKeepRuleType.KEEP_CLASS_MEMBERS, rule.getType());
assertTrue(rule.getModifiers().allowsObfuscation);
assertTrue(rule.getModifiers().allowsOptimization);
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -519,7 +518,7 @@
assertTrue(keptFields.containsKey("longField"));
assertTrue(keptFields.containsKey("objField"));
Iterables.filter(rules, ProguardIdentifierNameStringRule.class).forEach(rule -> {
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -683,7 +682,7 @@
private void defaultMethodCompatibilityRules(ProguardConfiguration configuration) {
assertEquals(1, configuration.getRules().size());
ProguardConfigurationRule rule = configuration.getRules().get(0);
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
@@ -708,7 +707,7 @@
private void defaultMethod2CompatibilityRules(ProguardConfiguration configuration) {
assertEquals(1, configuration.getRules().size());
ProguardConfigurationRule rule = configuration.getRules().get(0);
- Set<ProguardMemberRule> memberRules = rule.getMemberRules();
+ List<ProguardMemberRule> memberRules = rule.getMemberRules();
ProguardClassNameList classNames = rule.getClassNames();
assertEquals(1, classNames.size());
DexType type = classNames.asSpecificDexTypes().get(0);
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 0cea762..29676da 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -821,7 +821,7 @@
}
public abstract class FieldSubject extends MemberSubject {
- public abstract boolean hasStaticValue();
+ public abstract boolean hasExplicitStaticValue();
public abstract DexEncodedField getField();
@@ -863,7 +863,7 @@
}
@Override
- public boolean hasStaticValue() {
+ public boolean hasExplicitStaticValue() {
return false;
}
@@ -928,13 +928,13 @@
}
@Override
- public boolean hasStaticValue() {
- return dexField.staticValue != null;
+ public boolean hasExplicitStaticValue() {
+ return isStatic() && dexField.hasExplicitStaticValue();
}
@Override
public DexValue getStaticValue() {
- return dexField.staticValue;
+ return dexField.getStaticValue();
}
@Override