Merge "Make minimal main-dex dependent on compilation mode"
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java
index 840efae..e5d2952 100644
--- a/src/main/java/com/android/tools/r8/code/Format10t.java
+++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -10,7 +10,7 @@
abstract class Format10t extends Base1Format {
- public final /* offset */ byte AA;
+ public /* offset */ byte AA;
// +AA | op
Format10t(int high, BytecodeStream stream) {
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java
index 89d740b..b3e3627 100644
--- a/src/main/java/com/android/tools/r8/code/Format20t.java
+++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -10,7 +10,7 @@
abstract class Format20t extends Base2Format {
- public final /* offset */ short AAAA;
+ public /* offset */ short AAAA;
// øø | op | +AAAA
Format20t(int high, BytecodeStream stream) {
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java
index 12b752b..26f7650 100644
--- a/src/main/java/com/android/tools/r8/code/Format21t.java
+++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -11,10 +11,10 @@
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
-abstract class Format21t extends Base2Format {
+public abstract class Format21t extends Base2Format {
public final short AA;
- public final /* offset */ short BBBB;
+ public /* offset */ short BBBB;
// AA | op | +BBBB
Format21t(int high, BytecodeStream stream) {
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java
index 6cd51d3..3ff5190 100644
--- a/src/main/java/com/android/tools/r8/code/Format22t.java
+++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -11,11 +11,11 @@
import com.android.tools.r8.naming.ClassNameMapper;
import java.nio.ShortBuffer;
-abstract class Format22t extends Base2Format {
+public abstract class Format22t extends Base2Format {
public final byte A;
public final byte B;
- public final /* offset */ short CCCC;
+ public /* offset */ short CCCC;
// vB | vA | op | +CCCC
Format22t(int high, BytecodeStream stream) {
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java
index 927c879..77cb49d 100644
--- a/src/main/java/com/android/tools/r8/code/Format30t.java
+++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -10,7 +10,7 @@
abstract class Format30t extends Base3Format {
- public final /* offset */ int AAAAAAAA;
+ public /* offset */ int AAAAAAAA;
// øø | op | AAAAlo | AAAAhi
Format30t(int high, BytecodeStream stream) {
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 8820b4d..2553650 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -158,13 +158,13 @@
} catch (IOException e) {
throw new RuntimeException(e);
}
+ builder.addToMainDexList(
+ inputApp.getMainDexClasses()
+ .stream()
+ .map(clazz -> itemFactory.createType(DescriptorUtils.javaTypeToDescriptor(clazz)))
+ .collect(Collectors.toList()));
}));
}
- builder.addToMainDexList(
- inputApp.getMainDexClasses()
- .stream()
- .map(clazz -> itemFactory.createType(DescriptorUtils.javaTypeToDescriptor(clazz)))
- .collect(Collectors.toList()));
}
private final class ClassReader {
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index bdcbafe..a8f56a7 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -41,7 +41,6 @@
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.PresortedComparable;
import com.android.tools.r8.graph.ProgramClassVisitor;
-import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -153,14 +152,16 @@
return this;
}
- private void rewriteCodeWithJumboStrings(IRConverter converter, DexEncodedMethod method) {
+ private void rewriteCodeWithJumboStrings(DexEncodedMethod method) {
if (method.getCode() == null) {
return;
}
DexCode code = method.getCode().asDexCode();
if (code.highestSortingString != null) {
if (mapping.getOffsetFor(code.highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
- converter.processJumboStrings(method, mapping.getFirstJumboString());
+ JumboStringRewriter rewriter =
+ new JumboStringRewriter(method, mapping.getFirstJumboString(), options.itemFactory);
+ rewriter.rewrite();
}
}
}
@@ -176,9 +177,8 @@
return this;
}
// At least one method needs a jumbo string.
- IRConverter converter = new IRConverter(application, appInfo, options, false);
for (DexProgramClass clazz : classes) {
- clazz.forEachMethod(method -> rewriteCodeWithJumboStrings(converter, method));
+ clazz.forEachMethod(method -> rewriteCodeWithJumboStrings(method));
}
return this;
}
diff --git a/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
new file mode 100644
index 0000000..4b97c51
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/dex/JumboStringRewriter.java
@@ -0,0 +1,568 @@
+// 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.dex;
+
+import static com.android.tools.r8.graph.DexCode.TryHandler.NO_HANDLER;
+
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.ConstStringJumbo;
+import com.android.tools.r8.code.FillArrayDataPayload;
+import com.android.tools.r8.code.Format21t;
+import com.android.tools.r8.code.Format22t;
+import com.android.tools.r8.code.Format31t;
+import com.android.tools.r8.code.Goto;
+import com.android.tools.r8.code.Goto16;
+import com.android.tools.r8.code.Goto32;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.IfGe;
+import com.android.tools.r8.code.IfGez;
+import com.android.tools.r8.code.IfGt;
+import com.android.tools.r8.code.IfGtz;
+import com.android.tools.r8.code.IfLe;
+import com.android.tools.r8.code.IfLez;
+import com.android.tools.r8.code.IfLt;
+import com.android.tools.r8.code.IfLtz;
+import com.android.tools.r8.code.IfNe;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.Nop;
+import com.android.tools.r8.code.SwitchPayload;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexCode.TryHandler;
+import com.android.tools.r8.graph.DexCode.TryHandler.TypeAddrPair;
+import com.android.tools.r8.graph.DexDebugEvent;
+import com.android.tools.r8.graph.DexDebugEvent.AdvancePC;
+import com.android.tools.r8.graph.DexDebugEvent.Default;
+import com.android.tools.r8.graph.DexDebugInfo;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.google.common.collect.Lists;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class JumboStringRewriter {
+
+ private static class TryTargets {
+ private Instruction start;
+ private Instruction end;
+ private boolean endsAfterLastInstruction;
+
+ TryTargets(Instruction start, Instruction end, boolean endsAfterLastInstruction) {
+ assert start != null;
+ assert end != null;
+ this.start = start;
+ this.end = end;
+ this.endsAfterLastInstruction = endsAfterLastInstruction;
+ }
+
+ void replaceTarget(Instruction target, Instruction newTarget) {
+ if (start == target) {
+ start = newTarget;
+ }
+ if (end == target) {
+ end = newTarget;
+ }
+ }
+
+ int getStartOffset() {
+ return start.getOffset();
+ }
+
+ int getStartToEndDelta() {
+ if (endsAfterLastInstruction) {
+ return end.getOffset() + end.getSize() - start.getOffset();
+ }
+ return end.getOffset() - start.getOffset();
+ }
+ }
+
+ private final DexEncodedMethod method;
+ private final DexString firstJumboString;
+ private final DexItemFactory factory;
+ private final Map<Instruction, List<Instruction>> instructionTargets = new IdentityHashMap<>();
+ private final Int2ReferenceMap<Instruction> debugEventTargets
+ = new Int2ReferenceOpenHashMap<>();
+ private final Map<Instruction, Instruction> payloadToSwitch = new IdentityHashMap<>();
+ private final Map<Try, TryTargets> tryTargets = new IdentityHashMap<>();
+ private final Map<TryHandler, List<Instruction>> handlerTargets = new IdentityHashMap<>();
+
+ public JumboStringRewriter(
+ DexEncodedMethod method, DexString firstJumboString, DexItemFactory factory) {
+ this.method = method;
+ this.firstJumboString = firstJumboString;
+ this.factory = factory;
+ }
+
+ public void rewrite() {
+ // Build maps from everything in the code that uses offsets or direct addresses to reference
+ // instructions to the actual instruction referenced.
+ recordTargets();
+ // Expand the code by rewriting jumbo strings and branching instructions.
+ List<Instruction> newInstructions = expandCode();
+ // Commit to the new instruction offsets and update instructions, try-catch structures
+ // and debug info with the new offsets.
+ rewriteInstructionOffsets(newInstructions);
+ Try[] newTries = rewriteTryOffsets();
+ TryHandler[] newHandlers = rewriteHandlerOffsets();
+ DexDebugInfo newDebugInfo = rewriteDebugInfoOffsets();
+ // Set the new code on the method.
+ DexCode code = method.getCode().asDexCode();
+ method.setDexCode(new DexCode(
+ code.registerSize,
+ code.incomingRegisterSize,
+ code.outgoingRegisterSize,
+ newInstructions.toArray(new Instruction[newInstructions.size()]),
+ newTries,
+ newHandlers,
+ newDebugInfo,
+ code.highestSortingString));
+ }
+
+ private void rewriteInstructionOffsets(List<Instruction> instructions) {
+ for (Instruction instruction : instructions) {
+ if (instruction instanceof Format22t) { // IfEq, IfGe, IfGt, IfLe, IfLt, IfNe
+ Format22t condition = (Format22t) instruction;
+ int offset = instructionTargets.get(condition).get(0).getOffset() - instruction.getOffset();
+ assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+ condition.CCCC = (short) offset;
+ } else if (instruction instanceof Format21t) { // IfEqz, IfGez, IfGtz, IfLez, IfLtz, IfNez
+ Format21t condition = (Format21t) instruction;
+ int offset = instructionTargets.get(condition).get(0).getOffset() - instruction.getOffset();
+ assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+ condition.BBBB = (short) offset;
+ } else if (instruction instanceof Goto) {
+ Goto jump = (Goto) instruction;
+ int offset = instructionTargets.get(jump).get(0).getOffset() - instruction.getOffset();
+ assert Byte.MIN_VALUE <= offset && offset <= Byte.MAX_VALUE;
+ jump.AA = (byte) offset;
+ } else if (instruction instanceof Goto16) {
+ Goto16 jump = (Goto16) instruction;
+ int offset = instructionTargets.get(jump).get(0).getOffset() - instruction.getOffset();
+ assert Short.MIN_VALUE <= offset && offset <= Short.MAX_VALUE;
+ jump.AAAA = (short) offset;
+ } else if (instruction instanceof Goto32) {
+ Goto32 jump = (Goto32) instruction;
+ int offset = instructionTargets.get(jump).get(0).getOffset() - instruction.getOffset();
+ jump.AAAAAAAA = offset;
+ } else if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch
+ Format31t payloadUser = (Format31t) instruction;
+ int offset =
+ instructionTargets.get(payloadUser).get(0).getOffset() - instruction.getOffset();
+ payloadUser.setPayloadOffset(offset);
+ } else if (instruction instanceof SwitchPayload) {
+ SwitchPayload payload = (SwitchPayload) instruction;
+ Instruction switchInstruction = payloadToSwitch.get(payload);
+ List<Instruction> switchTargets = instructionTargets.get(payload);
+ int[] targets = payload.switchTargetOffsets();
+ for (int i = 0; i < switchTargets.size(); i++) {
+ Instruction target = switchTargets.get(i);
+ targets[i] = target.getOffset() - switchInstruction.getOffset();
+ }
+ }
+ }
+ }
+
+ private Try[] rewriteTryOffsets() {
+ DexCode code = method.getCode().asDexCode();
+ Try[] result = new Try[code.tries.length];
+ for (int i = 0; i < code.tries.length; i++) {
+ Try theTry = code.tries[i];
+ TryTargets targets = tryTargets.get(theTry);
+ result[i] = new Try(targets.getStartOffset(), targets.getStartToEndDelta(), -1);
+ result[i].handlerIndex = theTry.handlerIndex;
+ }
+ return result;
+ }
+
+ private TryHandler[] rewriteHandlerOffsets() {
+ DexCode code = method.getCode().asDexCode();
+ if (code.handlers == null) {
+ return null;
+ }
+ TryHandler[] result = new TryHandler[code.handlers.length];
+ for (int i = 0; i < code.handlers.length; i++) {
+ TryHandler handler = code.handlers[i];
+ List<Instruction> targets = handlerTargets.get(handler);
+ Iterator<Instruction> it = targets.iterator();
+ int catchAllAddr = NO_HANDLER;
+ if (handler.catchAllAddr != NO_HANDLER) {
+ catchAllAddr = it.next().getOffset();
+ }
+ TypeAddrPair[] newPairs = new TypeAddrPair[handler.pairs.length];
+ for (int j = 0; j < handler.pairs.length; j++) {
+ TypeAddrPair pair = handler.pairs[j];
+ newPairs[j] = new TypeAddrPair(pair.type, it.next().getOffset());
+ }
+ result[i] = new TryHandler(newPairs, catchAllAddr);
+ }
+ return result;
+ }
+
+ private DexDebugInfo rewriteDebugInfoOffsets() {
+ DexCode code = method.getCode().asDexCode();
+ if (debugEventTargets.size() != 0) {
+ int lastOriginalOffset = 0;
+ int lastNewOffset = 0;
+ List<DexDebugEvent> events = new ArrayList<>();
+ for (DexDebugEvent event : code.getDebugInfo().events) {
+ if (event instanceof AdvancePC) {
+ AdvancePC advance = (AdvancePC) event;
+ lastOriginalOffset += advance.delta;
+ Instruction target = debugEventTargets.get(lastOriginalOffset);
+ int pcDelta = target.getOffset() - lastNewOffset;
+ addAdvancementEvents(0, pcDelta, events);
+ lastNewOffset = target.getOffset();
+ } else if (event instanceof Default) {
+ Default defaultEvent = (Default) event;
+ lastOriginalOffset += defaultEvent.getPCDelta();
+ Instruction target = debugEventTargets.get(lastOriginalOffset);
+ int lineDelta = defaultEvent.getLineDelta();
+ int pcDelta = target.getOffset() - lastNewOffset;
+ addAdvancementEvents(lineDelta, pcDelta, events);
+ lastNewOffset = target.getOffset();
+ } else {
+ events.add(event);
+ }
+ }
+ return new DexDebugInfo(
+ code.getDebugInfo().startLine,
+ code.getDebugInfo().parameters,
+ events.toArray(new DexDebugEvent[events.size()]));
+ }
+ return code.getDebugInfo();
+ }
+
+ private void addAdvancementEvents(int lineDelta, int pcDelta, List<DexDebugEvent> events) {
+ if (lineDelta < Constants.DBG_LINE_BASE
+ || lineDelta - Constants.DBG_LINE_BASE >= Constants.DBG_LINE_RANGE) {
+ events.add(factory.createAdvanceLine(lineDelta));
+ lineDelta = 0;
+ if (pcDelta == 0) {
+ return;
+ }
+ }
+ if (pcDelta >= Constants.DBG_ADDRESS_RANGE) {
+ events.add(factory.createAdvancePC(pcDelta));
+ pcDelta = 0;
+ if (lineDelta == 0) {
+ return;
+ }
+ }
+ int specialOpcode =
+ 0x0a + (lineDelta - Constants.DBG_LINE_BASE) + Constants.DBG_LINE_RANGE * pcDelta;
+ assert specialOpcode >= 0x0a;
+ assert specialOpcode <= 0xff;
+ events.add(factory.createDefault(specialOpcode));
+ }
+
+ private List<Instruction> expandCode() {
+ LinkedList<Instruction> instructions = new LinkedList<>();
+ Collections.addAll(instructions, method.getCode().asDexCode().instructions);
+ int offsetDelta;
+ do {
+ ListIterator<Instruction> it = instructions.listIterator();
+ offsetDelta = 0;
+ while (it.hasNext()) {
+ Instruction instruction = it.next();
+ instruction.setOffset(instruction.getOffset() + offsetDelta);
+ if (instruction instanceof ConstString) {
+ ConstString string = (ConstString) instruction;
+ if (string.getString().compareTo(firstJumboString) >= 0) {
+ ConstStringJumbo jumboString = new ConstStringJumbo(string.AA, string.getString());
+ jumboString.setOffset(string.getOffset());
+ offsetDelta++;
+ it.set(jumboString);
+ replaceTarget(instruction, jumboString);
+ }
+ } else if (instruction instanceof Format22t) { // IfEq, IfGe, IfGt, IfLe, IfLt, IfNe
+ Format22t condition = (Format22t) instruction;
+ int offset =
+ instructionTargets.get(condition).get(0).getOffset() - instruction.getOffset();
+ if (Short.MIN_VALUE > offset || offset > Short.MAX_VALUE) {
+ Format22t newCondition = null;
+ switch (condition.getType().inverted()) {
+ case EQ:
+ newCondition = new IfEq(condition.A, condition.B, 0);
+ break;
+ case GE:
+ newCondition = new IfGe(condition.A, condition.B, 0);
+ break;
+ case GT:
+ newCondition = new IfGt(condition.A, condition.B, 0);
+ break;
+ case LE:
+ newCondition = new IfLe(condition.A, condition.B, 0);
+ break;
+ case LT:
+ newCondition = new IfLt(condition.A, condition.B, 0);
+ break;
+ case NE:
+ newCondition = new IfNe(condition.A, condition.B, 0);
+ break;
+ }
+ offsetDelta = rewriteIfToIfAndGoto(offsetDelta, it, condition, newCondition);
+ }
+ } else if (instruction instanceof Format21t) { // IfEqz, IfGez, IfGtz, IfLez, IfLtz, IfNez
+ Format21t condition = (Format21t) instruction;
+ int offset =
+ instructionTargets.get(condition).get(0).getOffset() - instruction.getOffset();
+ if (Short.MIN_VALUE > offset || offset > Short.MAX_VALUE) {
+ Format21t newCondition = null;
+ switch (condition.getType().inverted()) {
+ case EQ:
+ newCondition = new IfEqz(condition.AA, 0);
+ break;
+ case GE:
+ newCondition = new IfGez(condition.AA, 0);
+ break;
+ case GT:
+ newCondition = new IfGtz(condition.AA, 0);
+ break;
+ case LE:
+ newCondition = new IfLez(condition.AA, 0);
+ break;
+ case LT:
+ newCondition = new IfLtz(condition.AA, 0);
+ break;
+ case NE:
+ newCondition = new IfNez(condition.AA, 0);
+ break;
+ }
+ offsetDelta = rewriteIfToIfAndGoto(offsetDelta, it, condition, newCondition);
+ }
+ } else if (instruction instanceof Goto) {
+ Goto jump = (Goto) instruction;
+ int offset =
+ instructionTargets.get(jump).get(0).getOffset() - instruction.getOffset();
+ if (Byte.MIN_VALUE > offset || offset > Byte.MAX_VALUE) {
+ Instruction newJump;
+ if (Short.MIN_VALUE > offset || offset > Short.MAX_VALUE) {
+ newJump = new Goto32(offset);
+ } else {
+ newJump = new Goto16(offset);
+ }
+ newJump.setOffset(jump.getOffset());
+ it.set(newJump);
+ offsetDelta += (newJump.getSize() - jump.getSize());
+ replaceTarget(jump, newJump);
+ List<Instruction> targets = instructionTargets.remove(jump);
+ instructionTargets.put(newJump, targets);
+ }
+ } else if (instruction instanceof Goto16) {
+ Goto16 jump = (Goto16) instruction;
+ int offset =
+ instructionTargets.get(jump).get(0).getOffset() - instruction.getOffset();
+ if (Short.MIN_VALUE > offset || offset > Short.MAX_VALUE) {
+ Instruction newJump = new Goto32(offset);
+ newJump.setOffset(jump.getOffset());
+ it.set(newJump);
+ offsetDelta += (newJump.getSize() - jump.getSize());
+ replaceTarget(jump, newJump);
+ List<Instruction> targets = instructionTargets.remove(jump);
+ instructionTargets.put(newJump, targets);
+ }
+ } else if (instruction instanceof Goto32) {
+ // Instruction big enough for any offset.
+ } else if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch
+ // Instruction big enough for any offset.
+ } else if (instruction instanceof SwitchPayload
+ || instruction instanceof FillArrayDataPayload) {
+ if (instruction.getOffset() % 2 != 0) {
+ offsetDelta++;
+ it.previous();
+ Nop nop = new Nop();
+ nop.setOffset(instruction.getOffset());
+ it.add(nop);
+ it.next();
+ instruction.setOffset(instruction.getOffset() + 1);
+ }
+ // Instruction big enough for any offset.
+ }
+ }
+ } while (offsetDelta > 0);
+ return instructions;
+ }
+
+ private int rewriteIfToIfAndGoto(
+ int offsetDelta,
+ ListIterator<Instruction> it,
+ Instruction condition,
+ Instruction newCondition) {
+ int jumpOffset = condition.getOffset() + condition.getSize();
+ Goto32 jump = new Goto32(0);
+ jump.setOffset(jumpOffset);
+ newCondition.setOffset(condition.getOffset());
+ it.set(newCondition);
+ replaceTarget(condition, newCondition);
+ it.add(jump);
+ offsetDelta += jump.getSize();
+ instructionTargets.put(jump, instructionTargets.remove(condition));
+ Instruction fallthroughInstruction = it.next();
+ instructionTargets.put(newCondition, Lists.newArrayList(fallthroughInstruction));
+ it.previous();
+ return offsetDelta;
+ }
+
+ private void replaceTarget(Instruction target, Instruction newTarget) {
+ for (List<Instruction> instructions : instructionTargets.values()) {
+ instructions.replaceAll((i) -> i == target ? newTarget : i);
+ }
+ for (Int2ReferenceMap.Entry<Instruction> entry : debugEventTargets.int2ReferenceEntrySet()) {
+ if (entry.getValue() == target) {
+ entry.setValue(newTarget);
+ }
+ }
+ for (Entry<Try, TryTargets> entry : tryTargets.entrySet()) {
+ entry.getValue().replaceTarget(target, newTarget);
+ }
+ for (List<Instruction> instructions : handlerTargets.values()) {
+ instructions.replaceAll((i) -> i == target ? newTarget : i);
+ }
+ }
+
+ private void recordInstructionTargets(Int2ReferenceMap<Instruction> offsetToInstruction) {
+ Instruction[] instructions = method.getCode().asDexCode().instructions;
+ for (Instruction instruction : instructions) {
+ if (instruction instanceof Format22t) { // IfEq, IfGe, IfGt, IfLe, IfLt, IfNe
+ Format22t condition = (Format22t) instruction;
+ Instruction target = offsetToInstruction.get(condition.getOffset() + condition.CCCC);
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof Format21t) { // IfEqz, IfGez, IfGtz, IfLez, IfLtz, IfNez
+ Format21t condition = (Format21t) instruction;
+ Instruction target = offsetToInstruction.get(condition.getOffset() + condition.BBBB);
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof Goto) {
+ Goto jump = (Goto) instruction;
+ Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AA);
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof Goto16) {
+ Goto16 jump = (Goto16) instruction;
+ Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AAAA);
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof Goto32) {
+ Goto32 jump = (Goto32) instruction;
+ Instruction target = offsetToInstruction.get(jump.getOffset() + jump.AAAAAAAA);
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch
+ Format31t offsetInstruction = (Format31t) instruction;
+ Instruction target = offsetToInstruction.get(
+ offsetInstruction.getOffset() + offsetInstruction.getPayloadOffset());
+ assert target != null;
+ instructionTargets.put(instruction, Lists.newArrayList(target));
+ } else if (instruction instanceof SwitchPayload) {
+ SwitchPayload payload = (SwitchPayload) instruction;
+ int[] targetOffsets = payload.switchTargetOffsets();
+ int switchOffset = payloadToSwitch.get(instruction).getOffset();
+ List<Instruction> targets = new ArrayList<>();
+ for (int i = 0; i < targetOffsets.length; i++) {
+ Instruction target = offsetToInstruction.get(switchOffset + targetOffsets[i]);
+ assert target != null;
+ targets.add(target);
+ }
+ instructionTargets.put(instruction, targets);
+ }
+ }
+ }
+
+ private void recordDebugEventTargets(Int2ReferenceMap<Instruction> offsetToInstruction) {
+ DexDebugInfo debugInfo = method.getCode().asDexCode().getDebugInfo();
+ if (debugInfo != null) {
+ int address = 0;
+ for (DexDebugEvent event : debugInfo.events) {
+ if (event instanceof AdvancePC) {
+ AdvancePC advance = (AdvancePC) event;
+ address += advance.delta;
+ Instruction target = offsetToInstruction.get(address);
+ assert target != null;
+ debugEventTargets.put(address, target);
+ } else if (event instanceof Default) {
+ Default defaultEvent = (Default) event;
+ address += defaultEvent.getPCDelta();
+ Instruction target = offsetToInstruction.get(address);
+ assert target != null;
+ debugEventTargets.put(address, target);
+ }
+ }
+ }
+ }
+
+ private void recordTryAndHandlerTargets(
+ Int2ReferenceMap<Instruction> offsetToInstruction,
+ Instruction lastInstruction) {
+ DexCode code = method.getCode().asDexCode();
+ for (Try theTry : code.tries) {
+ Instruction start = offsetToInstruction.get(theTry.startAddress);
+ int endAddress = theTry.startAddress + theTry.instructionCount;
+ TryTargets targets;
+ if (endAddress > lastInstruction.getOffset()) {
+ targets = new TryTargets(start, lastInstruction, true);
+ } else {
+ Instruction end = offsetToInstruction.get(endAddress);
+ targets = new TryTargets(start, end, false);
+ }
+ assert theTry.startAddress == targets.getStartOffset();
+ assert theTry.instructionCount == targets.getStartToEndDelta();
+ tryTargets.put(theTry, targets);
+ }
+ if (code.handlers != null) {
+ for (TryHandler handler : code.handlers) {
+ List<Instruction> targets = new ArrayList<>();
+ if (handler.catchAllAddr != NO_HANDLER) {
+ Instruction target = offsetToInstruction.get(handler.catchAllAddr);
+ assert target != null;
+ targets.add(target);
+ }
+ for (TypeAddrPair pair : handler.pairs) {
+ Instruction target = offsetToInstruction.get(pair.addr);
+ assert target != null;
+ targets.add(target);
+ }
+ handlerTargets.put(handler, targets);
+ }
+ }
+ }
+
+ private void recordTargets() {
+ Int2ReferenceMap<Instruction> offsetToInstruction = new Int2ReferenceOpenHashMap<>();
+ Instruction[] instructions = method.getCode().asDexCode().instructions;
+ boolean containsPayloads = false;
+ for (Instruction instruction : instructions) {
+ offsetToInstruction.put(instruction.getOffset(), instruction);
+ if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch
+ containsPayloads = true;
+ }
+ }
+ if (containsPayloads) {
+ for (Instruction instruction : instructions) {
+ if (instruction instanceof Format31t) { // FillArrayData, SparseSwitch, PackedSwitch
+ Instruction payload =
+ offsetToInstruction.get(instruction.getOffset() + instruction.getPayloadOffset());
+ assert payload != null;
+ payloadToSwitch.put(payload, instruction);
+ }
+ }
+ }
+ recordInstructionTargets(offsetToInstruction);
+ recordDebugEventTargets(offsetToInstruction);
+ Instruction lastInstruction = instructions[instructions.length - 1];
+ recordTryAndHandlerTargets(offsetToInstruction, lastInstruction);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index a2edd92..7915df3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -314,7 +314,7 @@
public static final int NO_INDEX = -1;
- private final int handlerOffset;
+ public final int handlerOffset;
public /* offset */ int startAddress;
public /* offset */ int instructionCount;
public int handlerIndex;
@@ -378,7 +378,7 @@
public static final int NO_HANDLER = -1;
public final TypeAddrPair[] pairs;
- public /* offset */ int catchAllAddr;
+ public final /* offset */ int catchAllAddr;
public TryHandler(TypeAddrPair[] pairs, int catchAllAddr) {
this.pairs = pairs;
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
index 572e655..db99359 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -31,10 +31,9 @@
public abstract void addToBuilder(DexDebugEntryBuilder builder);
-
public static class AdvancePC extends DexDebugEvent {
- final int delta;
+ public final int delta;
public void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping) {
writer.putByte(Constants.DBG_ADVANCE_PC);
@@ -350,6 +349,16 @@
builder.setPosition(address, line);
}
+ public int getPCDelta() {
+ int adjustedOpcode = value - Constants.DBG_FIRST_SPECIAL;
+ return adjustedOpcode / Constants.DBG_LINE_RANGE;
+ }
+
+ public int getLineDelta() {
+ int adjustedOpcode = value - Constants.DBG_FIRST_SPECIAL;
+ return Constants.DBG_LINE_BASE + (adjustedOpcode % Constants.DBG_LINE_RANGE);
+ }
+
public String toString() {
return "DEFAULT " + value;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 700e45a..e66b984 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -171,14 +171,6 @@
code = builder.build(method.getArity());
}
- // Replaces the dex code in the method by setting code to result of compiling the IR.
- public void setCode(IRCode ir, RegisterAllocator registerAllocator,
- DexItemFactory dexItemFactory, DexString firstJumboString) {
- final DexBuilder builder =
- new DexBuilder(ir, registerAllocator, dexItemFactory, firstJumboString);
- code = builder.build(method.getArity());
- }
-
public String toString() {
return "Encoded method " + method;
}
@@ -206,6 +198,10 @@
return code;
}
+ public void setDexCode(DexCode code) {
+ this.code = code;
+ }
+
public void removeCode() {
code = null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 82db97a..bb1ff32 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
-import com.android.tools.r8.code.ConstStringJumbo;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.ir.conversion.DexBuilder;
@@ -30,11 +29,7 @@
public void buildDex(DexBuilder builder) {
builder.registerStringReference(value);
int dest = builder.allocatedRegister(dest(), getNumber());
- if (builder.isJumboString(value)) {
- builder.add(this, new ConstStringJumbo(dest, value));
- } else {
- builder.add(this, new com.android.tools.r8.code.ConstString(dest, value));
- }
+ builder.add(this, new com.android.tools.r8.code.ConstString(dest, value));
}
@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 737dc46..da067d8 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
@@ -85,9 +85,6 @@
// List of generated FillArrayData dex instructions.
private final List<FillArrayDataInfo> fillArrayDataInfos = new ArrayList<>();
- // First jumbo string if known.
- private final DexString firstJumboString;
-
// Set of if instructions that have offsets that are so large that they cannot be encoded in
// the if instruction format.
private Set<BasicBlock> ifsNeedingRewrite = Sets.newIdentityHashSet();
@@ -116,18 +113,6 @@
this.ir = ir;
this.registerAllocator = registerAllocator;
this.dexItemFactory = dexItemFactory;
- this.firstJumboString = null;
- }
-
- public DexBuilder(IRCode ir, RegisterAllocator registerAllocator,
- DexItemFactory dexItemFactory, DexString firstJumboString) {
- assert ir != null;
- assert registerAllocator != null;
- assert dexItemFactory != null;
- this.ir = ir;
- this.registerAllocator = registerAllocator;
- this.dexItemFactory = dexItemFactory;
- this.firstJumboString = firstJumboString;
}
private void reset() {
@@ -143,15 +128,6 @@
nextBlock = null;
}
- public boolean isJumboString(DexString string) {
- if (firstJumboString == null) {
- return false;
- }
- // We have to use compareTo here, as slowCompareTo will return the wrong order when minification
- // is used.
- return firstJumboString.compareTo(string) <= 0;
- }
-
/**
* Build the dex instructions added to this builder.
*
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 659a503..6a4777d 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
@@ -325,10 +325,6 @@
return builder.build();
}
- public void processJumboStrings(DexEncodedMethod method, DexString firstJumboString) {
- convertMethodJumboStringsOnly(method, firstJumboString);
- }
-
private void clearDexMethodCompilationState() {
application.classes().forEach(this::clearDexMethodCompilationState);
}
@@ -534,44 +530,6 @@
}
}
- // Convert a method ensuring that strings sorting equal or higher than the argument
- // firstJumboString are encoded as jumbo strings.
- // TODO(sgjesse): Consider replacing this with a direct dex2dex converter instead of going
- // through IR.
- private void convertMethodJumboStringsOnly(
- DexEncodedMethod method, DexString firstJumboString) {
- // This is only used for methods already converted to Dex, but missing jumbo strings.
- assert method.getCode() != null && method.getCode().isDexCode();
- if (options.verbose) {
- System.out.println("Processing jumbo strings: " + method.toSourceString());
- }
- if (Log.ENABLED) {
- Log.debug(getClass(), "Original code for %s:\n%s",
- method.toSourceString(), logCode(options, method));
- }
- IRCode code = method.buildIR(options);
- if (Log.ENABLED) {
- Log.debug(getClass(), "Initial (SSA) flow graph for %s:\n%s",
- method.toSourceString(), code);
- }
- // Compilation header if printing CFGs for this method.
- printC1VisualizerHeader(method);
- printMethod(code, "Initial IR (SSA)");
-
- // Methods passed through here should have been through IR processing already and
- // therefore, we skip most of the IR processing.
-
- // Perform register allocation.
- RegisterAllocator registerAllocator = performRegisterAllocation(code, method);
- method.setCode(code, registerAllocator, appInfo.dexItemFactory, firstJumboString);
-
- if (Log.ENABLED) {
- Log.debug(getClass(), "Resulting dex code for %s:\n%s",
- method.toSourceString(), logCode(options, method));
- }
- printMethod(code, "Final IR (non-SSA)");
- }
-
private RegisterAllocator performRegisterAllocation(IRCode code, DexEncodedMethod method) {
// Always perform dead code elimination before register allocation. The register allocator
// does not allow dead code (to make sure that we do not waste registers for unneeded values).
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
new file mode 100644
index 0000000..54302db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardCheckDiscardRule.java
@@ -0,0 +1,47 @@
+// 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.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardCheckDiscardRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardCheckDiscardRule build() {
+ return new ProguardCheckDiscardRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardCheckDiscardRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "checkdiscard";
+ }
+}
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 17e5128..31fb77d 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -137,16 +137,16 @@
} else if (acceptString("keepattributes")) {
parseKeepAttributes();
} else if (acceptString("keeppackagenames")) {
- ProguardKeepRule rule = parseKeepPackageNamesRule();
+ ProguardKeepPackageNamesRule rule = parseKeepPackageNamesRule();
configurationBuilder.addRule(rule);
} else if (acceptString("checkdiscard")) {
- ProguardKeepRule rule = parseCheckDiscardRule();
+ ProguardCheckDiscardRule rule = parseCheckDiscardRule();
configurationBuilder.addRule(rule);
} else if (acceptString("keep")) {
ProguardKeepRule rule = parseKeepRule();
configurationBuilder.addRule(rule);
} else if (acceptString("whyareyoukeeping")) {
- ProguardKeepRule rule = parseWhyAreYouKeepingRule();
+ ProguardWhyAreYouKeepingRule rule = parseWhyAreYouKeepingRule();
configurationBuilder.addRule(rule);
} else if (acceptString("dontoptimize")) {
configurationBuilder.setOptimize(false);
@@ -351,37 +351,36 @@
ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
parseRuleTypeAndModifiers(keepRuleBuilder);
parseClassSpec(keepRuleBuilder, false);
+ if (keepRuleBuilder.getMemberRules().isEmpty()) {
+ // If there are no member rules, a default rule for the parameterless constructor
+ // applies. So we add that here.
+ ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
+ defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
+ defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
+ defaultRuleBuilder.setArguments(Collections.emptyList());
+ keepRuleBuilder.getMemberRules().add(defaultRuleBuilder.build());
+ }
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseWhyAreYouKeepingRule()
+ private ProguardWhyAreYouKeepingRule parseWhyAreYouKeepingRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().whyAreYouKeeping = true;
- keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ ProguardWhyAreYouKeepingRule.Builder keepRuleBuilder = ProguardWhyAreYouKeepingRule.builder();
parseClassSpec(keepRuleBuilder, false);
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseKeepPackageNamesRule()
+ private ProguardKeepPackageNamesRule parseKeepPackageNamesRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().keepPackageNames = true;
- keepRuleBuilder.setType(ProguardKeepRuleType.KEEP);
+ ProguardKeepPackageNamesRule.Builder keepRuleBuilder = ProguardKeepPackageNamesRule.builder();
keepRuleBuilder.setClassNames(parseClassNames());
return keepRuleBuilder.build();
}
- private ProguardKeepRule parseCheckDiscardRule()
+ private ProguardCheckDiscardRule parseCheckDiscardRule()
throws ProguardRuleParserException {
- ProguardKeepRule.Builder keepRuleBuilder = ProguardKeepRule.builder();
- keepRuleBuilder.getModifiersBuilder().setFlagsToHaveNoEffect();
- keepRuleBuilder.getModifiersBuilder().checkDiscarded = true;
+ ProguardCheckDiscardRule.Builder keepRuleBuilder = ProguardCheckDiscardRule.builder();
parseClassSpec(keepRuleBuilder, false);
- keepRuleBuilder.setType(keepRuleBuilder.getMemberRules().isEmpty() ? ProguardKeepRuleType.KEEP
- : ProguardKeepRuleType.KEEP_CLASS_MEMBERS);
return keepRuleBuilder.build();
}
@@ -528,14 +527,6 @@
}
skipWhitespace();
expectChar('}');
- } else {
- // If there are no member rules, a default rule for the parameterless constructor
- // applies. So we add that here.
- ProguardMemberRule.Builder defaultRuleBuilder = ProguardMemberRule.builder();
- defaultRuleBuilder.setName(Constants.INSTANCE_INITIALIZER_NAME);
- defaultRuleBuilder.setRuleType(ProguardMemberType.INIT);
- defaultRuleBuilder.setArguments(Collections.emptyList());
- classSpecificationBuilder.getMemberRules().add(defaultRuleBuilder.build());
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
new file mode 100644
index 0000000..787a31c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepPackageNamesRule.java
@@ -0,0 +1,47 @@
+// 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.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardKeepPackageNamesRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardKeepPackageNamesRule build() {
+ return new ProguardKeepPackageNamesRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardKeepPackageNamesRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static ProguardKeepPackageNamesRule.Builder builder() {
+ return new ProguardKeepPackageNamesRule.Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "keeppackagenames";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index e2f53b5..5db35cd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -8,51 +8,30 @@
public boolean allowsShrinking = false;
public boolean allowsOptimization = false;
public boolean allowsObfuscation = false;
- public boolean whyAreYouKeeping = false;
public boolean includeDescriptorClasses = false;
- public boolean keepPackageNames = false;
- public boolean checkDiscarded = false;
-
- void setFlagsToHaveNoEffect() {
- allowsShrinking = true;
- allowsOptimization = true;
- allowsObfuscation = true;
- whyAreYouKeeping = false;
- includeDescriptorClasses = false;
- keepPackageNames = false;
- }
private Builder() {}
ProguardKeepRuleModifiers build() {
return new ProguardKeepRuleModifiers(allowsShrinking, allowsOptimization, allowsObfuscation,
- whyAreYouKeeping, includeDescriptorClasses, keepPackageNames, checkDiscarded);
+ includeDescriptorClasses);
}
}
public final boolean allowsShrinking;
public final boolean allowsOptimization;
public final boolean allowsObfuscation;
- public final boolean whyAreYouKeeping;
public final boolean includeDescriptorClasses;
- public final boolean keepPackageNames;
- public final boolean checkDiscarded;
private ProguardKeepRuleModifiers(
boolean allowsShrinking,
boolean allowsOptimization,
boolean allowsObfuscation,
- boolean whyAreYouKeeping,
- boolean includeDescriptorClasses,
- boolean keepPackageNames,
- boolean checkDiscarded) {
+ boolean includeDescriptorClasses) {
this.allowsShrinking = allowsShrinking;
this.allowsOptimization = allowsOptimization;
this.allowsObfuscation = allowsObfuscation;
- this.whyAreYouKeeping = whyAreYouKeeping;
this.includeDescriptorClasses = includeDescriptorClasses;
- this.keepPackageNames = keepPackageNames;
- this.checkDiscarded = checkDiscarded;
}
/**
* Create a new empty builder.
@@ -71,8 +50,7 @@
return allowsShrinking == that.allowsShrinking
&& allowsOptimization == that.allowsOptimization
&& allowsObfuscation == that.allowsObfuscation
- && includeDescriptorClasses == that.includeDescriptorClasses
- && keepPackageNames == that.keepPackageNames;
+ && includeDescriptorClasses == that.includeDescriptorClasses;
}
@Override
@@ -80,9 +58,7 @@
return (allowsShrinking ? 1 : 0)
| (allowsOptimization ? 2 : 0)
| (allowsObfuscation ? 4 : 0)
- | (whyAreYouKeeping ? 8 : 0)
- | (includeDescriptorClasses ? 16 : 0)
- | (keepPackageNames ? 32 : 0);
+ | (includeDescriptorClasses ? 8 : 0);
}
@Override
@@ -91,9 +67,7 @@
appendWithComma(builder, allowsObfuscation, "allowobfuscation");
appendWithComma(builder, allowsShrinking, "allowshrinking");
appendWithComma(builder, allowsOptimization, "allowoptimization");
- appendWithComma(builder, whyAreYouKeeping, "whyareyoukeeping");
appendWithComma(builder, includeDescriptorClasses, "includedescriptorclasses");
- appendWithComma(builder, keepPackageNames, "keeppackagenames");
return builder.toString();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
new file mode 100644
index 0000000..581a0db
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java
@@ -0,0 +1,47 @@
+// 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.DexAccessFlags;
+import java.util.List;
+import java.util.Set;
+
+public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule {
+
+ public static class Builder extends ProguardConfigurationRule.Builder {
+
+ private Builder() {
+ }
+
+ public ProguardWhyAreYouKeepingRule build() {
+ return new ProguardWhyAreYouKeepingRule(classAnnotation, classAccessFlags,
+ negatedClassAccessFlags, classTypeNegated, classType, classNames, inheritanceAnnotation,
+ inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+ }
+
+ private ProguardWhyAreYouKeepingRule(
+ ProguardTypeMatcher classAnnotation,
+ DexAccessFlags classAccessFlags,
+ DexAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ List<ProguardTypeMatcher> classNames,
+ ProguardTypeMatcher inheritanceAnnotation,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ Set<ProguardMemberRule> memberRules) {
+ super(classAnnotation, classAccessFlags, negatedClassAccessFlags, classTypeNegated, classType,
+ classNames, inheritanceAnnotation, inheritanceClassName, inheritanceIsExtends, memberRules);
+ }
+
+ public static ProguardWhyAreYouKeepingRule.Builder builder() {
+ return new ProguardWhyAreYouKeepingRule.Builder();
+ }
+
+ @Override
+ String typeString() {
+ return "whyareyoukeeping";
+ }
+}
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 6ac5694..1ddd7d3 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetBuilder.java
@@ -3,9 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
-import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Sets;
-
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -24,9 +21,11 @@
import com.android.tools.r8.shaking.ProguardTypeMatcher.MatchSpecificType;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
import com.android.tools.r8.utils.ThreadUtils;
-
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.Sets;
import java.io.PrintStream;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -185,12 +184,23 @@
}
case KEEP: {
markClass(clazz, rule);
- 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);
@@ -247,13 +257,24 @@
Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
DexType onlyIfClassKept) {
Set<Wrapper<DexMethod>> methodsMarked = new HashSet<>();
+ Arrays.stream(clazz.directMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
while (clazz != null) {
- clazz.forEachMethod(method ->
+ Arrays.stream(clazz.virtualMethods()).forEach(method ->
markMethod(method, memberKeepRules, rule, methodsMarked, onlyIfClassKept));
clazz = application.definitionFor(clazz.superType);
}
}
+ private void markMatchingMethods(DexClass clazz,
+ Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
+ DexType onlyIfClassKept) {
+ Arrays.stream(clazz.directMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+ Arrays.stream(clazz.virtualMethods()).forEach(method ->
+ markMethod(method, memberKeepRules, rule, null, onlyIfClassKept));
+ }
+
private void markMatchingFields(DexClass clazz,
Collection<ProguardMemberRule> memberKeepRules, ProguardConfigurationRule rule,
DexType onlyIfClassKept) {
@@ -375,7 +396,8 @@
private void markMethod(DexEncodedMethod method, Collection<ProguardMemberRule> rules,
ProguardConfigurationRule context, Set<Wrapper<DexMethod>> methodsMarked,
DexType onlyIfClassKept) {
- if (methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
+ if ((methodsMarked != null)
+ && methodsMarked.contains(MethodSignatureEquivalence.get().wrap(method.method))) {
return;
}
for (ProguardMemberRule rule : rules) {
@@ -384,6 +406,9 @@
Log.verbose(getClass(), "Marking method `%s` due to `%s { %s }`.", method, context,
rule);
}
+ if (methodsMarked != null) {
+ methodsMarked.add(MethodSignatureEquivalence.get().wrap(method.method));
+ }
addItemToSets(method, context, rule, onlyIfClassKept);
}
}
@@ -461,24 +486,19 @@
if (!modifiers.allowsObfuscation) {
noObfuscation.add(item);
}
- if (modifiers.whyAreYouKeeping) {
- assert onlyIfClassKept == null;
- reasonAsked.add(item);
- }
- if (modifiers.keepPackageNames) {
- assert onlyIfClassKept == null;
- keepPackageName.add(item);
- }
if (modifiers.includeDescriptorClasses) {
includeDescriptorClasses(item, keepRule);
}
- if (modifiers.checkDiscarded) {
- checkDiscarded.add(item);
- }
} else if (context instanceof ProguardAssumeNoSideEffectRule) {
noSideEffects.put(item, rule);
+ } else if (context instanceof ProguardWhyAreYouKeepingRule) {
+ reasonAsked.add(item);
+ } else if (context instanceof ProguardKeepPackageNamesRule) {
+ keepPackageName.add(item);
} else if (context instanceof ProguardAssumeValuesRule) {
assumedValues.put(item, rule);
+ } else if (context instanceof ProguardCheckDiscardRule) {
+ checkDiscarded.add(item);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index a297c10..2480417 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -245,9 +245,16 @@
}
/**
- * True if the main dex list resource exists.
+ * True if the main dex list resources exists.
*/
public boolean hasMainDexList() {
+ return !(mainDexListResources.isEmpty() && mainDexClasses.isEmpty());
+ }
+
+ /**
+ * True if the main dex list resources exists.
+ */
+ public boolean hasMainDexListResources() {
return !mainDexListResources.isEmpty();
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
similarity index 87%
rename from src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java
rename to src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
index c33d5b4..6e3c114 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestoremove/RemoveVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/RemoveVisibilityBridgeMethodsTest.java
@@ -2,11 +2,13 @@
// 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.bridgeremoval.bridgestoremove;
+package com.android.tools.r8.bridgeremoval;
import static org.junit.Assert.assertFalse;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.bridgeremoval.bridgestoremove.Main;
+import com.android.tools.r8.bridgeremoval.bridgestoremove.Outer;
import com.android.tools.r8.utils.DexInspector;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
new file mode 100644
index 0000000..a56c0c8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -0,0 +1,74 @@
+// 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.checkdiscarded;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.checkdiscarded.testclasses.Main;
+import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.WillBeGone;
+import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class CheckDiscardedTest extends TestBase {
+
+ private void run(boolean obfuscate, Class annotation, boolean checkMembers, boolean shouldFail)
+ throws Exception {
+ List<Class> classes = ImmutableList.of(
+ UnusedClass.class,
+ UsedClass.class,
+ Main.class);
+ String proguardConfig = keepMainProguardConfiguration(Main.class, true, obfuscate)
+ + checkDiscardRule(checkMembers, annotation);
+ try {
+ compileWithR8(classes, proguardConfig, this::noInlining);
+ } catch (CompilationError e) {
+ Assert.assertTrue(shouldFail);
+ return;
+ }
+ Assert.assertFalse(shouldFail);
+ }
+
+ private void noInlining(InternalOptions options) {
+ options.inlineAccessors = false;
+ }
+
+ private String checkDiscardRule(boolean member, Class annotation) {
+ if (member) {
+ return "-checkdiscard class * { @" + annotation.getCanonicalName() + " *; }";
+ } else {
+ return "-checkdiscard @" + annotation.getCanonicalName() + " class *";
+ }
+ }
+
+ @Test
+ public void classesAreGone() throws Exception {
+ run(false, WillBeGone.class, false, false);
+ run(true, WillBeGone.class, false, false);
+ }
+
+ @Test
+ public void classesAreNotGone() throws Exception {
+ run(false, WillStay.class, false, true);
+ run(true, WillStay.class, false, true);
+ }
+
+ @Test
+ public void membersAreGone() throws Exception {
+ run(false, WillBeGone.class, true, false);
+ run(true, WillBeGone.class, true, false);
+ }
+
+ @Test
+ public void membersAreNotGone() throws Exception {
+ run(false, WillStay.class, true, true);
+ run(true, WillStay.class, true, true);
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.java
new file mode 100644
index 0000000..677330a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/Main.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 com.android.tools.r8.checkdiscarded.testclasses;
+
+@WillStay
+public class Main {
+
+ public static void main(String... args) {
+ UsedClass usedClass = new UsedClass();
+ System.out.println(usedClass.hello());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.java
new file mode 100644
index 0000000..1b1689b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UnusedClass.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 com.android.tools.r8.checkdiscarded.testclasses;
+
+@WillBeGone
+public class UnusedClass {
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java
new file mode 100644
index 0000000..253c114
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/UsedClass.java
@@ -0,0 +1,18 @@
+// 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.checkdiscarded.testclasses;
+
+@WillStay
+public class UsedClass {
+
+ @WillStay
+ public String hello() {
+ return "hello";
+ }
+
+ @WillBeGone
+ public String world() {
+ return "world";
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java
new file mode 100644
index 0000000..9176911
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillBeGone.java
@@ -0,0 +1,8 @@
+// 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.checkdiscarded.testclasses;
+
+public @interface WillBeGone {
+
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java
new file mode 100644
index 0000000..e1925c3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/testclasses/WillStay.java
@@ -0,0 +1,8 @@
+// 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.checkdiscarded.testclasses;
+
+public @interface WillStay {
+
+}
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
new file mode 100644
index 0000000..98f1071
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -0,0 +1,108 @@
+// 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.dex;
+
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.ConstString;
+import com.android.tools.r8.code.Goto32;
+import com.android.tools.r8.code.IfEq;
+import com.android.tools.r8.code.IfEqz;
+import com.android.tools.r8.code.IfNe;
+import com.android.tools.r8.code.IfNez;
+import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.graph.DexAccessFlags;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexCode.Try;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.naming.NamingLens;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+
+public class JumboStringProcessing {
+
+ @Test
+ public void branching() {
+ DexItemFactory factory = new DexItemFactory();
+ DexString string = factory.createString("turn into jumbo");
+ factory.sort(NamingLens.getIdentityLens());
+ Instruction[] instructions = buildInstructions(string, false);
+ DexCode code = jumboStringProcess(factory, string, instructions);
+ Instruction[] rewrittenInstructions = code.instructions;
+ assert rewrittenInstructions[1] instanceof IfEq;
+ IfEq condition = (IfEq) rewrittenInstructions[1];
+ assert condition.getOffset() + condition.CCCC == rewrittenInstructions[3].getOffset();
+ assert rewrittenInstructions[2] instanceof Goto32;
+ Goto32 jump = (Goto32) rewrittenInstructions[2];
+ Instruction lastInstruction = rewrittenInstructions[rewrittenInstructions.length - 1];
+ assert jump.getOffset() + jump.AAAAAAAA == lastInstruction.getOffset();
+ }
+
+ @Test
+ public void branching2() {
+ DexItemFactory factory = new DexItemFactory();
+ DexString string = factory.createString("turn into jumbo");
+ factory.sort(NamingLens.getIdentityLens());
+ Instruction[] instructions = buildInstructions(string, true);
+ DexCode code = jumboStringProcess(factory, string, instructions);
+ Instruction[] rewrittenInstructions = code.instructions;
+ assert rewrittenInstructions[1] instanceof IfEqz;
+ IfEqz condition = (IfEqz) rewrittenInstructions[1];
+ assert condition.getOffset() + condition.BBBB == rewrittenInstructions[3].getOffset();
+ assert rewrittenInstructions[2] instanceof Goto32;
+ Goto32 jump = (Goto32) rewrittenInstructions[2];
+ Instruction lastInstruction = rewrittenInstructions[rewrittenInstructions.length - 1];
+ assert jump.getOffset() + jump.AAAAAAAA == lastInstruction.getOffset();
+ }
+
+ private Instruction[] buildInstructions(DexString string, boolean zeroCondition) {
+ List<Instruction> instructions = new ArrayList<>();
+ int offset = 0;
+ Instruction instr = new Const4(0, 0);
+ instr.setOffset(offset);
+ instructions.add(instr);
+ offset += instr.getSize();
+ int lastInstructionOffset = 15000 * 2 + 2 + offset;
+ if (zeroCondition) {
+ instr = new IfNez(0, lastInstructionOffset - offset);
+ } else {
+ instr = new IfNe(0, 0, lastInstructionOffset - offset);
+ }
+ instr.setOffset(offset);
+ instructions.add(instr);
+ offset += instr.getSize();
+ for (int i = 0; i < 15000; i++) {
+ instr = new ConstString(0, string);
+ instr.setOffset(offset);
+ instructions.add(instr);
+ offset += instr.getSize();
+ }
+ instr = new ReturnVoid();
+ instr.setOffset(offset);
+ instructions.add(instr);
+ assert instr.getOffset() == lastInstructionOffset;
+ return instructions.toArray(new Instruction[instructions.size()]);
+ }
+
+ private DexCode jumboStringProcess(
+ DexItemFactory factory, DexString string, Instruction[] instructions) {
+ DexCode code = new DexCode(
+ 1,
+ 0,
+ 0,
+ instructions,
+ new Try[0],
+ null,
+ null,
+ null);
+ DexAccessFlags flags = new DexAccessFlags(0);
+ flags.setPublic();
+ DexEncodedMethod method = new DexEncodedMethod(null, flags, null, null, code);
+ new JumboStringRewriter(method, string, factory).rewrite();
+ return method.getCode().asDexCode();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
index d0ade31..61b624e 100644
--- a/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
+++ b/src/test/java/com/android/tools/r8/jasmin/JumboStringTests.java
@@ -16,7 +16,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
-import org.junit.Ignore;
import org.junit.Test;
public class JumboStringTests extends JasminTestBase {
@@ -30,7 +29,6 @@
EXTRA_STRINGS_PER_CLASSES_COUNT + MIN_STRING_COUNT / CLASSES_COUNT;
@Test
- @Ignore("b/35701208")
public void test() throws Exception {
JasminBuilder builder = new JasminBuilder();
LinkedHashMap<String, MethodSignature> classes = new LinkedHashMap<>(CLASSES_COUNT);
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index d5f83c9..a113003 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -512,7 +512,7 @@
failedToFindClassInExpectedFile(outDir, clazz);
}
}
- if (minimalMainDex) {
+ if (minimalMainDex && mainDex.size() > 0) {
inspector.forAllClasses(clazz -> assertMainDexClass(clazz, mainDex));
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
index 9cc7eda..1e8555b 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -50,7 +50,7 @@
private void verifyEmptyCommand(D8Command command) throws IOException {
assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
- assertFalse(ToolHelper.getApp(command).hasMainDexList());
+ assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
assertFalse(ToolHelper.getApp(command).hasProguardMap());
assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
assertFalse(ToolHelper.getApp(command).hasPackageDistribution());
@@ -162,11 +162,11 @@
Path mainDexList2 = temp.newFile("main-dex-list-2.txt").toPath();
D8Command command = parse("--main-dex-list", mainDexList1.toString());
- assertTrue(ToolHelper.getApp(command).hasMainDexList());
+ assertTrue(ToolHelper.getApp(command).hasMainDexListResources());
command = parse(
"--main-dex-list", mainDexList1.toString(), "--main-dex-list", mainDexList2.toString());
- assertTrue(ToolHelper.getApp(command).hasMainDexList());
+ assertTrue(ToolHelper.getApp(command).hasMainDexListResources());
}
@Test
@@ -181,7 +181,7 @@
thrown.expect(CompilationException.class);
Path mainDexList = temp.newFile("main-dex-list.txt").toPath();
D8Command command = parse("--main-dex-list", mainDexList.toString(), "--file-per-class");
- assertTrue(ToolHelper.getApp(command).hasMainDexList());
+ assertTrue(ToolHelper.getApp(command).hasMainDexListResources());
}
@Test
@@ -189,7 +189,7 @@
thrown.expect(CompilationException.class);
Path mainDexList = temp.newFile("main-dex-list.txt").toPath();
D8Command command = parse("--main-dex-list", mainDexList.toString(), "--intermediate");
- assertTrue(ToolHelper.getApp(command).hasMainDexList());
+ assertTrue(ToolHelper.getApp(command).hasMainDexListResources());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
index 81ce88f..fdeb2dc 100644
--- a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -51,7 +51,7 @@
private void verifyEmptyCommand(R8Command command) throws IOException {
assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
- assertFalse(ToolHelper.getApp(command).hasMainDexList());
+ assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
assertFalse(ToolHelper.getApp(command).hasProguardMap());
assertFalse(ToolHelper.getApp(command).hasProguardSeeds());
assertFalse(ToolHelper.getApp(command).hasPackageDistribution());