Merge "-printusage part II: print dead code."
diff --git a/src/main/java/com/android/tools/r8/D8Logger.java b/src/main/java/com/android/tools/r8/D8Logger.java
index 25f238b..f0791b0 100644
--- a/src/main/java/com/android/tools/r8/D8Logger.java
+++ b/src/main/java/com/android/tools/r8/D8Logger.java
@@ -10,8 +10,6 @@
import java.nio.file.Paths;
import java.util.Arrays;
-import static java.util.Arrays.stream;
-
public final class D8Logger {
private static final int STATUS_ERROR = 1;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 030a478..4867b54 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -39,7 +39,6 @@
import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.AttributeRemovalOptions;
import com.android.tools.r8.utils.PackageDistribution;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -240,10 +239,6 @@
appInfo = appInfo.withLiveness().prunedCopyFrom(application);
new AbstractMethodRemover(appInfo).run();
new AnnotationRemover(appInfo.withLiveness(), options).run();
- } else if (!options.skipMinification) {
- // TODO(38188583): Ensure signatures are removed when minifying.
- new AnnotationRemover(appInfo.withLiveness(), true,
- AttributeRemovalOptions.filterOnlySignatures());
}
} finally {
timing.end();
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index 9053e9d..4fe3773 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -31,7 +30,6 @@
public class Bisect {
private final BisectOptions options;
- private final DexItemFactory factory = new DexItemFactory();
private final Timing timing = new Timing("bisect");
public interface Command {
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectState.java b/src/main/java/com/android/tools/r8/bisect/BisectState.java
index 148c167..654f1ec 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectState.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectState.java
@@ -142,7 +142,6 @@
}
private final String signature;
- private final DexApplication goodApp;
private final DexApplication badApp;
private final List<DexProgramClass> sortedGoodClasses;
private final Map<DexType, Integer> indexMap;
@@ -154,7 +153,6 @@
private Range nextRange = null;
public BisectState(DexApplication goodApp, DexApplication badApp, File stateFile) {
- this.goodApp = goodApp;
this.badApp = badApp;
this.stateFile = stateFile;
signature = makeSignature(goodApp);
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java
index 7072805..ceac4c8 100644
--- a/src/main/java/com/android/tools/r8/code/Format4rcc.java
+++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -56,7 +56,7 @@
return false;
}
Format4rcc o = (Format4rcc) other;
- return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB) & o.HHHH.equals(HHHH);
+ return o.AA == AA && o.CCCC == CCCC && o.BBBB.equals(BBBB) && o.HHHH.equals(HHHH);
}
public String toString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java
index c216cec..a5645a1 100644
--- a/src/main/java/com/android/tools/r8/dex/Constants.java
+++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -26,7 +26,6 @@
public static final int DEX_MAGIC_SIZE = 8;
- public static final int HEADER_SIZE = 0x70;
public static final int MAGIC_OFFSET = 0;
public static final int CHECKSUM_OFFSET = MAGIC_OFFSET + DEX_MAGIC_SIZE;
public static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + 4;
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 1c37073..768f207 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -771,8 +771,7 @@
for (int j = 0; j < realHsize; j++) {
int typeIdx = file.getUleb128();
int addr = file.getUleb128();
- pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr,
- encodedCatchHandlerOffset);
+ pairs[j] = new TypeAddrPair(indexedItems.getType(typeIdx), addr);
}
int catchAllAddr = -1;
if (hsize <= 0) {
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 b56f712..332f292 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -234,7 +234,7 @@
layout.setEndOfFile(dest.position());
// Now that we have all mixedSectionOffsets, lets write the indexed items.
- dest.moveTo(Constants.HEADER_SIZE);
+ dest.moveTo(Constants.TYPE_HEADER_ITEM_SIZE);
writeFixedSectionItems(mapping.getStrings(), layout.stringIdsOffset, this::writeStringItem);
writeFixedSectionItems(mapping.getTypes(), layout.typeIdsOffset, this::writeTypeItem);
writeFixedSectionItems(mapping.getProtos(), layout.protoIdsOffset, this::writeProtoItem);
@@ -790,7 +790,7 @@
// Leave out checksum and signature for now.
dest.moveTo(Constants.FILE_SIZE_OFFSET);
dest.putInt(layout.getEndOfFile());
- dest.putInt(Constants.HEADER_SIZE);
+ dest.putInt(Constants.TYPE_HEADER_ITEM_SIZE);
dest.putInt(Constants.ENDIAN_CONSTANT);
dest.putInt(0);
dest.putInt(0);
@@ -897,7 +897,7 @@
static Layout from(ObjectToOffsetMapping mapping) {
int offset = 0;
return new Layout(
- offset = Constants.HEADER_SIZE,
+ offset = Constants.TYPE_HEADER_ITEM_SIZE,
offset += mapping.getStrings().length * Constants.TYPE_STRING_ID_ITEM_SIZE,
offset += mapping.getTypes().length * Constants.TYPE_TYPE_ID_ITEM_SIZE,
offset += mapping.getProtos().length * Constants.TYPE_PROTO_ID_ITEM_SIZE,
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index a029a61..aedb6df 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -11,7 +11,6 @@
import com.google.common.base.MoreObjects;
import java.util.Arrays;
-import java.util.Comparator;
import java.util.function.Consumer;
public abstract class DexClass extends DexItem {
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 3535bfa..d939868 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -428,12 +428,10 @@
public final DexType type;
public final /* offset */ int addr;
- public final /* offset to the start of an encoded_catch_handler. */ int offset;
- public TypeAddrPair(DexType type, int addr, int offset) {
+ public TypeAddrPair(DexType type, int addr) {
this.type = type;
this.addr = addr;
- this.offset = offset;
}
public void collectIndexedItems(IndexedItemCollection indexedItems) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
index 4410f2a..39ec7d0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEntryBuilder.java
@@ -9,6 +9,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
/**
* Builder to construct a "per position" representation of the debug information.
@@ -160,8 +161,9 @@
private ImmutableMap<Integer, DebugLocalInfo> getLocals() {
ImmutableMap.Builder<Integer, DebugLocalInfo> builder = ImmutableMap.builder();
- for (Integer register : locals.keySet()) {
- LocalEntry entry = locals.get(register);
+ for (Entry<Integer, LocalEntry> mapEntry : locals.entrySet()) {
+ Integer register = mapEntry.getKey();
+ LocalEntry entry = mapEntry.getValue();
if (entry.current != null) {
builder.put(register, entry.current);
}
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 c8fbbea..db4a2ab 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -14,10 +14,8 @@
import com.android.tools.r8.code.Instruction;
import com.android.tools.r8.code.InvokeDirect;
import com.android.tools.r8.code.InvokeStatic;
-import com.android.tools.r8.code.InvokeSuper;
import com.android.tools.r8.code.NewInstance;
import com.android.tools.r8.code.Throw;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.ir.code.IRCode;
@@ -33,7 +31,6 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
-import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 04e34a5..6500e8f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -228,7 +228,6 @@
public final DexMethod appendObject;
public final DexMethod appendString;
public final DexMethod appendStringBuffer;
- public final DexMethod toString;
private StringBuildingMethods(DexType receiver) {
DexType sbufType = createType(createString("Ljava/lang/StringBuffer;"));
@@ -251,7 +250,6 @@
appendObject = createMethod(receiver, createProto(receiver, objectType), append);
appendString = createMethod(receiver, createProto(receiver, stringType), append);
appendStringBuffer = createMethod(receiver, createProto(receiver, sbufType), append);
- toString = createMethod(receiver, createProto(stringType), toStringMethodName);
}
public void forEachAppendMethod(Consumer<DexMethod> consumer) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 234773f..ac7d9ee 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -364,9 +364,8 @@
int leadingSquareBrackets = getNumberOfLeadingSquareBrackets();
byte[] content = new byte[newBase.descriptor.content.length + leadingSquareBrackets];
Arrays.fill(content, 0, leadingSquareBrackets, (byte) '[');
- for (int i = 0; i < newBase.descriptor.content.length; i++) {
- content[leadingSquareBrackets + i] = newBase.descriptor.content[i];
- }
+ System.arraycopy(newBase.descriptor.content, 0, content, leadingSquareBrackets,
+ newBase.descriptor.content.length);
DexString newDesc = dexItemFactory
.createString(newBase.descriptor.size + leadingSquareBrackets, content);
return dexItemFactory.createType(newDesc);
diff --git a/src/main/java/com/android/tools/r8/graph/DexValue.java b/src/main/java/com/android/tools/r8/graph/DexValue.java
index a0cfa7c..29f59e5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -593,6 +593,10 @@
this.values = values;
}
+ public DexValue[] getValues() {
+ return values;
+ }
+
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
collectAll(indexedItems, values);
diff --git a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
index e814dbe..b5a7b75 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DominatorTree.java
@@ -11,7 +11,6 @@
public class DominatorTree {
- IRCode code;
private BasicBlock[] sorted;
private BasicBlock[] doms;
@@ -21,7 +20,6 @@
// TODO(sgjesse) Get rid of this constructor and blocksToIgnore.
DominatorTree(IRCode code, List<BasicBlock> blocksToIgnore) {
- this.code = code;
this.sorted = code.topologicallySortedBlocks(blocksToIgnore);
numberBlocks();
build();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 9e5c972..f38614f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.ir.conversion.DexBuilder;
-import java.util.Arrays;
public class InstanceGet extends FieldInstruction {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
index 8b348a0..4e3f704 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraph.java
@@ -21,7 +21,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
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 737c7e5..737dc46 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
@@ -588,7 +588,7 @@
assert i == handlerGroup.getGuards().size() - 1;
catchAllOffset = targetOffset;
} else {
- pairs.add(new TypeAddrPair(type, targetOffset, -1));
+ pairs.add(new TypeAddrPair(type, targetOffset));
}
}
TypeAddrPair[] pairsArray = pairs.toArray(new TypeAddrPair[pairs.size()]);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index a2c1cbd..12d8b6a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -195,7 +195,7 @@
JarApplicationReader application) {
DebugLocalInfo info = new DebugLocalInfo(
application.getString(node.name),
- application.getType(Type.getType(node.desc)),
+ application.getTypeFromDescriptor(node.desc),
node.signature == null ? null : application.getString(node.signature));
DebugLocalInfo canonical = canonicalLocalVariables.putIfAbsent(info, info);
return canonical != null ? canonical : info;
@@ -892,15 +892,6 @@
}
}
- static Type getArrayElementType(Type array) {
- if (array == JarState.NULL_TYPE) {
- return null;
- }
- String desc = array.getDescriptor();
- assert desc.charAt(0) == '[';
- return Type.getType(desc.substring(1));
- }
-
private static Type makeArrayType(Type elementType) {
return Type.getObjectType("[" + elementType.getDescriptor());
}
@@ -1153,8 +1144,7 @@
case Opcodes.CALOAD:
case Opcodes.SALOAD: {
state.pop();
- Slot array = state.pop(JarState.ARRAY_TYPE);
- Type elementType = getArrayElementType(array.type);
+ Type elementType = state.pop(JarState.ARRAY_TYPE).getArrayElementType();
if (elementType == null) {
// We propagate the null type, which will then get resolved to an
// actual type if we have a non-null type on another flow edge.
@@ -1860,7 +1850,7 @@
case Opcodes.SALOAD: {
Slot index = state.pop(Type.INT_TYPE);
Slot array = state.pop(JarState.ARRAY_TYPE);
- Type elementType = getArrayElementType(array.type);
+ Type elementType = array.getArrayElementType();
if (elementType == null) {
elementType = getArrayElementTypeForOpcode(opcode);
}
@@ -1880,7 +1870,7 @@
Slot value = state.pop();
Slot index = state.pop(Type.INT_TYPE);
Slot array = state.pop(JarState.ARRAY_TYPE);
- Type elementType = getArrayElementType(array.type);
+ Type elementType = array.getArrayElementType();
if (elementType == null) {
elementType = getArrayElementTypeForOpcode(opcode);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 51a8117..9d11fae 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
-import static com.android.tools.r8.ir.conversion.JarSourceCode.getArrayElementType;
-
import com.android.tools.r8.graph.DebugLocalInfo;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
@@ -18,6 +16,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LocalVariableNode;
@@ -37,9 +36,6 @@
// Type representative for the null value (non-existent but works for tracking the types here).
public static final Type NULL_TYPE = Type.getObjectType("<null>");
- // Type representative for an address type (used by JSR/RET).
- public static final Type ADDR_TYPE = Type.getObjectType("<address>");
-
// Typed mapping from a local slot or stack slot to a virtual register.
public static class Slot {
public final int register;
@@ -66,6 +62,14 @@
return isCategory1(type);
}
+ public Type getArrayElementType() {
+ assert type == NULL_TYPE || type == ARRAY_TYPE || type.getSort() == Type.ARRAY;
+ if (type == JarState.NULL_TYPE) {
+ return null;
+ }
+ return getArrayElementType(type);
+ }
+
public static boolean isCategory1(Type type) {
return type != Type.LONG_TYPE && type != Type.DOUBLE_TYPE;
}
@@ -90,6 +94,12 @@
return type.equals(other);
}
+ private static Type getArrayElementType(Type type) {
+ String desc = type.getDescriptor();
+ assert desc.charAt(0) == '[';
+ return Type.getType(desc.substring(1));
+ }
+
private static boolean isIntCompatible(int sort) {
return Type.BOOLEAN <= sort && sort <= Type.INT;
}
@@ -357,9 +367,7 @@
Snapshot snapshot = targetStates.get(offset);
assert snapshot != null;
assert locals.length == snapshot.locals.length;
- for (int i = 0; i < locals.length; i++) {
- locals[i] = snapshot.locals[i];
- }
+ System.arraycopy(snapshot.locals, 0, locals, 0, locals.length);
stack.clear();
stack.addAll(snapshot.stack);
topOfStack = startOfStack + 2 * stack.size();
@@ -382,13 +390,14 @@
}
}
// TODO(zerny): Precompute and sort the local ranges.
- for (LocalVariableNode node : localVariables.keySet()) {
+ for (Entry<LocalVariableNode, DebugLocalInfo> entry : localVariables.entrySet()) {
+ LocalVariableNode node = entry.getKey();
int startOffset = source.getOffset(node.start);
int endOffset = source.getOffset(node.end);
if (startOffset <= target && target < endOffset) {
int register = getLocalRegister(node.index, Type.getType(node.desc));
Local local = locals[register];
- locals[register] = new Local(local.slot, localVariables.get(node));
+ locals[register] = new Local(local.slot, entry.getValue());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
index 5367a3a..1c20efb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/SwitchPayloadResolver.java
@@ -19,14 +19,12 @@
public final static int NO_SIZE = -1;
public int userOffset;
- public int fallthroughOffset;
public int[] absoluteTargets = null;
public int[] keys = null;
public int size = NO_SIZE;
- public PayloadData(int userOffset, int fallthroughOffset) {
+ public PayloadData(int userOffset) {
this.userOffset = userOffset;
- this.fallthroughOffset = fallthroughOffset;
}
}
@@ -36,7 +34,7 @@
public void addPayloadUser(Instruction dex) {
int offset = dex.getOffset();
int payloadOffset = offset + dex.getPayloadOffset();
- payloadToData.put(payloadOffset, new PayloadData(offset, offset + dex.getSize()));
+ payloadToData.put(payloadOffset, new PayloadData(offset));
if (unresolvedPayload.containsKey(payloadOffset)) {
SwitchPayload payload = unresolvedPayload.remove(payloadOffset);
resolve(payload);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 4a51135..850f37d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -162,7 +162,7 @@
boolean staticTarget = implHandle.type.isInvokeStatic();
boolean instanceTarget = implHandle.type.isInvokeInstance();
boolean initTarget = implHandle.type.isInvokeConstructor();
- assert instanceTarget || staticTarget | initTarget;
+ assert instanceTarget || staticTarget || initTarget;
if (targetMethod == null) {
// The target cannot be a private method, since otherwise it
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 fe59b79..0bd7188 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
@@ -39,7 +39,6 @@
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
index 9f26fb5..d8d3b8d 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -5,11 +5,17 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueArray;
+import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
+import com.android.tools.r8.naming.signature.GenericSignatureAction;
+import com.android.tools.r8.naming.signature.GenericSignatureParser;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -22,8 +28,9 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.Consumer;
-public class ClassNameMinifier {
+class ClassNameMinifier {
private final AppInfoWithLiveness appInfo;
private final RootSet rootSet;
@@ -31,11 +38,16 @@
private final Set<DexString> usedTypeNames = Sets.newIdentityHashSet();
private final Map<DexType, DexString> renaming = Maps.newIdentityHashMap();
- private final Map<String, NamingState> states = new HashMap<>();
+ private final Map<String, ClassNamingState> states = new HashMap<>();
private final List<String> dictionary;
private final boolean keepInnerClassStructure;
- public ClassNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, String packagePrefix,
+ private GenericSignatureRewriter genericSignatureRewriter = new GenericSignatureRewriter();
+
+ private GenericSignatureParser<DexType> genericSignatureParser =
+ new GenericSignatureParser<>(genericSignatureRewriter);
+
+ ClassNameMinifier(AppInfoWithLiveness appInfo, RootSet rootSet, String packagePrefix,
List<String> dictionary, boolean keepInnerClassStructure) {
this.appInfo = appInfo;
this.rootSet = rootSet;
@@ -44,26 +56,61 @@
this.keepInnerClassStructure = keepInnerClassStructure;
}
- public Map<DexType, DexString> computeRenaming() {
+ Map<DexType, DexString> computeRenaming() {
Iterable<DexProgramClass> classes = appInfo.classes();
// Collect names we have to keep.
- for (DexClass clazz : appInfo.classes()) {
+ for (DexClass clazz : classes) {
if (rootSet.noObfuscation.contains(clazz)) {
assert !renaming.containsKey(clazz.type);
registerClassAsUsed(clazz.type);
}
}
- for (DexClass clazz : appInfo.classes()) {
+ for (DexClass clazz : classes) {
if (!renaming.containsKey(clazz.type)) {
DexString renamed = computeName(clazz);
renaming.put(clazz.type, renamed);
}
}
+
+ renameTypesInGenericSignatures();
+
appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
return Collections.unmodifiableMap(renaming);
}
+ private void renameTypesInGenericSignatures() {
+ for (DexClass clazz : appInfo.classes()) {
+ rewriteGenericSignatures(clazz.annotations.annotations,
+ genericSignatureParser::parseClassSignature);
+ clazz.forEachField(field -> rewriteGenericSignatures(
+ field.annotations.annotations, genericSignatureParser::parseFieldSignature));
+ clazz.forEachMethod(method -> rewriteGenericSignatures(
+ method.annotations.annotations, genericSignatureParser::parseMethodSignature));
+ }
+ }
+
+ private void rewriteGenericSignatures(DexAnnotation[] annotations, Consumer<String> parser) {
+ for (int i = 0; i < annotations.length; i++) {
+ DexAnnotation annotation = annotations[i];
+ if (DexAnnotation.isSignatureAnnotation(annotation, appInfo.dexItemFactory)) {
+ parser.accept(getSignatureFromAnnotation(annotation));
+ annotations[i] = DexAnnotation.createSignatureAnnotation(
+ genericSignatureRewriter.getRenamedSignature(),
+ appInfo.dexItemFactory);
+ }
+ }
+ }
+
+ private static String getSignatureFromAnnotation(DexAnnotation signatureAnnotation) {
+ DexValueArray elements = (DexValueArray) signatureAnnotation.annotation.elements[0].value;
+ StringBuilder signature = new StringBuilder();
+ for (DexValue element : elements.getValues()) {
+ signature.append(((DexValueString) element).value.toString());
+ }
+ return signature.toString();
+ }
+
/**
* Registers the given type as used.
* <p>
@@ -105,7 +152,7 @@
}
private DexString computeName(DexClass clazz) {
- NamingState state = null;
+ ClassNamingState state = null;
if (keepInnerClassStructure) {
// When keeping the nesting structure of inner classes, we have to insert the name
// of the outer class for the $ prefix.
@@ -129,11 +176,11 @@
}
}
- private NamingState getStateFor(String packageName) {
- return states.computeIfAbsent(packageName, NamingState::new);
+ private ClassNamingState getStateFor(String packageName) {
+ return states.computeIfAbsent(packageName, ClassNamingState::new);
}
- private NamingState getStateForOuterClass(DexType outer) {
+ private ClassNamingState getStateForOuterClass(DexType outer) {
String prefix = DescriptorUtils
.getClassBinaryNameFromDescriptor(outer.toDescriptorString());
return states.computeIfAbsent(prefix, k -> {
@@ -150,7 +197,7 @@
}
}
String binaryName = DescriptorUtils.getClassBinaryNameFromDescriptor(renamed.toString());
- return new NamingState(binaryName, "$");
+ return new ClassNamingState(binaryName, "$");
});
}
@@ -171,21 +218,19 @@
}
}
- private class NamingState {
+ private class ClassNamingState {
private final char[] packagePrefix;
- private final String separator;
private int typeCounter = 1;
private Iterator<String> dictionaryIterator;
- NamingState(String packageName) {
+ ClassNamingState(String packageName) {
this(packageName, "/");
}
- NamingState(String packageName, String separator) {
+ ClassNamingState(String packageName, String separator) {
this.packagePrefix = ("L" + packageName + (packageName.isEmpty() ? "" : separator))
.toCharArray();
- this.separator = separator;
this.dictionaryIterator = dictionary.iterator();
}
@@ -211,4 +256,63 @@
return candidate;
}
}
+
+ private class GenericSignatureRewriter implements GenericSignatureAction<DexType> {
+
+ private StringBuilder renamedSignature;
+
+ public String getRenamedSignature() {
+ return renamedSignature.toString();
+ }
+
+ @Override
+ public void parsedSymbol(char symbol) {
+ renamedSignature.append(symbol);
+ }
+
+ @Override
+ public void parsedIdentifier(String identifier) {
+ renamedSignature.append(identifier);
+ }
+
+ @Override
+ public DexType parsedTypeName(String name) {
+ DexType type = appInfo.dexItemFactory.createType(
+ DescriptorUtils.getDescriptorFromClassBinaryName(name));
+ DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
+ renamedSignature.append(DescriptorUtils.getClassBinaryNameFromDescriptor(
+ renamedDescriptor.toString()));
+ return type;
+ }
+
+ @Override
+ public DexType parsedInnerTypeName(DexType enclosingType, String name) {
+ assert enclosingType.isClassType();
+ String enclosingDescriptor = enclosingType.toDescriptorString();
+ DexType type =
+ appInfo.dexItemFactory.createType(
+ DescriptorUtils.getDescriptorFromClassBinaryName(
+ DescriptorUtils.getClassBinaryNameFromDescriptor(enclosingDescriptor)
+ + '$' + name));
+ String enclosingRenamedBinaryName =
+ DescriptorUtils.getClassBinaryNameFromDescriptor(renaming.getOrDefault(enclosingType,
+ enclosingType.descriptor).toString());
+ String renamed = DescriptorUtils.getClassBinaryNameFromDescriptor(
+ renaming.getOrDefault(type, type.descriptor).toString());
+ assert renamed.startsWith(enclosingRenamedBinaryName + '$');
+ String outName = renamed.substring(enclosingRenamedBinaryName.length() + 1);
+ renamedSignature.append(outName);
+ return type;
+ }
+
+ @Override
+ public void start() {
+ renamedSignature = new StringBuilder();
+ }
+
+ @Override
+ public void stop() {
+ // nothing to do
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index 3a72512..8177e89 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -14,7 +14,7 @@
import java.util.List;
import java.util.Map;
-public class FieldNameMinifier {
+class FieldNameMinifier {
private final AppInfoWithSubtyping appInfo;
private final RootSet rootSet;
@@ -22,7 +22,7 @@
private final List<String> dictionary;
private final Map<DexType, NamingState<DexType>> states = new IdentityHashMap<>();
- public FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, List<String> dictionary) {
+ FieldNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet, List<String> dictionary) {
this.appInfo = appInfo;
this.rootSet = rootSet;
this.dictionary = dictionary;
@@ -50,18 +50,17 @@
return;
}
NamingState<DexType> newState = states.computeIfAbsent(type, t -> state.createChild());
- reserveFieldNames(newState, holder.instanceFields(), holder.isLibraryClass());
- reserveFieldNames(newState, holder.staticFields(), holder.isLibraryClass());
+ holder.forEachField(field -> reserveFieldName(field, newState, holder.isLibraryClass()));
type.forAllExtendsSubtypes(subtype -> reserveNamesInSubtypes(subtype, newState));
}
- private void reserveFieldNames(NamingState<DexType> state, DexEncodedField[] fields,
+ private void reserveFieldName(
+ DexEncodedField encodedField,
+ NamingState<DexType> state,
boolean isLibrary) {
- for (DexEncodedField encodedField : fields) {
- if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
- DexField field = encodedField.field;
- state.reserveName(field.name, field.type);
- }
+ if (isLibrary || rootSet.noObfuscation.contains(encodedField)) {
+ DexField field = encodedField.field;
+ state.reserveName(field.name, field.type);
}
}
@@ -72,17 +71,14 @@
}
NamingState<DexType> state = states.get(clazz.type);
assert state != null;
- renameFields(clazz.instanceFields(), state);
- renameFields(clazz.staticFields(), state);
+ clazz.forEachField(field -> renameField(field, state));
type.forAllExtendsSubtypes(this::renameFieldsInSubtypes);
}
- private void renameFields(DexEncodedField[] fields, NamingState<DexType> state) {
- for (DexEncodedField encodedField : fields) {
- DexField field = encodedField.field;
- if (!state.isReserved(field.name, field.type)) {
- renaming.put(field, state.assignNewNameFor(field.name, field.type, false));
- }
+ private void renameField(DexEncodedField encodedField, NamingState<DexType> state) {
+ DexField field = encodedField.field;
+ if (!state.isReserved(field.name, field.type)) {
+ renaming.put(field, state.assignNewNameFor(field.name, field.type, false));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index ec7fa46..4aca460 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -84,7 +84,7 @@
* TODO(herhut): Currently, we do not minify members of annotation interfaces, as this would require
* parsing and minification of the string arguments to annotations.
*/
-public class MethodNameMinifier {
+class MethodNameMinifier {
private final AppInfoWithSubtyping appInfo;
private final RootSet rootSet;
@@ -93,7 +93,7 @@
private MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get();
private final List<String> dictionary;
- public MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet,
+ MethodNameMinifier(AppInfoWithSubtyping appInfo, RootSet rootSet,
List<String> dictionary) {
this.appInfo = appInfo;
this.rootSet = rootSet;
@@ -101,7 +101,7 @@
this.globalState = NamingState.createRoot(appInfo.dexItemFactory, dictionary);
}
- public Map<DexMethod, DexString> computeRenaming(Timing timing) {
+ Map<DexMethod, DexString> computeRenaming(Timing timing) {
// Phase 1: Reserve all the names that need to be kept and allocate linked state in the
// library part.
timing.begin("Phase 1");
@@ -198,10 +198,8 @@
DexClass clazz = appInfo.definitionFor(iface);
if (clazz != null) {
Set<NamingState<DexProto>> collectedStates = getReachableStates(iface, frontierMap);
- addStatesToGlobalMapForMethods(clazz.directMethods(), collectedStates, globalStateMap,
- sourceMethodsMap, originStates, iface);
- addStatesToGlobalMapForMethods(clazz.virtualMethods(), collectedStates, globalStateMap,
- sourceMethodsMap, originStates, iface);
+ clazz.forEachMethod(method -> addStatesToGlobalMapForMethod(
+ method, collectedStates, globalStateMap, sourceMethodsMap, originStates, iface));
}
});
timing.end();
@@ -237,7 +235,6 @@
private void collectSubInterfaces(DexType iface, Set<DexType> interfaces) {
- DexClass clazz = appInfo.definitionFor(iface);
iface.forAllExtendsSubtypes(subtype -> {
assert subtype.isInterface();
if (interfaces.add(subtype)) {
@@ -246,19 +243,17 @@
});
}
- private void addStatesToGlobalMapForMethods(
- DexEncodedMethod[] methods, Set<NamingState<DexProto>> collectedStates,
+ private void addStatesToGlobalMapForMethod(
+ DexEncodedMethod method, Set<NamingState<DexProto>> collectedStates,
Map<Wrapper<DexMethod>, Set<NamingState<DexProto>>> globalStateMap,
Map<Wrapper<DexMethod>, Set<DexMethod>> sourceMethodsMap,
Map<Wrapper<DexMethod>, NamingState<DexProto>> originStates, DexType originInterface) {
- for (DexEncodedMethod method : methods) {
- Wrapper<DexMethod> key = equivalence.wrap(method.method);
- Set<NamingState<DexProto>> stateSet = globalStateMap
- .computeIfAbsent(key, k -> new HashSet<>());
- stateSet.addAll(collectedStates);
- sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
- originStates.putIfAbsent(key, states.get(originInterface));
- }
+ Wrapper<DexMethod> key = equivalence.wrap(method.method);
+ Set<NamingState<DexProto>> stateSet =
+ globalStateMap.computeIfAbsent(key, k -> new HashSet<>());
+ stateSet.addAll(collectedStates);
+ sourceMethodsMap.computeIfAbsent(key, k -> new HashSet<>()).add(method.method);
+ originStates.putIfAbsent(key, states.get(originInterface));
}
private void assignNameForInterfaceMethodInAllStates(DexMethod method,
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
new file mode 100644
index 0000000..6753e06
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureAction.java
@@ -0,0 +1,23 @@
+// 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.naming.signature;
+
+/**
+ * Actions triggered by the generic signature parser.
+ */
+public interface GenericSignatureAction<T> {
+
+ public void parsedSymbol(char symbol);
+
+ public void parsedIdentifier(String identifier);
+
+ public T parsedTypeName(String name);
+
+ public T parsedInnerTypeName(T enclosingType, String name);
+
+ public void start();
+
+ public void stop();
+}
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
new file mode 100644
index 0000000..c5d78f3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureParser.java
@@ -0,0 +1,403 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.naming.signature;
+
+import java.lang.reflect.GenericSignatureFormatError;
+
+/**
+ * Implements a parser for the generics signature attribute as defined by JVMS 7 $ 4.3.4.
+ * Uses a top-down, recursive descent parsing approach for the following grammar:
+ * <pre>
+ * ClassSignature ::=
+ * OptFormalTypeParams SuperclassSignature {SuperinterfaceSignature}.
+ * SuperclassSignature ::= ClassTypeSignature.
+ * SuperinterfaceSignature ::= ClassTypeSignature.
+ *
+ * OptFormalTypeParams ::=
+ * ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+ *
+ * FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+ * ClassBound ::= ":" [FieldTypeSignature].
+ * InterfaceBound ::= ":" FieldTypeSignature.
+ *
+ * FieldTypeSignature ::=
+ * ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
+ * ArrayTypeSignature ::= "[" TypSignature.
+ *
+ * ClassTypeSignature ::=
+ * "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";".
+ *
+ * OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+ *
+ * TypeArgument ::= ([WildcardIndicator] FieldTypeSignature) | "*".
+ * WildcardIndicator ::= "+" | "-".
+ *
+ * TypeVariableSignature ::= "T" Ident ";".
+ *
+ * TypSignature ::= FieldTypeSignature | BaseType.
+ * BaseType ::= "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z".
+ *
+ * MethodTypeSignature ::=
+ * OptFormalTypeParams "(" {TypeSignature} ")" ReturnType {ThrowsSignature}.
+ * ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
+ *
+ * ReturnType ::= TypSignature | VoidDescriptor.
+ * VoidDescriptor ::= "V".
+ * </pre>
+ */
+public class GenericSignatureParser<T> {
+
+ private final GenericSignatureAction<T> actions;
+
+ /*
+ * Parser:
+ */
+ private char symbol; // 0: eof; else valid term symbol or first char of identifier.
+
+ private String identifier;
+
+ /*
+ * Scanner:
+ * eof is private to the scan methods
+ * and it's set only when a scan is issued at the end of the buffer.
+ */
+ private boolean eof;
+
+ private char[] buffer;
+
+ private int pos;
+
+ public GenericSignatureParser(GenericSignatureAction<T> actions) {
+ this.actions = actions;
+ }
+
+ public void parseClassSignature(String signature) {
+ actions.start();
+ setInput(signature);
+ parseClassSignature();
+ actions.stop();
+ }
+
+ public void parseMethodSignature(String signature) {
+ actions.start();
+ setInput(signature);
+ parseMethodTypeSignature();
+ actions.stop();
+ }
+
+ public void parseFieldSignature(String signature) {
+ actions.start();
+ setInput(signature);
+ parseFieldTypeSignature();
+ actions.stop();
+ }
+
+ private void setInput(String input) {
+ this.buffer = input.toCharArray();
+ this.eof = false;
+ pos = 0;
+ symbol = 0;
+ identifier = null;
+ scanSymbol();
+ }
+
+ //
+ // Parser:
+ //
+
+ void parseClassSignature() {
+ // ClassSignature ::= OptFormalTypeParameters SuperclassSignature {SuperinterfaceSignature}.
+
+ parseOptFormalTypeParameters();
+
+ // SuperclassSignature ::= ClassTypeSignature.
+ parseClassTypeSignature();
+
+ while (symbol > 0) {
+ // SuperinterfaceSignature ::= ClassTypeSignature.
+ parseClassTypeSignature();
+ }
+ }
+
+ void parseOptFormalTypeParameters() {
+ // OptFormalTypeParameters ::= ["<" FormalTypeParameter {FormalTypeParameter} ">"].
+
+ if (symbol == '<') {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+
+ updateFormalTypeParameter();
+
+ while ((symbol != '>') && (symbol > 0)) {
+ updateFormalTypeParameter();
+ }
+
+ actions.parsedSymbol(symbol);
+ expect('>');
+ }
+ }
+
+ void updateFormalTypeParameter() {
+ // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}.
+ scanIdentifier();
+ assert identifier != null;
+ actions.parsedIdentifier(identifier);
+
+ // ClassBound ::= ":" [FieldTypeSignature].
+ actions.parsedSymbol(symbol);
+ expect(':');
+
+ if (symbol == 'L' || symbol == '[' || symbol == 'T') {
+ parseFieldTypeSignature();
+ }
+
+ while (symbol == ':') {
+ // InterfaceBound ::= ":" FieldTypeSignature.
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ parseFieldTypeSignature();
+ }
+ }
+
+ private void parseFieldTypeSignature() {
+ // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
+ switch (symbol) {
+ case 'L':
+ parseClassTypeSignature();
+ break;
+ case '[':
+ // ArrayTypeSignature ::= "[" TypSignature.
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ updateTypeSignature();
+ break;
+ case 'T':
+ updateTypeVariableSignature();
+ break;
+ default:
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ private void parseClassTypeSignature() {
+ // ClassTypeSignature ::= "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments}
+ // ";".
+ actions.parsedSymbol(symbol);
+ expect('L');
+
+ StringBuilder qualIdent = new StringBuilder();
+ scanIdentifier();
+ assert identifier != null;
+ while (symbol == '/') {
+ qualIdent.append(identifier).append(symbol);
+ scanSymbol();
+ scanIdentifier();
+ assert identifier != null;
+ }
+
+ qualIdent.append(this.identifier);
+ T parsedEnclosingType = actions.parsedTypeName(qualIdent.toString());
+
+ updateOptTypeArguments();
+
+ while (symbol == '.') {
+ // Deal with Member Classes:
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ scanIdentifier();
+ assert identifier != null;
+ parsedEnclosingType = actions.parsedInnerTypeName(parsedEnclosingType, identifier);
+ updateOptTypeArguments();
+ }
+
+ actions.parsedSymbol(symbol);
+ expect(';');
+ }
+
+ private void updateOptTypeArguments() {
+ // OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">".
+ if (symbol == '<') {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+
+ updateTypeArgument();
+ while ((symbol != '>') && (symbol > 0)) {
+ updateTypeArgument();
+ }
+
+ actions.parsedSymbol(symbol);
+ expect('>');
+ }
+ }
+
+ private void updateTypeArgument() {
+ // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
+ if (symbol == '*') {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ } else if (symbol == '+') {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ parseFieldTypeSignature();
+ } else if (symbol == '-') {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ parseFieldTypeSignature();
+ } else {
+ parseFieldTypeSignature();
+ }
+ }
+
+ private void updateTypeVariableSignature() {
+ // TypeVariableSignature ::= "T" Ident ";".
+ actions.parsedSymbol(symbol);
+ expect('T');
+
+ scanIdentifier();
+ assert identifier != null;
+ actions.parsedIdentifier(identifier);
+
+ actions.parsedSymbol(symbol);
+ expect(';');
+ }
+
+ private void updateTypeSignature() {
+ switch (symbol) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ break;
+ default:
+ // Not an elementary type, but a FieldTypeSignature.
+ parseFieldTypeSignature();
+ }
+ }
+
+ private void parseMethodTypeSignature() {
+ // MethodTypeSignature ::= [FormalTypeParameters] "(" {TypeSignature} ")" ReturnType
+ // {ThrowsSignature}.
+ parseOptFormalTypeParameters();
+
+ actions.parsedSymbol(symbol);
+ expect('(');
+
+ while (symbol != ')' && (symbol > 0)) {
+ updateTypeSignature();
+ }
+
+ actions.parsedSymbol(symbol);
+ expect(')');
+
+ updateReturnType();
+
+ if (symbol == '^') {
+ do {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+
+ // ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
+ if (symbol == 'T') {
+ updateTypeVariableSignature();
+ } else {
+ parseClassTypeSignature();
+ }
+ } while (symbol == '^');
+ }
+ }
+
+ private void updateReturnType() {
+ // ReturnType ::= TypeSignature | "V".
+ if (symbol != 'V') {
+ updateTypeSignature();
+ } else {
+ actions.parsedSymbol(symbol);
+ scanSymbol();
+ }
+ }
+
+
+ //
+ // Scanner:
+ //
+
+ private void scanSymbol() {
+ if (!eof) {
+ assert buffer != null;
+ if (pos < buffer.length) {
+ symbol = buffer[pos];
+ pos++;
+ } else {
+ symbol = 0;
+ eof = true;
+ }
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ private void expect(char c) {
+ if (symbol == c) {
+ scanSymbol();
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+
+ private boolean isStopSymbol(char ch) {
+ switch (ch) {
+ case ':':
+ case '/':
+ case ';':
+ case '<':
+ case '.':
+ return true;
+ }
+ return false;
+ }
+
+ // PRE: symbol is the first char of the identifier.
+ // POST: symbol = the next symbol AFTER the identifier.
+ private void scanIdentifier() {
+ if (!eof) {
+ StringBuilder identBuf = new StringBuilder(32);
+ if (!isStopSymbol(symbol)) {
+ identBuf.append(symbol);
+
+ // FINDBUGS
+ char[] bufferLocal = buffer;
+ assert bufferLocal != null;
+ do {
+ char ch = bufferLocal[pos];
+ if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z')
+ || !isStopSymbol(ch)) {
+ identBuf.append(bufferLocal[pos]);
+ pos++;
+ } else {
+ identifier = identBuf.toString();
+ scanSymbol();
+ return;
+ }
+ } while (pos != bufferLocal.length);
+ identifier = identBuf.toString();
+ symbol = 0;
+ eof = true;
+ } else {
+ // Ident starts with incorrect char.
+ symbol = 0;
+ eof = true;
+ throw new GenericSignatureFormatError();
+ }
+ } else {
+ throw new GenericSignatureFormatError();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index 4cd44ef..3106c0c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -21,17 +21,15 @@
public class AnnotationRemover {
private final AppInfoWithLiveness appInfo;
- private final boolean minificationEnabled;
private final AttributeRemovalOptions keep;
public AnnotationRemover(AppInfoWithLiveness appInfo, InternalOptions options) {
- this(appInfo, !options.skipMinification, options.attributeRemoval);
+ this(appInfo, options.attributeRemoval);
}
- public AnnotationRemover(AppInfoWithLiveness appInfo, boolean minificationEnabled,
+ public AnnotationRemover(AppInfoWithLiveness appInfo,
AttributeRemovalOptions keep) {
this.appInfo = appInfo;
- this.minificationEnabled = minificationEnabled;
this.keep = keep;
}
@@ -104,7 +102,7 @@
}
public void run() {
- keep.ensureValid(minificationEnabled);
+ keep.ensureValid();
for (DexProgramClass clazz : appInfo.classes()) {
clazz.annotations = stripAnnotations(clazz.annotations, this::filterAnnotations);
clazz.forEachMethod(this::processMethod);
diff --git a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
index a7a27fe..e675525 100644
--- a/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
+++ b/src/main/java/com/android/tools/r8/shaking/DiscardedChecker.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
-import java.util.Arrays;
import java.util.Set;
public class DiscardedChecker {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
index f6ac140..b589535 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexListBuilder.java
@@ -11,10 +11,8 @@
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index eae33d1..277ee0f 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -148,6 +148,18 @@
return classDescriptor.substring(1, classDescriptor.length() - 1);
}
+
+ /**
+ * Convert a class binary name to a descriptor.
+ *
+ * @param typeBinaryName class binary name i.e. "java/lang/Object"
+ * @return a class descriptor i.e. "Ljava/lang/Object;"
+ */
+ public static String getDescriptorFromClassBinaryName(String typeBinaryName) {
+ return ('L' + typeBinaryName + ';');
+ }
+
+
/**
* Get class name from its binary name.
*
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7155a13..d1f8290 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -7,14 +7,12 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.conversion.CallGraph;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.shaking.ProguardTypeMatcher;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.util.List;
-import java.util.function.BiFunction;
import java.util.function.Function;
public class InternalOptions {
@@ -249,7 +247,7 @@
annotationDefault = update(annotationDefault, ANNOTATION_DEFAULT, patterns);
}
- public void ensureValid(boolean isMinifying) {
+ public void ensureValid() {
if (innerClasses && !enclosingMethod) {
throw new CompilationError("Attribute InnerClasses requires EnclosingMethod attribute. "
+ "Check -keepattributes directive.");
@@ -259,10 +257,6 @@
} else if (signature && !innerClasses) {
throw new CompilationError("Attribute Signature requires InnerClasses attribute. Check "
+ "-keepattributes directive.");
- } else if (signature && isMinifying) {
- // TODO(38188583): Allow this once we can minify signatures.
- throw new CompilationError("Attribute Signature cannot be kept when minifying. "
- + "Check -keepattributes directive.");
}
}
}
diff --git a/src/test/examples/minifygeneric/AA.java b/src/test/examples/minifygeneric/AA.java
new file mode 100644
index 0000000..66b7aaf8
--- /dev/null
+++ b/src/test/examples/minifygeneric/AA.java
@@ -0,0 +1,7 @@
+// 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 minifygeneric;
+
+public class AA {
+}
diff --git a/src/test/examples/minifygeneric/BB.java b/src/test/examples/minifygeneric/BB.java
new file mode 100644
index 0000000..c29ca97
--- /dev/null
+++ b/src/test/examples/minifygeneric/BB.java
@@ -0,0 +1,7 @@
+// 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 minifygeneric;
+
+public class BB {
+}
diff --git a/src/test/examples/minifygeneric/Generic.java b/src/test/examples/minifygeneric/Generic.java
new file mode 100644
index 0000000..53f9e3d
--- /dev/null
+++ b/src/test/examples/minifygeneric/Generic.java
@@ -0,0 +1,32 @@
+// 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 minifygeneric;
+
+import java.util.List;
+import java.util.Map;
+
+public class Generic<T extends AA> {
+
+ public <U extends List> T m (Object o, T[] t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends Map> T m2 (Object o, T[] t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends List> T m3 (Object o, T t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends List> T m4 (Object o, T[] t, List<U> m) {
+ return null;
+ }
+
+ public Generic<T> f;
+ public Generic<T> get() {
+ return this;
+ }
+
+}
diff --git a/src/test/examples/minifygeneric/Minifygeneric.java b/src/test/examples/minifygeneric/Minifygeneric.java
new file mode 100644
index 0000000..36406bb
--- /dev/null
+++ b/src/test/examples/minifygeneric/Minifygeneric.java
@@ -0,0 +1,42 @@
+// 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 minifygeneric;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+public class Minifygeneric {
+
+ public static void main(String[] args) throws NoSuchMethodException, SecurityException, NoSuchFieldException {
+ for (TypeVariable<Class<Generic>> var : Generic.class.getTypeParameters()) {
+ System.out.println(var.getName());
+ Type bound = var.getBounds()[0];
+ System.out.println(((Class<?>) bound).getName().equals(AA.class.getName()));
+ }
+
+ Field f = Generic.class.getField("f");
+ ParameterizedType fieldType = (java.lang.reflect.ParameterizedType)f.getGenericType();
+ checkOneParameterType(fieldType, Generic.class, AA.class);
+
+ ParameterizedType methodReturnType =
+ (ParameterizedType) Generic.class.getMethod("get").getGenericReturnType();
+ checkOneParameterType(methodReturnType, Generic.class, AA.class);
+ }
+
+ private static void checkOneParameterType(ParameterizedType toCheck, Class<?> rawType,
+ Class<?>... bounds) {
+ System.out.println(((Class<?>) toCheck.getRawType()).getName()
+ .equals(rawType.getName()));
+ Type[] parameters = toCheck.getActualTypeArguments();
+ System.out.println(parameters.length);
+ TypeVariable<?> parameter = (TypeVariable<?>) parameters[0];
+ System.out.println(parameter.getName());
+ Type[] actualBounds = parameter.getBounds();
+ for (int i = 0; i < bounds.length; i++) {
+ System.out.println(((Class<?>) actualBounds[i]).getName().equals(bounds[i].getName()));
+ }
+ }
+}
diff --git a/src/test/examples/minifygeneric/keep-rules.txt b/src/test/examples/minifygeneric/keep-rules.txt
new file mode 100644
index 0000000..741d5c3
--- /dev/null
+++ b/src/test/examples/minifygeneric/keep-rules.txt
@@ -0,0 +1,15 @@
+# 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.
+
+-keep,allowobfuscation class ** {
+*;
+}
+-keep class *.Minifygeneric {
+*;
+}
+-keepclassmembernames class *.Generic {
+*;
+}
+-keepattributes *
+-allowaccessmodification
diff --git a/src/test/examples/minifygenericwithinner/AA.java b/src/test/examples/minifygenericwithinner/AA.java
new file mode 100644
index 0000000..ff283da
--- /dev/null
+++ b/src/test/examples/minifygenericwithinner/AA.java
@@ -0,0 +1,7 @@
+// 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 minifygenericwithinner;
+
+public class AA {
+}
diff --git a/src/test/examples/minifygenericwithinner/BB.java b/src/test/examples/minifygenericwithinner/BB.java
new file mode 100644
index 0000000..31ceb33
--- /dev/null
+++ b/src/test/examples/minifygenericwithinner/BB.java
@@ -0,0 +1,7 @@
+// 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 minifygenericwithinner;
+
+public class BB {
+}
diff --git a/src/test/examples/minifygenericwithinner/Generic.java b/src/test/examples/minifygenericwithinner/Generic.java
new file mode 100644
index 0000000..203561e
--- /dev/null
+++ b/src/test/examples/minifygenericwithinner/Generic.java
@@ -0,0 +1,42 @@
+// 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 minifygenericwithinner;
+
+import java.util.List;
+import java.util.Map;
+
+public class Generic<T extends AA> {
+
+ public <U extends List> T m (Object o, T[] t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends Map> T m2 (Object o, T[] t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends List> T m3 (Object o, T t, Map<T,U> m) {
+ return null;
+ }
+
+ public <U extends List> T m4 (Object o, T[] t, List<U> m) {
+ return null;
+ }
+
+ public <V extends BB> Inner<V> getInner(V obj) {
+ return new Inner<>();
+ }
+
+ public class Inner<V extends BB> {
+
+ public Generic<T>.Inner<V> f;
+ public <U extends List> T m5 (V o, T[] t, Map<T,U> m) {
+ return m(o, t, m);
+ }
+
+ public Generic<T>.Inner<V> get() {
+ return this;
+ }
+ }
+}
diff --git a/src/test/examples/minifygenericwithinner/Minifygenericwithinner.java b/src/test/examples/minifygenericwithinner/Minifygenericwithinner.java
new file mode 100644
index 0000000..882fa83
--- /dev/null
+++ b/src/test/examples/minifygenericwithinner/Minifygenericwithinner.java
@@ -0,0 +1,52 @@
+// 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 minifygenericwithinner;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+public class Minifygenericwithinner {
+
+ public static void main(String[] args)
+ throws NoSuchMethodException, SecurityException, NoSuchFieldException {
+ for (TypeVariable<Class<Generic>> var : Generic.class.getTypeParameters()) {
+ System.out.println(var.getName());
+ Type bound = var.getBounds()[0];
+ System.out.println(((Class<?>) bound).getName().equals(AA.class.getName()));
+ }
+ for (TypeVariable<Class<Generic.Inner>> var : Generic.Inner.class.getTypeParameters()) {
+ System.out.println(var.getName());
+ Type bound = var.getBounds()[0];
+ System.out.println(((Class<?>) bound).getName().equals(BB.class.getName()));
+ }
+
+ Field f = Generic.Inner.class.getField("f");
+ ParameterizedType fieldType = (java.lang.reflect.ParameterizedType)f.getGenericType();
+ checkOneParameterType(fieldType, Generic.Inner.class, BB.class);
+ ParameterizedType ownerType = (ParameterizedType) fieldType.getOwnerType();
+ checkOneParameterType(ownerType, Generic.class, AA.class);
+
+ ParameterizedType methodReturnType =
+ (ParameterizedType) Generic.Inner.class.getMethod("get").getGenericReturnType();
+ checkOneParameterType(methodReturnType, Generic.Inner.class, BB.class);
+ ownerType = (ParameterizedType) methodReturnType.getOwnerType();
+ checkOneParameterType(ownerType, Generic.class, AA.class);
+ }
+
+ private static void checkOneParameterType(ParameterizedType toCheck, Class<?> rawType,
+ Class<?>... bounds) {
+ System.out.println(((Class<?>) toCheck.getRawType()).getName()
+ .equals(rawType.getName()));
+ Type[] parameters = toCheck.getActualTypeArguments();
+ System.out.println(parameters.length);
+ TypeVariable<?> parameter = (TypeVariable<?>) parameters[0];
+ System.out.println(parameter.getName());
+ Type[] actualBounds = parameter.getBounds();
+ for (int i = 0; i < bounds.length; i++) {
+ System.out.println(((Class<?>) actualBounds[i]).getName().equals(bounds[i].getName()));
+ }
+ }
+}
diff --git a/src/test/examples/minifygenericwithinner/keep-rules.txt b/src/test/examples/minifygenericwithinner/keep-rules.txt
new file mode 100644
index 0000000..357b321
--- /dev/null
+++ b/src/test/examples/minifygenericwithinner/keep-rules.txt
@@ -0,0 +1,16 @@
+# 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.
+
+-keep,allowobfuscation class ** {
+*;
+}
+-keep class *.Minifygenericwithinner {
+*;
+}
+-keepclassmembernames class *.Generic$* {
+*;
+}
+
+-keepattributes *
+-allowaccessmodification
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 283d424..5b94e6b 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -69,7 +69,12 @@
"shaking16:keep-rules-2.txt:DEX:false",
"shaking16:keep-rules-2.txt:JAR:false",
"shaking15:keep-rules.txt:DEX:false",
- "shaking15:keep-rules.txt:JAR:false"
+ "shaking15:keep-rules.txt:JAR:false",
+ "minifygeneric:keep-rules.txt:DEX:false",
+ "minifygeneric:keep-rules.txt:JAR:false",
+ "minifygenericwithinner:keep-rules.txt:DEX:false",
+ "minifygenericwithinner:keep-rules.txt:JAR:false"
+
);
private final boolean minify;
@@ -534,6 +539,8 @@
"shaking16",
"inlining",
"minification",
+ "minifygeneric",
+ "minifygenericwithinner",
"assumenosideeffects1",
"assumenosideeffects2",
"assumenosideeffects3",
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 5bae2ed..6f45137 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -172,11 +172,11 @@
tmpClassesDir.toString());
AndroidApp inputApp = ToolHelper.getApp(command);
assertEquals(1, inputApp.getClasspathResourceProviders().size());
- assertEquals(tmpClassesDir,
- ((DirectoryClassFileProvider) inputApp.getClasspathResourceProviders().get(0)).getRoot());
+ assertTrue(Files.isSameFile(tmpClassesDir,
+ ((DirectoryClassFileProvider) inputApp.getClasspathResourceProviders().get(0)).getRoot()));
assertEquals(1, inputApp.getLibraryResourceProviders().size());
- assertEquals(tmpClassesDir,
- ((DirectoryClassFileProvider) inputApp.getLibraryResourceProviders().get(0)).getRoot());
+ assertTrue(Files.isSameFile(tmpClassesDir,
+ ((DirectoryClassFileProvider) inputApp.getLibraryResourceProviders().get(0)).getRoot()));
}
@Test
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index a207ed8..1dabbd3 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -100,6 +100,7 @@
return os.path.abspath(f.name)
def main(argv):
+ utils.check_java_version()
app_provided_pg_conf = False;
(options, args) = ParseOptions(argv)
outdir = options.out
diff --git a/tools/run_proguard_dx_on_app.py b/tools/run_proguard_dx_on_app.py
index 8578d1e..92a75ba 100755
--- a/tools/run_proguard_dx_on_app.py
+++ b/tools/run_proguard_dx_on_app.py
@@ -56,6 +56,7 @@
return parser.parse_args(argv)
def Main(argv):
+ utils.check_java_version()
options = parse_arguments(argv)
outdir = options.out
diff --git a/tools/test_framework.py b/tools/test_framework.py
index 04a12d3..b328375 100755
--- a/tools/test_framework.py
+++ b/tools/test_framework.py
@@ -59,6 +59,7 @@
return parser.parse_args()
def Main():
+ utils.check_java_version()
args = parse_arguments()
with utils.TempDir() as temp_dir:
diff --git a/tools/utils.py b/tools/utils.py
index dfc35d7..dab1dd1 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -170,3 +170,18 @@
for segment_name, size in getDexSegmentSizes(dex_files).items():
print('{}-{}(CodeSize): {}'
.format(prefix, segment_name, size))
+
+# ensure that java version is 1.8.*-internal,
+# as opposed to e.g. 1.7* or 1.8.*-google-v7
+def check_java_version():
+ cmd= ['java', '-version']
+ output = subprocess.check_output(cmd, stderr = subprocess.STDOUT)
+ m = re.search('openjdk version "([^"]*)"', output)
+ if m is None:
+ raise Exception("Can't check java version: no version string in output"
+ " of 'java -version': '{}'".format(output))
+ version = m.groups(0)[0]
+ m = re.search('1[.]8[.].*-internal', version)
+ if m is None:
+ raise Exception("Incorrect java version, expected: '1.8.*-internal',"
+ " actual: {}".format(version))