Merge "Revert "Run CF backend on Art tests""
diff --git a/build.gradle b/build.gradle
index f857f9e..716f983 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,7 +36,7 @@
joptSimpleVersion = '4.6'
jsonSimpleVersion = '1.1'
junitVersion = '4.12'
- kotlinVersion = '1.2.0'
+ kotlinVersion = '1.2.30'
protobufVersion = '3.0.0'
smaliVersion = '2.2b4'
}
@@ -300,7 +300,7 @@
"android_jar/lib-v25",
"android_jar/lib-v26",
"proguard/proguard5.2.1",
- "proguard/proguard6.0",
+ "proguard/proguard6.0.1",
"gradle/gradle",
"jdwp-tests",
"jasmin",
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 670211f..9404e68 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.1.9-dev";
+ public static final String LABEL = "v1.1.10-dev";
private Version() {
}
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 42acb7c..d6b15eb 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -146,6 +146,46 @@
return result;
}
+ /**
+ * For all annotations on the class and all annotations on its methods and fields apply the
+ * specified consumer.
+ */
+ public void forEachAnnotation(Consumer<DexAnnotation> consumer) {
+ for (DexAnnotation annotation : annotations.annotations) {
+ consumer.accept(annotation);
+ }
+ for (DexEncodedMethod method : directMethods()) {
+ for (DexAnnotation annotation : method.annotations.annotations) {
+ consumer.accept(annotation);
+ }
+ for (DexAnnotationSet parameterAnnotations : method.parameterAnnotations.values) {
+ for (DexAnnotation annotation : parameterAnnotations.annotations) {
+ consumer.accept(annotation);
+ }
+ }
+ }
+ for (DexEncodedMethod method : virtualMethods()) {
+ for (DexAnnotation annotation : method.annotations.annotations) {
+ consumer.accept(annotation);
+ }
+ for (DexAnnotationSet parameterAnnotations : method.parameterAnnotations.values) {
+ for (DexAnnotation annotation : parameterAnnotations.annotations) {
+ consumer.accept(annotation);
+ }
+ }
+ }
+ for (DexEncodedField field : instanceFields()) {
+ for (DexAnnotation annotation : field.annotations.annotations) {
+ consumer.accept(annotation);
+ }
+ }
+ for (DexEncodedField field : staticFields()) {
+ for (DexAnnotation annotation : field.annotations.annotations) {
+ consumer.accept(annotation);
+ }
+ }
+ }
+
public void forEachField(Consumer<DexEncodedField> consumer) {
for (DexEncodedField field : staticFields()) {
consumer.accept(field);
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 b75cf1f..dd08190 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -91,6 +91,8 @@
throw new Unreachable("No default value for unexpected type " + type);
}
+ public abstract Object getBoxedValue();
+
// Returns a const instruction for the non default value.
public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return null;
@@ -141,6 +143,11 @@
}
@Override
+ public Object getBoxedValue() {
+ throw new Unreachable();
+ }
+
+ @Override
public Object asAsmEncodedObject() {
throw new Unreachable();
}
@@ -213,6 +220,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_BYTE, 0, dest);
dest.putSignedEncodedValue(value, 1);
@@ -265,6 +277,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_SHORT, value, Short.BYTES, dest);
}
@@ -316,6 +333,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = dest.putUnsignedEncodedValue(value, 2);
@@ -371,6 +393,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest);
}
@@ -422,6 +449,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_LONG, value, Long.BYTES, dest);
}
@@ -473,6 +505,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = EncodedValueUtils.putFloat(dest, value);
@@ -526,6 +563,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = EncodedValueUtils.putDouble(dest, value);
@@ -580,6 +622,11 @@
}
@Override
+ public Object getBoxedValue() {
+ throw new Unreachable("No boxed value for DexValue " + this.getClass().getSimpleName());
+ }
+
+ @Override
public Object asAsmEncodedObject() {
throw new Unreachable("No ASM conversion for DexValue " + this.getClass().getSimpleName());
}
@@ -750,6 +797,11 @@
}
@Override
+ public Object getBoxedValue() {
+ throw new Unreachable("No boxed value for DexValueArray");
+ }
+
+ @Override
public Object asAsmEncodedObject() {
throw new Unreachable("No ASM conversion for DexValueArray");
}
@@ -804,6 +856,11 @@
}
@Override
+ public Object getBoxedValue() {
+ throw new Unreachable("No boxed value for DexValueAnnotation");
+ }
+
+ @Override
public Object asAsmEncodedObject() {
throw new Unreachable("No ASM conversion for DexValueAnnotation");
}
@@ -854,6 +911,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return null;
+ }
+
+ @Override
public Object asAsmEncodedObject() {
return null;
}
@@ -899,6 +961,11 @@
}
@Override
+ public Object getBoxedValue() {
+ return getValue();
+ }
+
+ @Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_BOOLEAN, value ? 1 : 0, dest);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Add.java b/src/main/java/com/android/tools/r8/ir/code/Add.java
index 783d672..2abc743 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Add.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Add.java
@@ -35,12 +35,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new AddLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/And.java b/src/main/java/com/android/tools/r8/ir/code/And.java
index ad46715..c0bdb2a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/And.java
+++ b/src/main/java/com/android/tools/r8/ir/code/And.java
@@ -41,12 +41,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new AndLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Or.java b/src/main/java/com/android/tools/r8/ir/code/Or.java
index 3df3a83..2cfe9b9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Or.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Or.java
@@ -40,12 +40,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new OrLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Sub.java b/src/main/java/com/android/tools/r8/ir/code/Sub.java
index f9f5920..f2a173b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Sub.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Sub.java
@@ -39,12 +39,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new SubLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Xor.java b/src/main/java/com/android/tools/r8/ir/code/Xor.java
index d2897ed..0f37a65 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Xor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Xor.java
@@ -39,12 +39,6 @@
@Override
public com.android.tools.r8.code.Instruction CreateLong(int dest, int left, int right) {
- // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
- // the first part of the result long before reading the second part of the input longs.
- // Therefore, there can be no overlap of the second part of an input long and the first
- // part of the output long.
- assert dest != left + 1;
- assert dest != right + 1;
return new XorLong(dest, left, right);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 76a1a43..20051f9 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -396,10 +396,6 @@
// necessary.
ir.splitCriticalEdges();
- // Create block order and make sure that all blocks are immediately followed by their
- // fallthrough block if any.
- ir.traceBlocks();
-
// Clear the code so we don't build multiple times.
source.clear();
source = null;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 42b06ef..0f0b44a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -476,6 +476,7 @@
Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s", method.toSourceString(), code);
}
assert code.isConsistentSSA();
+ code.traceBlocks();
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
method.setCode(code, registerAllocator, options);
if (Log.ENABLED) {
@@ -635,7 +636,6 @@
if (options.testing.invertConditionals) {
invertConditionalsForTesting(code);
- code.traceBlocks();
}
if (options.enableNonNullTracking && nonNullTracker != null) {
@@ -699,6 +699,7 @@
}
private void finalizeIR(DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
+ code.traceBlocks();
if (options.isGeneratingClassFiles()) {
finalizeToCf(method, code, feedback);
} else {
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 a6b29e8..30e9bae4 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
@@ -565,7 +565,6 @@
// being split on the way in but does not maintain this property. We therefore split
// critical edges at exit.
code.splitCriticalEdges();
- code.traceBlocks();
assert code.isConsistentSSA();
}
@@ -2028,9 +2027,6 @@
}
code.removeMarkedBlocks(color);
code.returnMarkingColor(color);
- if (ifBranchFlipped) {
- code.traceBlocks();
- }
assert code.isConsistentSSA();
}
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 fefe8c5..85e0060 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
@@ -54,6 +54,8 @@
import java.util.PriorityQueue;
import java.util.Set;
import java.util.TreeSet;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
/**
* Linear scan register allocator.
@@ -1090,6 +1092,46 @@
return arrayReg == register;
}
+ private boolean needsSingleResultOverlappingLongOperandsWorkaround(LiveIntervals intervals) {
+ if (!options.canHaveCmpLongBug()) {
+ return false;
+ }
+ if (intervals.requiredRegisters() == 2) {
+ // Not the live range for a single value and therefore not the output of cmp-long.
+ return false;
+ }
+ if (intervals.getValue().isPhi()) {
+ // If this writes a new register pair it will be via a move and not an cmp-long operation.
+ return false;
+ }
+ if (intervals.getSplitParent() != intervals) {
+ // This is a split of a parent interval and therefore if this leads to a write of a
+ // register it will be via a move and not an cmp-long operation.
+ return false;
+ }
+ Instruction definition = intervals.getValue().definition;
+ return definition.isCmp() && definition.asCmp().inValues().get(0).outType().isWide();
+ }
+
+ private boolean singleOverlappingLong(int register1, int register2) {
+ return register1 == register2 || register1 == (register2 + 1);
+ }
+
+ // Is one of the cmp-long argument registers the same as the register we are
+ // allocating for the result?
+ private boolean isSingleResultOverlappingLongOperands(LiveIntervals intervals, int register) {
+ assert needsSingleResultOverlappingLongOperandsWorkaround(intervals);
+ Value left = intervals.getValue().definition.asCmp().leftValue();
+ Value right = intervals.getValue().definition.asCmp().rightValue();
+ int leftReg =
+ left.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
+ int rightReg =
+ right.getLiveIntervals().getSplitCovering(intervals.getStart()).getRegister();
+ assert leftReg != NO_REGISTER;
+ assert rightReg != NO_REGISTER;
+ return singleOverlappingLong(register, leftReg) || singleOverlappingLong(register, rightReg);
+ }
+
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
// the first part of the result long before reading the second part of the input longs.
//
@@ -1099,7 +1141,10 @@
//
// Dalvik would add v0 and v2 and write that to v3. It would then read v1 and v3 and produce
// the wrong result.
- private boolean needsOverlappingLongRegisterWorkaround(LiveIntervals intervals) {
+ private boolean needsLongResultOverlappingLongOperandsWorkaround(LiveIntervals intervals) {
+ if (!options.canHaveOverlappingLongRegisterBug()) {
+ return false;
+ }
if (intervals.requiredRegisters() == 1) {
// Not the live range for a wide value.
return false;
@@ -1125,8 +1170,14 @@
return false;
}
- private boolean hasOverlappingLongRegisters(LiveIntervals unhandledInterval, int register) {
- assert needsOverlappingLongRegisterWorkaround(unhandledInterval);
+ private boolean longOverlappingLong(int register1, int register2) {
+ return register1 == register2 || register1 == (register2 + 1)
+ || (register1 + 1) == register2 || (register1 + 1) == (register2 + 1);
+ }
+
+ private boolean isLongResultOverlappingLongOperands(
+ LiveIntervals unhandledInterval, int register) {
+ assert needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval);
Value left = unhandledInterval.getValue().definition.asBinop().leftValue();
Value right = unhandledInterval.getValue().definition.asBinop().rightValue();
int leftReg =
@@ -1135,11 +1186,9 @@
right.getLiveIntervals().getSplitCovering(unhandledInterval.getStart()).getRegister();
assert leftReg != NO_REGISTER && rightReg != NO_REGISTER;
// The dalvik bug is actually only for overlap with the second operand, For now we
- // make sure that there is no overlap with either operand.
- if ((leftReg + 1) == register || (rightReg + 1) == register) {
- return true;
- }
- return false;
+ // make sure that there is no overlap with either register of either operand. Some vendor
+ // optimization have bees seen to need this more conservative check.
+ return longOverlappingLong(register, leftReg) || longOverlappingLong(register, rightReg);
}
// Intervals overlap a move exception interval if one of the splits of the intervals does.
@@ -1301,10 +1350,17 @@
// free position.
int candidate = getLargestValidCandidate(
unhandledInterval, registerConstraint, needsRegisterPair, freePositions, Type.ANY);
- assert candidate != REGISTER_CANDIDATE_NOT_FOUND;
- int largestFreePosition = freePositions.get(candidate);
- if (needsRegisterPair) {
- largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
+
+ // It is not always possible to find a largest valid candidate. If none of the usable register
+ // are free we typically get the last candidate. However, if that candidate has to be
+ // discarded in order to workaround bugs we get REGISTER_CANDIDATE_NOT_FOUND. In both cases
+ // we need to spill a valid candidate. That path is triggered when largestFreePosition is 0.
+ int largestFreePosition = 0;
+ if (candidate != REGISTER_CANDIDATE_NOT_FOUND) {
+ largestFreePosition = freePositions.get(candidate);
+ if (needsRegisterPair) {
+ largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
+ }
}
// Determine what to do based on how long the selected candidate is free.
@@ -1416,8 +1472,8 @@
}
if (freePosition >= unhandledInterval.getEnd()) {
// Check for overlapping long registers issue.
- if (needsOverlappingLongRegisterWorkaround(unhandledInterval) &&
- hasOverlappingLongRegisters(unhandledInterval, register)) {
+ if (needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval) &&
+ isLongResultOverlappingLongOperands(unhandledInterval, register)) {
return false;
}
// Check for aget-wide bug in recent Art VMs.
@@ -1507,42 +1563,47 @@
return candidate;
}
+ private int handleWorkaround(
+ Predicate<LiveIntervals> workaroundNeeded,
+ BiPredicate<LiveIntervals, Integer> workaroundNeededForCandidate,
+ int candidate, LiveIntervals unhandledInterval, int registerConstraint,
+ boolean needsRegisterPair, RegisterPositions freePositions, RegisterPositions.Type type) {
+ if (workaroundNeeded.test(unhandledInterval)) {
+ int lastCandidate = candidate;
+ while (workaroundNeededForCandidate.test(unhandledInterval, candidate)) {
+ // Make the unusable register unavailable for allocation and try again.
+ freePositions.set(candidate, 0);
+ candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
+ // If there are only invalid candidates of the give type we will end up with the same
+ // candidate returned again once we have tried them all. In that case we didn't find a
+ // valid register candidate and we need to broaden the search to other types.
+ if (lastCandidate == candidate) {
+ return REGISTER_CANDIDATE_NOT_FOUND;
+ }
+ lastCandidate = candidate;
+ }
+ }
+ return candidate;
+ }
+
private int getLargestValidCandidate(LiveIntervals unhandledInterval, int registerConstraint,
boolean needsRegisterPair, RegisterPositions freePositions, RegisterPositions.Type type) {
int candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
return candidate;
}
- if (needsOverlappingLongRegisterWorkaround(unhandledInterval)) {
- int lastCandidate = candidate;
- while (hasOverlappingLongRegisters(unhandledInterval, candidate)) {
- // Make the overlapping register unavailable for allocation and try again.
- freePositions.set(candidate, 0);
- candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
- // If there are only invalid candidates of the give type we will end up with the same
- // candidate returned again once we have tried them all. In that case we didn't find a
- // valid register candidate and we need to broaden the search to other types.
- if (lastCandidate == candidate) {
- return REGISTER_CANDIDATE_NOT_FOUND;
- }
- lastCandidate = candidate;
- }
- }
- if (needsArrayGetWideWorkaround(unhandledInterval)) {
- int lastCandidate = candidate;
- while (isArrayGetArrayRegister(unhandledInterval, candidate)) {
- // Make the overlapping register unavailable for allocation and try again.
- freePositions.set(candidate, 0);
- candidate = getLargestCandidate(registerConstraint, freePositions, needsRegisterPair, type);
- // If there are only invalid candidates of the give type we will end up with the same
- // candidate returned again once we have tried them all. In that case we didn't find a
- // valid register candidate and we need to broaden the search to other types.
- if (lastCandidate == candidate) {
- return REGISTER_CANDIDATE_NOT_FOUND;
- }
- lastCandidate = candidate;
- }
- }
+ candidate = handleWorkaround(
+ this::needsLongResultOverlappingLongOperandsWorkaround,
+ this::isLongResultOverlappingLongOperands,
+ candidate, unhandledInterval, registerConstraint, needsRegisterPair, freePositions, type);
+ candidate = handleWorkaround(
+ this::needsSingleResultOverlappingLongOperandsWorkaround,
+ this::isSingleResultOverlappingLongOperands,
+ candidate, unhandledInterval, registerConstraint, needsRegisterPair, freePositions, type);
+ candidate = handleWorkaround(
+ this::needsArrayGetWideWorkaround,
+ this::isArrayGetArrayRegister,
+ candidate, unhandledInterval, registerConstraint, needsRegisterPair, freePositions, type);
return candidate;
}
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 3814e28..5eb2bb7 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -8,23 +8,46 @@
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.Code;
+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.DexApplication;
+import com.android.tools.r8.graph.DexEncodedAnnotation;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
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.DexValueAnnotation;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueEnum;
+import com.android.tools.r8.graph.DexValue.DexValueField;
+import com.android.tools.r8.graph.DexValue.DexValueMethod;
+import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
+import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueString;
+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.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMap.Builder;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ExecutorService;
+import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.CheckClassAdapter;
@@ -87,7 +110,7 @@
int access = clazz.accessFlags.getAsCfAccessFlags();
String desc = clazz.type.toDescriptorString();
String name = clazz.type.getInternalName();
- String signature = null; // TODO(zerny): Support generic signatures.
+ String signature = getSignature(clazz.annotations);
String superName =
clazz.type == options.itemFactory.objectType ? null : clazz.superType.getInternalName();
String[] interfaces = new String[clazz.interfaces.values.length];
@@ -95,6 +118,8 @@
interfaces[i] = clazz.interfaces.values[i].getInternalName();
}
writer.visit(version, access, name, signature, superName, interfaces);
+ writeAnnotations(writer::visitAnnotation, clazz.annotations.annotations);
+ ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations);
if (clazz.getEnclosingMethod() != null) {
clazz.getEnclosingMethod().write(writer);
@@ -111,10 +136,10 @@
writeField(field, writer);
}
for (DexEncodedMethod method : clazz.directMethods()) {
- writeMethod(method, writer);
+ writeMethod(method, writer, defaults);
}
for (DexEncodedMethod method : clazz.virtualMethods()) {
- writeMethod(method, writer);
+ writeMethod(method, writer, defaults);
}
writer.visitEnd();
@@ -132,6 +157,62 @@
options.reporter, handler -> consumer.accept(result, desc, handler));
}
+ private DexValue getSystemAnnotationValue(DexAnnotationSet annotations, DexType type) {
+ DexAnnotation annotation = annotations.getFirstMatching(type);
+ if (annotation == null) {
+ return null;
+ }
+ assert annotation.visibility == DexAnnotation.VISIBILITY_SYSTEM;
+ DexEncodedAnnotation encodedAnnotation = annotation.annotation;
+ assert encodedAnnotation.elements.length == 1;
+ return encodedAnnotation.elements[0].value;
+ }
+
+ private String getSignature(DexAnnotationSet annotations) {
+ DexValueArray value =
+ (DexValueArray)
+ getSystemAnnotationValue(annotations, application.dexItemFactory.annotationSignature);
+ if (value == null) {
+ return null;
+ }
+ DexValue[] parts = value.getValues();
+ StringBuilder res = new StringBuilder();
+ for (DexValue part : parts) {
+ res.append(((DexValueString) part).getValue().toString());
+ }
+ return res.toString();
+ }
+
+ private ImmutableMap<DexString, DexValue> getAnnotationDefaults(DexAnnotationSet annotations) {
+ DexValueAnnotation value =
+ (DexValueAnnotation)
+ getSystemAnnotationValue(annotations, application.dexItemFactory.annotationDefault);
+ if (value == null) {
+ return ImmutableMap.of();
+ }
+ DexEncodedAnnotation annotation = value.value;
+ Builder<DexString, DexValue> builder = ImmutableMap.builder();
+ for (DexAnnotationElement element : annotation.elements) {
+ builder.put(element.name, element.value);
+ }
+ return builder.build();
+ }
+
+ private String[] getExceptions(DexAnnotationSet annotations) {
+ DexValueArray value =
+ (DexValueArray)
+ getSystemAnnotationValue(annotations, application.dexItemFactory.annotationThrows);
+ if (value == null) {
+ return null;
+ }
+ DexValue[] values = value.getValues();
+ String[] res = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ res[i] = ((DexValueType) values[i]).value.getInternalName();
+ }
+ return res;
+ }
+
private Object getStaticValue(DexEncodedField field) {
if (!field.accessFlags.isStatic() || field.staticValue == null) {
return null;
@@ -143,22 +224,110 @@
int access = field.accessFlags.getAsCfAccessFlags();
String name = field.field.name.toString();
String desc = field.field.type.toDescriptorString();
- String signature = null; // TODO(zerny): Support generic signatures.
+ String signature = getSignature(field.annotations);
Object value = getStaticValue(field);
- writer.visitField(access, name, desc, signature, value);
- // TODO(zerny): Add annotations to the field.
+ FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
+ writeAnnotations(visitor::visitAnnotation, field.annotations.annotations);
+ visitor.visitEnd();
}
- private void writeMethod(DexEncodedMethod method, ClassWriter writer) {
+ private void writeMethod(
+ DexEncodedMethod method, ClassWriter writer, ImmutableMap<DexString, DexValue> defaults) {
int access = method.accessFlags.getAsCfAccessFlags();
String name = method.method.name.toString();
String desc = method.descriptor();
- String signature = null; // TODO(zerny): Support generic signatures.
- String[] exceptions = null;
+ String signature = getSignature(method.annotations);
+ String[] exceptions = getExceptions(method.annotations);
MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions);
+ if (defaults.containsKey(method.method.name)) {
+ AnnotationVisitor defaultVisitor = visitor.visitAnnotationDefault();
+ if (defaultVisitor != null) {
+ writeAnnotationElement(defaultVisitor, null, defaults.get(method.method.name));
+ defaultVisitor.visitEnd();
+ }
+ }
+ 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);
+ }
if (!method.accessFlags.isAbstract() && !method.accessFlags.isNative()) {
writeCode(method.getCode(), visitor);
}
+ visitor.visitEnd();
+ }
+
+ private interface AnnotationConsumer {
+ AnnotationVisitor visit(String desc, boolean visible);
+ }
+
+ private void writeAnnotations(AnnotationConsumer visitor, DexAnnotation[] annotations) {
+ for (DexAnnotation dexAnnotation : annotations) {
+ if (dexAnnotation.visibility == DexAnnotation.VISIBILITY_SYSTEM) {
+ // Annotations with VISIBILITY_SYSTEM are not annotations in CF, but are special
+ // annotations in Dex, i.e. default, enclosing class, enclosing method, member classes,
+ // signature, throws.
+ continue;
+ }
+ AnnotationVisitor v =
+ visitor.visit(
+ dexAnnotation.annotation.type.toDescriptorString(),
+ dexAnnotation.visibility == DexAnnotation.VISIBILITY_RUNTIME);
+ if (v != null) {
+ writeAnnotation(v, dexAnnotation.annotation);
+ v.visitEnd();
+ }
+ }
+ }
+
+ private void writeAnnotation(AnnotationVisitor v, DexEncodedAnnotation annotation) {
+ for (DexAnnotationElement element : annotation.elements) {
+ writeAnnotationElement(v, element.name.toString(), element.value);
+ }
+ }
+
+ private void writeAnnotationElement(AnnotationVisitor visitor, String name, DexValue value) {
+ if (value instanceof DexValueAnnotation) {
+ DexValueAnnotation valueAnnotation = (DexValueAnnotation) value;
+ AnnotationVisitor innerVisitor =
+ visitor.visitAnnotation(name, valueAnnotation.value.type.toDescriptorString());
+ if (innerVisitor != null) {
+ writeAnnotation(innerVisitor, valueAnnotation.value);
+ innerVisitor.visitEnd();
+ }
+ } else if (value instanceof DexValueArray) {
+ DexValue[] values = ((DexValueArray) value).getValues();
+ AnnotationVisitor innerVisitor = visitor.visitArray(name);
+ if (innerVisitor != null) {
+ for (DexValue arrayValue : values) {
+ writeAnnotationElement(innerVisitor, null, arrayValue);
+ }
+ innerVisitor.visitEnd();
+ }
+ } else if (value instanceof DexValueEnum) {
+ DexValueEnum en = (DexValueEnum) value;
+ visitor.visitEnum(name, en.value.type.toDescriptorString(), en.value.name.toString());
+ } else if (value instanceof DexValueField) {
+ throw new Unreachable("writeAnnotationElement of DexValueField");
+ } else if (value instanceof DexValueMethod) {
+ throw new Unreachable("writeAnnotationElement of DexValueMethod");
+ } else if (value instanceof DexValueMethodHandle) {
+ throw new Unreachable("writeAnnotationElement of DexValueMethodHandle");
+ } else if (value instanceof DexValueMethodType) {
+ throw new Unreachable("writeAnnotationElement of DexValueMethodType");
+ } else if (value instanceof DexValueString) {
+ DexValueString str = (DexValueString) value;
+ visitor.visit(name, str.getValue().toString());
+ } else if (value instanceof DexValueType) {
+ DexValueType ty = (DexValueType) value;
+ visitor.visit(name, Type.getType(ty.value.toDescriptorString()));
+ } else if (value instanceof UnknownDexValue) {
+ throw new Unreachable("writeAnnotationElement of UnknownDexValue");
+ } else {
+ visitor.visit(name, value.getBoxedValue());
+ }
}
private void writeCode(Code code, MethodVisitor visitor) {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index de21187..5c51786 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -85,13 +85,13 @@
addMainDexType(dexType);
continue;
}
- for (DexAnnotation annotation : clazz.annotations.annotations) {
- if (annotation.visibility == DexAnnotation.VISIBILITY_RUNTIME
+ clazz.forEachAnnotation(annotation -> {
+ if (!mainDexTypes.contains(dexType)
+ && annotation.visibility == DexAnnotation.VISIBILITY_RUNTIME
&& isAnnotationWithEnum(annotation.annotation.type)) {
addMainDexType(dexType);
- break;
}
- }
+ });
}
}
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 b526973..e96de6f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -535,7 +535,7 @@
private ProguardIfRule parseIfRule(TextPosition optionStart)
throws ProguardRuleParserException {
ProguardIfRule.Builder ifRuleBuilder = ProguardIfRule.builder();
- parseClassSpec(ifRuleBuilder, true);
+ parseClassSpec(ifRuleBuilder, false);
// Required a subsequent keep rule.
skipWhitespace();
@@ -1086,15 +1086,52 @@
return Integer.parseInt(s);
}
+ private final Predicate<Character> CLASS_NAME_PREDICATE =
+ character -> IdentifierUtils.isDexIdentifierPart(character)
+ || character == '.'
+ || character == '*'
+ || character == '?'
+ || character == '%'
+ || character == '['
+ || character == ']';
+
private String acceptClassName() {
- return acceptString(character ->
- IdentifierUtils.isDexIdentifierPart(character)
- || character == '.'
- || character == '*'
- || character == '?'
- || character == '%'
- || character == '['
- || character == ']');
+ return acceptString(CLASS_NAME_PREDICATE);
+ }
+
+ private String acceptClassNameWithNthWildcard() {
+ StringBuilder nthWildcard = null;
+ skipWhitespace();
+ int start = position;
+ int end = position;
+ while (!eof(end)) {
+ char current = contents.charAt(end);
+ if (nthWildcard != null) {
+ if (current == '>') {
+ try {
+ Integer.parseUnsignedInt(nthWildcard.toString());
+ } catch (NullPointerException | NumberFormatException e) {
+ return null;
+ }
+ nthWildcard = null;
+ } else {
+ nthWildcard.append(current);
+ }
+ end++;
+ } else if (CLASS_NAME_PREDICATE.test(current)) {
+ end++;
+ } else if (current == '<') {
+ nthWildcard = new StringBuilder();
+ end++;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ return null;
+ }
+ position = end;
+ return contents.substring(start, end);
}
private String acceptFieldNameOrIntegerForReturn() {
@@ -1194,7 +1231,7 @@
}
private String parseClassName() throws ProguardRuleParserException {
- String name = acceptClassName();
+ String name = acceptClassNameWithNthWildcard();
if (name == null) {
throw parseError("Class name expected");
}
diff --git a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
index 46669ec..c37102c 100644
--- a/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/protolite/ProtoLitePruner.java
@@ -348,7 +348,6 @@
// the fallthrough label. This can introduce critical edges. Therefore, we split critical
// edges to maintain our edge-split form.
code.splitCriticalEdges();
- code.traceBlocks();
assert code.isConsistentSSA();
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 3414d43..1073cf9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -501,4 +501,17 @@
public boolean canHaveThisTypeVerifierBug() {
return minApiLevel < AndroidApiLevel.M.getLevel();
}
+
+ // The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
+ // the first part of the result long before reading the second part of the input longs.
+ public boolean canHaveOverlappingLongRegisterBug() {
+ return minApiLevel < AndroidApiLevel.L.getLevel();
+ }
+
+ // Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
+ // instructions where the result register overlaps with the input registers.
+ // See b/74084493.
+ public boolean canHaveCmpLongBug() {
+ return minApiLevel < AndroidApiLevel.L.getLevel();
+ }
}
diff --git a/src/test/examples/multidex002/AnnotatedDirectMethod.java b/src/test/examples/multidex002/AnnotatedDirectMethod.java
new file mode 100644
index 0000000..761ea9a
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedDirectMethod.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+public class AnnotatedDirectMethod {
+
+ @AnnotationWithEnum3(AnnotationWithEnum3.Value3.VAL3_2)
+ private void foo() {
+ }
+}
diff --git a/src/test/examples/multidex002/AnnotatedInstanceField.java b/src/test/examples/multidex002/AnnotatedInstanceField.java
new file mode 100644
index 0000000..0209f44
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedInstanceField.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+public class AnnotatedInstanceField {
+
+ @AnnotationWithEnum3(AnnotationWithEnum3.Value3.VAL3_2)
+ public String value = "myValue";
+}
diff --git a/src/test/examples/multidex002/AnnotatedMethodParameter.java b/src/test/examples/multidex002/AnnotatedMethodParameter.java
new file mode 100644
index 0000000..49a9e6d
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedMethodParameter.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+public class AnnotatedMethodParameter {
+
+ public void foo(@AnnotationWithEnum3(AnnotationWithEnum3.Value3.VAL3_2) String val) {
+ }
+}
diff --git a/src/test/examples/multidex002/AnnotatedNotKept.java b/src/test/examples/multidex002/AnnotatedNotKept.java
new file mode 100644
index 0000000..500e7f0
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedNotKept.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+@AnnotationWithoutEnum
+public class AnnotatedNotKept {
+
+}
diff --git a/src/test/examples/multidex002/AnnotatedStaticField.java b/src/test/examples/multidex002/AnnotatedStaticField.java
new file mode 100644
index 0000000..385d1d9
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedStaticField.java
@@ -0,0 +1,9 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+public class AnnotatedStaticField {
+ @AnnotationWithEnum3(AnnotationWithEnum3.Value3.VAL3_2) public static String sValue = "myValue";
+}
diff --git a/src/test/examples/multidex002/AnnotatedVirtualMethod.java b/src/test/examples/multidex002/AnnotatedVirtualMethod.java
new file mode 100644
index 0000000..569bf84
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotatedVirtualMethod.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+public class AnnotatedVirtualMethod {
+
+ @AnnotationWithEnum3(AnnotationWithEnum3.Value3.VAL3_2)
+ public void foo() {
+
+ }
+}
diff --git a/src/test/examples/multidex002/AnnotationWithEnum3.java b/src/test/examples/multidex002/AnnotationWithEnum3.java
new file mode 100644
index 0000000..a8f8344
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotationWithEnum3.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnnotationWithEnum3 {
+
+ enum Value3 {
+ VAL3_1,
+ VAL3_2,
+ }
+
+ Value3 value();
+}
diff --git a/src/test/examples/multidex002/AnnotationWithoutEnum.java b/src/test/examples/multidex002/AnnotationWithoutEnum.java
new file mode 100644
index 0000000..5244971
--- /dev/null
+++ b/src/test/examples/multidex002/AnnotationWithoutEnum.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package multidex002;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AnnotationWithoutEnum {
+
+}
diff --git a/src/test/examples/multidex002/ref-list-1.txt b/src/test/examples/multidex002/ref-list-1.txt
index 26dae34..922d417 100644
--- a/src/test/examples/multidex002/ref-list-1.txt
+++ b/src/test/examples/multidex002/ref-list-1.txt
@@ -1,8 +1,15 @@
Lmultidex002/Annotated2;
Lmultidex002/Annotated;
+Lmultidex002/AnnotatedDirectMethod;
+Lmultidex002/AnnotatedInstanceField;
+Lmultidex002/AnnotatedMethodParameter;
+Lmultidex002/AnnotatedStaticField;
+Lmultidex002/AnnotatedVirtualMethod;
Lmultidex002/AnnotationWithClass;
Lmultidex002/AnnotationWithEnum2;
+Lmultidex002/AnnotationWithEnum3;
Lmultidex002/AnnotationWithEnum;
+Lmultidex002/AnnotationWithoutEnum;
Lmultidex002/InterfaceWithEnum;
Lmultidex002/IntermediateClass;
Lmultidex002/MainActivity;
diff --git a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
index 8ff72c7..c4351ce 100644
--- a/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
+++ b/src/test/java/com/android/tools/r8/JctfTestSpecifications.java
@@ -4542,7 +4542,7 @@
// java.lang.AssertionError: Failed to load serialization resource file: serialization/com/google/jctf/test/lib/java/util/concurrent/PriorityBlockingQueue/serialization/PriorityBlockingQueue_serialization_A01.golden.0.ser
.put("lang.ThreadGroup.destroy.ThreadGroup_destroy_A01",
- match(D8_COMPILER, runtimesUpTo(Version.V6_0_1)))
+ match(runtimesUpTo(Version.V6_0_1)))
// 1) t02
// java.lang.IllegalThreadStateException: Thread group still contains threads: Test group
// 2) t04
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index d1bed68..ba036dd 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -128,7 +128,6 @@
@Override
protected Map<String, TestCondition> getFailingRunCf() {
return new ImmutableMap.Builder<String, TestCondition>()
- .put("floating_point_annotations.FloatingPointValuedAnnotationTest", match(R8_COMPILER))
.build();
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 0c0cd84..1e07b94 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -91,7 +91,7 @@
private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I;
private static final String PROGUARD5_2_1 = "third_party/proguard/proguard5.2.1/bin/proguard.sh";
- private static final String PROGUARD6_0 = "third_party/proguard/proguard6.0/bin/proguard.sh";
+ private static final String PROGUARD6_0_1 = "third_party/proguard/proguard6.0.1/bin/proguard.sh";
private static final String PROGUARD = PROGUARD5_2_1;
public enum DexVm {
diff --git a/src/test/java/com/android/tools/r8/cf/AnnotationTest.java b/src/test/java/com/android/tools/r8/cf/AnnotationTest.java
new file mode 100644
index 0000000..d586bb2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/AnnotationTest.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+import java.lang.annotation.Annotation;
+
+public class AnnotationTest {
+ // @Deprecated is a runtime-visible annotation.
+ @Deprecated public static boolean foo = true;
+
+ @Deprecated
+ public static boolean bar() {
+ return true;
+ }
+
+ public static void main(String[] args) {
+ try {
+ testField();
+ testMethod();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void testField() throws Exception {
+ checkDeprecated("field 'foo'", AnnotationTest.class.getDeclaredField("foo").getAnnotations());
+ }
+
+ private static void testMethod() throws Exception {
+ checkDeprecated("method 'bar'", AnnotationTest.class.getMethod("bar").getAnnotations());
+ }
+
+ private static void checkDeprecated(String what, Annotation[] annotations) {
+ Annotation n;
+ try {
+ n = annotations[0];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new RuntimeException(what + ": No annotations, expected @Deprecated", e);
+ }
+ if (!(n instanceof Deprecated)) {
+ throw new RuntimeException(what + ": Expected @Deprecated, got: " + n);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/AnnotationTestRunner.java b/src/test/java/com/android/tools/r8/cf/AnnotationTestRunner.java
new file mode 100644
index 0000000..5c48073
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/AnnotationTestRunner.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer.DirectoryConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class AnnotationTestRunner {
+ static final Class CLASS = AnnotationTest.class;
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ @Ignore
+ public void test() throws Exception {
+ ProcessResult runInput =
+ ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+ assertEquals(0, runInput.exitCode);
+ Path out = temp.getRoot().toPath();
+ R8.run(
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addClassProgramData(ToolHelper.getClassAsBytes(CLASS), Origin.unknown())
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(new DirectoryConsumer(out))
+ .build());
+ ProcessResult runOutput = ToolHelper.runJava(out, CLASS.getCanonicalName());
+ assertEquals(runInput.toString(), runOutput.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 5f6a5cc..84f6a08 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -37,6 +37,7 @@
import java.util.Collections;
import java.util.List;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
public class ProguardConfigurationParserTest extends TestBase {
@@ -544,7 +545,7 @@
assertTrue(
config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;")));
assertTrue(
- config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;")));
+ config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobar;")));
assertTrue(
config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;")));
}
@@ -561,12 +562,47 @@
assertTrue(
config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;")));
assertTrue(
- config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobaz;")));
+ config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboobar;")));
assertTrue(
config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;")));
}
@Test
+ public void testAdaptClassStringsNthWildcard() throws Exception {
+ DexItemFactory dexItemFactory = new DexItemFactory();
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(dexItemFactory, reporter);
+ String wildcard = "-adaptclassstrings *foo<1>";
+ parser.parse(createConfigurationForTesting(ImmutableList.of(wildcard)));
+ verifyParserEndsCleanly();
+ ProguardConfiguration config = parser.getConfig();
+ assertFalse(
+ config.getAdaptClassStrings().matches(dexItemFactory.createType("Lfoobar;")));
+ assertFalse(
+ config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboofoobar;")));
+ // TODO(b/73800755): Use <n> while matching class name list.
+ //assertTrue(
+ // config.getAdaptClassStrings().matches(dexItemFactory.createType("Lboofooboo;")));
+ }
+
+ @Ignore("b/73800755: verify the range of <n>")
+ @Test
+ public void testAdaptClassStringsNthWildcard_outOfRange() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-adaptclassstrings *foo<2>"
+ );
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+ "wildcard", "out", "range");
+ }
+ }
+
+ @Test
public void testIdentifierNameString() throws Exception {
ProguardConfigurationParser parser =
new ProguardConfigurationParser(new DexItemFactory(), reporter);
@@ -1173,6 +1209,43 @@
}
@Test
+ public void parse_if_nthWildcard() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **$D<2>" // <2> corresponds to the 2nd ** in -if rule.
+ );
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ checkDiagnostic(handler.warnings, proguardConfig, 1, 1,
+ "Ignoring", "-if");
+ ProguardConfiguration config = parser.getConfig();
+ assertEquals(1, config.getRules().size());
+ ProguardIfRule if0 = (ProguardIfRule) config.getRules().get(0);
+ assertEquals("**$R**", if0.getClassNames().toString());
+ assertEquals(ProguardKeepRuleType.KEEP, if0.subsequentRule.getType());
+ assertEquals("**$D<2>", if0.subsequentRule.getClassNames().toString());
+ }
+
+ @Ignore("b/73800755: verify the range of <n>")
+ @Test
+ public void parse_if_nthWildcard_outOfRange() throws Exception {
+ Path proguardConfig = writeTextToTempFile(
+ "-if class **$R**",
+ "-keep class **D<4>" // There are 3 ** in this rule.
+ );
+ try {
+ ProguardConfigurationParser parser =
+ new ProguardConfigurationParser(new DexItemFactory(), reporter);
+ parser.parse(proguardConfig);
+ fail();
+ } catch (AbortException e) {
+ checkDiagnostic(handler.errors, proguardConfig, 1, 1,
+ "wildcard", "out", "range");
+ }
+ }
+
+ @Test
public void parse_if_if() throws Exception {
Path proguardConfig = writeTextToTempFile(
"-if class **$$ModuleAdapter",
diff --git a/third_party/kotlin.tar.gz.sha1 b/third_party/kotlin.tar.gz.sha1
index e55712d..40f26fa 100644
--- a/third_party/kotlin.tar.gz.sha1
+++ b/third_party/kotlin.tar.gz.sha1
@@ -1 +1 @@
-6dc49791e5fcf4318ae5246eacc73718180508ce
\ No newline at end of file
+4b18d827485f53990ad47b81db2a025abaa325d1
\ No newline at end of file
diff --git a/third_party/proguard/README.google b/third_party/proguard/README.google
index 4f85601..abe0568 100644
--- a/third_party/proguard/README.google
+++ b/third_party/proguard/README.google
@@ -1,8 +1,8 @@
URL: https://sourceforge.net/projects/proguard/files/proguard/5.2/
URL: https://sourceforge.net/projects/proguard/files/proguard/6.0/
-Version: 5.2.1, 6.0
+Version: 5.2.1, 6.0.1
License: GPL
-License File: proguard5.2.1/docs/license.html, proguard6.0/docs/license.html
+License File: proguard5.2.1/docs/license.html, proguard6.0.1/docs/license.html
Description:
ProGuard Java Optimizer and Obfuscator
diff --git a/third_party/proguard/proguard6.0.1.tar.gz.sha1 b/third_party/proguard/proguard6.0.1.tar.gz.sha1
new file mode 100644
index 0000000..e5e8e3c
--- /dev/null
+++ b/third_party/proguard/proguard6.0.1.tar.gz.sha1
@@ -0,0 +1 @@
+ef075c414299327dae5f96cce539422dc9088946
\ No newline at end of file
diff --git a/third_party/proguard/proguard6.0.tar.gz.sha1 b/third_party/proguard/proguard6.0.tar.gz.sha1
deleted file mode 100644
index 4596bc8..0000000
--- a/third_party/proguard/proguard6.0.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-57d0702f38196c81ff506d2e34a4a5569c3af583
\ No newline at end of file