Merge "Fix warnings related to ASM5 deprecated method"
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index 21ed16e..7e2e851 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -42,6 +42,41 @@
this.valueNumberGenerator = valueNumberGenerator;
}
+ /**
+ * Trace blocks and attempt to put fallthrough blocks immediately after the block that
+ * falls through. When we fail to do that we create a new fallthrough block with an explicit
+ * goto to the actual fallthrough block.
+ */
+ public void traceBlocks() {
+ BasicBlock[] sorted = topologicallySortedBlocks();
+ clearMarks();
+ int nextBlockNumber = blocks.size();
+ LinkedList<BasicBlock> tracedBlocks = new LinkedList<>();
+ for (BasicBlock block : sorted) {
+ if (!block.isMarked()) {
+ block.mark();
+ tracedBlocks.add(block);
+ BasicBlock current = block;
+ BasicBlock fallthrough = block.exit().fallthroughBlock();
+ while (fallthrough != null && !fallthrough.isMarked()) {
+ fallthrough.mark();
+ tracedBlocks.add(fallthrough);
+ current = fallthrough;
+ fallthrough = fallthrough.exit().fallthroughBlock();
+ }
+ if (fallthrough != null) {
+ BasicBlock newFallthrough = BasicBlock.createGotoBlock(fallthrough, nextBlockNumber++);
+ current.exit().setFallthroughBlock(newFallthrough);
+ newFallthrough.getPredecessors().add(current);
+ fallthrough.replacePredecessor(current, newFallthrough);
+ newFallthrough.mark();
+ tracedBlocks.add(newFallthrough);
+ }
+ }
+ }
+ blocks = tracedBlocks;
+ }
+
private void ensureBlockNumbering() {
if (!numbered) {
numbered = true;
@@ -327,6 +362,10 @@
return normalExitBlock;
}
+ public void invalidateNormalExitBlock() {
+ normalExitBlock = null;
+ }
+
public ListIterator<BasicBlock> listIterator() {
return new BasicBlockIterator(this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 28a4d74..a644470 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -363,4 +363,8 @@
public Set<Value> getDebugValues() {
return debugValues != null ? debugValues : ImmutableSet.of();
}
+
+ public boolean usesValueOneTime(Value usedValue) {
+ return operands.indexOf(usedValue) == operands.lastIndexOf(usedValue);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Return.java b/src/main/java/com/android/tools/r8/ir/code/Return.java
index 337ed71..ba2fcb0 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Return.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Return.java
@@ -67,7 +67,7 @@
@Override
public void buildDex(DexBuilder builder) {
- builder.add(this, createDexInstruction(builder));
+ builder.addReturn(this, createDexInstruction(builder));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index cbd8941..8540bf3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -402,6 +402,33 @@
add(argument, new FallThroughInfo(argument));
}
+ public void addReturn(Return ret, Instruction dex) {
+ if (nextBlock != null) {
+ Return followingRet = nextBlock.exit().asReturn();
+ if (nextBlock.getInstructions().size() == 1
+ && followingRet != null
+ && ret.getReturnType() == followingRet.getReturnType()) {
+ if (ret.isReturnVoid() && followingRet.isReturnVoid()) {
+ addNop(ret);
+ return;
+ }
+ if (!ret.isReturnVoid()
+ && !followingRet.isReturnVoid()
+ && ret.returnValue().outType() == followingRet.returnValue().outType()) {
+ int thisRegister = registerAllocator.getRegisterForValue(
+ ret.returnValue(), ret.getNumber());
+ int otherRegister = registerAllocator.getRegisterForValue(
+ followingRet.returnValue(), followingRet.getNumber());
+ if (thisRegister == otherRegister) {
+ addNop(ret);
+ return;
+ }
+ }
+ }
+ }
+ add(ret, dex);
+ }
+
private void add(com.android.tools.r8.ir.code.Instruction ir, Info info) {
assert ir != null;
assert info != null;
@@ -436,10 +463,24 @@
return info;
}
}
- assert instruction != null && instruction.isGoto();
+ assert instruction != null;
+ if (instruction.isReturn()) {
+ assert getInfo(instruction) instanceof FallThroughInfo;
+ return getTargetInfo(computeNextBlock(block));
+ }
+ assert instruction.isGoto();
return getTargetInfo(instruction.asGoto().getTarget());
}
+ private BasicBlock computeNextBlock(BasicBlock block) {
+ ListIterator<BasicBlock> it = ir.listIterator();
+ BasicBlock current = it.next();
+ while (current != block) {
+ current = it.next();
+ }
+ return it.next();
+ }
+
// Helper for computing switch payloads.
private Nop createSwitchPayload(SwitchPayloadInfo info, int offset) {
Switch ir = info.ir;
@@ -844,8 +885,10 @@
// Set the size to the min of the size of the return and the size of the goto. When
// adding instructions, we use the return if the computed size matches the size of the
// return.
+ assert !(targetInfo instanceof FallThroughInfo);
size = Math.min(targetInfo.getSize(), size);
}
+ assert size != 0;
return size;
}
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 1b7a276..77f1a16 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
@@ -399,7 +399,7 @@
// Create block order and make sure that all blocks are immediately followed by their
// fallthrough block if any.
- traceBlocks(ir);
+ ir.traceBlocks();
// Clear the code so we don't build multiple times.
source.clear();
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 0ddd43a..8e5b26f 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
@@ -522,6 +522,9 @@
}
printMethod(code, "Optimized IR (SSA)");
+
+ codeRewriter.inlineReturnBlock(code);
+
// Perform register allocation.
RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
method.setCode(code, registerAllocator, appInfo.dexItemFactory);
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 fbde02b..5c1114b 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
@@ -87,6 +87,7 @@
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@@ -1177,7 +1178,16 @@
// needs to go in the immediate dominator block so that it is available for phi moves.
for (Phi phi : instruction.outValue().uniquePhiUsers()) {
if (phi.getBlock() == dominator) {
- dominator = dominatorTree.immediateDominator(dominator);
+ if (instruction.outValue().numberOfAllUsers() == 1 &&
+ phi.usesValueOneTime(instruction.outValue())) {
+ // Out value is used only one time, move the constant directly to the corresponding
+ // branch rather than into the dominator to avoid to generate a const on paths which
+ // does not required it.
+ int predIndex = phi.getOperands().indexOf(instruction.outValue());
+ dominator = dominator.getPredecessors().get(predIndex);
+ } else {
+ dominator = dominatorTree.immediateDominator(dominator);
+ }
break;
}
}
@@ -1466,6 +1476,87 @@
}
}
+ /**
+ * Inline the return block at its targets.
+ *
+ * The inlining of return mostly undoes the merge performed at IR build time. This helps avoid
+ * unneeded moves as values are forced into the same register at all returns, which there can be
+ * a lot of when compiling in debug mode. Measurements show that iterating the inlining of returns
+ * does not pay off as it can lead to code size increase, eg, when a sequence of ifs all jump to
+ * a common return.
+ */
+ public void inlineReturnBlock(IRCode code) {
+ BasicBlock block = code.getNormalExitBlock();
+ code.invalidateNormalExitBlock();
+ if (block == null
+ || block.getPredecessors().size() <= 1
+ || block.getInstructions().size() > 1) {
+ return;
+ }
+ int predIndex = 0;
+ for (BasicBlock pred : block.getPredecessors()) {
+ ListIterator<Instruction> iterator = pred.listIterator(pred.exit());
+ iterator.previous();
+ for (Instruction origInstruction : block.getInstructions()) {
+ assert origInstruction.isReturn();
+ // Create an instruction copy replacing phi values of this block by their operands.
+ Instruction instruction;
+ Return ret = origInstruction.asReturn();
+ if (ret.isReturnVoid()) {
+ instruction = new Return();
+ } else {
+ Value origValue = ret.returnValue();
+ Value copyValue = origValue.isPhi() && block.getPhis().contains(origValue)
+ ? origValue.asPhi().getOperand(predIndex)
+ : origValue;
+ instruction = new Return(copyValue, ret.getReturnType());
+ }
+ // Copy over each debug value replacing phi values of this block by their operands.
+ for (Value value : origInstruction.getDebugValues()) {
+ assert value.getLocalInfo() != null;
+ if (value.isPhi() && block.getPhis().contains(value)) {
+ Phi phi = value.asPhi();
+ Value operand = phi.getOperand(predIndex);
+ if (phi.getLocalInfo() == operand.getLocalInfo()) {
+ instruction.addDebugValue(operand);
+ } else {
+ // If the phi and its operand are different locals insert a local write.
+ Value localValue = code.createValue(phi.outType(), phi.getLocalInfo());
+ DebugLocalWrite write = new DebugLocalWrite(localValue, operand);
+ write.setBlock(pred);
+ iterator.add(write);
+ instruction.addDebugValue(localValue);
+ }
+ } else {
+ instruction.addDebugValue(value);
+ }
+ }
+ instruction.setBlock(pred);
+ iterator.add(instruction);
+ }
+ iterator.previous();
+ Instruction ret = iterator.next();
+ Instruction jump = iterator.next();
+ assert !iterator.hasNext();
+ jump.moveDebugValues(ret);
+ iterator.remove();
+ assert pred.exit().isReturn();
+ pred.removeSuccessor(block);
+ predIndex++;
+ }
+ // Clean out all users and remove the inlined block.
+ while (!block.getPredecessors().isEmpty()) {
+ block.removePredecessor(block.getPredecessors().get(0));
+ }
+ for (Instruction instruction : block.getInstructions()) {
+ for (Value value : instruction.inValues()) {
+ value.removeUser(instruction);
+ }
+ instruction.clearDebugValues();
+ }
+ code.removeBlocks(Collections.singletonList(block));
+ }
+
private static class ExpressionEquivalence extends Equivalence<Instruction> {
@Override
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 2aa9015..a8b3972 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
@@ -369,10 +369,10 @@
}
}
spillCount = 0;
- if (localsChanged && shouldEmitChangesAtInstruction(instruction)) {
+ if (localsChanged && instruction.getBlock().exit() != instruction) {
DebugLocalsChange change = createLocalsChange(ending, starting);
if (change != null) {
- if (instruction.isDebugPosition() || instruction.isJumpInstruction()) {
+ if (instruction.isDebugPosition()) {
instructionIterator.previous();
instructionIterator.add(change);
instructionIterator.next();
@@ -505,14 +505,6 @@
return new DebugLocalsChange(ending, starting);
}
- private boolean shouldEmitChangesAtInstruction(Instruction instruction) {
- BasicBlock block = instruction.getBlock();
- // We emit local changes on all non-exit instructions or, since we have only a singe return
- // block, any exits directly targeting that.
- return instruction != block.exit()
- || (instruction.isGoto() && instruction.asGoto().getTarget() == code.getNormalExitBlock());
- }
-
private void clearState() {
liveAtEntrySets = null;
liveIntervals = null;
@@ -1764,8 +1756,7 @@
for (LiveIntervals intervals : liveIntervals) {
if (intervals.hasSplits()) {
LiveIntervals current = intervals;
- PriorityQueue<LiveIntervals> sortedChildren =
- new PriorityQueue<>((o1, o2) -> Integer.compare(o1.getStart(), o2.getStart()));
+ PriorityQueue<LiveIntervals> sortedChildren = new PriorityQueue<>();
sortedChildren.addAll(current.getSplitChildren());
for (LiveIntervals split = sortedChildren.poll();
split != null;
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 e404f51..8869715 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAlwaysInlineRule.java
@@ -27,7 +27,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 cc54a49..c7d9ca3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeNoSideEffectRule.java
@@ -26,7 +26,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 73825f1..70f18f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAssumeValuesRule.java
@@ -25,7 +25,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 54302db..20d1070 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -27,7 +27,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
new file mode 100644
index 0000000..cd1c5fc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassNameList.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import it.unimi.dsi.fastutil.objects.Object2BooleanArrayMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Object2BooleanMap.Entry;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public abstract class ProguardClassNameList {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static ProguardClassNameList singletonList(ProguardTypeMatcher matcher) {
+ return new SingleClassNameList(matcher);
+ }
+
+ public abstract int size();
+
+ public static class Builder {
+
+ /**
+ * Map used to store pairs of patterns and whether they are negated.
+ */
+ private Object2BooleanMap<ProguardTypeMatcher> matchers = new Object2BooleanArrayMap<>();
+
+ private Builder() {
+ }
+
+ public Builder addClassName(boolean isNegated, ProguardTypeMatcher className) {
+ matchers.put(className, isNegated);
+ return this;
+ }
+
+ ProguardClassNameList build() {
+ if (matchers.containsValue(true)) {
+ // At least one pattern is negated.
+ return new MixedClassNameList(matchers);
+ } else {
+ if (matchers.size() == 1) {
+ return new SingleClassNameList(Iterables.getOnlyElement(matchers.keySet()));
+ } else {
+ return new PositiveClassNameList(matchers.keySet());
+ }
+ }
+ }
+
+ }
+
+ public abstract void writeTo(StringBuilder builder);
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ writeTo(builder);
+ return builder.toString();
+ }
+
+ public abstract List<DexType> asSpecificDexTypes();
+
+ public abstract boolean matches(DexType type);
+
+ private static class SingleClassNameList extends ProguardClassNameList {
+
+ private final ProguardTypeMatcher className;
+
+ private SingleClassNameList(ProguardTypeMatcher className) {
+ this.className = className;
+ }
+
+ @Override
+ public int size() {
+ return 1;
+ }
+
+ @Override
+ public void writeTo(StringBuilder builder) {
+ builder.append(className.toString());
+ }
+
+ @Override
+ public List<DexType> asSpecificDexTypes() {
+ DexType specific = className.getSpecificType();
+ return specific == null ? null : Collections.singletonList(specific);
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ return className.matches(type);
+ }
+ }
+
+ private static class PositiveClassNameList extends ProguardClassNameList {
+
+ private final ImmutableList<ProguardTypeMatcher> classNames;
+
+ private PositiveClassNameList(Collection<ProguardTypeMatcher> classNames) {
+ this.classNames = ImmutableList.copyOf(classNames);
+ }
+
+ @Override
+ public int size() {
+ return classNames.size();
+ }
+
+ @Override
+ public void writeTo(StringBuilder builder) {
+ boolean first = true;
+ for (ProguardTypeMatcher className : classNames) {
+ if (!first) {
+ builder.append(',');
+ }
+ builder.append(className);
+ first = false;
+ }
+ }
+
+ @Override
+ public List<DexType> asSpecificDexTypes() {
+ if (classNames.stream().allMatch(k -> k.getSpecificType() != null)) {
+ return classNames.stream().map(ProguardTypeMatcher::getSpecificType)
+ .collect(Collectors.toList());
+ }
+ return null;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ return classNames.stream().anyMatch(name -> name.matches(type));
+ }
+ }
+
+ private static class MixedClassNameList extends ProguardClassNameList {
+
+ private final Object2BooleanMap<ProguardTypeMatcher> classNames;
+
+ private MixedClassNameList(Object2BooleanMap<ProguardTypeMatcher> classNames) {
+ this.classNames = classNames;
+ }
+
+ @Override
+ public int size() {
+ return classNames.size();
+ }
+
+ @Override
+ public void writeTo(StringBuilder builder) {
+ boolean first = true;
+ for (Entry<ProguardTypeMatcher> className : classNames.object2BooleanEntrySet()) {
+ if (!first) {
+ builder.append(',');
+ }
+ if (className.getBooleanValue()) {
+ builder.append('!');
+ }
+ builder.append(className.getKey().toString());
+ first = false;
+ }
+ }
+
+ @Override
+ public List<DexType> asSpecificDexTypes() {
+ return null;
+ }
+
+ @Override
+ public boolean matches(DexType type) {
+ for (Entry<ProguardTypeMatcher> className : classNames.object2BooleanEntrySet()) {
+ if (className.getKey().matches(type)) {
+ // If we match a negation, abort as non-match. If we match a positive, return true.
+ return !className.getBooleanValue();
+ }
+ }
+ return false;
+ }
+ }
+}
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 396d37d..745bcf1 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardClassSpecification.java
@@ -20,7 +20,7 @@
protected DexAccessFlags negatedClassAccessFlags = new DexAccessFlags(0);
protected boolean classTypeNegated = false;
protected ProguardClassType classType;
- protected List<ProguardTypeMatcher> classNames;
+ protected ProguardClassNameList classNames;
protected ProguardTypeMatcher inheritanceAnnotation;
protected ProguardTypeMatcher inheritanceClassName;
protected boolean inheritanceIsExtends = false;
@@ -65,11 +65,11 @@
this.inheritanceAnnotation = inheritanceAnnotation;
}
- public List<ProguardTypeMatcher> getClassNames() {
+ public ProguardClassNameList getClassNames() {
return classNames;
}
- public void setClassNames(List<ProguardTypeMatcher> classNames) {
+ public void setClassNames(ProguardClassNameList classNames) {
this.classNames = classNames;
}
@@ -114,7 +114,7 @@
}
protected void matchAllSpecification() {
- setClassNames(Collections.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
+ setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()));
setMemberRules(Collections.singleton(ProguardMemberRule.defaultKeepAllRule()));
}
}
@@ -124,7 +124,7 @@
private final DexAccessFlags negatedClassAccessFlags;
private final boolean classTypeNegated;
private final ProguardClassType classType;
- private final List<ProguardTypeMatcher> classNames;
+ private final ProguardClassNameList classNames;
private final ProguardTypeMatcher inheritanceAnnotation;
private final ProguardTypeMatcher inheritanceClassName;
private final boolean inheritanceIsExtends;
@@ -136,7 +136,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
@@ -173,7 +173,7 @@
return inheritanceAnnotation;
}
- public List<ProguardTypeMatcher> getClassNames() {
+ public ProguardClassNameList getClassNames() {
return classNames;
}
@@ -262,14 +262,7 @@
}
builder.append(classType);
builder.append(' ');
- boolean first = true;
- for (ProguardTypeMatcher className : classNames) {
- builder.append(className);
- if (!first) {
- builder.append(',');
- }
- first = false;
- }
+ classNames.writeTo(builder);
if (hasInheritanceClassName()) {
builder.append(inheritanceIsExtends ? " extends" : " implements");
StringUtils.appendNonEmpty(builder, " @", inheritanceAnnotation, null);
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 22fdf45..1c3051d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -132,9 +132,9 @@
|| parseOptimizationOption()) {
// Intentionally left empty.
} else if (
- (option = Iterables.find(warnedSingleArgOptions,
- this::skipOptionWithSingleArg, null)) != null
- || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
+ (option = Iterables.find(warnedSingleArgOptions,
+ this::skipOptionWithSingleArg, null)) != null
+ || (option = Iterables.find(warnedFlagOptions, this::skipFlag, null)) != null) {
warnIgnoringOptions(option);
} else if ((option = Iterables.find(unsupportedFlagOptions, this::skipFlag, null)) != null) {
throw parseError("Unsupported option: -" + option);
@@ -515,7 +515,7 @@
} else {
DexAccessFlags flags =
parseNegation() ? builder.getNegatedClassAccessFlags() :
- builder.getClassAccessFlags();
+ builder.getClassAccessFlags();
skipWhitespace();
if (acceptString("public")) {
flags.setPublic();
@@ -992,26 +992,20 @@
}
}
- private void checkNotNegatedPattern() throws ProguardRuleParserException {
+ private ProguardClassNameList parseClassNames() throws ProguardRuleParserException {
+ ProguardClassNameList.Builder builder = ProguardClassNameList.builder();
skipWhitespace();
- if (acceptChar('!')) {
- throw parseError("Negated filters are not supported");
- }
- }
-
- private List<ProguardTypeMatcher> parseClassNames() throws ProguardRuleParserException {
- List<ProguardTypeMatcher> classNames = new ArrayList<>();
- checkNotNegatedPattern();
- classNames
- .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+ boolean negated = acceptChar('!');
+ builder.addClassName(negated,
+ ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
skipWhitespace();
while (acceptChar(',')) {
- checkNotNegatedPattern();
- classNames
- .add(ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
+ negated = acceptChar('!');
+ builder.addClassName(negated,
+ ProguardTypeMatcher.create(parseClassName(), ClassOrType.CLASS, dexItemFactory));
skipWhitespace();
}
- return classNames;
+ return builder.build();
}
private String parsePackageNameOrEmptyString() {
@@ -1034,7 +1028,7 @@
for (int lineNumber = 0; lineNumber < lines.length; lineNumber++) {
String line = lines[lineNumber];
if (remaining <= line.length() || lineNumber == lines.length - 1) {
- String arrow = CharBuffer.allocate(remaining).toString().replace( '\0', ' ' ) + '^';
+ String arrow = CharBuffer.allocate(remaining).toString().replace('\0', ' ') + '^';
return name + ":" + (lineNumber + 1) + ":" + remaining + "\n" + line
+ '\n' + arrow;
}
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 deb11a1..1cd7100 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationRule.java
@@ -15,7 +15,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 787a31c..e449048 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -27,7 +27,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 fac7d99..1932c83 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRule.java
@@ -41,7 +41,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
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 fd123d4..9b06a2f 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -65,6 +65,10 @@
@Override
public abstract int hashCode();
+ public DexType getSpecificType() {
+ return null;
+ }
+
private static class MatchAllTypes extends ProguardTypeMatcher {
private static final ProguardTypeMatcher MATCH_ALL_TYPES = new MatchAllTypes();
@@ -210,6 +214,11 @@
public int hashCode() {
return type.hashCode();
}
+
+ @Override
+ public DexType getSpecificType() {
+ return type;
+ }
}
private static class MatchTypePattern extends ProguardTypeMatcher {
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 581a0db..a679fde 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -27,7 +27,7 @@
DexAccessFlags negatedClassAccessFlags,
boolean classTypeNegated,
ProguardClassType classType,
- List<ProguardTypeMatcher> classNames,
+ ProguardClassNameList classNames,
ProguardTypeMatcher inheritanceAnnotation,
ProguardTypeMatcher inheritanceClassName,
boolean inheritanceIsExtends,
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
index 4dd1880..74cec83 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -108,22 +108,6 @@
return anyImplementedInterfaceMatches(superClass, className, annotation);
}
- // Returns a list of types iff the keep rule only contains specific type matches.
- // Otherwise, null is returned.
- private DexType[] specificDexTypes(ProguardConfigurationRule rule) {
- for (ProguardTypeMatcher matcher : rule.getClassNames()) {
- if (!(matcher instanceof MatchSpecificType)) {
- return null;
- }
- }
- final int length = rule.getClassNames().size();
- DexType[] result = new DexType[length];
- for (int i = 0; i < length; i++) {
- result[i] = ((MatchSpecificType) rule.getClassNames().get(i)).type;
- }
- return result;
- }
-
// Process a class with the keep rule.
private void process(DexClass clazz, ProguardConfigurationRule rule) {
if (!clazz.accessFlags.containsAllOf(rule.getClassAccessFlags())) {
@@ -168,51 +152,49 @@
}
}
- for (ProguardTypeMatcher className : rule.getClassNames()) {
- if (className.matches(clazz.type)) {
- Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
- if (rule instanceof ProguardKeepRule) {
- switch (((ProguardKeepRule) rule).getType()) {
- case KEEP_CLASS_MEMBERS: {
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
- markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
- break;
- }
- case KEEP_CLASSES_WITH_MEMBERS: {
- if (!allRulesSatisfied(memberKeepRules, clazz)) {
- break;
- }
- // fallthrough;
- }
- case KEEP: {
- markClass(clazz, rule);
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
- markMatchingFields(clazz, memberKeepRules, rule, null);
- break;
- }
- }
- } else if (rule instanceof ProguardCheckDiscardRule) {
- if (memberKeepRules.isEmpty()) {
- markClass(clazz, rule);
- } else {
+ if (rule.getClassNames().matches(clazz.type)) {
+ Collection<ProguardMemberRule> memberKeepRules = rule.getMemberRules();
+ if (rule instanceof ProguardKeepRule) {
+ switch (((ProguardKeepRule) rule).getType()) {
+ case KEEP_CLASS_MEMBERS: {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, clazz.type);
markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
- markMatchingMethods(clazz, memberKeepRules, rule, clazz.type);
+ break;
}
- } else if (rule instanceof ProguardWhyAreYouKeepingRule
- || rule instanceof ProguardKeepPackageNamesRule) {
- markClass(clazz, rule);
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
- markMatchingFields(clazz, memberKeepRules, rule, null);
- } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
- markMatchingFields(clazz, memberKeepRules, rule, null);
- } else if (rule instanceof ProguardAlwaysInlineRule) {
- markMatchingMethods(clazz, memberKeepRules, rule, null);
- } else {
- assert rule instanceof ProguardAssumeValuesRule;
- markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
- markMatchingFields(clazz, memberKeepRules, rule, null);
+ case KEEP_CLASSES_WITH_MEMBERS: {
+ if (!allRulesSatisfied(memberKeepRules, clazz)) {
+ break;
+ }
+ // fallthrough;
+ }
+ case KEEP: {
+ markClass(clazz, rule);
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ break;
+ }
}
+ } else if (rule instanceof ProguardCheckDiscardRule) {
+ if (memberKeepRules.isEmpty()) {
+ markClass(clazz, rule);
+ } else {
+ markMatchingFields(clazz, memberKeepRules, rule, clazz.type);
+ markMatchingMethods(clazz, memberKeepRules, rule, clazz.type);
+ }
+ } else if (rule instanceof ProguardWhyAreYouKeepingRule
+ || rule instanceof ProguardKeepPackageNamesRule) {
+ markClass(clazz, rule);
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ } else if (rule instanceof ProguardAssumeNoSideEffectRule) {
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
+ } else if (rule instanceof ProguardAlwaysInlineRule) {
+ markMatchingMethods(clazz, memberKeepRules, rule, null);
+ } else {
+ assert rule instanceof ProguardAssumeValuesRule;
+ markMatchingVisibleMethods(clazz, memberKeepRules, rule, null);
+ markMatchingFields(clazz, memberKeepRules, rule, null);
}
}
}
@@ -224,7 +206,7 @@
// Mark all the things explicitly listed in keep rules.
if (rules != null) {
for (ProguardConfigurationRule rule : rules) {
- DexType[] specifics = specificDexTypes(rule);
+ List<DexType> specifics = rule.getClassNames().asSpecificDexTypes();
if (specifics != null) {
// This keep rule only lists specific type matches.
// This means there is no need to iterate over all classes.
diff --git a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
index 1573b34..92a755e 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JasminTestBase.java
@@ -4,21 +4,29 @@
package com.android.tools.r8.jasmin;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompilationException;
import com.android.tools.r8.R8;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.errors.DexOverflowException;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.DexInspector.MethodSubject;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OutputMode;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
import jasmin.ClassFile;
import java.io.File;
@@ -28,6 +36,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
@@ -135,4 +144,65 @@
throws IOException, CompilationException, ExecutionException {
return ToolHelper.optimizeWithR8(app, new AppInfoWithSubtyping(app), options);
}
+
+ protected DexApplication buildApplication(JasminBuilder builder, InternalOptions options) {
+ try {
+ return buildApplication(AndroidApp.fromClassProgramData(builder.buildClasses()), options);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexApplication buildApplication(AndroidApp input, InternalOptions options) {
+ try {
+ options.itemFactory.resetSortedIndices();
+ return new ApplicationReader(input, options, new Timing("JasminTest")).read();
+ } catch (IOException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String runArt(DexApplication application, InternalOptions options, String mainClass)
+ throws DexOverflowException {
+ try {
+ AndroidApp app = writeDex(application, options);
+ return runOnArt(app, mainClass);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public AndroidApp writeDex(DexApplication application, InternalOptions options)
+ throws DexOverflowException {
+ AppInfo appInfo = new AppInfo(application);
+ try {
+ return R8.writeApplication(
+ Executors.newSingleThreadExecutor(),
+ application,
+ appInfo,
+ null,
+ NamingLens.getIdentityLens(),
+ null,
+ null,
+ options);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected DexEncodedMethod getMethod(DexApplication application, String clazz,
+ MethodSignature signature) {
+ return getMethod(application,
+ clazz, signature.type, signature.name, signature.parameters);
+ }
+
+ protected DexEncodedMethod getMethod(DexApplication application, String className,
+ String returnType, String methodName, String[] parameters) {
+ DexInspector inspector = new DexInspector(application);
+ ClassSubject clazz = inspector.clazz(className);
+ assertTrue(clazz.isPresent());
+ MethodSubject method = clazz.method(returnType, methodName, Arrays.asList(parameters));
+ assertTrue(method.isPresent());
+ return method.getMethod();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java b/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java
new file mode 100644
index 0000000..e8efd38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/jasmin/Regress65432240.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.jasmin;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+
+public class Regress65432240 extends JasminTestBase {
+
+ @Test
+ public void testConstantNotIntoEntryBlock() throws Exception {
+ JasminBuilder builder = new JasminBuilder();
+ JasminBuilder.ClassBuilder clazz = builder.addClass("Test");
+
+ MethodSignature signature = clazz.addStaticMethod("test1", ImmutableList.of("I"), "I",
+ ".limit stack 3",
+ ".limit locals 2",
+ " iload 0",
+ " ifne L2",
+ "L1:",
+ " iconst_0",
+ " ireturn",
+ "L2:",
+ " iload 0",
+ " iload 0",
+ " iconst_1",
+ " isub",
+ " invokestatic Test/test1(I)I",
+ " iadd",
+ " ireturn");
+
+ clazz.addMainMethod(
+ ".limit stack 2",
+ ".limit locals 1",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_0",
+ " invokestatic Test/test1(I)I",
+ " invokestatic java/lang/Integer/toString(I)Ljava/lang/String;",
+ " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ " getstatic java/lang/System/out Ljava/io/PrintStream;",
+ " iconst_0",
+ " invokestatic Test/test1(I)I",
+ " invokestatic java/lang/Integer/toString(I)Ljava/lang/String;",
+ " invokevirtual java/io/PrintStream/print(Ljava/lang/String;)V",
+ " return");
+
+ String expected = runOnJava(builder, clazz.name);
+
+ InternalOptions options = new InternalOptions();
+ DexApplication originalApplication = buildApplication(builder, options);
+ DexApplication processedApplication = process(originalApplication, options);
+
+ DexEncodedMethod method = getMethod(processedApplication, clazz.name, signature);
+ DexCode code = method.getCode().asDexCode();
+ assertTrue(code.instructions[0] instanceof IfNez);
+
+ String artResult = runArt(processedApplication, options, clazz.name);
+ assertEquals(expected, artResult);
+ }
+}
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 0f2e456..48f8835 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -129,8 +129,7 @@
assertEquals(1, rules.size());
ProguardConfigurationRule rule = rules.get(0);
assertEquals(1, rule.getMemberRules().size());
- assertEquals("com.company.hello.**", rule.getClassNames().get(0).toString());
- assertEquals("com.company.world.**", rule.getClassNames().get(1).toString());
+ assertEquals("com.company.hello.**,com.company.world.**", rule.getClassNames().toString());
assertEquals(ProguardKeepRuleType.KEEP, ((ProguardKeepRule) rule).getType());
assertTrue(rule.getInheritanceIsExtends());
assertEquals("some.library.Class", rule.getInheritanceClassName().toString());
@@ -188,7 +187,7 @@
assertEquals(1, rules.size());
ProguardConfigurationRule rule = rules.get(0);
assertEquals(1, rule.getClassNames().size());
- assertEquals("*", rule.getClassNames().get(0).toString());
+ assertEquals("*", rule.getClassNames().toString());
assertTrue(rule.getInheritanceIsExtends());
assertEquals("foo.bar", rule.getInheritanceClassName().toString());
}
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 c38c379..af87086 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardNameMatchingTest.java
@@ -18,87 +18,86 @@
private static final DexItemFactory dexItemFactory = new DexItemFactory();
- private static boolean matchTypeName(String pattern, String typeName,
- DexItemFactory dexItemFactory) {
+ private static boolean matchTypeName(String typeName, String pattern) {
return ProguardTypeMatcher.create(pattern, ClassOrType.TYPE, dexItemFactory)
.matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName)));
}
- private static boolean matchClassName(String pattern, String className,
- DexItemFactory dexItemFactory) {
- return ProguardTypeMatcher.create(pattern, ClassOrType.CLASS, dexItemFactory)
+ private static boolean matchClassName(String className, String... patterns) {
+ ProguardClassNameList.Builder builder = ProguardClassNameList.builder();
+ for (String pattern : patterns) {
+ boolean isNegated = pattern.startsWith("!");
+ String actualPattern = isNegated ? pattern.substring(1) : pattern;
+ builder.addClassName(isNegated,
+ ProguardTypeMatcher.create(actualPattern, ClassOrType.CLASS, dexItemFactory));
+ }
+ return builder.build()
.matches(dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(className)));
}
@Test
public void matchClassNames() {
- assertTrue(matchClassName("**", "", dexItemFactory));
- assertTrue(matchClassName("**", "a", dexItemFactory));
- assertTrue(matchClassName("*", "", dexItemFactory));
- assertTrue(matchClassName("*", "a", dexItemFactory));
- assertFalse(matchClassName("?", "", dexItemFactory));
- assertTrue(matchClassName("?", "a", dexItemFactory));
+ assertTrue(matchClassName("", "**"));
+ assertTrue(matchClassName("a", "**"));
+ assertTrue(matchClassName("", "*"));
+ assertTrue(matchClassName("a", "*"));
+ assertFalse(matchClassName("", "?"));
+ assertTrue(matchClassName("a", "?"));
- assertTrue(matchClassName("**", "java.lang.Object", dexItemFactory));
- assertFalse(
- matchClassName("ja*", "java.lang.Object", dexItemFactory));
- assertTrue(
- matchClassName("ja**", "java.lang.Object", dexItemFactory));
- assertTrue(matchClassName("ja**ject", "java.lang.Object",
- dexItemFactory));
+ assertTrue(matchClassName("java.lang.Object", "**"));
+ assertFalse(matchClassName("java.lang.Object", "ja*"));
+ assertTrue(matchClassName("java.lang.Object", "ja**"));
+ assertTrue(matchClassName("java.lang.Object", "ja**ject"));
// Oddly, the proguard specification for this makes a lonely * synonymous with **.
- assertTrue(matchClassName("*", "java.lang.Object", dexItemFactory));
- assertFalse(matchClassName("ja*ject", "java.lang.Object",
- dexItemFactory));
+ assertTrue(matchClassName("java.lang.Object", "*"));
+ assertFalse(matchClassName("java.lang.Object", "ja*ject"));
- assertTrue(matchClassName("java.*g.O*", "java.lang.Object",
- dexItemFactory));
- assertTrue(matchClassName("java.*g.O?je?t", "java.lang.Object",
- dexItemFactory));
- assertFalse(matchClassName("java.*g.O?je?t?", "java.lang.Object",
- dexItemFactory));
- assertFalse(matchClassName("java?lang.Object", "java.lang.Object",
- dexItemFactory));
- assertTrue(matchClassName("*a*.*a**", "java.lang.Object",
- dexItemFactory));
- assertTrue(matchClassName("*a**a**", "java.lang.Object",
- dexItemFactory));
+ assertTrue(matchClassName("java.lang.Object", "java.*g.O*"));
+ assertTrue(matchClassName("java.lang.Object", "java.*g.O?je?t"));
+ assertFalse(matchClassName("java.lang.Object", "java.*g.O?je?t?"));
+ assertFalse(matchClassName("java.lang.Object", "java?lang.Object"));
+ assertTrue(matchClassName("java.lang.Object", "*a*.*a**"));
+ assertTrue(matchClassName("java.lang.Object", "*a**a**"));
+
+ assertTrue(matchClassName("java.lang.Object", "!java.util.**", "java**"));
+ assertFalse(matchClassName("java.lang.Object", "!java.**", "java.lang.*"));
+ assertTrue(matchClassName("java.lang.Object", "java.lang.*", "!java.**"));
}
private void assertMatchesBasicTypes(String pattern) {
for (String type : BASIC_TYPES) {
- assertTrue(matchTypeName(pattern, type, dexItemFactory));
+ assertTrue(matchTypeName(type, pattern));
}
}
private void assertDoesNotMatchBasicTypes(String pattern) {
for (String type : BASIC_TYPES) {
- assertFalse(matchTypeName(pattern, type, dexItemFactory));
+ assertFalse(matchTypeName(type, pattern));
}
}
@Test
public void matchTypeNames() {
- assertTrue(matchTypeName("**", "java.lang.Object", dexItemFactory));
+ assertTrue(matchTypeName("java.lang.Object", "**"));
assertDoesNotMatchBasicTypes("**");
assertDoesNotMatchBasicTypes("*");
assertFalse(
- matchTypeName("**", "java.lang.Object[]", dexItemFactory));
+ matchTypeName("java.lang.Object[]", "**"));
assertFalse(
- matchTypeName("**z", "java.lang.Object", dexItemFactory));
- assertFalse(matchTypeName("java.**", "java.lang.Object[]",
- dexItemFactory));
+ matchTypeName("java.lang.Object", "**z"));
+ assertFalse(matchTypeName("java.lang.Object[]", "java.**"
+ ));
assertTrue(
- matchTypeName("***", "java.lang.Object[]", dexItemFactory));
- assertTrue(matchTypeName("***", "java.lang.Object[][]",
- dexItemFactory));
- assertFalse(matchTypeName("%", "java.lang.Object", dexItemFactory));
- assertTrue(matchTypeName("**", "a", dexItemFactory));
- assertTrue(matchTypeName("**[]", "java.lang.Object[]",
- dexItemFactory));
- assertFalse(matchTypeName("**[]", "java.lang.Object[][]",
- dexItemFactory));
- assertTrue(matchTypeName("*", "abc", dexItemFactory));
+ matchTypeName("java.lang.Object[]", "***"));
+ assertTrue(matchTypeName("java.lang.Object[][]", "***"
+ ));
+ assertFalse(matchTypeName("java.lang.Object", "%"));
+ assertTrue(matchTypeName("a", "**"));
+ assertTrue(matchTypeName("java.lang.Object[]", "**[]"
+ ));
+ assertFalse(matchTypeName("java.lang.Object[][]", "**[]"
+ ));
+ assertTrue(matchTypeName("abc", "*"));
assertMatchesBasicTypes("***");
assertMatchesBasicTypes("%");
}
diff --git a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
index 919f569..9c41f21 100644
--- a/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
+++ b/src/test/java/com/android/tools/r8/smali/IfSimplificationTest.java
@@ -443,6 +443,7 @@
DexCode code = method.getCode().asDexCode();
// TODO(sgjesse): Maybe this test is too fragile, as it leaves quite a lot of code, so the
// expectation might need changing with other optimizations.
- assertEquals(27, code.instructions.length);
+ // TODO(zerny): Consider optimizing the fallthrough branch of conditionals to not be return.
+ assertEquals(28, code.instructions.length);
}
}
diff --git a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
index 3750a86..21d3b9e 100644
--- a/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SwitchRewritingTest.java
@@ -302,11 +302,10 @@
DexCode code = method.getCode().asDexCode();
if (key == 0) {
assertEquals(5, code.instructions.length);
- assertTrue(code.instructions[2] instanceof IfEqz);
+ assertTrue(code.instructions[0] instanceof IfEqz);
} else {
assertEquals(6, code.instructions.length);
- assertTrue(some16BitConst(code.instructions[2]));
- assertTrue(code.instructions[3] instanceof IfEq);
+ assertTrue(code.instructions[1] instanceof IfEq);
}
}
@@ -360,13 +359,13 @@
DexEncodedMethod method = getMethod(app, signature);
DexCode code = method.getCode().asDexCode();
if (twoCaseWillUsePackedSwitch(key1, key2)) {
- assertTrue(code.instructions[3] instanceof PackedSwitch);
+ assertTrue(code.instructions[0] instanceof PackedSwitch);
} else {
if (key1 == 0) {
- assertTrue(code.instructions[3] instanceof IfEqz);
+ assertTrue(code.instructions[0] instanceof IfEqz);
} else {
// Const instruction before if.
- assertTrue(code.instructions[4] instanceof IfEq);
+ assertTrue(code.instructions[1] instanceof IfEq);
}
}
}