Fix keep rule debugging support.
When debugging keep rules, we used to keep all classes and methods. This
blows up the dex to a size that no longer runs on devices. With this change
we only keep default constructors, to handle the use of newInstance without
corresponding keep rule.
R=ager@google.com
Bug:
Change-Id: I3bca90c14ede030d03b4b0ae9e4c84dcdbbb41a8
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 3df2fab..c8fbbea 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.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.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 @@
* 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 @@
.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 984215b..5dff4b4 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 @@
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 @@
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/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index 4d3d8cb..234773f 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 @@
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/ir/desugar/LambdaMainMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaMainMethodSourceCode.java
index 2355e5e..9ec776f 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 @@
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 10908f0..7d3bc33 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 @@
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 @@
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 @@
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/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 04f6786..3968c9a 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 @@
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 @@
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 @@
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 @@
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]