aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Gavrilovic <gavra@google.com>2017-07-13 20:19:57 +0000
committerandroid-build-merger <android-build-merger@google.com>2017-07-13 20:19:57 +0000
commit3b76a16fd242043b68e6659e605c3ed741e73eb2 (patch)
tree65af4d76ce0f547cffd9d0e415fe1fedd2bb7337
parent47bd2e1de3296f7a86f27f5c781c8102f40984c6 (diff)
parent4e98faf4922a5465cc86fb89606a3c2b1fe7d2d2 (diff)
downloadr8-3b76a16fd242043b68e6659e605c3ed741e73eb2.tar.gz
Merge remote-tracking branch 'aosp/upstream-mirror' into master am: e60b2c831a am: 2a277aff30 am: fc5cad1237
am: 4e98faf492 Change-Id: I4ed30934ecab49844db8c5dde385e4036ef86640
-rw-r--r--src/main/java/com/android/tools/r8/R8Command.java3
-rw-r--r--src/main/java/com/android/tools/r8/ReadMainDexList.java34
-rw-r--r--src/main/java/com/android/tools/r8/dex/DexFileReader.java3
-rw-r--r--src/main/java/com/android/tools/r8/dex/FileWriter.java18
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexAnnotation.java3
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java46
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexItemFactory.java40
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexMethodHandle.java2
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexType.java2
-rw-r--r--src/main/java/com/android/tools/r8/graph/DexValue.java8
-rw-r--r--src/main/java/com/android/tools/r8/graph/JarClassFileReader.java3
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java4
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java18
-rw-r--r--src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java16
-rw-r--r--src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java48
-rw-r--r--src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java28
-rw-r--r--src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java22
-rw-r--r--src/main/java/com/android/tools/r8/shaking/TreePruner.java21
-rw-r--r--src/main/java/com/android/tools/r8/utils/InternalOptions.java2
-rw-r--r--src/test/java/com/android/tools/r8/R8RunArtTestsTest.java48
-rw-r--r--src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java38
-rw-r--r--src/test/java/com/android/tools/r8/debug/DebugTestBase.java39
-rw-r--r--src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java74
-rw-r--r--src/test/proguard/valid/dontshrink.flags5
-rw-r--r--src/test/proguard/valid/printusage-to-file.flags5
-rw-r--r--src/test/proguard/valid/printusage.flags5
26 files changed, 336 insertions, 199 deletions
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a71091252..aed488649 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -363,6 +363,9 @@ public class R8Command extends BaseCommand {
internal.skipMinification = !useMinification();
assert internal.useTreeShaking;
internal.useTreeShaking = useTreeShaking();
+ assert !internal.printUsage;
+ internal.printUsage = proguardConfiguration.isPrintUsage();
+ internal.printUsageFile = proguardConfiguration.getPrintUsageFile();
assert !internal.ignoreMissingClasses;
internal.ignoreMissingClasses = ignoreMissingClasses;
diff --git a/src/main/java/com/android/tools/r8/ReadMainDexList.java b/src/main/java/com/android/tools/r8/ReadMainDexList.java
index 3a76c1fbb..a0c47895e 100644
--- a/src/main/java/com/android/tools/r8/ReadMainDexList.java
+++ b/src/main/java/com/android/tools/r8/ReadMainDexList.java
@@ -6,7 +6,11 @@ package com.android.tools.r8;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ProguardMapReader;
import com.android.tools.r8.utils.FileUtils;
+import com.google.common.collect.Iterators;
+import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.Iterator;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -20,8 +24,12 @@ public class ReadMainDexList {
return name.endsWith(DOT_CLASS) ? name.substring(0, name.length() - DOT_CLASS.length()) : name;
}
- private String addDotClass(String name) {
- return name + DOT_CLASS;
+ private String toClassFilePath(String name) {
+ return name.replace('.', '/') + DOT_CLASS;
+ }
+
+ private String toKeepRule(String className) {
+ return "-keep class " + className + " {}";
}
private String deobfuscateClassName(String name, ClassNameMapper mapper) {
@@ -32,21 +40,31 @@ public class ReadMainDexList {
}
private void run(String[] args) throws Exception {
- if (args.length != 1 && args.length != 2) {
- System.out.println("Usage: command <main_dex_list> [<proguard_map>]");
+ if (args.length < 1 || args.length > 3) {
+ System.out.println("Usage: command [-k] <main_dex_list> [<proguard_map>]");
System.exit(0);
}
+ Iterator<String> arguments = Iterators.forArray(args);
+ Function<String, String> outputGenerator;
+ String arg = arguments.next();
+ if (arg.equals("-k")) {
+ outputGenerator = this::toKeepRule;
+ arg = arguments.next();
+ } else {
+ outputGenerator = this::toClassFilePath;
+ }
+ Path mainDexList = Paths.get(arg);
+
final ClassNameMapper mapper =
- args.length == 2 ? ProguardMapReader.mapperFromFile(Paths.get(args[1])) : null;
+ arguments.hasNext() ? ProguardMapReader.mapperFromFile(Paths.get(arguments.next())) : null;
- FileUtils.readTextFile(Paths.get(args[0]))
+ FileUtils.readTextFile(mainDexList)
.stream()
.map(this::stripDotClass)
.map(name -> name.replace('/', '.'))
.map(name -> deobfuscateClassName(name, mapper))
- .map(name -> name.replace('.', '/'))
- .map(this::addDotClass)
+ .map(outputGenerator)
.sorted()
.collect(Collectors.toList())
.forEach(System.out::println);
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 a0644948f..1c3707337 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -47,6 +47,7 @@ import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexValue.DexValueMethodHandle;
import com.android.tools.r8.graph.DexValue.DexValueMethodType;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.OffsetToObjectMapping;
import com.android.tools.r8.logging.Log;
@@ -226,7 +227,7 @@ public class DexFileReader {
}
case DexValue.VALUE_NULL: {
assert valueArg == 0;
- return DexValue.NULL;
+ return DexValueNull.NULL;
}
case DexValue.VALUE_BOOLEAN: {
// 0 is false, and 1 is true.
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 6c6c3e16a..00eea7cb0 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -215,18 +215,16 @@ public class FileWriter {
this::writeStringData);
writeItems(mixedSectionOffsets.getAnnotations(), layout::setAnnotationsOffset,
this::writeAnnotation);
+ writeItems(mixedSectionOffsets.getClassesWithData(), layout::setClassDataOffset,
+ this::writeClassData);
+ writeItems(mixedSectionOffsets.getEncodedArrays(), layout::setEncodedArrarysOffset,
+ this::writeEncodedArray);
writeItems(mixedSectionOffsets.getAnnotationSets(), layout::setAnnotationSetsOffset,
this::writeAnnotationSet, 4);
writeItems(mixedSectionOffsets.getAnnotationSetRefLists(),
layout::setAnnotationSetRefListsOffset, this::writeAnnotationSetRefList, 4);
- // Write the annotation directories.
writeItems(mixedSectionOffsets.getAnnotationDirectories(),
layout::setAnnotationDirectoriesOffset, this::writeAnnotationDirectory, 4);
- // Write the rest.
- writeItems(mixedSectionOffsets.getClassesWithData(), layout::setClassDataOffset,
- this::writeClassData);
- writeItems(mixedSectionOffsets.getEncodedArrays(), layout::setEncodedArrarysOffset,
- this::writeEncodedArray);
// Add the map at the end
layout.setMapOffset(dest.align(4));
@@ -760,6 +758,10 @@ public class FileWriter {
mixedSectionOffsets.getStringData().size());
size += writeMapItem(Constants.TYPE_ANNOTATION_ITEM, layout.getAnnotationsOffset(),
mixedSectionOffsets.getAnnotations().size());
+ size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
+ mixedSectionOffsets.getClassesWithData().size());
+ size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
+ mixedSectionOffsets.getEncodedArrays().size());
size += writeMapItem(Constants.TYPE_ANNOTATION_SET_ITEM, layout.getAnnotationSetsOffset(),
mixedSectionOffsets.getAnnotationSets().size());
size += writeMapItem(Constants.TYPE_ANNOTATION_SET_REF_LIST,
@@ -768,10 +770,6 @@ public class FileWriter {
size += writeMapItem(Constants.TYPE_ANNOTATIONS_DIRECTORY_ITEM,
layout.getAnnotationDirectoriesOffset(),
mixedSectionOffsets.getAnnotationDirectories().size());
- size += writeMapItem(Constants.TYPE_CLASS_DATA_ITEM, layout.getClassDataOffset(),
- mixedSectionOffsets.getClassesWithData().size());
- size += writeMapItem(Constants.TYPE_ENCODED_ARRAY_ITEM, layout.getEncodedArrarysOffset(),
- mixedSectionOffsets.getEncodedArrays().size());
size += writeMapItem(Constants.TYPE_MAP_LIST, layout.getMapOffset(), 1);
dest.moveTo(startOfMap);
dest.putInt(size);
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 66d977418..55aa9868a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.DexValue.DexValueAnnotation;
import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueMethod;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import java.util.ArrayList;
@@ -111,7 +112,7 @@ public class DexAnnotation extends DexItem {
new DexAnnotationElement(
factory.createString("name"),
(clazz == null)
- ? DexValue.NULL
+ ? DexValueNull.NULL
: new DexValueString(factory.createString(clazz)))
}));
}
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 3df2fabe7..c8fbbeadf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -12,9 +12,12 @@ import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.ConstString;
import com.android.tools.r8.code.ConstStringJumbo;
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;
@@ -30,6 +33,7 @@ import com.android.tools.r8.logging.Log;
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> {
@@ -238,7 +242,7 @@ public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
* templates might incur a size overhead.
*/
private DexCode generateCodeFromTemplate(
- int numberOfRegisters, int outRegisters, Instruction[] instructions) {
+ int numberOfRegisters, int outRegisters, Instruction... instructions) {
int offset = 0;
for (Instruction instruction : instructions) {
assert !(instruction instanceof ConstString);
@@ -276,21 +280,33 @@ public class DexEncodedMethod extends KeyedDexItem<DexMethod> {
.createMethod(itemFactory.createType("Landroid/util/Log;"), proto,
itemFactory.createString("e"));
DexType exceptionType = itemFactory.createType("Ljava/lang/RuntimeException;");
- DexType[] exceptionArgs = {exceptionType, itemFactory.stringType};
- DexMethod initMethod = itemFactory
- .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType, exceptionArgs),
+ DexMethod exceptionInitMethod = itemFactory
+ .createMethod(exceptionType, itemFactory.createProto(itemFactory.voidType,
+ itemFactory.stringType),
itemFactory.constructorMethodName);
- // These methods might not get registered for jumbo string processing, therefore we always
- // use the jumbo string encoding for the const string instruction.
- Instruction insn[] = {
- new ConstStringJumbo(0, tag),
- new ConstStringJumbo(1, message),
- new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
- new NewInstance(0, exceptionType),
- new InvokeStatic(2, initMethod, 0, 1, 0, 0, 0),
- new Throw(0)
- };
- DexCode code = generateCodeFromTemplate(2, 2, insn);
+ DexCode code;
+ if (accessFlags.isConstructor() && !accessFlags.isStatic()) {
+ // The Java VM Spec requires that a constructor calls an initializer from the super class
+ // or another constructor from the current class. For simplicity we do the latter by just
+ // calling outself. This is ok, as the constructor always throws before the recursive call.
+ code = generateCodeFromTemplate(3, 2, new ConstStringJumbo(0, tag),
+ new ConstStringJumbo(1, message),
+ new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+ new NewInstance(0, exceptionType),
+ new InvokeDirect(2, exceptionInitMethod, 0, 1, 0, 0, 0),
+ new Throw(0),
+ new InvokeDirect(1, method, 2, 0, 0, 0, 0));
+
+ } else {
+ // These methods might not get registered for jumbo string processing, therefore we always
+ // use the jumbo string encoding for the const string instruction.
+ code = generateCodeFromTemplate(2, 2, new ConstStringJumbo(0, tag),
+ new ConstStringJumbo(1, message),
+ new InvokeStatic(2, logMethod, 0, 1, 0, 0, 0),
+ new NewInstance(0, exceptionType),
+ new InvokeDirect(2, exceptionInitMethod, 0, 1, 0, 0, 0),
+ new Throw(0));
+ }
Builder builder = builder(this);
builder.setCode(code);
return builder.build();
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 984215b80..5dff4b432 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -222,32 +222,22 @@ public class DexItemFactory {
DexString toStringMethodName = createString("toString");
- appendBoolean =
- createMethod(receiver, createProto(receiver, new DexType[]{booleanType}), append);
- appendChar = createMethod(receiver, createProto(receiver, new DexType[]{charType}), append);
- appendCharArray =
- createMethod(receiver, createProto(receiver, new DexType[]{charArrayType}), append);
+ appendBoolean = createMethod(receiver, createProto(receiver, booleanType), append);
+ appendChar = createMethod(receiver, createProto(receiver, charType), append);
+ appendCharArray = createMethod(receiver, createProto(receiver, charArrayType), append);
appendSubCharArray =
- createMethod(receiver,
- createProto(receiver, new DexType[]{charArrayType, intType, intType}), append);
- appendCharSequence =
- createMethod(receiver, createProto(receiver, new DexType[]{charSequenceType}), append);
+ createMethod(receiver, createProto(receiver, charArrayType, intType, intType), append);
+ appendCharSequence = createMethod(receiver, createProto(receiver, charSequenceType), append);
appendSubCharSequence =
- createMethod(receiver,
- createProto(receiver, new DexType[]{charSequenceType, intType, intType}), append);
- appendInt = createMethod(receiver, createProto(receiver, new DexType[]{intType}), append);
- appendDouble =
- createMethod(receiver, createProto(receiver, new DexType[]{doubleType}), append);
- appendFloat = createMethod(receiver, createProto(receiver, new DexType[]{floatType}), append);
- appendLong = createMethod(receiver, createProto(receiver, new DexType[]{longType}), append);
- appendObject =
- createMethod(receiver, createProto(receiver, new DexType[]{objectType}), append);
- appendString =
- createMethod(receiver, createProto(receiver, new DexType[]{stringType}), append);
- appendStringBuffer =
- createMethod(receiver, createProto(receiver, new DexType[]{sbufType}), append);
- toString =
- createMethod(receiver, createProto(stringType, DexType.EMPTY_ARRAY), toStringMethodName);
+ createMethod(receiver, createProto(receiver, charSequenceType, intType, intType), append);
+ appendInt = createMethod(receiver, createProto(receiver, intType), append);
+ appendDouble = createMethod(receiver, createProto(receiver, doubleType), append);
+ appendFloat = createMethod(receiver, createProto(receiver, floatType), append);
+ appendLong = createMethod(receiver, createProto(receiver, longType), append);
+ 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) {
@@ -322,7 +312,7 @@ public class DexItemFactory {
parameters.length == 0 ? DexTypeList.empty() : new DexTypeList(parameters));
}
- public DexProto createProto(DexType returnType, DexType[] parameters) {
+ public DexProto createProto(DexType returnType, DexType... parameters) {
return createProto(createShorty(returnType, parameters), returnType, parameters);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index e59e7c97a..d2237352f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -132,7 +132,7 @@ public class DexMethodHandle extends IndexedDexItem {
public boolean computeEquals(Object other) {
if (other instanceof DexMethodHandle) {
DexMethodHandle o = (DexMethodHandle) other;
- return type.equals(o.type) && fieldOrMethod.computeEquals(o.fieldOrMethod);
+ return type.equals(o.type) && fieldOrMethod.equals(o.fieldOrMethod);
}
return false;
}
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 4d3d8cb25..234773f96 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -19,8 +19,6 @@ import java.util.function.Function;
public class DexType extends IndexedDexItem implements PresortedComparable<DexType> {
- public static final DexType[] EMPTY_ARRAY = new DexType[]{};
-
private final static int ROOT_LEVEL = 0;
private final static int UNKNOWN_LEVEL = -1;
private final static int INTERFACE_LEVEL = -2;
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 5430d6e1e..a0cfa7c3a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexValue.java
+++ b/src/main/java/com/android/tools/r8/graph/DexValue.java
@@ -36,8 +36,6 @@ public abstract class DexValue extends DexItem {
public static final byte VALUE_NULL = 0x1e;
public static final byte VALUE_BOOLEAN = 0x1f;
- public static final DexValue NULL = new DexValueNull();
-
private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
dest.putByte((byte) ((arg << 5) | type));
}
@@ -87,7 +85,7 @@ public abstract class DexValue extends DexItem {
return DexValueDouble.DEFAULT;
}
if (type.isArrayType() || type.isClassType()) {
- return DexValue.NULL;
+ return DexValueNull.NULL;
}
throw new Unreachable("No default value for unexpected type " + type);
}
@@ -688,7 +686,9 @@ public abstract class DexValue extends DexItem {
static public class DexValueNull extends SimpleDexValue {
- // See DexValue.NULL
+ public static final DexValue NULL = new DexValueNull();
+
+ // See DexValueNull.NULL
private DexValueNull() {
}
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 59f187f0c..ce38bc967 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -20,6 +20,7 @@ import com.android.tools.r8.graph.DexValue.DexValueEnum;
import com.android.tools.r8.graph.DexValue.DexValueFloat;
import com.android.tools.r8.graph.DexValue.DexValueInt;
import com.android.tools.r8.graph.DexValue.DexValueLong;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.DexValue.DexValueShort;
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
@@ -699,7 +700,7 @@ public class JarClassFileReader {
private DexValue getDexValue(Object value) {
if (value == null) {
- return DexValue.NULL;
+ return DexValueNull.NULL;
}
if (value instanceof Byte) {
return DexValueByte.create((Byte) value);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index cb518a3d0..c2600b4e4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -23,7 +23,7 @@ import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
import java.util.List;
@@ -219,7 +219,7 @@ final class LambdaClass {
new DexAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_FINAL
| Constants.ACC_SYNTHETIC | Constants.ACC_STATIC),
DexAnnotationSet.empty(),
- DexValue.NULL);
+ DexValueNull.NULL);
return fields;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 2355e5eb5..9ec776f47 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import static com.android.tools.r8.ir.desugar.LambdaRewriter.EMPTY_TYPE_ARRAY;
-
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -421,28 +419,28 @@ final class LambdaMainMethodSourceCode extends SynthesizedLambdaSourceCode {
DexProto proto;
switch (primitive) {
case 'Z': // byte
- proto = factory.createProto(factory.booleanType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.booleanType);
return factory.createMethod(boxType, proto, factory.unboxBooleanMethodName);
case 'B': // byte
- proto = factory.createProto(factory.byteType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.byteType);
return factory.createMethod(boxType, proto, factory.unboxByteMethodName);
case 'S': // short
- proto = factory.createProto(factory.shortType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.shortType);
return factory.createMethod(boxType, proto, factory.unboxShortMethodName);
case 'C': // char
- proto = factory.createProto(factory.charType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.charType);
return factory.createMethod(boxType, proto, factory.unboxCharMethodName);
case 'I': // int
- proto = factory.createProto(factory.intType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.intType);
return factory.createMethod(boxType, proto, factory.unboxIntMethodName);
case 'J': // long
- proto = factory.createProto(factory.longType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.longType);
return factory.createMethod(boxType, proto, factory.unboxLongMethodName);
case 'F': // float
- proto = factory.createProto(factory.floatType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.floatType);
return factory.createMethod(boxType, proto, factory.unboxFloatMethodName);
case 'D': // double
- proto = factory.createProto(factory.doubleType, EMPTY_TYPE_ARRAY);
+ proto = factory.createProto(factory.doubleType);
return factory.createMethod(boxType, proto, factory.unboxDoubleMethodName);
default:
throw new Unreachable("Invalid primitive type descriptor: " + primitive);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 10908f038..7d3bc336d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -54,7 +54,6 @@ public class LambdaRewriter {
static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
- static final DexType[] EMPTY_TYPE_ARRAY = new DexType[0];
static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
final IRConverter converter;
@@ -103,20 +102,17 @@ public class LambdaRewriter {
DexType objectArrayType = factory.createType(OBJECT_ARRAY_TYPE_DESCR);
this.metafactoryMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, new DexType[] {
- lookupType, factory.stringType, methodTypeType,
- methodTypeType, methodHandleType, methodTypeType
- }),
+ factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
+ methodTypeType, methodHandleType, methodTypeType),
factory.createString(METAFACTORY_METHOD_NAME));
this.metafactoryAltMethod = factory.createMethod(metafactoryType,
- factory.createProto(callSiteType, new DexType[] {
- lookupType, factory.stringType, methodTypeType, objectArrayType
- }),
+ factory.createProto(callSiteType, lookupType, factory.stringType, methodTypeType,
+ objectArrayType),
factory.createString(METAFACTORY_ALT_METHOD_NAME));
this.constructorName = factory.createString(Constants.INSTANCE_INITIALIZER_NAME);
- DexProto initProto = factory.createProto(factory.voidType, EMPTY_TYPE_ARRAY);
+ DexProto initProto = factory.createProto(factory.voidType);
this.objectInitMethod = factory.createMethod(factory.objectType, initProto, constructorName);
this.classConstructorName = factory.createString(Constants.CLASS_INITIALIZER_NAME);
this.instanceFieldName = factory.createString(LAMBDA_INSTANCE_FIELD_NAME);
@@ -124,7 +120,7 @@ public class LambdaRewriter {
this.deserializeLambdaMethodName = factory.createString(DESERIALIZE_LAMBDA_METHOD_NAME);
this.deserializeLambdaMethodProto = factory.createProto(
- factory.objectType, new DexType[] { factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR) });
+ factory.objectType, factory.createType(SERIALIZED_LAMBDA_TYPE_DESCR));
}
/**
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 de8033808..7c12590b3 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMinifier.java
@@ -22,16 +22,6 @@ import java.util.Set;
public class ClassNameMinifier {
- /**
- * We ban classes from these prefixes from minification. This is needed as some classes
- * in the android sdk are given a @hide annotation, which will remove them from the
- * sdk we tree-shake and minify against. Thus, the class will not be available and hence
- * we won't find out that it is a library class.
- * To save space, we by default minify classes we do not have an implementation for.
- */
- private static final Set<String> BANNED_CLASS_PREFIXES = ImmutableSet
- .of("Ljava", "Landroid", "Ldalvik");
-
private final AppInfoWithLiveness appInfo;
private final RootSet rootSet;
private final String packagePrefix;
@@ -65,17 +55,13 @@ public class ClassNameMinifier {
renaming.put(clazz.type, state.nextTypeName());
}
}
- renameTypesInProtosOf(appInfo.staticInvokes);
- renameTypesInProtosOf(appInfo.superInvokes);
- renameTypesInProtosOf(appInfo.directInvokes);
- renameTypesInProtosOf(appInfo.virtualInvokes);
appInfo.dexItemFactory.forAllTypes(this::renameArrayTypeIfNeeded);
return Collections.unmodifiableMap(renaming);
}
private String getPackageNameFor(DexClass clazz) {
- if (packagePrefix == null || rootSet.keepPackageName.contains(clazz)) {
+ if ((packagePrefix == null) || rootSet.keepPackageName.contains(clazz)) {
return clazz.type.getPackageDescriptor();
} else {
return packagePrefix;
@@ -86,38 +72,6 @@ public class ClassNameMinifier {
return states.computeIfAbsent(packageName, NamingState::new);
}
- private void renameTypesInProtosOf(Iterable<DexMethod> methods) {
- for (DexMethod method : methods) {
- renameTypeWithoutClassDefinition(method.proto.returnType);
- for (DexType type : method.proto.parameters.values) {
- renameTypeWithoutClassDefinition(type);
- }
- }
- }
-
- private void renameTypeWithoutClassDefinition(DexType type) {
- if (type.isArrayType()) {
- type = type.toBaseType(appInfo.dexItemFactory);
- }
- if (type.isClassType() && !renaming.containsKey(type)) {
- DexClass clazz = appInfo.definitionFor(type);
- if (clazz == null || !clazz.isLibraryClass()) {
- if (!classIsBannedFromRenaming(type)) {
- String packageName = packagePrefix == null ? type.getPackageDescriptor() : packagePrefix;
- NamingState state = getStateFor(packageName);
- renaming.put(type, state.nextTypeName());
- }
- }
- }
- }
-
- private boolean classIsBannedFromRenaming(DexType type) {
- String desc = type.toDescriptorString();
- int index = desc.indexOf('/');
- String prefix = desc.substring(0, index);
- return index != -1 && BANNED_CLASS_PREFIXES.contains(prefix);
- }
-
private void renameArrayTypeIfNeeded(DexType type) {
if (type.isArrayType()) {
DexType base = type.toBaseType(appInfo.dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 9a6300800..71a4dc634 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -24,6 +24,8 @@ public class ProguardConfiguration {
private boolean ignoreWarnings = false;
private boolean obfuscating = true;
private boolean shrinking = true;
+ private boolean printUsage = false;
+ private Path printUsageFile;
private boolean printMapping;
private Path printMappingOutput;
private boolean verbose = false;
@@ -69,6 +71,14 @@ public class ProguardConfiguration {
this.shrinking = shrinking;
}
+ public void setPrintUsage(boolean printUsage) {
+ this.printUsage = printUsage;
+ }
+
+ public void setPrintUsageFile(Path printUsageFile) {
+ this.printUsageFile = printUsageFile;
+ }
+
public void setPrintMapping(boolean printMapping) {
this.printMapping = printMapping;
}
@@ -123,6 +133,8 @@ public class ProguardConfiguration {
ignoreWarnings,
obfuscating,
shrinking,
+ printUsage,
+ printUsageFile,
printMapping,
printMappingOutput,
verbose,
@@ -145,6 +157,8 @@ public class ProguardConfiguration {
private final boolean ignoreWarnings;
private final boolean obfuscating;
private final boolean shrinking;
+ private final boolean printUsage;
+ private final Path printUsageFile;
private final boolean printMapping;
private final Path printMappingOutput;
private final boolean verbose;
@@ -166,6 +180,8 @@ public class ProguardConfiguration {
boolean ignoreWarnings,
boolean obfuscating,
boolean shrinking,
+ boolean printUsage,
+ Path printUsageFile,
boolean printMapping,
Path printMappingOutput,
boolean verbose,
@@ -185,6 +201,8 @@ public class ProguardConfiguration {
this.ignoreWarnings = ignoreWarnings;
this.obfuscating = obfuscating;
this.shrinking = shrinking;
+ this.printUsage = printUsage;
+ this.printUsageFile = printUsageFile;
this.printMapping = printMapping;
this.printMappingOutput = printMappingOutput;
this.verbose = verbose;
@@ -249,6 +267,14 @@ public class ProguardConfiguration {
return shrinking;
}
+ public boolean isPrintUsage() {
+ return printUsage;
+ }
+
+ public Path getPrintUsageFile() {
+ return printUsageFile;
+ }
+
public boolean isVerbose() {
return verbose;
}
@@ -293,6 +319,8 @@ public class ProguardConfiguration {
false /* ignoreWarnings */,
false /* obfuscating */,
false /* shrinking */,
+ false /* printUsage */,
+ null /* printUsageFile */,
false /* printMapping */,
null /* outputMapping */,
false /* verbose */,
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 9fcb50a92..3b78e1b56 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -55,8 +55,7 @@ public class ProguardConfigurationParser {
"alwaysinline", "identifiernamestring", "whyarenotsimple");
private static final List<String> warnedSingleArgOptions = ImmutableList
- .of("printusage",
- "renamesourcefileattribute",
+ .of("renamesourcefileattribute",
"dontnote",
"printconfiguration",
// TODO -outjars (http://b/37137994) and -adaptresourcefilecontents (http://b/37139570)
@@ -154,6 +153,14 @@ public class ProguardConfigurationParser {
configurationBuilder.setObfuscating(false);
} else if (acceptString("dontshrink")) {
configurationBuilder.setShrinking(false);
+ } else if (acceptString("printusage")) {
+ configurationBuilder.setPrintUsage(true);
+ skipWhitespace();
+ if (isOptionalArgumentGiven()) {
+ configurationBuilder.setPrintUsageFile(parseFileName());
+ }
+ // TODO(b/36799826): once fully implemented, no longer necessary to warn.
+ System.out.println("WARNING: Ignoring option: -printusage");
} else if (acceptString("verbose")) {
configurationBuilder.setVerbose(true);
} else if (acceptString("ignorewarnings")) {
@@ -177,7 +184,7 @@ public class ProguardConfigurationParser {
} else if (acceptString("printmapping")) {
configurationBuilder.setPrintMapping(true);
skipWhitespace();
- if (!eof() && peekChar() != '-') {
+ if (isOptionalArgumentGiven()) {
configurationBuilder.setPrintMappingOutput(parseFileName());
}
} else if (acceptString("assumenosideeffects")) {
@@ -199,7 +206,7 @@ public class ProguardConfigurationParser {
} else if (acceptString("printseeds")) {
configurationBuilder.setPrintSeed(true);
skipWhitespace();
- if (!eof() && peekChar() != '-') {
+ if (isOptionalArgumentGiven()) {
configurationBuilder.setSeedFile(parseFileName());
}
} else if (acceptString("obfuscationdictionary")) {
@@ -271,7 +278,7 @@ public class ProguardConfigurationParser {
Log.debug(ProguardConfigurationParser.class, "Skipping '-%s` option", name);
}
skipWhitespace();
- if (!eof() && peekChar() != '-') {
+ if (isOptionalArgumentGiven()) {
skipSingleArgument();
}
return true;
@@ -794,6 +801,10 @@ public class ProguardConfigurationParser {
return peekChar() == c;
}
+ private boolean isOptionalArgumentGiven() {
+ return !eof() && !hasNextChar('-');
+ }
+
private boolean acceptChar(char c) {
if (hasNextChar(c)) {
position++;
@@ -938,7 +949,6 @@ public class ProguardConfigurationParser {
}
}
-
private void checkNotNegatedPattern() throws ProguardRuleParserException {
skipWhitespace();
if (acceptChar('!')) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 04f67864d..3968c9ae0 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -55,14 +55,15 @@ public class TreePruner {
private List<DexProgramClass> getNewProgramClasses(List<DexProgramClass> classes) {
List<DexProgramClass> newClasses = new ArrayList<>();
for (DexProgramClass clazz : classes) {
- if (!appInfo.liveTypes.contains(clazz.type) && !options.debugKeepRules) {
+ if (!appInfo.liveTypes.contains(clazz.type)) {
// The class is completely unused and we can remove it.
if (Log.ENABLED) {
Log.debug(getClass(), "Removing class: " + clazz);
}
} else {
newClasses.add(clazz);
- if (!appInfo.instantiatedTypes.contains(clazz.type) && !options.debugKeepRules) {
+ if (!appInfo.instantiatedTypes.contains(clazz.type) &&
+ (!options.debugKeepRules || !hasDefaultConstructor(clazz))) {
// The class is only needed as a type but never instantiated. Make it abstract to reflect
// this.
if (clazz.accessFlags.isFinal()) {
@@ -86,6 +87,15 @@ public class TreePruner {
return newClasses;
}
+ private boolean hasDefaultConstructor(DexProgramClass clazz) {
+ for (DexEncodedMethod method : clazz.directMethods()) {
+ if (isDefaultConstructor(method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private <S extends PresortedComparable<S>, T extends KeyedDexItem<S>> int firstUnreachableIndex(
T[] items, Set<S> live) {
for (int i = 0; i < items.length; i++) {
@@ -96,6 +106,11 @@ public class TreePruner {
return -1;
}
+ private boolean isDefaultConstructor(DexEncodedMethod method) {
+ return method.accessFlags.isConstructor() && !method.accessFlags.isStatic()
+ && method.method.proto.parameters.isEmpty();
+ }
+
private DexEncodedMethod[] reachableMethods(DexEncodedMethod[] methods, DexClass clazz) {
int firstUnreachable = firstUnreachableIndex(methods, appInfo.liveMethods);
// Return the original array if all methods are used.
@@ -109,7 +124,7 @@ public class TreePruner {
for (int i = firstUnreachable; i < methods.length; i++) {
if (appInfo.liveMethods.contains(methods[i].getKey())) {
reachableMethods.add(methods[i]);
- } else if (options.debugKeepRules) {
+ } else if (options.debugKeepRules && isDefaultConstructor(methods[i])) {
// Keep the method but rewrite its body, if it has one.
reachableMethods.add(methods[i].accessFlags.isAbstract()
? methods[i]
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 01d8c2692..9d6cfa5ea 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -59,6 +59,8 @@ public class InternalOptions {
public OutputMode outputMode = OutputMode.Indexed;
public boolean useTreeShaking = true;
+ public boolean printUsage = false;
+ public Path printUsageFile = null;
public boolean printCfg = false;
public String printCfgFile;
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 0d0446d85..22278e2aa 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -96,6 +96,7 @@ public abstract class R8RunArtTestsTest {
private static Map<String, Integer> needMinSdkVersion =
new ImmutableMap.Builder<String, Integer>()
// Android O
+ .put("952-invoke-custom", Constants.ANDROID_O_API)
.put("953-invoke-polymorphic-compiler", Constants.ANDROID_O_API)
.put("957-methodhandle-transforms", Constants.ANDROID_O_API)
.put("958-methodhandle-stackframe", Constants.ANDROID_O_API)
@@ -218,37 +219,25 @@ public abstract class R8RunArtTestsTest {
"073-mismatched-field"
);
- // Tests that make use of Java 8 features.
- private static List<String> usesJava8 = ImmutableList.of(
- "914-hello-obsolescence",
- "915-obsolete-2",
- "916-obsolete-jit",
- "919-obsolete-fields",
- "926-multi-obsolescence",
- "936-search-onload",
- "940-recursive-obsolete",
- "941-recurive-obsolete-jit",
- "942-private-recursive",
- "943-private-recursive-jit",
- "946-obsolete-throw",
- "951-threaded-obsolete",
- "952-invoke-custom"
- );
-
// Tests that make use of agents/native code. Our test setup does handle flags/linking of these.
private static List<String> usesNativeAgentCode = ImmutableList.of(
"497-inlining-and-class-loader",
"626-const-class-linking",
"642-fp-callees",
"909-attach-agent",
+ "914-hello-obsolescence",
+ "915-obsolete-2",
+ "916-obsolete-jit",
"917-fields-transformation",
"918-fields",
+ "919-obsolete-fields",
"920-objects",
"921-hello-failure",
"922-properties",
"923-monitors",
"924-threads",
"925-threadgroups",
+ "926-multi-obsolescence",
"927-timers",
"928-jni-table",
"929-search",
@@ -262,12 +251,18 @@ public abstract class R8RunArtTestsTest {
"937-hello-retransform-package",
"938-load-transform-bcp",
"939-hello-transformation-bcp",
+ "940-recursive-obsolete",
+ "941-recurive-obsolete-jit",
+ "942-private-recursive",
+ "943-private-recursive-jit",
"944-transform-classloaders",
"945-obsolete-native",
+ "946-obsolete-throw",
"947-reflect-method",
"948-change-annotations",
"949-in-memory-transform",
"950-redefine-intrinsic",
+ "951-threaded-obsolete",
"980-redefine-object"
);
@@ -630,6 +625,9 @@ public abstract class R8RunArtTestsTest {
// is in dex code which D8 does not convert. Therefore the error is a verification error
// at runtime and that is expected.
.put("142-classloader2", TestCondition.match(TestCondition.D8_COMPILER))
+ // Invoke-custom is supported by D8 and R8, but it can only run on our newest version
+ // of art.
+ .put("952-invoke-custom", beforeAndroidO)
// Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version
// of art.
.put("953-invoke-polymorphic-compiler", beforeAndroidO)
@@ -734,6 +732,17 @@ public abstract class R8RunArtTestsTest {
.build())
.build();
+ // Tests to skip on some conditions
+ private static final Multimap<String, TestCondition> testToSkip =
+ new ImmutableListMultimap.Builder<String, TestCondition>()
+ // Old runtimes used the legacy test directory which does not contain input for tools
+ // NONE and DX.
+ .put("952-invoke-custom", TestCondition.match(
+ TestCondition.tools(DexTool.NONE, DexTool.DX),
+ TestCondition.runtimes(
+ DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0)))
+ .build();
+
private static List<String> failuresToTriage = ImmutableList.of(
// This is flaky.
"104-growth-limit",
@@ -896,7 +905,6 @@ public abstract class R8RunArtTestsTest {
skipArt.addAll(customRun);
Set<String> skipTest = Sets.newHashSet(skipAltogether);
- skipTest.addAll(usesJava8);
skipTest.addAll(usesNativeAgentCode);
skipTest.addAll(failuresToTriage);
@@ -915,6 +923,10 @@ public abstract class R8RunArtTestsTest {
skipTest.addAll(noInputJar);
}
+ // Collect the test that we should skip in this configuration
+ skipTest.addAll(collectTestsMatchingConditions(
+ dexTool, compilerUnderTest, dexVm, compilationMode, testToSkip));
+
// Collect the test that we should skip in this configuration.
skipArt.addAll(
collectTestsMatchingConditions(
diff --git a/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
new file mode 100644
index 000000000..ad94a531b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/ContinuousSteppingTest.java
@@ -0,0 +1,38 @@
+// 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.debug;
+
+import java.util.Map;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ContinuousSteppingTest extends DebugTestBase {
+
+ @Test
+ public void testArithmetic() throws Throwable {
+ runContinuousTest("Arithmetic");
+ }
+
+ @Test
+ public void testLocals() throws Throwable {
+ runContinuousTest("Locals");
+ }
+
+ private void runContinuousTest(String debuggeeClassName) throws Throwable {
+ runDebugTest(debuggeeClassName,
+ breakpoint(debuggeeClassName, "main"),
+ run(),
+ stepUntil(StepKind.OVER, StepLevel.INSTRUCTION, debuggeeState -> {
+ // Fetch local variables.
+ Map<String, Value> localValues = debuggeeState.getLocalValues();
+ Assert.assertNotNull(localValues);
+
+ // Always step until we actually exit the program.
+ return false;
+ }));
+ }
+
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a272eca6c..be2509780 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -18,10 +18,10 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Queue;
import java.util.TreeMap;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -341,7 +341,7 @@ public abstract class DebugTestBase {
*/
private DebuggeeState debuggeeState = null;
- private final Queue<Command> commandsQueue;
+ private final Deque<Command> commandsQueue;
// Active event requests.
private final Map<Integer, EventHandler> events = new TreeMap<>();
@@ -473,6 +473,9 @@ public abstract class DebugTestBase {
artCommandBuilder.appendArtOption("-Xcompiler-option");
artCommandBuilder.appendArtOption("--compiler-filter=interpret-only");
}
+ if (DEBUG_TESTS) {
+ artCommandBuilder.appendArtOption("-verbose:jdwp");
+ }
setProperty("jpda.settings.debuggeeJavaPath", artCommandBuilder.build());
}
@@ -1031,8 +1034,8 @@ public abstract class DebugTestBase {
stepRequestID = replyPacket.getNextValueAsInt();
testBase.assertAllDataRead(replyPacket);
}
- testBase.events.put(stepRequestID, new StepEventHandler(stepRequestID, stepFilter,
- stepUntil));
+ testBase.events
+ .put(stepRequestID, new StepEventHandler(this, stepRequestID, stepFilter, stepUntil));
// Resume all threads.
testBase.resume();
@@ -1092,13 +1095,17 @@ public abstract class DebugTestBase {
private static class StepEventHandler extends DefaultEventHandler {
+ private final JUnit3Wrapper.Command.StepCommand stepCommand;
private final int stepRequestID;
private final StepFilter stepFilter;
private final Function<DebuggeeState, Boolean> stepUntil;
- private StepEventHandler(int stepRequestID,
+ private StepEventHandler(
+ JUnit3Wrapper.Command.StepCommand stepCommand,
+ int stepRequestID,
StepFilter stepFilter,
Function<DebuggeeState, Boolean> stepUntil) {
+ this.stepCommand = stepCommand;
this.stepRequestID = stepRequestID;
this.stepFilter = stepFilter;
this.stepUntil = stepUntil;
@@ -1106,19 +1113,23 @@ public abstract class DebugTestBase {
@Override
public void handle(JUnit3Wrapper testBase) {
+ // Clear step event.
+ testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
+ testBase.events.remove(Integer.valueOf(stepRequestID));
+
+ // Do we need to step again ?
+ boolean repeatStep = false;
if (stepFilter
.skipLocation(testBase.getMirror(), testBase.getDebuggeeState().getLocation())) {
- // Keep the step active and resume so that we do another step.
- testBase.resume();
+ repeatStep = true;
} else if (stepUntil.apply(testBase.getDebuggeeState()) == Boolean.FALSE) {
- // We must not stop yet.
- testBase.resume();
- } else {
- // When hit, the single step must be cleared.
- testBase.getMirror().clearEvent(EventKind.SINGLE_STEP, stepRequestID);
- testBase.events.remove(Integer.valueOf(stepRequestID));
- super.handle(testBase);
+ repeatStep = true;
+ }
+ if (repeatStep) {
+ // In order to repeat the step now, we need to add it at the beginning of the queue.
+ testBase.commandsQueue.addFirst(stepCommand);
}
+ super.handle(testBase);
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index 05b06af78..242a7d5a4 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -18,9 +18,7 @@ import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
public class ProguardConfigurationParserTest extends TestBase {
@@ -47,7 +45,7 @@ public class ProguardConfigurationParserTest extends TestBase {
private static final String LIBRARY_JARS =
VALID_PROGUARD_DIR + "library-jars.flags";
private static final String LIBRARY_JARS_WIN =
- VALID_PROGUARD_DIR + "library-jars-win.flags";
+ VALID_PROGUARD_DIR + "library-jars-win.flags";
private static final String SEEDS =
VALID_PROGUARD_DIR + "seeds.flags";
private static final String SEEDS_2 =
@@ -58,6 +56,8 @@ public class ProguardConfigurationParserTest extends TestBase {
VALID_PROGUARD_DIR + "keepdirectories.flags";
private static final String DONT_OBFUSCATE =
VALID_PROGUARD_DIR + "dontobfuscate.flags";
+ private static final String DONT_SHRINK =
+ VALID_PROGUARD_DIR + "dontshrink.flags";
private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASSES =
VALID_PROGUARD_DIR + "dontskipnonpubliclibraryclasses.flags";
private static final String DONT_SKIP_NON_PUBLIC_LIBRARY_CLASS_MEMBERS =
@@ -70,12 +70,13 @@ public class ProguardConfigurationParserTest extends TestBase {
VALID_PROGUARD_DIR + "skipnonpubliclibraryclasses.flags";
private static final String PARSE_AND_SKIP_SINGLE_ARGUMENT =
VALID_PROGUARD_DIR + "parse-and-skip-single-argument.flags";
+ private static final String PRINT_USAGE =
+ VALID_PROGUARD_DIR + "printusage.flags";
+ private static final String PRINT_USAGE_TO_FILE =
+ VALID_PROGUARD_DIR + "printusage-to-file.flags";
private static final String TARGET =
VALID_PROGUARD_DIR + "target.flags";
- @Rule
- public ExpectedException thrown = ExpectedException.none();
-
@Test
public void parse() throws IOException, ProguardRuleParserException {
ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -327,6 +328,14 @@ public class ProguardConfigurationParserTest extends TestBase {
}
@Test
+ public void parseDontshrink() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(DONT_SHRINK));
+ ProguardConfiguration config = parser.getConfig();
+ assertFalse(config.isShrinking());
+ }
+
+ @Test
public void parseDontSkipNonPublicLibraryClasses()
throws IOException, ProguardRuleParserException {
ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -355,12 +364,14 @@ public class ProguardConfigurationParserTest extends TestBase {
}
@Test
- public void parseSkipNonPublicLibraryClasses()
- throws IOException, ProguardRuleParserException {
- thrown.expect(ProguardRuleParserException.class);
- thrown.expectMessage("Unsupported option: -skipnonpubliclibraryclasses");
- ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
- parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+ public void parseSkipNonPublicLibraryClasses() throws IOException {
+ try {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(SKIP_NON_PUBLIC_LIBRARY_CLASSES));
+ fail();
+ } catch (ProguardRuleParserException e) {
+ assertTrue(e.getMessage().contains("Unsupported option: -skipnonpubliclibraryclasses"));
+ }
}
@Test
@@ -370,6 +381,24 @@ public class ProguardConfigurationParserTest extends TestBase {
}
@Test
+ public void parsePrintUsage() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(PRINT_USAGE));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.isPrintUsage());
+ assertNull(config.getPrintUsageFile());
+ }
+
+ @Test
+ public void parsePrintUsageToFile() throws IOException, ProguardRuleParserException {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ parser.parse(Paths.get(PRINT_USAGE_TO_FILE));
+ ProguardConfiguration config = parser.getConfig();
+ assertTrue(config.isPrintUsage());
+ assertNotNull(config.getPrintUsageFile());
+ }
+
+ @Test
public void parseTarget()
throws IOException, ProguardRuleParserException {
ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
@@ -378,15 +407,18 @@ public class ProguardConfigurationParserTest extends TestBase {
@Test
public void parseInvalidKeepClassOption() throws IOException, ProguardRuleParserException {
- thrown.expect(ProguardRuleParserException.class);
- thrown.expectMessage("Unknown option at ");
- ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
- Path proguardConfig = writeTextToTempFile(
- "-keepclassx public class * { ",
- " native <methods>; ",
- "} "
- );
- parser.parse(proguardConfig);
+ try {
+ ProguardConfigurationParser parser = new ProguardConfigurationParser(new DexItemFactory());
+ Path proguardConfig = writeTextToTempFile(
+ "-keepclassx public class * { ",
+ " native <methods>; ",
+ "} "
+ );
+ parser.parse(proguardConfig);
+ fail();
+ } catch (ProguardRuleParserException e) {
+ assertTrue(e.getMessage().contains("Unknown option at "));
+ }
}
@Test
diff --git a/src/test/proguard/valid/dontshrink.flags b/src/test/proguard/valid/dontshrink.flags
new file mode 100644
index 000000000..3d7dbaa3c
--- /dev/null
+++ b/src/test/proguard/valid/dontshrink.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-dontshrink
diff --git a/src/test/proguard/valid/printusage-to-file.flags b/src/test/proguard/valid/printusage-to-file.flags
new file mode 100644
index 000000000..c3dcdc5b6
--- /dev/null
+++ b/src/test/proguard/valid/printusage-to-file.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-printusage ~/tmp/random_output_file.txt
diff --git a/src/test/proguard/valid/printusage.flags b/src/test/proguard/valid/printusage.flags
new file mode 100644
index 000000000..2606411da
--- /dev/null
+++ b/src/test/proguard/valid/printusage.flags
@@ -0,0 +1,5 @@
+# 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.
+
+-printusage