Merge commit '89bf9b479b86813e1840fc57f8e3151e104121d2' into 1.7.15-dev
Change-Id: I537c7fbf4310e0d52ebbcd9295055d0451294d0d
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 7ce2685..988bf75 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -797,6 +797,10 @@
return false;
}
+ public boolean definesFinalizer(DexItemFactory factory) {
+ return lookupVirtualMethod(factory.objectMethods.finalize) != null;
+ }
+
public boolean defaultValuesForStaticFieldsMayTriggerAllocation() {
return staticFields().stream()
.anyMatch(field -> field.getStaticValue().mayHaveSideEffects());
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 f60e877..ee50ef0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -302,6 +302,8 @@
public final DexType classType = createType(classDescriptor);
public final DexType classLoaderType = createType(classLoaderDescriptor);
+ public final DexType fieldType = createType(fieldDescriptor);
+ public final DexType methodType = createType(methodDescriptor);
public final DexType autoCloseableType = createType(autoCloseableDescriptor);
public final DexType stringBuilderType = createType(stringBuilderDescriptor);
@@ -660,6 +662,7 @@
public final DexMethod desiredAssertionStatus;
public final DexMethod forName;
+ public final DexMethod forName3;
public final DexMethod getName;
public final DexMethod getCanonicalName;
public final DexMethod getSimpleName;
@@ -676,8 +679,18 @@
private ClassMethods() {
desiredAssertionStatus = createMethod(classDescriptor,
desiredAssertionStatusMethodName, booleanDescriptor, DexString.EMPTY_ARRAY);
- forName = createMethod(classDescriptor,
- forNameMethodName, classDescriptor, new DexString[]{stringDescriptor});
+ forName =
+ createMethod(
+ classDescriptor,
+ forNameMethodName,
+ classDescriptor,
+ new DexString[] {stringDescriptor});
+ forName3 =
+ createMethod(
+ classDescriptor,
+ forNameMethodName,
+ classDescriptor,
+ new DexString[] {stringDescriptor, booleanDescriptor, classLoaderDescriptor});
getName = createMethod(classDescriptor, getNameName, stringDescriptor, DexString.EMPTY_ARRAY);
getCanonicalName = createMethod(
classDescriptor, getCanonicalNameName, stringDescriptor, DexString.EMPTY_ARRAY);
@@ -692,19 +705,23 @@
constructorDescriptor,
new DexString[] {classArrayDescriptor});
getField = createMethod(classDescriptor, getFieldName, fieldDescriptor,
- new DexString[]{stringDescriptor});
+ new DexString[] {stringDescriptor});
getDeclaredField = createMethod(classDescriptor, getDeclaredFieldName, fieldDescriptor,
- new DexString[]{stringDescriptor});
+ new DexString[] {stringDescriptor});
getMethod = createMethod(classDescriptor, getMethodName, methodDescriptor,
- new DexString[]{stringDescriptor, classArrayDescriptor});
+ new DexString[] {stringDescriptor, classArrayDescriptor});
getDeclaredMethod = createMethod(classDescriptor, getDeclaredMethodName, methodDescriptor,
- new DexString[]{stringDescriptor, classArrayDescriptor});
+ new DexString[] {stringDescriptor, classArrayDescriptor});
newInstance =
createMethod(classDescriptor, newInstanceName, objectDescriptor, DexString.EMPTY_ARRAY);
getMembers = ImmutableSet.of(getField, getDeclaredField, getMethod, getDeclaredMethod);
getNames = ImmutableSet.of(getName, getCanonicalName, getSimpleName, getTypeName);
}
+ public boolean isReflectiveClassLookup(DexMethod method) {
+ return method == forName || method == forName3;
+ }
+
public boolean isReflectiveMemberLookup(DexMethod method) {
return getMembers.contains(method);
}
@@ -842,19 +859,19 @@
intFieldUpdaterDescriptor,
newUpdaterName,
intFieldUpdaterDescriptor,
- new DexString[]{classDescriptor, stringDescriptor});
+ new DexString[] {classDescriptor, stringDescriptor});
longUpdater =
createMethod(
longFieldUpdaterDescriptor,
newUpdaterName,
longFieldUpdaterDescriptor,
- new DexString[]{classDescriptor, stringDescriptor});
+ new DexString[] {classDescriptor, stringDescriptor});
referenceUpdater =
createMethod(
referenceFieldUpdaterDescriptor,
newUpdaterName,
referenceFieldUpdaterDescriptor,
- new DexString[]{classDescriptor, classDescriptor, stringDescriptor});
+ new DexString[] {classDescriptor, classDescriptor, stringDescriptor});
updaters = ImmutableSet.of(intUpdater, longUpdater, referenceUpdater);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
index 55dce75..86d04e5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstantValueUtils.java
@@ -28,7 +28,8 @@
if (alias.definition.isInvokeStatic()) {
InvokeStatic invoke = alias.definition.asInvokeStatic();
- if (invoke.getInvokedMethod() == definitions.dexItemFactory().classMethods.forName) {
+ if (definitions.dexItemFactory().classMethods
+ .isReflectiveClassLookup(invoke.getInvokedMethod())) {
return getDexTypeFromClassForName(invoke, definitions);
}
}
@@ -38,8 +39,9 @@
public static DexType getDexTypeFromClassForName(
InvokeStatic invoke, DexDefinitionSupplier definitions) {
- assert invoke.getInvokedMethod() == definitions.dexItemFactory().classMethods.forName;
- if (invoke.arguments().size() == 1) {
+ assert definitions.dexItemFactory().classMethods
+ .isReflectiveClassLookup(invoke.getInvokedMethod());
+ if (invoke.arguments().size() == 1 || invoke.arguments().size() == 3) {
Value argument = invoke.arguments().get(0);
if (argument.isConstString()) {
ConstString constStringInstruction = argument.getConstInstruction().asConstString();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
index fc57fdc..ca84fba 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeInterface.java
@@ -22,6 +22,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
public class InvokeInterface extends InvokeMethodWithReceiver {
@@ -111,11 +112,23 @@
return Collections.singletonList(singleTarget);
}
DexMethod method = getInvokedMethod();
- // TODO(b/141580674): we could filter out some targets based on refined receiver type.
- return appView
- .appInfo()
- .resolveMethodOnInterface(method.holder, method)
- .lookupInterfaceTargets(appView.appInfo());
+ Collection<DexEncodedMethod> targets =
+ appView
+ .appInfo()
+ .resolveMethodOnInterface(method.holder, method)
+ .lookupInterfaceTargets(appView.appInfo());
+ if (targets == null) {
+ return targets;
+ }
+ DexType staticReceiverType = getInvokedMethod().holder;
+ DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView.withLiveness(), this);
+ // Leverage refined receiver type if available.
+ if (refinedReceiverType != staticReceiverType) {
+ return targets.stream()
+ .filter(m -> appView.isSubtype(m.method.holder, refinedReceiverType).isPossiblyTrue())
+ .collect(Collectors.toSet());
+ }
+ return targets;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
index d7585c3..9ecc99d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -25,6 +25,7 @@
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
public class InvokeVirtual extends InvokeMethodWithReceiver {
@@ -114,11 +115,23 @@
return Collections.singletonList(singleTarget);
}
DexMethod method = getInvokedMethod();
- // TODO(b/141580674): we could filter out some targets based on refined receiver type.
- return appView
- .appInfo()
- .resolveMethodOnClass(method.holder, method)
- .lookupVirtualTargets(appView.appInfo());
+ Collection<DexEncodedMethod> targets =
+ appView
+ .appInfo()
+ .resolveMethodOnClass(method.holder, method)
+ .lookupVirtualTargets(appView.appInfo());
+ if (targets == null) {
+ return targets;
+ }
+ DexType staticReceiverType = getInvokedMethod().holder;
+ DexType refinedReceiverType = TypeAnalysis.getRefinedReceiverType(appView.withLiveness(), this);
+ // Leverage refined receiver type if available.
+ if (refinedReceiverType != staticReceiverType) {
+ return targets.stream()
+ .filter(m -> appView.isSubtype(m.method.holder, refinedReceiverType).isPossiblyTrue())
+ .collect(Collectors.toSet());
+ }
+ return targets;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 8cbc644..fb442d1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -203,7 +203,11 @@
usages.add(usage);
}
}
- feedback.setParameterUsages(method, usages.isEmpty() ? null : new ParameterUsagesInfo(usages));
+ feedback.setParameterUsages(
+ method,
+ usages.isEmpty()
+ ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
+ : new ParameterUsagesInfo(usages));
}
private ParameterUsage collectParameterUsages(int i, Value value) {
@@ -372,6 +376,8 @@
// This method defines trivial instance initializer as follows:
//
+ // ** The holder class must not define a finalize method.
+ //
// ** The initializer may call the initializer of the base class, which
// itself must be trivial.
//
@@ -385,6 +391,10 @@
// (Note that this initializer does not have to have zero arguments.)
private TrivialInitializer computeInstanceInitializerInfo(
IRCode code, DexClass clazz, Function<DexType, DexClass> typeToClass) {
+ if (clazz.definesFinalizer(options.itemFactory)) {
+ // Defining a finalize method can observe the side-effect of Object.<init> GC registration.
+ return null;
+ }
Value receiver = code.getThis();
for (Instruction insn : code.instructions()) {
if (insn.isReturn()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
index 7e61600..e41989a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ParameterUsagesInfo.java
@@ -36,15 +36,38 @@
parametersUsages.stream().map(usage -> usage.index).collect(Collectors.toSet()).size();
}
- ParameterUsage getParameterUsage(int parameter) {
+ ParameterUsage getParameterUsage(int index) {
for (ParameterUsage usage : parametersUsages) {
- if (usage.index == parameter) {
+ if (usage.index == index) {
return usage;
}
}
return null;
}
+ ParameterUsagesInfo remove(int index) {
+ assert parametersUsages.size() > 0;
+ assert 0 <= index && index <= ListUtils.last(parametersUsages).index;
+ ImmutableList.Builder<ParameterUsage> builder = ImmutableList.builder();
+ for (ParameterUsage usage : parametersUsages) {
+ // Once we remove or pass the designated index, copy-n-shift the remaining usages.
+ if (index < usage.index) {
+ builder.add(ParameterUsage.copyAndShift(usage, 1));
+ } else if (index == usage.index) {
+ // Do not add the parameter usage with the matched index.
+ } else {
+ // Until we meet the `parameter` of interest, keep copying.
+ assert usage.index < index;
+ builder.add(usage);
+ }
+ }
+ ImmutableList<ParameterUsage> adjustedUsages = builder.build();
+ if (adjustedUsages.isEmpty()) {
+ return DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
+ }
+ return new ParameterUsagesInfo(adjustedUsages);
+ }
+
public final static class ParameterUsage {
public final int index;
@@ -82,6 +105,18 @@
this.isReturned = isReturned;
}
+ static ParameterUsage copyAndShift(ParameterUsage original, int shift) {
+ assert original.index >= shift;
+ return new ParameterUsage(
+ original.index - shift,
+ original.ifZeroTest,
+ original.callsReceiver,
+ original.hasFieldAssignment,
+ original.hasFieldRead,
+ original.isAssignedToField,
+ original.isReturned);
+ }
+
public boolean notUsed() {
return ifZeroTest == null
&& callsReceiver == null
@@ -104,7 +139,7 @@
private boolean isAssignedToField = false;
private boolean isReturned = false;
- public ParameterUsageBuilder(Value arg, int index) {
+ ParameterUsageBuilder(Value arg, int index) {
this.arg = arg;
this.index = index;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 09a8d13..1516f88 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -4,9 +4,6 @@
package com.android.tools.r8.ir.optimize.info;
-import static com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
-import static com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
-
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod.ClassInlinerEligibility;
@@ -40,8 +37,9 @@
private boolean returnsConstantString = DefaultMethodOptimizationInfo.UNKNOWN_RETURNS_CONSTANT;
private DexString returnedConstantString =
DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_CONSTANT_STRING;
- private TypeLatticeElement returnsObjectOfType = UNKNOWN_TYPE;
- private ClassTypeLatticeElement returnsObjectWithLowerBoundType = UNKNOWN_CLASS_TYPE;
+ private TypeLatticeElement returnsObjectOfType = DefaultMethodOptimizationInfo.UNKNOWN_TYPE;
+ private ClassTypeLatticeElement returnsObjectWithLowerBoundType =
+ DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE;
private InlinePreference inlining = InlinePreference.Default;
private boolean useIdentifierNameString =
DefaultMethodOptimizationInfo.DOES_NOT_USE_IDNETIFIER_NAME_STRING;
@@ -84,9 +82,16 @@
// Intentionally left empty, just use the default values.
}
+ // Copy constructor used to create a mutable copy. Do not forget to copy from template when a new
+ // field is added.
private UpdatableMethodOptimizationInfo(UpdatableMethodOptimizationInfo template) {
cannotBeKept = template.cannotBeKept;
+ classInitializerMayBePostponed = template.classInitializerMayBePostponed;
+ hasBeenInlinedIntoSingleCallSite = template.hasBeenInlinedIntoSingleCallSite;
+ initializedClassesOnNormalExit = template.initializedClassesOnNormalExit;
returnedArgument = template.returnedArgument;
+ mayHaveSideEffects = template.mayHaveSideEffects;
+ returnValueOnlyDependsOnArguments = template.returnValueOnlyDependsOnArguments;
neverReturnsNull = template.neverReturnsNull;
neverReturnsNormally = template.neverReturnsNormally;
returnsConstantNumber = template.returnsConstantNumber;
@@ -106,6 +111,7 @@
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
reachabilitySensitive = template.reachabilitySensitive;
+ returnValueHasBeenPropagated = template.returnValueHasBeenPropagated;
}
public void fixupClassTypeReferences(
@@ -375,7 +381,7 @@
// Nullability could be less precise, though. For example, suppose a value is known to be
// non-null after a safe invocation, hence recorded with the non-null variant. If that call is
// inlined and the method is reprocessed, such non-null assumption cannot be made again.
- assert returnsObjectOfType == UNKNOWN_TYPE
+ assert returnsObjectOfType == DefaultMethodOptimizationInfo.UNKNOWN_TYPE
|| type.lessThanOrEqualUpToNullability(returnsObjectOfType, appView)
: "return type changed from " + returnsObjectOfType + " to " + type;
returnsObjectOfType = type;
@@ -385,7 +391,7 @@
assert type != null;
// Currently, we only have a lower bound type when we have _exact_ runtime type information.
// Thus, the type should never become more precise (although the nullability could).
- assert returnsObjectWithLowerBoundType == UNKNOWN_CLASS_TYPE
+ assert returnsObjectWithLowerBoundType == DefaultMethodOptimizationInfo.UNKNOWN_CLASS_TYPE
|| (type.equalUpToNullability(returnsObjectWithLowerBoundType)
&& type.nullability()
.lessThanOrEqual(returnsObjectWithLowerBoundType.nullability()))
@@ -452,8 +458,12 @@
// initializedClassesOnNormalExit: `this` could trigger <clinit> of the previous holder.
initializedClassesOnNormalExit =
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZED_CLASSES_ON_NORMAL_EXIT;
- // TODO(b/142401154): adjustable
- returnedArgument = DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT;
+ // At least, `this` pointer is not used in `returnedArgument`.
+ assert returnedArgument == DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT
+ || returnedArgument > 0;
+ returnedArgument =
+ returnedArgument == DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT
+ ? DefaultMethodOptimizationInfo.UNKNOWN_RETURNED_ARGUMENT : returnedArgument - 1;
// mayHaveSideEffects: `this` Argument didn't have side effects, so removing it doesn't affect
// whether or not the method may have side effects.
// returnValueOnlyDependsOnArguments:
@@ -483,11 +493,19 @@
// initializerEnablingJavaAssertions: `this` could trigger <clinit> of the previous holder.
initializerEnablingJavaAssertions =
DefaultMethodOptimizationInfo.UNKNOWN_INITIALIZER_ENABLING_JAVA_ASSERTIONS;
- // TODO(b/142401154): adjustable
- parametersUsages = DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO;
- nonNullParamOrThrow = DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS;
+ parametersUsages =
+ parametersUsages == DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
+ ? DefaultMethodOptimizationInfo.UNKNOWN_PARAMETER_USAGE_INFO
+ : parametersUsages.remove(0);
+ nonNullParamOrThrow =
+ nonNullParamOrThrow == DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
+ ? DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_OR_THROW_FACTS
+ : nonNullParamOrThrow.get(1, nonNullParamOrThrow.length());
nonNullParamOnNormalExits =
- DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS;
+ nonNullParamOnNormalExits
+ == DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS
+ ? DefaultMethodOptimizationInfo.NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS
+ : nonNullParamOnNormalExits.get(1, nonNullParamOnNormalExits.length());
// reachabilitySensitive: doesn't depend on `this`
// returnValueHasBeenPropagated: doesn't depend on `this`
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 725c68c..75d1037 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -341,7 +341,8 @@
boolean isClassForName = returnType == appView.dexItemFactory().classType;
if (isClassForName) {
- assert invoke.getInvokedMethod() == appView.dexItemFactory().classMethods.forName;
+ assert appView.dexItemFactory().classMethods
+ .isReflectiveClassLookup(invoke.getInvokedMethod());
return 0;
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
index 8cfd094..da38ed4 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringUtils.java
@@ -42,8 +42,20 @@
* @return {@code true} if the given {@param method} is a reflection method in Java.
*/
public static boolean isReflectionMethod(DexItemFactory dexItemFactory, DexMethod method) {
+ // So, why is this simply not like:
+ // return dexItemFactory.classMethods.isReflectiveClassLookup(method)
+ // || dexItemFactory.classMethods.isReflectiveMemberLookup(method)
+ // || dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method);
+ // ?
+ // That is because the counter part of other shrinkers supports users' own reflective methods
+ // whose signature matches with reflection methods in Java. Hence, explicit signature matching.
+ // See tests {@link IdentifierMinifierTest#test2_rule3},
+ // {@link IdentifierNameStringMarkerTest#reflective_field_singleUseOperand_renamed}, or
+ // {@link IdentifierNameStringMarkerTest#reflective_method_singleUseOperand_renamed}.
+ //
// For java.lang.Class:
// (String) -> java.lang.Class | java.lang.reflect.Field
+ // (String, boolean, ClassLoader) -> java.lang.Class
// (String, Class[]) -> java.lang.reflect.Method
// For java.util.concurrent.atomic.Atomic(Integer|Long)FieldUpdater:
// (Class, String) -> $holderType
@@ -53,43 +65,53 @@
// (Class, String) -> java.lang.reflect.Field
// (Class, String, Class[]) -> java.lang.reflect.Method
int arity = method.getArity();
- if (method.holder.descriptor == dexItemFactory.classDescriptor) {
+ if (method.holder == dexItemFactory.classType) {
// Virtual methods of java.lang.Class, such as getField, getMethod, etc.
- if (arity != 1 && arity != 2) {
+ if (arity == 0 || arity > 3) {
return false;
}
if (arity == 1) {
- if (method.proto.returnType.descriptor != dexItemFactory.classDescriptor
- && method.proto.returnType.descriptor != dexItemFactory.fieldDescriptor) {
+ if (method.proto.returnType != dexItemFactory.classType
+ && method.proto.returnType != dexItemFactory.fieldType) {
+ return false;
+ }
+ } else if (arity == 2) {
+ if (method.proto.returnType != dexItemFactory.methodType) {
return false;
}
} else {
- if (method.proto.returnType.descriptor != dexItemFactory.methodDescriptor) {
+ if (method.proto.returnType != dexItemFactory.classType) {
return false;
}
}
- if (method.proto.parameters.values[0].descriptor != dexItemFactory.stringDescriptor) {
+ if (method.proto.parameters.values[0] != dexItemFactory.stringType) {
return false;
}
if (arity == 2) {
- if (method.proto.parameters.values[1].descriptor != dexItemFactory.classArrayDescriptor) {
+ if (method.proto.parameters.values[1] != dexItemFactory.classArrayType) {
+ return false;
+ }
+ }
+ if (arity == 3) {
+ if (method.proto.parameters.values[1] != dexItemFactory.booleanType
+ && method.proto.parameters.values[2] != dexItemFactory.classLoaderType) {
return false;
}
}
} else if (
method.holder.descriptor == dexItemFactory.intFieldUpdaterDescriptor
- || method.holder.descriptor == dexItemFactory.longFieldUpdaterDescriptor) {
+ || method.holder.descriptor == dexItemFactory.longFieldUpdaterDescriptor) {
// Atomic(Integer|Long)FieldUpdater->newUpdater(Class, String)AtomicFieldUpdater
if (arity != 2) {
return false;
}
- if (method.proto.returnType.descriptor != method.holder.descriptor) {
+ if (method.proto.returnType != method.holder) {
return false;
}
- if (method.proto.parameters.values[0].descriptor != dexItemFactory.classDescriptor) {
+ if (method.proto.parameters.values[0] != dexItemFactory.classType) {
return false;
}
- if (method.proto.parameters.values[1].descriptor != dexItemFactory.stringDescriptor) {
+ if (method.proto.parameters.values[1] != dexItemFactory.stringType) {
return false;
}
} else if (method.holder.descriptor == dexItemFactory.referenceFieldUpdaterDescriptor) {
@@ -97,16 +119,16 @@
if (arity != 3) {
return false;
}
- if (method.proto.returnType.descriptor != method.holder.descriptor) {
+ if (method.proto.returnType != method.holder) {
return false;
}
- if (method.proto.parameters.values[0].descriptor != dexItemFactory.classDescriptor) {
+ if (method.proto.parameters.values[0] != dexItemFactory.classType) {
return false;
}
- if (method.proto.parameters.values[1].descriptor != dexItemFactory.classDescriptor) {
+ if (method.proto.parameters.values[1] != dexItemFactory.classType) {
return false;
}
- if (method.proto.parameters.values[2].descriptor != dexItemFactory.stringDescriptor) {
+ if (method.proto.parameters.values[2] != dexItemFactory.stringType) {
return false;
}
} else {
@@ -115,22 +137,22 @@
return false;
}
if (arity == 2) {
- if (method.proto.returnType.descriptor != dexItemFactory.fieldDescriptor) {
+ if (method.proto.returnType != dexItemFactory.fieldType) {
return false;
}
} else {
- if (method.proto.returnType.descriptor != dexItemFactory.methodDescriptor) {
+ if (method.proto.returnType != dexItemFactory.methodType) {
return false;
}
}
- if (method.proto.parameters.values[0].descriptor != dexItemFactory.classDescriptor) {
+ if (method.proto.parameters.values[0] != dexItemFactory.classType) {
return false;
}
- if (method.proto.parameters.values[1].descriptor != dexItemFactory.stringDescriptor) {
+ if (method.proto.parameters.values[1] != dexItemFactory.stringType) {
return false;
}
if (arity == 3) {
- if (method.proto.parameters.values[2].descriptor != dexItemFactory.classArrayDescriptor) {
+ if (method.proto.parameters.values[2] != dexItemFactory.classArrayType) {
return false;
}
}
@@ -143,12 +165,12 @@
* java.lang.String)`, and one of the arguments is defined by an invoke-instruction that calls
* `java.lang.String java.lang.Class.getName()`.
*/
- public static boolean isClassNameComparison(InvokeMethod invoke, DexItemFactory dexItemFactory) {
+ static boolean isClassNameComparison(InvokeMethod invoke, DexItemFactory dexItemFactory) {
return invoke.isInvokeVirtual()
&& isClassNameComparison(invoke.asInvokeVirtual(), dexItemFactory);
}
- public static boolean isClassNameComparison(InvokeVirtual invoke, DexItemFactory dexItemFactory) {
+ static boolean isClassNameComparison(InvokeVirtual invoke, DexItemFactory dexItemFactory) {
return invoke.getInvokedMethod() == dexItemFactory.stringMethods.equals
&& (isClassNameValue(invoke.getReceiver(), dexItemFactory)
|| isClassNameValue(invoke.inValues().get(1), dexItemFactory));
@@ -174,10 +196,12 @@
public static DexReference identifyIdentifier(
InvokeMethod invoke, DexDefinitionSupplier definitions) {
List<Value> ins = invoke.arguments();
- // The only static call: Class#forName, which receives (String) as ins.
+ // The only static calls: Class#forName,
+ // which receive either (String) or (String, boolean, ClassLoader) as ins.
if (invoke.isInvokeStatic()) {
InvokeStatic invokeStatic = invoke.asInvokeStatic();
- if (invokeStatic.getInvokedMethod() == definitions.dexItemFactory().classMethods.forName) {
+ if (definitions.dexItemFactory().classMethods
+ .isReflectiveClassLookup(invokeStatic.getInvokedMethod())) {
return ConstantValueUtils.getDexTypeFromClassForName(invokeStatic, definitions);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
index 3d4d186..d5517f7 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexDefinition;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -439,7 +440,20 @@
DexMethod method,
InternalNamingState internalState,
BiPredicate<DexString, DexMethod> isAvailable) {
- return nextName(method, method.name, method.holder, internalState, isAvailable, super::next);
+ DexEncodedMethod definition = appView.definitionFor(method);
+ DexString nextName =
+ nextName(
+ method,
+ definition,
+ method.name,
+ method.holder,
+ internalState,
+ isAvailable,
+ super::next);
+ assert nextName == method.name || !definition.isClassInitializer();
+ assert nextName == method.name
+ || !appView.definitionFor(method.holder).accessFlags.isAnnotation();
+ return nextName;
}
@Override
@@ -447,22 +461,32 @@
DexField field,
InternalNamingState internalState,
BiPredicate<DexString, DexField> isAvailable) {
- return nextName(field, field.name, field.holder, internalState, isAvailable, super::next);
+ return nextName(
+ field,
+ appView.definitionFor(field),
+ field.name,
+ field.holder,
+ internalState,
+ isAvailable,
+ super::next);
}
private <T extends DexReference> DexString nextName(
T reference,
+ DexDefinition definition,
DexString name,
DexType holderType,
InternalNamingState internalState,
BiPredicate<DexString, T> isAvailable,
TriFunction<T, InternalNamingState, BiPredicate<DexString, T>, DexString> generateName) {
+ assert definition.isDexEncodedMethod() || definition.isDexEncodedField();
+ assert definition.toReference() == reference;
DexClass holder = appView.definitionFor(holderType);
assert holder != null;
- DexString reservedName = getReservedName(reference, name, holder);
+ DexString reservedName = getReservedName(definition, name, holder);
if (reservedName != null) {
if (!isAvailable.test(reservedName, reference)) {
- reportReservationError(reference, reservedName);
+ reportReservationError(definition.asDexReference(), reservedName);
}
return reservedName;
}
@@ -473,26 +497,36 @@
@Override
public DexString getReservedName(DexEncodedMethod method, DexClass holder) {
- return getReservedName(method.method, method.method.name, holder);
+ return getReservedName(method, method.method.name, holder);
}
@Override
public DexString getReservedName(DexEncodedField field, DexClass holder) {
- return getReservedName(field.field, field.field.name, holder);
+ return getReservedName(field, field.field.name, holder);
}
- private DexString getReservedName(DexReference reference, DexString name, DexClass holder) {
+ private DexString getReservedName(DexDefinition definition, DexString name, DexClass holder) {
+ assert definition.isDexEncodedMethod() || definition.isDexEncodedField();
// Always consult the mapping for renamed members that are not on program path.
- if (holder.isNotProgramClass() && mappedNames.containsKey(reference)) {
- return factory.createString(mappedNames.get(reference).getRenamedName());
- }
- if (holder.isProgramClass() && appView.rootSet().mayBeMinified(reference, appView)) {
+ DexReference reference = definition.toReference();
+ if (holder.isNotProgramClass()) {
if (mappedNames.containsKey(reference)) {
return factory.createString(mappedNames.get(reference).getRenamedName());
}
- return null;
+ return name;
}
- return name;
+ assert holder.isProgramClass();
+ DexString reservedName =
+ definition.isDexEncodedMethod()
+ ? super.getReservedName(definition.asDexEncodedMethod(), holder)
+ : super.getReservedName(definition.asDexEncodedField(), holder);
+ if (reservedName != null) {
+ return reservedName;
+ }
+ if (mappedNames.containsKey(reference)) {
+ return factory.createString(mappedNames.get(reference).getRenamedName());
+ }
+ return null;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 5eebf8b..246dfa1 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -434,6 +434,7 @@
DexEncodedMethod defaultInitializer = clazz.getDefaultInitializer();
if (forceProguardCompatibility) {
workList.enqueueMarkMethodKeptAction(
+ clazz,
defaultInitializer,
graphReporter.reportCompatKeepDefaultInitializer(clazz, defaultInitializer));
}
@@ -443,13 +444,23 @@
}
}
} else if (item.isDexEncodedField()) {
- KeepReasonWitness witness =
- graphReporter.reportKeepField(precondition, rules, item.asDexEncodedField());
- workList.enqueueMarkFieldKeptAction(item.asDexEncodedField(), witness);
+ DexEncodedField dexEncodedField = item.asDexEncodedField();
+ DexProgramClass holder = getProgramClassOrNull(dexEncodedField.field.holder);
+ if (holder != null) {
+ workList.enqueueMarkFieldKeptAction(
+ holder,
+ dexEncodedField,
+ graphReporter.reportKeepField(precondition, rules, dexEncodedField));
+ }
} else if (item.isDexEncodedMethod()) {
- KeepReasonWitness witness =
- graphReporter.reportKeepMethod(precondition, rules, item.asDexEncodedMethod());
- workList.enqueueMarkMethodKeptAction(item.asDexEncodedMethod(), witness);
+ DexEncodedMethod encodedMethod = item.asDexEncodedMethod();
+ DexProgramClass holder = getProgramClassOrNull(encodedMethod.method.holder);
+ if (holder != null) {
+ workList.enqueueMarkMethodKeptAction(
+ holder,
+ encodedMethod,
+ graphReporter.reportKeepMethod(precondition, rules, encodedMethod));
+ }
} else {
throw new IllegalArgumentException(item.toString());
}
@@ -627,7 +638,7 @@
boolean registerInvokeStatic(DexMethod method, KeepReason keepReason) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
- if (method == dexItemFactory.classMethods.forName
+ if (dexItemFactory.classMethods.isReflectiveClassLookup(method)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(method)) {
// Implicitly add -identifiernamestring rule for the Java reflection in use.
identifierNameStrings.add(method);
@@ -2077,7 +2088,7 @@
if (valuesMethod != null) {
// TODO(sgjesse): Does this have to be enqueued as a root item? Right now it is done as the
// marking for not renaming it is in the root set.
- workList.enqueueMarkMethodKeptAction(valuesMethod, reason);
+ workList.enqueueMarkMethodKeptAction(clazz, valuesMethod, reason);
pinnedItems.add(valuesMethod.toReference());
rootSet.shouldNotBeMinified(valuesMethod.toReference());
}
@@ -2371,12 +2382,8 @@
}
// Package protected due to entry point from worklist.
- void markMethodAsKept(DexEncodedMethod target, KeepReason reason) {
+ void markMethodAsKept(DexProgramClass holder, DexEncodedMethod target, KeepReason reason) {
DexMethod method = target.method;
- DexProgramClass holder = getProgramClassOrNull(method.holder);
- if (holder == null) {
- return;
- }
if (target.isVirtualMethod()) {
// A virtual method. Mark it as reachable so that subclasses, if instantiated, keep
// their overrides. However, we don't mark it live, as a keep rule might not imply that
@@ -2411,11 +2418,8 @@
}
// Package protected due to entry point from worklist.
- void markFieldAsKept(DexEncodedField target, KeepReason reason) {
- DexProgramClass clazz = getProgramClassOrNull(target.field.holder);
- if (clazz == null) {
- return;
- }
+ void markFieldAsKept(DexProgramClass holder, DexEncodedField target, KeepReason reason) {
+ assert holder.type == target.field.holder;
if (target.accessFlags.isStatic()) {
markStaticFieldAsLive(target, reason);
} else {
@@ -2624,7 +2628,7 @@
if (keepClass) {
markInstantiated(clazz, null, KeepReason.reflectiveUseIn(method));
}
- markFieldAsKept(encodedField, KeepReason.reflectiveUseIn(method));
+ markFieldAsKept(clazz, encodedField, KeepReason.reflectiveUseIn(method));
// Fields accessed by reflection is marked as both read and written.
registerFieldRead(encodedField.field, method);
registerFieldWrite(encodedField.field, method);
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 275e5a7..24f72ab 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
import java.util.ArrayDeque;
import java.util.Queue;
@@ -127,32 +128,38 @@
}
static class MarkMethodKeptAction extends Action {
+ final DexProgramClass holder;
final DexEncodedMethod target;
final KeepReason reason;
- public MarkMethodKeptAction(DexEncodedMethod target, KeepReason reason) {
+ public MarkMethodKeptAction(
+ DexProgramClass holder, DexEncodedMethod target, KeepReason reason) {
+ this.holder = holder;
this.target = target;
this.reason = reason;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markMethodAsKept(target, reason);
+ enqueuer.markMethodAsKept(holder, target, reason);
}
}
static class MarkFieldKeptAction extends Action {
+ final DexProgramClass holder;
final DexEncodedField target;
- final KeepReason reason;
+ final KeepReasonWitness witness;
- public MarkFieldKeptAction(DexEncodedField target, KeepReason reason) {
+ public MarkFieldKeptAction(
+ DexProgramClass holder, DexEncodedField target, KeepReasonWitness witness) {
+ this.holder = holder;
this.target = target;
- this.reason = reason;
+ this.witness = witness;
}
@Override
public void run(Enqueuer enqueuer) {
- enqueuer.markFieldAsKept(target, reason);
+ enqueuer.markFieldAsKept(holder, target, witness);
}
}
@@ -211,13 +218,15 @@
queue.add(new MarkMethodLiveAction(method, reason));
}
- void enqueueMarkMethodKeptAction(DexEncodedMethod method, KeepReason reason) {
- assert method.isProgramMethod(appView);
- queue.add(new MarkMethodKeptAction(method, reason));
+ void enqueueMarkMethodKeptAction(
+ DexProgramClass clazz, DexEncodedMethod method, KeepReason reason) {
+ assert method.method.holder == clazz.type;
+ queue.add(new MarkMethodKeptAction(clazz, method, reason));
}
- void enqueueMarkFieldKeptAction(DexEncodedField field, KeepReason reason) {
+ void enqueueMarkFieldKeptAction(
+ DexProgramClass holder, DexEncodedField field, KeepReasonWitness witness) {
assert field.isProgramField(appView);
- queue.add(new MarkFieldKeptAction(field, reason));
+ queue.add(new MarkFieldKeptAction(holder, field, witness));
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 79f4f6c..f570956 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -17,7 +17,6 @@
}
public static <T> T last(List<T> list) {
- assert list instanceof ArrayList;
return list.get(list.size() - 1);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerInitTest.java b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerInitTest.java
new file mode 100644
index 0000000..3285930
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/VerticalClassMergerInitTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2019, 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.classmerging;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+// This is a reproduction of b/142909854 where we vertically merge a class with a constructor that
+// references a class outside the main-dex collection. We did not inline those, even when
+// force-inlining, so the renamed constructor broke the init chain.
+public class VerticalClassMergerInitTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().build();
+ }
+
+ public VerticalClassMergerInitTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testMergingClassWithConstructorNotInMainDex()
+ throws IOException, CompilationFailedException, ExecutionException {
+ // TODO(b/142909854): Fix test expectation when bug has been resolved.
+ testForR8(parameters.getBackend())
+ .addInnerClasses(VerticalClassMergerInitTest.class)
+ .addKeepMainRule(Main.class)
+ .addMainDexRules("-keep class " + Main.class.getTypeName())
+ .setMinApi(AndroidApiLevel.K_WATCH)
+ .addOptionsModification(
+ options -> {
+ options.forceProguardCompatibility = true;
+ // The initializer is small in this example so only allow FORCE inlining.
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ })
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Base.class), not(isPresent()));
+ assertThat(inspector.clazz(Child.class), isPresent());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatMatches(containsString("initialized"));
+ }
+
+ public static class Base {
+
+ public Base() {
+ System.out.println("Base.init()");
+ new Outside();
+ }
+ }
+
+ private static class Outside {
+ Outside() {
+ System.out.println("Outside.init()");
+ }
+ }
+
+ public static class Child extends Base {
+
+ // We need a static member to force the main-dex tracing to include Child and Base.
+ public static Object foo() {
+ return null;
+ }
+
+ public Child() {
+ System.out.println("Child.init()");
+ }
+ }
+
+ public static class Main {
+
+ {
+ if (Child.foo() == null) {
+ System.out.println("Main.clinit()");
+ }
+ }
+
+ public static void main(String[] args) {
+ new Child();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/AnonymousObjectWithFinalizeTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/AnonymousObjectWithFinalizeTest.java
new file mode 100644
index 0000000..7337693
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/AnonymousObjectWithFinalizeTest.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2019, 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.ir.analysis.sideeffect;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// See b/143189461
+@RunWith(Parameterized.class)
+public class AnonymousObjectWithFinalizeTest extends TestBase {
+
+ final static String EXPECTED = StringUtils.lines(
+ "set up fence",
+ "run gc",
+ "count down fence",
+ "passed fence"
+ );
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+
+ System.out.println("set up fence");
+ final CountDownLatch fence = new CountDownLatch(1);
+ new Object() {
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ System.out.println("count down fence");
+ fence.countDown();
+ } finally {
+ super.finalize();
+ }
+ }
+ };
+ try {
+ System.out.println("run gc");
+ Runtime.getRuntime().gc();
+ Runtime.getRuntime().runFinalization();
+ if (fence.await(10, TimeUnit.SECONDS)) {
+ System.out.println("passed fence");
+ } else {
+ System.out.println("fence await timed out");
+ }
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ }
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ final TestParameters parameters;
+
+ public AnonymousObjectWithFinalizeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters.getRuntime(), parameters.getApiLevel())
+ .addProgramClassesAndInnerClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .addProgramClassesAndInnerClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java
index 7b8b8aa..d2687fd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectNegativeTest.java
@@ -26,7 +26,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -42,7 +42,7 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1", "Sub2")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
index 1890534..0145b82 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeDirectPositiveTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java
index 4db0012..3508b62 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfaceNegativeTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -48,7 +48,7 @@
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1", "Sub2")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
index c720d09..8cca30a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeInterfacePositiveTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -48,7 +48,7 @@
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A:Sub1", "B:Sub2")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java
index 1579439..fb463a0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticNegativeTest.java
@@ -25,7 +25,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -40,7 +40,7 @@
.addInnerClasses(InvokeStaticNegativeTest.class)
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1", "Sub2")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
index 98012dc..47e8c1c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeStaticPositiveTest.java
@@ -26,7 +26,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -42,7 +42,7 @@
.addKeepMainRule(MAIN)
.enableMergeAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Sub1")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java
index b32e117..9da6912 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualNegativeTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A:Sub1", "A:Sub2", "B:Sub1", "B:Sub2")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
index cd4852d..cc0f93d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamictype/InvokeVirtualPositiveTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A:Sub1", "B:Sub1")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java
index 859365b..d84cf8e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java
@@ -26,7 +26,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -42,7 +42,7 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("null", "non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index 1db3da7..ef0c90b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -26,7 +26,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -42,7 +42,7 @@
.addKeepMainRule(MAIN)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
index 9d4e509..7481286 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -48,7 +48,7 @@
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("null", "A")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
index b838d12..613db78 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -26,7 +26,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -46,7 +46,7 @@
// To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
o.enableDevirtualization = false;
})
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceWithRefinedReceiverTest.java
new file mode 100644
index 0000000..601b19b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceWithRefinedReceiverTest.java
@@ -0,0 +1,185 @@
+// Copyright (c) 2019, 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.ir.optimize.callsites.nullability;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeInterfaceWithRefinedReceiverTest extends TestBase {
+ private static final Class<?> MAIN = Main.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public InvokeInterfaceWithRefinedReceiverTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InvokeInterfaceWithRefinedReceiverTest.class)
+ .addKeepMainRule(MAIN)
+ .enableMergeAnnotations()
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .addOptionsModification(o -> {
+ // To prevent invoke-interface from being rewritten to invoke-virtual w/ a single target.
+ o.enableDevirtualization = false;
+ })
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines("null", "C")
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject i = inspector.clazz(I.class);
+ assertThat(i, isPresent());
+
+ ClassSubject b = inspector.clazz(B.class);
+ assertThat(b, isPresent());
+
+ MethodSubject b_m = b.uniqueMethodWithName("m");
+ assertThat(b_m, isPresent());
+ // Can optimize branches since `arg` is definitely null.
+ assertTrue(b_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject bSub = inspector.clazz(BSub.class);
+ assertThat(bSub, isPresent());
+
+ MethodSubject bSub_m = bSub.uniqueMethodWithName("m");
+ assertThat(bSub_m, isPresent());
+ // Can optimize branches since `arg` is definitely null.
+ assertTrue(bSub_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject c = inspector.clazz(C.class);
+ assertThat(c, isPresent());
+
+ MethodSubject c_m = c.uniqueMethodWithName("m");
+ assertThat(c_m, isPresent());
+ // Can optimize branches since `arg` is definitely not null.
+ assertTrue(c_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject cSub = inspector.clazz(CSub.class);
+ assertThat(cSub, not(isPresent()));
+ }
+
+ interface I {
+ void m(Object arg);
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ static class B implements I {
+ @NeverInline
+ @Override
+ public void m(Object arg) {
+ // Technically same as String#valueOf.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+
+ @NeverClassInline
+ static class BSub extends B {
+ @NeverInline
+ @Override
+ public void m(Object arg) {
+ // Same as B#m.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "BSub";
+ }
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ static class C implements I {
+ @NeverInline
+ @Override
+ public void m(Object arg) {
+ // Technically same as String#valueOf.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ static class CSub extends C {
+ @NeverInline
+ @Override
+ public void m(Object arg) {
+ // Same as C#m.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "CSub";
+ }
+ }
+
+ static class Main {
+ public static void main(String... args) {
+ I i = System.currentTimeMillis() > 0 ? new B() : new BSub();
+ i.m(null); // No single target, but should be able to filter out C(Sub)#m
+
+ I c = new C(); // with the exact type:
+ c.m(c); // calls C.m() with non-null instance.
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java
index 5d10e42..f796efe 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java
@@ -25,7 +25,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -40,7 +40,7 @@
.addInnerClasses(InvokeStaticNegativeTest.class)
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("null", "non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index f1081fc..401855c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -25,7 +25,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -40,7 +40,7 @@
.addInnerClasses(InvokeStaticPositiveTest.class)
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java
index d313c03..c9b215a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A", "A", "B", "B")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
index 45976a0..93588d4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("null", "A", "null", "B")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
index 70aa862..f8ad42c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.enableMergeAnnotations()
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("A", "null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualWithRefinedReceiverTest.java
new file mode 100644
index 0000000..a9a62d9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualWithRefinedReceiverTest.java
@@ -0,0 +1,181 @@
+// Copyright (c) 2019, 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.ir.optimize.callsites.nullability;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InvokeVirtualWithRefinedReceiverTest extends TestBase {
+ private static final Class<?> MAIN = Main.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public InvokeVirtualWithRefinedReceiverTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(InvokeVirtualWithRefinedReceiverTest.class)
+ .addKeepMainRule(MAIN)
+ .enableMergeAnnotations()
+ .enableClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines("null", "C")
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject a = inspector.clazz(A.class);
+ assertThat(a, isPresent());
+
+ ClassSubject b = inspector.clazz(B.class);
+ assertThat(b, isPresent());
+
+ MethodSubject b_m = b.uniqueMethodWithName("m");
+ assertThat(b_m, isPresent());
+ // Can optimize branches since `arg` is definitely null.
+ assertTrue(b_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject bSub = inspector.clazz(BSub.class);
+ assertThat(bSub, isPresent());
+
+ MethodSubject bSub_m = bSub.uniqueMethodWithName("m");
+ assertThat(bSub_m, isPresent());
+ // Can optimize branches since `arg` is definitely null.
+ assertTrue(bSub_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject c = inspector.clazz(C.class);
+ assertThat(c, isPresent());
+
+ MethodSubject c_m = c.uniqueMethodWithName("m");
+ assertThat(c_m, isPresent());
+ // Can optimize branches since `arg` is definitely not null.
+ assertTrue(c_m.streamInstructions().noneMatch(InstructionSubject::isIf));
+
+ ClassSubject cSub = inspector.clazz(CSub.class);
+ assertThat(cSub, not(isPresent()));
+ }
+
+ abstract static class A {
+ abstract void m(Object arg);
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ static class B extends A {
+ @NeverInline
+ @Override
+ void m(Object arg) {
+ // Technically same as String#valueOf.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "B";
+ }
+ }
+
+ @NeverClassInline
+ static class BSub extends B {
+ @NeverInline
+ @Override
+ void m(Object arg) {
+ // Same as B#m.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "BSub";
+ }
+ }
+
+ @NeverMerge
+ @NeverClassInline
+ static class C extends A {
+ @NeverInline
+ @Override
+ void m(Object arg) {
+ // Technically same as String#valueOf.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "C";
+ }
+ }
+
+ @NeverClassInline
+ static class CSub extends C {
+ @NeverInline
+ @Override
+ void m(Object arg) {
+ // Same as C#m.
+ if (arg != null) {
+ System.out.println(arg.toString());
+ } else {
+ System.out.println("null");
+ }
+ }
+
+ @NeverInline
+ @Override
+ public String toString() {
+ return "CSub";
+ }
+ }
+
+ static class Main {
+ public static void main(String... args) {
+ A a = System.currentTimeMillis() > 0 ? new B() : new BSub();
+ a.m(null); // No single target, but should be able to filter out C(Sub)#m
+
+ A c = new C(); // with the exact type:
+ c.m(c); // calls C.m() with non-null instance.
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/KeptMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/KeptMethodTest.java
index d77c180..d5b13af 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/KeptMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/KeptMethodTest.java
@@ -27,7 +27,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.addKeepClassAndMembersRules(A.class)
.enableClassInliningAnnotations()
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("non-null", "non-null")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
index d55484d..e600ebc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/WithStaticizerTest.java
@@ -28,7 +28,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
// TODO(b/112831361): support for class staticizer in CF backend.
- return getTestParameters().withDexRuntimes().build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
private final TestParameters parameters;
@@ -44,7 +44,7 @@
.addKeepMainRule(MAIN)
.enableInliningAnnotations()
.enableClassInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutputLines("Input")
.inspect(this::inspect);
diff --git a/src/test/java/com/android/tools/r8/naming/ForNameRenamingTest.java b/src/test/java/com/android/tools/r8/naming/ForNameRenamingTest.java
new file mode 100644
index 0000000..1d776b5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/ForNameRenamingTest.java
@@ -0,0 +1,79 @@
+// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForNameRenamingTest extends TestBase {
+ private static final Class<?> MAIN = Main.class;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private final TestParameters parameters;
+
+ public ForNameRenamingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ForNameRenamingTest.class)
+ .addKeepMainRule(MAIN)
+ .addKeepClassRulesWithAllowObfuscation(Boo.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutputLines("true")
+ .inspect(this::inspect);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject boo = inspector.clazz(Boo.class);
+ assertThat(boo, isPresent());
+ assertThat(boo, isRenamed());
+
+ ClassSubject main = inspector.clazz(MAIN);
+ assertThat(main, isPresent());
+
+ MethodSubject mainMethod = main.mainMethod();
+ assertThat(mainMethod, isPresent());
+
+ assertTrue(
+ mainMethod.streamInstructions()
+ .noneMatch(i -> i.isConstString(
+ "com.android.tools.r8.naming.ForNameRenamingTest$Boo", JumboStringMode.ALLOW)));
+ assertTrue(
+ mainMethod.streamInstructions()
+ .anyMatch(i -> i.isConstString(boo.getFinalName(), JumboStringMode.ALLOW)));
+ }
+
+ static class Main {
+ public static void main(String... args) throws Exception {
+ Class<?> a = Class.forName("com.android.tools.r8.naming.ForNameRenamingTest$Boo");
+ Class<?> b = Class.forName(
+ "com.android.tools.r8.naming.ForNameRenamingTest$Boo", true, Boo.class.getClassLoader());
+ System.out.println(a.getSimpleName().equals(b.getSimpleName()));
+ }
+ }
+
+ static class Boo {}
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index 2cd766b..e9383ef 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -5,6 +5,8 @@
import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType;
import static com.android.tools.r8.utils.DescriptorUtils.isValidJavaType;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -18,13 +20,10 @@
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
-import com.google.common.collect.Streams;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
@@ -129,19 +128,17 @@
private static void test1_rules(
CodeInspector inspector, int countInMain, int countInABar, int countInAFields) {
ClassSubject mainClass = inspector.clazz("adaptclassstrings.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
- int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
+ int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, main);
assertEquals(countInMain, renamedYetFoundIdentifierCount);
ClassSubject aClass = inspector.clazz("adaptclassstrings.A");
- MethodSubject bar = aClass.method("void", "bar", ImmutableList.of());
- assertTrue(bar instanceof FoundMethodSubject);
- FoundMethodSubject foundBar = (FoundMethodSubject) bar;
- verifyPresenceOfConstString(foundBar);
- renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundBar);
+ MethodSubject bar = aClass.uniqueMethodWithName("bar");
+ assertThat(bar, isPresent());
+ verifyPresenceOfConstString(bar);
+ renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, bar);
assertEquals(countInABar, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
@@ -151,64 +148,57 @@
private static void test_atomicfieldupdater(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("atomicfieldupdater.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
ClassSubject a = inspector.clazz("atomicfieldupdater.A");
Set<InstructionSubject> constStringInstructions =
- getRenamedMemberIdentifierConstStrings(a, foundMain);
+ getRenamedMemberIdentifierConstStrings(a, main);
assertEquals(3, constStringInstructions.size());
}
private static void test_forname(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("forname.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
- int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
+ int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, main);
assertEquals(1, renamedYetFoundIdentifierCount);
}
private static void test_getmembers(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("getmembers.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
ClassSubject a = inspector.clazz("getmembers.A");
Set<InstructionSubject> constStringInstructions =
- getRenamedMemberIdentifierConstStrings(a, foundMain);
+ getRenamedMemberIdentifierConstStrings(a, main);
assertEquals(2, constStringInstructions.size());
ClassSubject b = inspector.clazz("getmembers.B");
- MethodSubject inliner = b.method("java.lang.String", "inliner", ImmutableList.of());
- assertTrue(inliner instanceof FoundMethodSubject);
- FoundMethodSubject foundInliner = (FoundMethodSubject) inliner;
- constStringInstructions = getRenamedMemberIdentifierConstStrings(a, foundInliner);
+ MethodSubject inliner = b.uniqueMethodWithName("inliner");
+ assertThat(inliner, isPresent());
+ constStringInstructions = getRenamedMemberIdentifierConstStrings(a, inliner);
assertEquals(1, constStringInstructions.size());
}
// Without -identifiernamestring
private static void test2_rule1(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("identifiernamestring.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
- int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
+ int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, main);
assertEquals(1, renamedYetFoundIdentifierCount);
ClassSubject aClass = inspector.clazz("identifiernamestring.A");
- MethodSubject aInit =
- aClass.method("void", "<init>", ImmutableList.of());
- assertTrue(aInit instanceof FoundMethodSubject);
- FoundMethodSubject foundAInit = (FoundMethodSubject) aInit;
- verifyPresenceOfConstString(foundAInit);
- renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundAInit);
+ MethodSubject aInit = aClass.init();
+ assertThat(aInit, isPresent());
+ verifyPresenceOfConstString(aInit);
+ renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, aInit);
assertEquals(0, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
@@ -219,20 +209,17 @@
// With -identifiernamestring for annotations and name-based filters
private static void test2_rule2(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("identifiernamestring.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
- int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
+ int renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, main);
assertEquals(parameters.isCfRuntime() ? 2 : 1, renamedYetFoundIdentifierCount);
ClassSubject aClass = inspector.clazz("identifiernamestring.A");
- MethodSubject aInit =
- aClass.method("void", "<init>", ImmutableList.of());
- assertTrue(aInit instanceof FoundMethodSubject);
- FoundMethodSubject foundAInit = (FoundMethodSubject) aInit;
- verifyPresenceOfConstString(foundAInit);
- renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, foundAInit);
+ MethodSubject aInit = aClass.init();
+ assertThat(aInit, isPresent());
+ verifyPresenceOfConstString(aInit);
+ renamedYetFoundIdentifierCount = countRenamedClassIdentifier(inspector, aInit);
assertEquals(1, renamedYetFoundIdentifierCount);
renamedYetFoundIdentifierCount =
@@ -243,31 +230,30 @@
// With -identifiernamestring for reflective methods in testing class R.
private static void test2_rule3(TestParameters parameters, CodeInspector inspector) {
ClassSubject mainClass = inspector.clazz("identifiernamestring.Main");
- MethodSubject main = mainClass.method(CodeInspector.MAIN);
- assertTrue(main instanceof FoundMethodSubject);
- FoundMethodSubject foundMain = (FoundMethodSubject) main;
- verifyPresenceOfConstString(foundMain);
+ MethodSubject main = mainClass.mainMethod();
+ assertThat(main, isPresent());
+ verifyPresenceOfConstString(main);
ClassSubject b = inspector.clazz("identifiernamestring.B");
Set<InstructionSubject> constStringInstructions =
- getRenamedMemberIdentifierConstStrings(b, foundMain);
+ getRenamedMemberIdentifierConstStrings(b, main);
assertEquals(2, constStringInstructions.size());
}
- private static void verifyPresenceOfConstString(FoundMethodSubject method) {
+ private static void verifyPresenceOfConstString(MethodSubject method) {
assertTrue(
method
.iterateInstructions(instruction -> instruction.isConstString(JumboStringMode.ALLOW))
.hasNext());
}
- private static Stream<InstructionSubject> getConstStringInstructions(FoundMethodSubject method) {
- return Streams.stream(method.iterateInstructions())
+ private static Stream<InstructionSubject> getConstStringInstructions(MethodSubject method) {
+ return method.streamInstructions()
.filter(instr -> instr.isConstString(JumboStringMode.ALLOW));
}
private static int countRenamedClassIdentifier(
- CodeInspector inspector, FoundMethodSubject method) {
+ CodeInspector inspector, MethodSubject method) {
return getConstStringInstructions(method)
.reduce(
0,
@@ -307,7 +293,7 @@
}
private static Set<InstructionSubject> getRenamedMemberIdentifierConstStrings(
- ClassSubject clazz, FoundMethodSubject method) {
+ ClassSubject clazz, MethodSubject method) {
Set<InstructionSubject> result = Sets.newIdentityHashSet();
getConstStringInstructions(method)
.forEach(
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
index cb4bed0..6887f59 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceClInitTest.java
@@ -40,7 +40,6 @@
}
@Test
- @Ignore("b/142909857")
public void testNotRenamingClInitIfNotInMap()
throws ExecutionException, CompilationFailedException, IOException {
testForR8(parameters.getBackend())
@@ -54,7 +53,6 @@
}
@Test
- @Ignore("b/142909857")
public void testNotRenamingClInitIfInMap()
throws ExecutionException, CompilationFailedException, IOException {
String interfaceName = TestInterface.class.getTypeName();
diff --git a/tools/historic_memory_usage.py b/tools/historic_memory_usage.py
index 376426f..bb08a2e 100755
--- a/tools/historic_memory_usage.py
+++ b/tools/historic_memory_usage.py
@@ -10,13 +10,13 @@
# It will then run the oldest and newest such commit, and gradually fill in
# the commits in between.
+import historic_run
import optparse
import os
import subprocess
import sys
import utils
-MASTER_COMMITS = 'gs://r8-releases/raw/master'
APPS = ['gmscore', 'nest', 'youtube', 'gmail', 'chrome']
COMPILERS = ['d8', 'r8']
@@ -31,7 +31,7 @@
default='gmail',
choices=APPS)
result.add_option('--top',
- default=utils.get_HEAD_sha1(),
+ default=historic_run.top_or_default(),
help='The most recent commit to test')
result.add_option('--bottom',
help='The oldest commit to test')
@@ -44,84 +44,8 @@
help='Set timeout instead of waiting for OOM.')
return result.parse_args(argv)
-
-class GitCommit(object):
- def __init__(self, git_hash, destination_dir, destination, timestamp):
- self.git_hash = git_hash
- self.destination_dir = destination_dir
- self.destination = destination
- self.timestamp = timestamp
-
- def __str__(self):
- return '%s : %s (%s)' % (self.git_hash, self.destination, self.timestamp)
-
- def __repr__(self):
- return self.__str__()
-
-def git_commit_from_hash(hash):
- commit_timestamp = subprocess.check_output(['git', 'show', '--no-patch',
- '--no-notes', '--pretty=\'%ct\'',
- hash]).strip().strip('\'')
- destination_dir = '%s/%s/' % (MASTER_COMMITS, hash)
- destination = '%s%s' % (destination_dir, 'r8.jar')
- commit = GitCommit(hash, destination_dir, destination, commit_timestamp)
- return commit
-
-def enumerate_git_commits(options):
- top = options.top if options.top else utils.get_HEAD_sha1()
- # TODO(ricow): if not set, search back 1000
- if not options.bottom:
- raise Exception('No bottom specified')
- bottom = options.bottom
- output = subprocess.check_output(['git', 'rev-list', '--first-parent', top])
- found_bottom = False
- commits = []
- for c in output.splitlines():
- commits.append(git_commit_from_hash(c.strip()))
- if c.strip() == bottom:
- found_bottom = True
- break
- if not found_bottom:
- raise Exception('Bottom not found, did you not use a merge commit')
- return commits
-
-def get_available_commits(commits):
- cloud_commits = subprocess.check_output(['gsutil.py', 'ls', MASTER_COMMITS]).splitlines()
- available_commits = []
- for commit in commits:
- if commit.destination_dir in cloud_commits:
- available_commits.append(commit)
- return available_commits
-
-def print_commits(commits):
- for commit in commits:
- print(commit)
-
-def permutate_range(start, end):
- diff = end - start
- assert diff >= 0
- if diff == 1:
- return [start, end]
- if diff == 0:
- return [start]
- half = end - (diff / 2)
- numbers = [half]
- first_half = permutate_range(start, half - 1)
- second_half = permutate_range(half + 1, end)
- for index in range(len(first_half)):
- numbers.append(first_half[index])
- if index < len(second_half):
- numbers.append(second_half[index])
- return numbers
-
-def permutate(number_of_commits):
- assert number_of_commits > 0
- numbers = permutate_range(0, number_of_commits - 1)
- assert all(n in numbers for n in range(number_of_commits))
- return numbers
-
-def pull_r8_from_cloud(commit):
- utils.download_file_from_cloud_storage(commit.destination, utils.R8_JAR)
+def make_run_on_app_command(options):
+ return lambda commit: run_on_app(options, commit)
def run_on_app(options, commit):
app = options.app
@@ -142,30 +66,14 @@
f.write(stdout)
print('Wrote stdout to: %s' % stdout_path)
-
-def benchmark(commits, options):
- commit_permutations = permutate(len(commits))
- count = 0
- for index in commit_permutations:
- count += 1
- print('Running commit %s out of %s' % (count, len(commits)))
- commit = commits[index]
- if not utils.cloud_storage_exists(commit.destination):
- # We may have a directory, but no r8.jar
- continue
- pull_r8_from_cloud(commit)
- print('Running for commit: %s' % commit.git_hash)
- run_on_app(options, commit)
-
def main(argv):
(options, args) = ParseOptions(argv)
if not options.app:
raise Exception('Please specify an app')
- commits = enumerate_git_commits(options)
- available_commits = get_available_commits(commits)
- print('Running for:')
- print_commits(available_commits)
- benchmark(available_commits, options)
+ top = historic_run.top_or_default(options.top)
+ bottom = historic_run.bottom_or_default(options.bottom)
+ command = make_run_on_app_command(options)
+ historic_run.run(command, top, bottom)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))
diff --git a/tools/historic_run.py b/tools/historic_run.py
new file mode 100755
index 0000000..b0f181f
--- /dev/null
+++ b/tools/historic_run.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, 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.
+
+# Convenience script for running a command over builds back in time. This
+# utilizes the prebuilt full r8 jars on cloud storage. The script find all
+# commits that exists on cloud storage in the given range. It will then run the
+# oldest and newest such commit, and gradually fill in the commits in between.
+
+import optparse
+import os
+import subprocess
+import sys
+import time
+import utils
+
+MASTER_COMMITS = 'gs://r8-releases/raw/master'
+
+def ParseOptions(argv):
+ result = optparse.OptionParser()
+ result.add_option(
+ '--cmd',
+ help='Command to run')
+ result.add_option(
+ '--top',
+ default=top_or_default(),
+ help='The most recent commit to test')
+ result.add_option(
+ '--bottom',
+ help='The oldest commit to test')
+ result.add_option(
+ '--dry-run',
+ help='Do not download or run the command, but print the actions',
+ default=False,
+ action='store_true')
+ result.add_option(
+ '--output',
+ default='build',
+ help='Directory where to output results')
+ return result.parse_args(argv)
+
+
+class GitCommit(object):
+ def __init__(self, git_hash, destination_dir, destination, timestamp):
+ self.git_hash = git_hash
+ self.destination_dir = destination_dir
+ self.destination = destination
+ self.timestamp = timestamp
+
+ def __str__(self):
+ return '%s : %s (%s)' % (self.git_hash, self.destination, self.timestamp)
+
+ def __repr__(self):
+ return self.__str__()
+
+def git_commit_from_hash(hash):
+ commit_timestamp = subprocess.check_output(['git', 'show', '--no-patch',
+ '--no-notes', '--pretty=\'%ct\'',
+ hash]).strip().strip('\'')
+ destination_dir = '%s/%s/' % (MASTER_COMMITS, hash)
+ destination = '%s%s' % (destination_dir, 'r8.jar')
+ commit = GitCommit(hash, destination_dir, destination, commit_timestamp)
+ return commit
+
+def enumerate_git_commits(top, bottom):
+ output = subprocess.check_output(['git', 'rev-list', '--first-parent', top])
+ found_bottom = False
+ commits = []
+ for c in output.splitlines():
+ commit_hash = c.strip()
+ commits.append(git_commit_from_hash(commit_hash))
+ if commit_hash == bottom:
+ found_bottom = True
+ break
+ if not found_bottom:
+ raise Exception('Bottom not found, did you not use a merge commit')
+ return commits
+
+def get_available_commits(commits):
+ cloud_commits = subprocess.check_output(
+ ['gsutil.py', 'ls', MASTER_COMMITS]).splitlines()
+ available_commits = []
+ for commit in commits:
+ if commit.destination_dir in cloud_commits:
+ available_commits.append(commit)
+ return available_commits
+
+def print_commits(commits):
+ for commit in commits:
+ print(commit)
+
+def permutate_range(start, end):
+ diff = end - start
+ assert diff >= 0
+ if diff == 1:
+ return [start, end]
+ if diff == 0:
+ return [start]
+ half = end - (diff / 2)
+ numbers = [half]
+ first_half = permutate_range(start, half - 1)
+ second_half = permutate_range(half + 1, end)
+ for index in range(len(first_half)):
+ numbers.append(first_half[index])
+ if index < len(second_half):
+ numbers.append(second_half[index])
+ return numbers
+
+def permutate(number_of_commits):
+ assert number_of_commits > 0
+ numbers = permutate_range(0, number_of_commits - 1)
+ assert all(n in numbers for n in range(number_of_commits))
+ return numbers
+
+def pull_r8_from_cloud(commit):
+ utils.download_file_from_cloud_storage(commit.destination, utils.R8_JAR)
+
+def benchmark(commits, command, dryrun=False):
+ commit_permutations = permutate(len(commits))
+ count = 0
+ for index in commit_permutations:
+ count += 1
+ print('Running commit %s out of %s' % (count, len(commits)))
+ commit = commits[index]
+ if not utils.cloud_storage_exists(commit.destination):
+ # We may have a directory, but no r8.jar
+ continue
+ if not dryrun:
+ pull_r8_from_cloud(commit)
+ print('Running for commit: %s' % commit.git_hash)
+ command(commit)
+
+def top_or_default(top=None):
+ return top if top else utils.get_HEAD_sha1()
+
+def bottom_or_default(bottom=None):
+ # TODO(ricow): if not set, search back 1000
+ if not bottom:
+ raise Exception('No bottom specified')
+ return bottom
+
+def run(command, top, bottom, dryrun=False):
+ commits = enumerate_git_commits(top, bottom)
+ available_commits = get_available_commits(commits)
+ print('Running for:')
+ print_commits(available_commits)
+ benchmark(available_commits, command, dryrun=dryrun)
+
+def make_cmd(options):
+ return lambda commit: run_cmd(options, commit)
+
+def run_cmd(options, commit):
+ cmd = [options.cmd, commit.git_hash]
+ output_path = options.output or 'build'
+ time_commit = '%s_%s' % (commit.timestamp, commit.git_hash)
+ time_commit_path = os.path.join(output_path, time_commit)
+ print ' '.join(cmd)
+ if not options.dry_run:
+ if not os.path.exists(time_commit_path):
+ os.makedirs(time_commit_path)
+ stdout_path = os.path.join(time_commit_path, 'stdout')
+ stderr_path = os.path.join(time_commit_path, 'stderr')
+ with open(stdout_path, 'w') as stdout:
+ with open(stderr_path, 'w') as stderr:
+ process = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
+ timeout = 1000
+ while process.poll() is None and timeout > 0:
+ time.sleep(1)
+ timeout -= 1
+ if process.poll() is None:
+ process.kill()
+ print "Task timed out"
+ stderr.write("timeout\n")
+ print('Wrote outputs to: %s' % time_commit_path)
+
+def main(argv):
+ (options, args) = ParseOptions(argv)
+ if not options.cmd:
+ raise Exception('Please specify a command')
+ top = top_or_default(options.top)
+ bottom = bottom_or_default(options.bottom)
+ command = make_cmd(options)
+ run(command, top, bottom, dryrun=options.dry_run)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/internal_test.py b/tools/internal_test.py
index c85942b..d761a98 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -36,6 +36,7 @@
import sys
import time
import utils
+import run_on_app
# How often the bot/tester should check state
PULL_DELAY = 30
@@ -265,6 +266,8 @@
gs_location = '%s%s' % (entry, to_print)
value = utils.cat_file_on_cloud_storage(gs_location)
print('\n\n%s had value:\n%s' % (to_print, value))
+ print("\n\nPrinting find-min-xmx ranges for apps")
+ run_on_app.print_min_xmx_ranges_for_hash(hash, 'r8', 'lib')
def run_bot():
print_magic_file_state()
diff --git a/tools/minify_tool.py b/tools/minify_tool.py
index 8b086e5..cdbab78 100755
--- a/tools/minify_tool.py
+++ b/tools/minify_tool.py
@@ -87,7 +87,7 @@
def minify_tool(mainclass=None, input_jar=utils.R8_JAR, output_jar=None,
lib=utils.RT_JAR, debug=True, build=True, benchmark_name=None,
- track_memory_file=None):
+ track_memory_file=None, additional_args=[]):
if output_jar is None:
output_jar = generate_output_name(input_jar, mainclass)
with utils.TempDir() as path:
@@ -100,12 +100,12 @@
keep_path = os.path.join(path, 'keep.txt')
with open(keep_path, 'w') as fp:
fp.write(KEEP % mainclass)
- args = ('--lib', lib,
+ args = ['--lib', lib,
'--classfile',
'--output', output_jar,
'--pg-conf', keep_path,
'--release',
- tmp_input_path)
+ tmp_input_path] + additional_args
start_time = time.time()
return_code = toolhelper.run('r8', args, debug=debug, build=build,
track_memory_file=track_memory_file)
diff --git a/tools/r8_release.py b/tools/r8_release.py
index 4d9273c..1d3cfbb 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -23,7 +23,7 @@
with utils.ChangedWorkingDirectory(path):
subprocess.call(['repo', 'abandon', 'update-r8'])
if not options.no_sync:
- subprocess.check_call(['repo', 'sync'])
+ subprocess.check_call(['repo', 'sync', '-cq', '-j', '16'])
prebuilts_r8 = os.path.join(path, 'prebuilts', 'r8')
diff --git a/tools/run_bootstrap_benchmark.py b/tools/run_bootstrap_benchmark.py
index 90834f8..e12051b 100755
--- a/tools/run_bootstrap_benchmark.py
+++ b/tools/run_bootstrap_benchmark.py
@@ -66,4 +66,15 @@
utils.PINNED_PGR8_JAR)
print "BootstrapR8PGDex(CodeSize):", utils.uncompressed_size(d8_pg_output)
+ r8_notreeshaking_output = os.path.join(temp, 'r8-notreeshaking.zip')
+ return_code = minify_tool.minify_tool(
+ input_jar=utils.PINNED_R8_JAR,
+ output_jar=r8_notreeshaking_output,
+ debug=False,
+ build=False,
+ benchmark_name="BootstrapR8NoTreeShaking",
+ additional_args=["--no-tree-shaking"])
+ if return_code != 0:
+ sys.exit(return_code)
+
sys.exit(0)
diff --git a/tools/run_on_app.py b/tools/run_on_app.py
index b32581b..426fef4 100755
--- a/tools/run_on_app.py
+++ b/tools/run_on_app.py
@@ -272,6 +272,21 @@
return 0
+def print_min_xmx_ranges_for_hash(hash, compiler, compiler_build):
+ app_directory = os.path.join(
+ utils.R8_TEST_RESULTS_BUCKET,
+ FIND_MIN_XMX_DIR,
+ hash,
+ compiler,
+ compiler_build)
+ gs_base = 'gs://%s' % app_directory
+ for app in utils.ls_files_on_cloud_storage(gs_base).strip().split('\n'):
+ for version in utils.ls_files_on_cloud_storage(app).strip().split('\n'):
+ for type in utils.ls_files_on_cloud_storage(version).strip().split('\n'):
+ gs_location = '%s%s' % (type, FIND_MIN_XMX_FILE)
+ value = utils.cat_file_on_cloud_storage(gs_location, ignore_errors=True)
+ print('%s\n' % value)
+
def main(argv):
(options, args) = ParseOptions(argv)
if options.expect_oom and not options.max_memory: