diff options
author | Ivan Gavrilovic <gavra@google.com> | 2017-07-13 20:19:57 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-07-13 20:19:57 +0000 |
commit | 3b76a16fd242043b68e6659e605c3ed741e73eb2 (patch) | |
tree | 65af4d76ce0f547cffd9d0e415fe1fedd2bb7337 | |
parent | 47bd2e1de3296f7a86f27f5c781c8102f40984c6 (diff) | |
parent | 4e98faf4922a5465cc86fb89606a3c2b1fe7d2d2 (diff) | |
download | r8-3b76a16fd242043b68e6659e605c3ed741e73eb2.tar.gz |
Merge remote-tracking branch 'aosp/upstream-mirror' into master am: e60b2c831a am: 2a277aff30 am: fc5cad1237
am: 4e98faf492
Change-Id: I4ed30934ecab49844db8c5dde385e4036ef86640
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 |