Staticize methods that don't use receiver
Change-Id: I935446199fa61385bf801e8dd0bd9bb77a44f7d9
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 1977132..1f58402 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1089,9 +1089,6 @@
public DexEncodedMethod toTypeSubstitutedMethod(DexMethod method, Consumer<Builder> consumer) {
checkIfObsolete();
- if (this.getReference() == method) {
- return this;
- }
Builder builder = builder(this);
if (isNonPrivateVirtualMethod() && isLibraryMethodOverride() != OptionalBool.unknown()) {
builder.setIsLibraryMethodOverride(isLibraryMethodOverride());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index 66747a7..d0cfd52 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -295,9 +295,9 @@
return;
}
- assert definition.isInvokeMethodWithReceiver()
+ assert definition.isInvokeMethod()
&& references.isFindLiteExtensionByNumberMethod(
- definition.asInvokeMethodWithReceiver().getInvokedMethod());
+ definition.asInvokeMethod().getInvokedMethod());
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java
index 47549e9..9fd0c74 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Position.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.util.function.Predicate;
public abstract class Position implements StructuralItem<Position> {
@@ -150,6 +151,26 @@
return lastPosition;
}
+ public Position getOutermostCallerMatchingOrElse(
+ Predicate<Position> predicate, Position defaultValue) {
+ return getOutermostCallerMatchingOrElse(predicate, defaultValue, false);
+ }
+
+ private Position getOutermostCallerMatchingOrElse(
+ Predicate<Position> predicate, Position defaultValue, boolean isCallerPosition) {
+ if (hasCallerPosition()) {
+ Position position =
+ getCallerPosition().getOutermostCallerMatchingOrElse(predicate, defaultValue, true);
+ if (position != null) {
+ return position;
+ }
+ }
+ if (isCallerPosition && predicate.test(this)) {
+ return this;
+ }
+ return defaultValue;
+ }
+
public Position withOutermostCallerPosition(Position newOutermostCallerPosition) {
return builderWithCopy()
.setCallerPosition(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index f6c957c..e5602b2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -100,7 +100,6 @@
import com.android.tools.r8.optimize.MemberRebindingAnalysis;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.verticalclassmerging.InterfaceTypeToClassTypeLensCodeRewriterHelper;
-import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -151,10 +150,7 @@
/** Replace type appearances, invoke targets and field accesses with actual definitions. */
public void rewrite(IRCode code, ProgramMethod method, MethodProcessor methodProcessor) {
- Set<Phi> affectedPhis =
- enumUnboxer != null
- ? enumUnboxer.rewriteCode(code, methodProcessor)
- : Sets.newIdentityHashSet();
+ Set<Phi> affectedPhis = enumUnboxer.rewriteCode(code, methodProcessor);
GraphLens graphLens = appView.graphLens();
DexItemFactory factory = appView.dexItemFactory();
// Rewriting types that affects phi can cause us to compute TOP for cyclic phi's. To solve this
@@ -256,6 +252,9 @@
Invoke.Type actualInvokeType = lensLookup.getType();
iterator =
+ insertNullCheckForInvokeReceiverIfNeeded(
+ code, blocks, iterator, invoke, lensLookup);
+ iterator =
insertCastsForInvokeArgumentsIfNeeded(code, blocks, iterator, invoke, lensLookup);
RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
@@ -772,6 +771,56 @@
return iterator;
}
+ private InstructionListIterator insertNullCheckForInvokeReceiverIfNeeded(
+ IRCode code,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ InvokeMethod invoke,
+ MethodLookupResult lookup) {
+ // If the invoke has been staticized, then synthesize a null check for the receiver.
+ if (!invoke.isInvokeMethodWithReceiver() || !lookup.getType().isStatic()) {
+ return iterator;
+ }
+
+ TypeElement receiverType = invoke.asInvokeMethodWithReceiver().getReceiver().getType();
+ if (receiverType.isDefinitelyNotNull()) {
+ return iterator;
+ }
+
+ iterator.previous();
+
+ Position nullCheckPosition =
+ invoke
+ .getPosition()
+ .getOutermostCallerMatchingOrElse(
+ Position::isRemoveInnerFramesIfThrowingNpe, invoke.getPosition());
+ InvokeMethod nullCheck =
+ iterator.insertNullCheckInstruction(
+ appView, code, blocks, invoke.getFirstArgument(), nullCheckPosition);
+
+ // TODO(b/199864962): Lens code rewriting should follow the order of graph lenses, i.e., enum
+ // unboxing rewriting should happen after method staticizing.
+ if (receiverType.isClassType()
+ && appView.unboxedEnums().isUnboxedEnum(receiverType.asClassType().getClassType())) {
+ iterator.previousUntil(instruction -> instruction == nullCheck);
+ iterator.next();
+ enumUnboxer.rewriteNullCheck(iterator, nullCheck);
+ }
+
+ // Reset the block iterator.
+ if (invoke.getBlock().hasCatchHandlers()) {
+ BasicBlock splitBlock = invoke.getBlock();
+ BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+ assert previousBlock == splitBlock;
+ blocks.next();
+ iterator = splitBlock.listIterator(code);
+ }
+
+ Instruction next = iterator.next();
+ assert next == invoke;
+ return iterator;
+ }
+
private InstructionListIterator insertCastsForInvokeArgumentsIfNeeded(
IRCode code,
BasicBlockIterator blocks,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
index e7edb01..1b9d97b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EmptyEnumUnboxer.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -62,6 +64,11 @@
}
@Override
+ public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
+ // Intentionally empty.
+ }
+
+ @Override
public void unboxEnums(
AppView<AppInfoWithLiveness> appView,
IRConverter converter,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index 659e685..4179fed 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
@@ -44,6 +46,8 @@
public abstract Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor);
+ public abstract void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke);
+
public abstract void unboxEnums(
AppView<AppInfoWithLiveness> appView,
IRConverter converter,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index d35c7b7..acde184 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -62,6 +62,7 @@
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
@@ -1457,6 +1458,13 @@
}
@Override
+ public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
+ if (enumUnboxerRewriter != null) {
+ enumUnboxerRewriter.rewriteNullCheck(iterator, invoke);
+ }
+ }
+
+ @Override
public void unsetRewriter() {
enumUnboxerRewriter = null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 5fc743e..787b4da 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -148,9 +148,7 @@
continue;
}
} else if (invokedMethod == factory.objectMembers.getClass) {
- assert !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
- replaceEnumInvoke(
- iterator, invoke, getSharedUtilityClass().ensureCheckNotZeroMethod(appView));
+ rewriteNullCheck(iterator, invoke);
continue;
}
} else if (invokedMethod == factory.stringBuilderMethods.appendObject
@@ -346,10 +344,7 @@
Value argument = invoke.getFirstArgument();
DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
if (enumType != null) {
- replaceEnumInvoke(
- instructionIterator,
- invoke,
- getSharedUtilityClass().ensureCheckNotZeroMethod(appView));
+ rewriteNullCheck(instructionIterator, invoke);
}
} else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
assert invoke.arguments().size() == 2;
@@ -434,6 +429,11 @@
}
}
+ public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
+ assert !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
+ replaceEnumInvoke(iterator, invoke, getSharedUtilityClass().ensureCheckNotZeroMethod(appView));
+ }
+
private void removeRedundantValuesArrayCloning(
InvokeStatic invoke, Set<Instruction> instructionsToRemove, Set<BasicBlock> seenBlocks) {
for (Instruction user : invoke.outValue().aliasedUsers()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 7c196b1..d8757d1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -86,7 +86,8 @@
DexMethod methodReferenceBeforeParameterRemoval = method.getReference();
DexMethod methodReferenceAfterParameterRemoval =
graphLens.internalGetNextMethodSignature(methodReferenceBeforeParameterRemoval);
- if (methodReferenceAfterParameterRemoval == methodReferenceBeforeParameterRemoval) {
+ if (methodReferenceAfterParameterRemoval == methodReferenceBeforeParameterRemoval
+ && !graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
return method;
}
@@ -97,6 +98,13 @@
RewrittenPrototypeDescription prototypeChanges =
graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+
+ if (method.isInstance()
+ && prototypeChanges.getArgumentInfoCollection().isArgumentRemoved(0)) {
+ builder
+ .modifyAccessFlags(flags -> flags.demoteFromFinal().promoteToStatic())
+ .unsetIsLibraryMethodOverride();
+ }
}
});
});
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 17865e4..0955d0a 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
+import com.android.tools.r8.ir.code.Invoke.Type;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
@@ -48,6 +49,11 @@
}
@Override
+ protected boolean isLegitimateToHaveEmptyMappings() {
+ return true;
+ }
+
+ @Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
if (lookupResult.getReference().getType() != previous.getReference().getType()) {
@@ -91,6 +97,17 @@
return super.internalGetNextMethodSignature(method);
}
+ @Override
+ protected Type mapInvocationType(DexMethod newMethod, DexMethod originalMethod, Type type) {
+ if (!type.isStatic() && hasPrototypeChanges(newMethod)) {
+ RewrittenPrototypeDescription prototypeChanges = getPrototypeChanges(newMethod);
+ if (prototypeChanges.getArgumentInfoCollection().isArgumentRemoved(0)) {
+ return Type.STATIC;
+ }
+ }
+ return super.mapInvocationType(newMethod, originalMethod, type);
+ }
+
public static class Builder {
private final AppView<AppInfoWithLiveness> appView;
@@ -106,7 +123,9 @@
}
public boolean isEmpty() {
- return newFieldSignatures.isEmpty() && newMethodSignatures.isEmpty();
+ return newFieldSignatures.isEmpty()
+ && newMethodSignatures.isEmpty()
+ && prototypeChanges.isEmpty();
}
public ArgumentPropagatorGraphLens.Builder mergeDisjoint(
@@ -136,6 +155,12 @@
return this;
}
+ public Builder recordStaticized(
+ DexMethod method, RewrittenPrototypeDescription prototypeChangesForMethod) {
+ prototypeChanges.put(method, prototypeChangesForMethod);
+ return this;
+ }
+
public ArgumentPropagatorGraphLens build() {
if (isEmpty()) {
return null;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index d79d23e..6479ad3 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -201,7 +201,8 @@
ProgramMethod resolvedMethod = resolutionResult.getResolvedProgramMethod();
DexMethod rewrittenMethodReference =
graphLens.internalGetNextMethodSignature(resolvedMethod.getReference());
- if (rewrittenMethodReference != resolvedMethod.getReference()) {
+ if (rewrittenMethodReference != resolvedMethod.getReference()
+ || graphLens.hasPrototypeChanges(rewrittenMethodReference)) {
markAffected();
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 4890dd9..0e68d70 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder;
+import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.utils.AccessUtils;
@@ -345,7 +346,7 @@
// all possible to strengthen to the same stronger type, in all methods.
Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
- for (int parameterIndex = 1;
+ for (int parameterIndex = 0;
parameterIndex < signature.getProto().getArity() + 1;
parameterIndex++) {
if (canRemoveParameterFromVirtualMethods(methods, parameterIndex)) {
@@ -455,6 +456,17 @@
private boolean canRemoveParameterFromVirtualMethods(
ProgramMethodSet methods, int parameterIndex) {
+ if (parameterIndex == 0) {
+ if (methods.size() > 1) {
+ // Method staticizing would break dynamic dispatch.
+ return false;
+ }
+ ProgramMethod method = methods.getFirst();
+ return method.getOptimizationInfo().hasUnusedArguments()
+ && method.getOptimizationInfo().getUnusedArguments().get(0)
+ && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
+ && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, parameterIndex);
+ }
for (ProgramMethod method : methods) {
if (method.getDefinition().isAbstract()) {
// OK, this parameter can be removed.
@@ -501,6 +513,9 @@
private DexType getNewParameterTypeForVirtualMethods(
ProgramMethodSet methods, int parameterIndex) {
+ if (parameterIndex == 0) {
+ return null;
+ }
DexType newParameterType = null;
for (ProgramMethod method : methods) {
if (method.getAccessFlags().isAbstract()) {
@@ -548,6 +563,9 @@
partialGraphLensBuilder.recordMove(
method.getReference(), newMethodSignature, prototypeChanges);
affected.set();
+ } else if (!prototypeChanges.isEmpty()) {
+ partialGraphLensBuilder.recordStaticized(method.getReference(), prototypeChanges);
+ affected.set();
}
});
return affected.get();
@@ -907,42 +925,50 @@
ProgramMethod method,
IntFunction<DexType> newParameterTypes,
IntPredicate removableParameterIndices) {
- ConcreteCallSiteOptimizationInfo optimizationInfo =
- method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
- if (optimizationInfo == null) {
- return ArgumentInfoCollection.empty();
+ ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
+ if (method.getDefinition().isInstance()
+ && removableParameterIndices.test(0)
+ && method.getOptimizationInfo().hasUnusedArguments()
+ && method.getOptimizationInfo().getUnusedArguments().get(0)
+ && ParameterRemovalUtils.canRemoveUnusedParametersFrom(appView, method)
+ && ParameterRemovalUtils.canRemoveUnusedParameter(appView, method, 0)) {
+ parameterChangesBuilder.addArgumentInfo(
+ 0, RemovedArgumentInfo.builder().setType(method.getHolderType()).build());
}
- ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
- for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
- argumentIndex < method.getDefinition().getNumberOfArguments();
- argumentIndex++) {
- if (removableParameterIndices.test(argumentIndex)) {
- AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
- if (abstractValue.isSingleValue()
- && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+ ConcreteCallSiteOptimizationInfo optimizationInfo =
+ method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
+ if (optimizationInfo != null) {
+ for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+ argumentIndex < method.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ if (removableParameterIndices.test(argumentIndex)) {
+ AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+ if (abstractValue.isSingleValue()
+ && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RemovedArgumentInfo.builder()
+ .setSingleValue(abstractValue.asSingleValue())
+ .setType(method.getArgumentType(argumentIndex))
+ .build());
+ continue;
+ }
+ }
+
+ DexType dynamicType = newParameterTypes.apply(argumentIndex);
+ if (dynamicType != null) {
+ DexType staticType = method.getArgumentType(argumentIndex);
+ assert dynamicType != staticType;
parameterChangesBuilder.addArgumentInfo(
argumentIndex,
- RemovedArgumentInfo.builder()
- .setSingleValue(abstractValue.asSingleValue())
- .setType(method.getArgumentType(argumentIndex))
+ RewrittenTypeInfo.builder()
+ .setCastType(dynamicType)
+ .setOldType(staticType)
+ .setNewType(dynamicType)
.build());
- continue;
}
}
-
- DexType dynamicType = newParameterTypes.apply(argumentIndex);
- if (dynamicType != null) {
- DexType staticType = method.getArgumentType(argumentIndex);
- assert dynamicType != staticType;
- parameterChangesBuilder.addArgumentInfo(
- argumentIndex,
- RewrittenTypeInfo.builder()
- .setCastType(dynamicType)
- .setOldType(staticType)
- .setNewType(dynamicType)
- .build());
- }
}
return parameterChangesBuilder.build();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
new file mode 100644
index 0000000..0d16b37
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/utils/ParameterRemovalUtils.java
@@ -0,0 +1,45 @@
+// Copyright (c) 2022, 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.optimize.argumentpropagation.utils;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.InternalOptions;
+
+public class ParameterRemovalUtils {
+
+ public static boolean canRemoveUnusedParametersFrom(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ InternalOptions options = appView.options();
+ if (!keepInfo.isParameterRemovalAllowed(options)) {
+ return false;
+ }
+ return !appView.appInfoWithLiveness().isMethodTargetedByInvokeDynamic(method);
+ }
+
+ public static boolean canRemoveUnusedParameter(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method, int argumentIndex) {
+ assert canRemoveUnusedParametersFrom(appView, method);
+ if (argumentIndex == 0) {
+ if (method.getDefinition().isInstanceInitializer()) {
+ return false;
+ }
+ if (method.getDefinition().isInstance()) {
+ if (method.getAccessFlags().isSynchronized()) {
+ return false;
+ }
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ InternalOptions options = appView.options();
+ if (!keepInfo.isMethodStaticizingAllowed(options)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
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 2a5405b..68dba3e 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -53,6 +53,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.DirectMappedDexApplication.Builder;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
@@ -1009,6 +1010,14 @@
LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callSite, appInfo(), context);
if (descriptor == null) {
+ for (DexValue bootstrapArgument : callSite.getBootstrapArgs()) {
+ if (bootstrapArgument.isDexValueMethodHandle()) {
+ DexMethodHandle method = bootstrapArgument.asDexValueMethodHandle().getValue();
+ if (method.isMethodHandle()) {
+ methodsTargetedByInvokeDynamic.add(method.asMethod());
+ }
+ }
+ }
return;
}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
index c531710..d510c33 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingClassStaticizerTest.java
@@ -44,6 +44,11 @@
.addKeepMainRule(TestClass.class)
.addKeepRules(enumKeepRules.getKeepRules())
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(UnboxableEnum.class))
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertMergedInto(CompanionHost.class, Companion.class)
+ .assertNoOtherClassesMerged())
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.noMinification() // For assertions.
@@ -65,10 +70,9 @@
isPresent());
return;
}
- MethodSubject method =
- codeInspector.clazz(CompanionHost.class).uniqueMethodWithName(renamedMethodName);
+ MethodSubject method = codeInspector.clazz(Companion.class).uniqueMethodWithName("method");
assertThat(method, isPresent());
- assertEquals("int", method.getMethod().getReference().proto.parameters.toString());
+ assertEquals("int", method.getMethod().getParameters().toString());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index 62a44a1..bded17f 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -363,7 +363,7 @@
assertThat(clazz.uniqueMethodWithName("conditionalOperator"), isAbsent());
// The enum parameter is unboxed.
- MethodSubject m = clazz.uniqueMethodWithName("moreControlFlows$enumunboxing$");
+ MethodSubject m = clazz.uniqueMethodWithName("moreControlFlows");
assertTrue(m.isPresent());
// Verify that a.b() is resolved to an inline instance-get.
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
index 07df10b..3dc868d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
@@ -4,8 +4,7 @@
package com.android.tools.r8.ir.optimize.callsites;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static junit.framework.TestCase.assertTrue;
-import static org.hamcrest.CoreMatchers.not;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -16,7 +15,6 @@
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;
@@ -43,6 +41,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(WithStaticizerTest.class)
.addKeepMainRule(MAIN)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .assertMergedInto(Host.class, Host.Companion.class)
+ .assertNoOtherClassesMerged())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -55,14 +58,10 @@
private void inspect(CodeInspector inspector) {
// Check if the candidate is indeed staticized.
ClassSubject companion = inspector.clazz(Host.Companion.class);
- assertThat(companion, not(isPresent()));
-
- // Null check in Companion#foo is migrated to Host#foo.
- ClassSubject host = inspector.clazz(Host.class);
- assertThat(host, isPresent());
- MethodSubject foo = host.uniqueMethodWithName("foo");
+ assertThat(companion, isPresent());
+ MethodSubject foo = companion.uniqueMethodWithName("foo");
assertThat(foo, isPresent());
- assertTrue(foo.streamInstructions().noneMatch(InstructionSubject::isIf));
+ assertThat(foo, isStatic());
}
@NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index f3a61c4..5987d3c 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.staticizer;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -168,9 +169,9 @@
// instantiation of SimpleWithParams, it is marked as ineligible for staticizing.
assertEquals(
Lists.newArrayList(
+ "STATIC: String SimpleWithParams.bar(String)",
"STATIC: String TrivialTestClass.next()",
"SimpleWithParams SimpleWithParams.INSTANCE",
- "VIRTUAL: String SimpleWithParams.bar(String)",
"VIRTUAL: String SimpleWithParams.foo()"),
references(clazz, "testSimpleWithParams", "void"));
@@ -204,9 +205,9 @@
assertEquals(
Lists.newArrayList(
+ "STATIC: String SimpleWithThrowingGetter.bar(String)",
"STATIC: String TrivialTestClass.next()",
"SimpleWithThrowingGetter SimpleWithThrowingGetter.INSTANCE",
- "VIRTUAL: String SimpleWithThrowingGetter.bar(String)",
"VIRTUAL: String SimpleWithThrowingGetter.foo()"),
references(clazz, "testSimpleWithThrowingGetter", "void"));
@@ -219,6 +220,7 @@
Lists.newArrayList(
"DIRECT: void SimpleWithLazyInit.<init>()",
"DIRECT: void SimpleWithLazyInit.<init>()",
+ "STATIC: String SimpleWithLazyInit.bar(String)",
"STATIC: String TrivialTestClass.next()",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
@@ -226,7 +228,6 @@
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
"SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "VIRTUAL: String SimpleWithLazyInit.bar(String)",
"VIRTUAL: String SimpleWithLazyInit.foo()"),
references(clazz, "testSimpleWithLazyInit", "void"));
@@ -300,35 +301,37 @@
assertEquals(
Lists.newArrayList(
- "STATIC: String movetohost.HostOk.bar(String)",
- "STATIC: String movetohost.HostOk.foo()",
+ "STATIC: String movetohost.CandidateOk.bar(String)",
+ "STATIC: String movetohost.CandidateOk.foo()",
"STATIC: String movetohost.MoveToHostTestClass.next()",
"STATIC: String movetohost.MoveToHostTestClass.next()",
- "STATIC: void movetohost.HostOk.blah(String)"),
+ "STATIC: void movetohost.CandidateOk.blah(String)"),
references(clazz, "testOk", "void"));
- assertThat(inspector.clazz(CandidateOk.class), not(isPresent()));
+ assertThat(inspector.clazz(HostOk.class), isAbsent());
+ assertThat(inspector.clazz(CandidateOk.class), isPresent());
assertEquals(
Lists.newArrayList(
- "STATIC: String movetohost.HostOkSideEffects.bar(String)",
- "STATIC: String movetohost.HostOkSideEffects.foo()",
+ "STATIC: String movetohost.CandidateOkSideEffects.bar(String)",
+ "STATIC: String movetohost.CandidateOkSideEffects.foo()",
"STATIC: String movetohost.MoveToHostTestClass.next()",
- "movetohost.HostOkSideEffects movetohost.HostOkSideEffects.INSTANCE"),
+ "movetohost.CandidateOkSideEffects movetohost.HostOkSideEffects.INSTANCE"),
references(clazz, "testOkSideEffects", "void"));
- assertThat(inspector.clazz(CandidateOkSideEffects.class), not(isPresent()));
+ assertThat(inspector.clazz(HostOkSideEffects.class), isPresent());
+ assertThat(inspector.clazz(CandidateOkSideEffects.class), isPresent());
assertEquals(
Lists.newArrayList(
- "DIRECT: void movetohost.HostConflictMethod.<init>()",
"STATIC: String movetohost.CandidateConflictMethod.bar(String)",
"STATIC: String movetohost.CandidateConflictMethod.foo()",
+ "STATIC: String movetohost.HostConflictMethod.bar(String)",
"STATIC: String movetohost.MoveToHostTestClass.next()",
- "STATIC: String movetohost.MoveToHostTestClass.next()",
- "VIRTUAL: String movetohost.HostConflictMethod.bar(String)"),
+ "STATIC: String movetohost.MoveToHostTestClass.next()"),
references(clazz, "testConflictMethod", "void"));
+ assertThat(inspector.clazz(HostConflictMethod.class), isPresent());
assertThat(inspector.clazz(CandidateConflictMethod.class), isPresent());
assertEquals(
@@ -427,12 +430,14 @@
assertThat(clazz.uniqueMethodWithName("calledTwice"), not(isPresent()));
// Check that the two inlines of "calledTwice" is correctly rewritten.
- assertThat(clazz.uniqueMethodWithName("foo"), isPresent());
- assertThat(clazz.uniqueMethodWithName("bar"), isPresent());
+ ClassSubject candidateClassSubject = inspector.clazz(Candidate.class);
+ assertThat(candidateClassSubject, isPresent());
+ assertThat(candidateClassSubject.uniqueMethodWithName("foo"), isPresent());
+ assertThat(candidateClassSubject.uniqueMethodWithName("bar"), isPresent());
assertEquals(
Lists.newArrayList(
- "STATIC: String dualcallinline.DualCallTest.foo()",
- "STATIC: String dualcallinline.DualCallTest.foo()"),
+ "STATIC: String dualcallinline.Candidate.foo()",
+ "STATIC: String dualcallinline.Candidate.foo()"),
references(clazz, "main", "void", "java.lang.String[]"));
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
index 74dc3f4..76bee19 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/CompanionClassWithNewInstanceUserTest.java
@@ -1,7 +1,7 @@
package com.android.tools.r8.ir.optimize.staticizer;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
@@ -10,6 +10,7 @@
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.MethodSubject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -43,18 +44,16 @@
}
private void inspect(CodeInspector inspector) {
- if (parameters.isCfRuntime()) {
- // Class staticizer is disabled when generating class files.
- assertThat(inspector.clazz(Companion.class), isPresent());
- } else {
- // The companion class has been removed.
- assertThat(inspector.clazz(Companion.class), not(isPresent()));
+ ClassSubject hostClassSubject = inspector.clazz(CompanionHost.class);
+ assertThat(hostClassSubject, isPresent());
- // The companion method has been moved to the companion host class.
- ClassSubject hostClassSubject = inspector.clazz(CompanionHost.class);
- assertThat(hostClassSubject, isPresent());
- assertThat(hostClassSubject.uniqueMethodWithName("method"), isPresent());
- }
+ // The companion class has been removed.
+ ClassSubject companionClassSubject = inspector.clazz(Companion.class);
+ assertThat(companionClassSubject, isPresent());
+
+ MethodSubject companionMethodSubject = companionClassSubject.uniqueMethodWithName("method");
+ assertThat(companionMethodSubject, isPresent());
+ assertThat(companionMethodSubject, isStatic());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverInUnboxedEnumTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverInUnboxedEnumTest.java
new file mode 100644
index 0000000..0218614
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverInUnboxedEnumTest.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnusedReceiverInUnboxedEnumTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ MyEnum alwaysNull = System.currentTimeMillis() > 0 ? null : MyEnum.A;
+ alwaysNull.foo();
+ }
+ }
+
+ enum MyEnum {
+ A;
+
+ @NeverInline
+ void foo() {
+ System.out.println("Hello!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverTest.java
new file mode 100644
index 0000000..175b2cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedReceiverTest.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2022, 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.unusedarguments;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnusedReceiverTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject testMethodSubject = mainClassSubject.uniqueMethodWithName("test");
+ assertThat(testMethodSubject, isStatic());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new Main().test();
+ }
+
+ @NeverInline
+ void test() {
+ System.out.println("Hello world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
index 670b6ae..ccdaae7 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingAfterDevirtualizationTest.java
@@ -9,13 +9,16 @@
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ApplyMappingAfterDevirtualizationTest extends TestBase {
@@ -40,7 +43,7 @@
}
}
- // LibClassB should be devirtualized into LibInterfaceB
+ // LibInterfaceB should be devirtualized into LibClassB
public static class LibClassB implements LibInterfaceB {
@Override
@@ -77,35 +80,32 @@
private static final Class<?>[] PROGRAM_CLASSES = {ProgramClass.class};
- private Backend backend;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public ApplyMappingAfterDevirtualizationTest(Backend backend) {
- this.backend = backend;
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
public void runOnJvm() throws Throwable {
- Assume.assumeTrue(backend == Backend.CF);
+ Assume.assumeTrue(parameters.isCfRuntime());
testForJvm()
.addProgramClasses(LIBRARY_CLASSES)
.addProgramClasses(PROGRAM_CLASSES)
- .run(ProgramClass.class)
+ .run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void devirtualizingNoRenamingOfOverriddenNotKeptInterfaceMethods() throws Exception {
R8TestCompileResult libraryResult =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addProgramClasses(LIBRARY_CLASSES)
- .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class)
- .addKeepMainRule(LibClassB.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class, LibClassB.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
@@ -116,27 +116,29 @@
assertThat(inspector.clazz(LibInterfaceA.class), not(isPresent()));
assertThat(inspector.clazz(LibInterfaceB.class), not(isPresent()));
- testForR8(backend)
+ testForR8(parameters.getBackend())
.noTreeShaking()
.noMinification()
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(libraryResult.getProguardMap())
.addLibraryClasses(LIBRARY_CLASSES)
- .addLibraryFiles(runtimeJar(backend))
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
- .run(ProgramClass.class)
+ .run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@Test
public void devirtualizingNoRenamingOfOverriddenKeptInterfaceMethods() throws Exception {
R8TestCompileResult libraryResult =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addProgramClasses(LIBRARY_CLASSES)
- .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class, LibInterfaceA.class)
- .addKeepMainRule(LibClassB.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(
+ LibClassA.class, LibClassB.class, LibInterfaceA.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
@@ -147,16 +149,17 @@
assertThat(inspector.clazz(LibInterfaceA.class), isPresent());
assertThat(inspector.clazz(LibInterfaceB.class), not(isPresent()));
- testForR8(backend)
+ testForR8(parameters.getBackend())
.noTreeShaking()
.noMinification()
.addProgramClasses(PROGRAM_CLASSES)
.addApplyMapping(libraryResult.getProguardMap())
.addLibraryClasses(LIBRARY_CLASSES)
- .addLibraryFiles(runtimeJar(backend))
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
- .run(ProgramClass.class)
+ .run(parameters.getRuntime(), ProgramClass.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
index 7c0f956..bf8856b 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckSequenceTest.java
@@ -4,13 +4,16 @@
package com.android.tools.r8.naming.retrace;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import java.util.List;
+import java.util.Objects;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -61,7 +64,27 @@
.assertFailureWithErrorThatThrows(NullPointerException.class)
.inspectStackTrace(
(stackTrace, codeInspector) -> {
- assertThat(stackTrace, isSame(expectedStackTrace));
+ if (canUseJavaUtilObjectsRequireNonNull(parameters)) {
+ StackTrace requireNonNullFrame =
+ StackTrace.builder().add(stackTrace.get(0)).build();
+ assertThat(
+ requireNonNullFrame,
+ isSameExceptForLineNumbers(
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName(Objects.class.getTypeName())
+ .setMethodName("requireNonNull")
+ .setFileName("Objects.java")
+ .build())
+ .build()));
+
+ StackTrace stackTraceWithoutRequireNonNull =
+ StackTrace.builder().add(stackTrace).remove(0).build();
+ assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
+ } else {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ }
});
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
index 81c2200..13e3e7f 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming.retrace;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForLineNumbers;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
@@ -13,8 +14,10 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestBuilder;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
+import java.util.Objects;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -80,7 +83,27 @@
.assertFailureWithErrorThatThrows(NullPointerException.class)
.inspectStackTrace(
(stackTrace, codeInspector) -> {
- assertThat(stackTrace, isSame(expectedStackTrace));
+ if (throwReceiverNpe && canUseJavaUtilObjectsRequireNonNull(parameters)) {
+ StackTrace requireNonNullFrame =
+ StackTrace.builder().add(stackTrace.get(0)).build();
+ assertThat(
+ requireNonNullFrame,
+ isSameExceptForLineNumbers(
+ StackTrace.builder()
+ .add(
+ StackTraceLine.builder()
+ .setClassName(Objects.class.getTypeName())
+ .setMethodName("requireNonNull")
+ .setFileName("Objects.java")
+ .build())
+ .build()));
+
+ StackTrace stackTraceWithoutRequireNonNull =
+ StackTrace.builder().add(stackTrace).remove(0).build();
+ assertThat(stackTraceWithoutRequireNonNull, isSame(expectedStackTrace));
+ } else {
+ assertThat(stackTrace, isSame(expectedStackTrace));
+ }
});
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index aea2a25..f4158a1 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -76,6 +76,11 @@
return this;
}
+ public Builder remove(int i) {
+ stackTraceLines.remove(i);
+ return this;
+ }
+
public Builder applyIf(boolean condition, Consumer<Builder> fn) {
if (condition) {
fn.accept(this);
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index 01d1c30..9a81a6a 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -6,6 +6,7 @@
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -92,13 +93,10 @@
.inspector();
List<FoundClassSubject> classes = inspector.allClasses();
-
- // The synthetic class is still present when generating class files.
- assertEquals(parameters.isCfRuntime() ? 3 : 2, classes.size());
- assertEquals(
- parameters.isCfRuntime(),
+ assertEquals(2, classes.size());
+ assertTrue(
classes.stream()
.map(FoundClassSubject::getOriginalName)
- .anyMatch(name -> name.endsWith("$1")));
+ .noneMatch(name -> name.endsWith("$1")));
}
}
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
index 760215a..cbbfa70 100644
--- a/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/access/NestStaticMethodAccessTest.java
@@ -7,6 +7,7 @@
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRunResult;
@@ -64,7 +65,7 @@
public void testResolutionAccess() throws Exception {
AppView<AppInfoWithLiveness> appView =
computeAppViewWithLiveness(
- buildClasses(getClasses())
+ buildClassesWithTestingAnnotations(getClasses())
.addClassProgramData(getTransformedClasses())
.addLibraryFile(parameters.getDefaultRuntimeLibrary())
.build(),
@@ -91,6 +92,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(getClasses())
.addProgramClassFileData(getTransformedClasses())
+ .enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.run(parameters.getRuntime(), Main.class)
@@ -114,12 +116,14 @@
return transformer(clazz).setNest(clazz);
}
+ @NoHorizontalClassMerging
static class A {
/* will be private */ static void bar() {
System.out.println("A::bar");
}
}
+ @NoHorizontalClassMerging
static class B {
public void foo() {
// Static invoke to private method.
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/classstaticizer/IfRuleWithClassStaticizerTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/classstaticizer/IfRuleWithClassStaticizerTest.java
index b4e4fce..8c67d3e 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/classstaticizer/IfRuleWithClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/classstaticizer/IfRuleWithClassStaticizerTest.java
@@ -4,15 +4,17 @@
package com.android.tools.r8.shaking.ifrule.classstaticizer;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
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.assertEquals;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ifrule.classstaticizer.IfRuleWithClassStaticizerTest.StaticizerCandidate.Companion;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -22,69 +24,67 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class IfRuleWithClassStaticizerTest extends TestBase {
- private final Backend backend;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public IfRuleWithClassStaticizerTest(Backend backend) {
- this.backend = backend;
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
public void test() throws Exception {
String expectedOutput = StringUtils.lines("In method()");
- if (backend == Backend.CF) {
- testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput);
+ if (parameters.isCfRuntime()) {
+ testForJvm()
+ .addTestClasspath()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
}
CodeInspector inspector =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addInnerClasses(IfRuleWithClassStaticizerTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
- "-if class " + StaticizerCandidate.Companion.class.getTypeName() + " {",
+ "-if class " + Companion.class.getTypeName() + " {",
" public !static void method();",
"}",
"-keep class " + Unused.class.getTypeName())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
ClassSubject classSubject = inspector.clazz(StaticizerCandidate.class);
- assertThat(classSubject, isPresent());
+ assertThat(classSubject, isAbsent());
- if (backend == Backend.CF) {
+ if (parameters.isCfRuntime()) {
// The class staticizer is not enabled for CF.
assertThat(inspector.clazz(Unused.class), isPresent());
} else {
- assert backend == Backend.DEX;
+ assert parameters.isDexRuntime();
- // There should be a static method on StaticizerCandidate after staticizing.
+ // There should be a static method after staticizing.
+ ClassSubject companionClassSubject = inspector.clazz(StaticizerCandidate.Companion.class);
+ assertThat(companionClassSubject, isPresent());
List<FoundMethodSubject> staticMethods =
- classSubject.allMethods().stream()
+ companionClassSubject.allMethods().stream()
.filter(method -> method.isStatic() && !method.isClassInitializer())
.collect(Collectors.toList());
assertEquals(1, staticMethods.size());
- assertEquals(
- "void " + StaticizerCandidate.Companion.class.getTypeName() + ".method()",
- staticMethods.get(0).getOriginalSignature().toString());
+ assertEquals("void method()", staticMethods.get(0).getOriginalSignature().toString());
- // The Companion class should not be present after staticizing.
- assertThat(inspector.clazz(StaticizerCandidate.Companion.class), not(isPresent()));
-
- // TODO(b/122867080): The Unused class should be present due to the -if rule.
- assertThat(inspector.clazz(Unused.class), not(isPresent()));
+ assertThat(inspector.clazz(Unused.class), isPresent());
}
}