Merge commit '0aec7b3cb857285b21e47094b8cf5c56ff6e3b61' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/AccessFlags.java b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
index 68921ca..cdf0ad1 100644
--- a/src/main/java/com/android/tools/r8/graph/AccessFlags.java
+++ b/src/main/java/com/android/tools/r8/graph/AccessFlags.java
@@ -230,8 +230,9 @@
promote(Constants.ACC_FINAL);
}
- public void demoteFromFinal() {
+ public T demoteFromFinal() {
demote(Constants.ACC_FINAL);
+ return self();
}
public boolean isPromotedToPublic() {
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index 11d3e17..7689694 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -8,7 +8,6 @@
import com.android.tools.r8.code.ReturnVoid;
import com.android.tools.r8.code.SwitchPayload;
import com.android.tools.r8.dex.CodeToKeep;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.JumboStringRewriter;
import com.android.tools.r8.dex.MixedSectionCollection;
@@ -153,7 +152,7 @@
|| Arrays.stream(instructions).noneMatch(Instruction::isConstString);
assert Arrays.stream(instructions).noneMatch(Instruction::isDexItemBasedConstString);
if (highestSortingString != null
- && mapping.getOffsetFor(highestSortingString) > Constants.MAX_NON_JUMBO_INDEX) {
+ && highestSortingString.isGreaterThanOrEqualTo(mapping.getFirstJumboString())) {
firstJumboString = mapping.getFirstJumboString();
}
}
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 865a536..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());
@@ -1184,7 +1181,7 @@
}
public static DexEncodedMethod createDesugaringForwardingMethod(
- DexClassAndMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
+ DexEncodedMethod target, DexClass clazz, DexMethod forwardMethod, DexItemFactory factory) {
DexMethod method = target.getReference();
assert forwardMethod != null;
// New method will have the same name, proto, and also all the flags of the
@@ -1206,8 +1203,8 @@
.setNonStaticSource(newMethod)
.setStaticTarget(forwardMethod, isInterfaceMethodReference)
.build())
- .setApiLevelForDefinition(target.getDefinition().getApiLevelForDefinition())
- .setApiLevelForCode(target.getDefinition().getApiLevelForCode())
+ .setApiLevelForDefinition(target.getApiLevelForDefinition())
+ .setApiLevelForCode(target.getApiLevelForCode())
.build();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexTypeList.java b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
index f399895..f07c38e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexTypeList.java
+++ b/src/main/java/com/android/tools/r8/graph/DexTypeList.java
@@ -64,7 +64,7 @@
}
public DexTypeList keepIf(Predicate<DexType> predicate) {
- DexType[] filtered = ArrayUtils.filter(DexType[].class, values, predicate);
+ DexType[] filtered = ArrayUtils.filter(values, predicate, DexType.EMPTY_ARRAY);
if (filtered != values) {
return DexTypeList.create(filtered);
}
diff --git a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
index 6930912..545645e 100644
--- a/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/MethodArrayBacking.java
@@ -13,6 +13,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -337,33 +338,75 @@
@Override
public void replaceMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
- replaceDirectMethods(replacement);
- replaceVirtualMethods(replacement);
+ List<DexEncodedMethod> newVirtualMethods = internalReplaceDirectMethods(replacement);
+ List<DexEncodedMethod> newDirectMethods = internalReplaceVirtualMethods(replacement);
+ addDirectMethods(newDirectMethods);
+ addVirtualMethods(newVirtualMethods);
}
@Override
public void replaceDirectMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> newVirtualMethods = internalReplaceDirectMethods(replacement);
+ addVirtualMethods(newVirtualMethods);
+ }
+
+ private List<DexEncodedMethod> internalReplaceDirectMethods(
+ Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> newVirtualMethods = new ArrayList<>();
for (int i = 0; i < directMethods.length; i++) {
DexEncodedMethod method = directMethods[i];
DexEncodedMethod newMethod = replacement.apply(method);
assert newMethod != null;
if (method != newMethod) {
- assert belongsToDirectPool(newMethod);
- directMethods[i] = newMethod;
+ if (belongsToDirectPool(newMethod)) {
+ directMethods[i] = newMethod;
+ } else {
+ directMethods[i] = null;
+ newVirtualMethods.add(newMethod);
+ }
}
}
+ if (!newVirtualMethods.isEmpty()) {
+ directMethods =
+ ArrayUtils.filter(
+ directMethods,
+ Objects::nonNull,
+ DexEncodedMethod.EMPTY_ARRAY,
+ directMethods.length - newVirtualMethods.size());
+ }
+ return newVirtualMethods;
}
@Override
public void replaceVirtualMethods(Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> newDirectMethods = internalReplaceVirtualMethods(replacement);
+ addDirectMethods(newDirectMethods);
+ }
+
+ private List<DexEncodedMethod> internalReplaceVirtualMethods(
+ Function<DexEncodedMethod, DexEncodedMethod> replacement) {
+ List<DexEncodedMethod> newDirectMethods = new ArrayList<>();
for (int i = 0; i < virtualMethods.length; i++) {
DexEncodedMethod method = virtualMethods[i];
DexEncodedMethod newMethod = replacement.apply(method);
if (method != newMethod) {
- assert belongsToVirtualPool(newMethod);
- virtualMethods[i] = newMethod;
+ if (belongsToVirtualPool(newMethod)) {
+ virtualMethods[i] = newMethod;
+ } else {
+ virtualMethods[i] = null;
+ newDirectMethods.add(newMethod);
+ }
}
}
+ if (!newDirectMethods.isEmpty()) {
+ virtualMethods =
+ ArrayUtils.filter(
+ virtualMethods,
+ Objects::nonNull,
+ DexEncodedMethod.EMPTY_ARRAY,
+ virtualMethods.length - newDirectMethods.size());
+ }
+ return newDirectMethods;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
index 4f901fd..68c1dde 100644
--- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -36,6 +36,10 @@
return new Builder();
}
+ public Builder toBuilder() {
+ return new Builder(this);
+ }
+
public static PrunedItems empty(DexApplication application) {
return new Builder().setPrunedApp(application).build();
}
@@ -113,6 +117,17 @@
private final Set<DexField> removedFields = Sets.newIdentityHashSet();
private Set<DexMethod> removedMethods = Sets.newIdentityHashSet();
+ Builder() {}
+
+ Builder(PrunedItems prunedItems) {
+ additionalPinnedItems.addAll(prunedItems.getAdditionalPinnedItems());
+ noLongerSyntheticItems.addAll(prunedItems.getNoLongerSyntheticItems());
+ prunedApp = prunedItems.getPrunedApp();
+ removedClasses.addAll(prunedItems.getRemovedClasses());
+ removedFields.addAll(prunedItems.getRemovedFields());
+ removedMethods.addAll(prunedItems.getRemovedMethods());
+ }
+
public Builder setPrunedApp(DexApplication prunedApp) {
this.prunedApp = prunedApp;
return this;
@@ -129,6 +144,12 @@
return this;
}
+ public Builder addRemovedClass(DexType removedClass) {
+ this.noLongerSyntheticItems.add(removedClass);
+ this.removedClasses.add(removedClass);
+ return this;
+ }
+
public Builder addRemovedClasses(Set<DexType> removedClasses) {
this.noLongerSyntheticItems.addAll(removedClasses);
this.removedClasses.addAll(removedClasses);
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index dda7746..bf3849c 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoFixer;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.IntObjConsumer;
import com.android.tools.r8.utils.IteratorUtils;
@@ -450,6 +451,11 @@
return removed;
}
+ public int numberOfRemovedNonReceiverArguments(DexEncodedMethod method) {
+ return numberOfRemovedArguments()
+ - BooleanUtils.intValue(method.isInstance() && isArgumentRemoved(0));
+ }
+
public boolean hasArgumentInfo(int argumentIndex) {
return argumentInfos.containsKey(argumentIndex);
}
@@ -542,14 +548,12 @@
}
public DexType[] rewriteParameters(DexEncodedMethod encodedMethod) {
- // Currently not allowed to remove the receiver of an instance method. This would involve
- // changing invoke-direct/invoke-virtual into invoke-static.
- assert encodedMethod.isStatic() || !getArgumentInfo(0).isRemovedArgumentInfo();
- DexType[] params = encodedMethod.getReference().proto.parameters.values;
+ DexType[] params = encodedMethod.getParameters().values;
if (isEmpty()) {
return params;
}
- DexType[] newParams = new DexType[params.length - numberOfRemovedArguments()];
+ DexType[] newParams =
+ new DexType[params.length - numberOfRemovedNonReceiverArguments(encodedMethod)];
int offset = encodedMethod.getFirstNonReceiverArgumentIndex();
int newParamIndex = 0;
for (int oldParamIndex = 0; oldParamIndex < params.length; oldParamIndex++) {
diff --git a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java
index 97839cf..5d40177 100644
--- a/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java
+++ b/src/main/java/com/android/tools/r8/graph/bytecodemetadata/BytecodeInstructionMetadata.java
@@ -4,6 +4,11 @@
package com.android.tools.r8.graph.bytecodemetadata;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
+import java.util.Collections;
+import java.util.Set;
+
/**
* A piece of information that can be attached to instructions in {@link
* com.android.tools.r8.graph.CfCode} and {@link com.android.tools.r8.graph.DexCode}.
@@ -11,6 +16,16 @@
public class BytecodeInstructionMetadata {
/**
+ * Set for field-get instructions that are only used to invoke one or more instance methods.
+ *
+ * <p>This is used to skip field-get instructions that will be pruned as a result of method
+ * staticizing in the {@link TrivialFieldAccessReprocessor}. A common use case for this is Kotlin
+ * companion fields, which will generally not be read after the methods of a given companion class
+ * have been staticized.
+ */
+ private final Set<DexMethod> isReadForInvokeReceiver;
+
+ /**
* Set for instance and static field read instructions which are only used to write the same
* field.
*
@@ -19,7 +34,9 @@
*/
private final boolean isReadForWrite;
- BytecodeInstructionMetadata(boolean isReadForWrite) {
+ private BytecodeInstructionMetadata(
+ Set<DexMethod> isReadForInvokeReceiver, boolean isReadForWrite) {
+ this.isReadForInvokeReceiver = isReadForInvokeReceiver;
this.isReadForWrite = isReadForWrite;
}
@@ -31,16 +48,32 @@
return null;
}
+ public boolean isReadForInvokeReceiver() {
+ return isReadForInvokeReceiver != null;
+ }
+
+ public Set<DexMethod> getReadForInvokeReceiver() {
+ return isReadForInvokeReceiver;
+ }
+
public boolean isReadForWrite() {
return isReadForWrite;
}
public static class Builder {
+ private Set<DexMethod> isReadForInvokeReceiver = Collections.emptySet();
private boolean isReadForWrite;
private boolean isEmpty() {
- return !isReadForWrite;
+ return isReadForInvokeReceiver.isEmpty() && !isReadForWrite;
+ }
+
+ public Builder setIsReadForInvokeReceiver(Set<DexMethod> isReadForInvokeReceiver) {
+ assert isReadForInvokeReceiver != null;
+ assert !isReadForInvokeReceiver.isEmpty();
+ this.isReadForInvokeReceiver = isReadForInvokeReceiver;
+ return this;
}
public Builder setIsReadForWrite() {
@@ -50,7 +83,7 @@
public BytecodeInstructionMetadata build() {
assert !isEmpty();
- return new BytecodeInstructionMetadata(isReadForWrite);
+ return new BytecodeInstructionMetadata(isReadForInvokeReceiver, isReadForWrite);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index c76f43f..d7dc429 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.code.ClassInitializerMerger;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
@@ -290,16 +291,21 @@
group.getTarget().setInterfaces(DexTypeList.create(interfaces));
}
- void mergeFields() {
+ void mergeFields(PrunedItems.Builder prunedItemsBuilder) {
if (group.hasClassIdField()) {
appendClassIdField();
}
- mergeInstanceFields();
+ mergeInstanceFields(prunedItemsBuilder);
mergeStaticFields();
}
- void mergeInstanceFields() {
- group.forEachSource(DexClass::clearInstanceFields);
+ void mergeInstanceFields(PrunedItems.Builder prunedItemsBuilder) {
+ group.forEachSource(
+ clazz -> {
+ clazz.forEachInstanceField(
+ field -> prunedItemsBuilder.addRemovedField(field.getReference()));
+ clazz.clearInstanceFields();
+ });
group.getTarget().setInstanceFields(classInstanceFieldsMerger.merge());
}
@@ -310,16 +316,18 @@
}
public void mergeGroup(
+ PrunedItems.Builder prunedItemsBuilder,
SyntheticArgumentClass syntheticArgumentClass,
SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
fixAccessFlags();
fixNestMemberAttributes();
mergeAnnotations();
mergeInterfaces();
- mergeFields();
+ mergeFields(prunedItemsBuilder);
mergeMethods(syntheticArgumentClass, syntheticInitializerConverterBuilder);
group.getTarget().clearClassSignature();
group.getTarget().forEachProgramMember(ProgramMember::clearGenericSignature);
+ group.forEachSource(clazz -> prunedItemsBuilder.addRemovedClass(clazz.getType()));
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index b14f07c..ad8d9df 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -103,7 +103,9 @@
: null;
SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder =
SyntheticInitializerConverter.builder(appView, codeProvider);
- applyClassMergers(classMergers, syntheticArgumentClass, syntheticInitializerConverterBuilder);
+ PrunedItems prunedItems =
+ applyClassMergers(
+ classMergers, syntheticArgumentClass, syntheticInitializerConverterBuilder);
SyntheticInitializerConverter syntheticInitializerConverter =
syntheticInitializerConverterBuilder.build();
@@ -124,10 +126,7 @@
// Prune keep info.
KeepInfoCollection keepInfo = appView.getKeepInfo();
- keepInfo.mutate(
- mutator ->
- mutator.removeKeepInfoForMergedClasses(
- PrunedItems.builder().setRemovedClasses(mergedClasses.getSources()).build()));
+ keepInfo.mutate(mutator -> mutator.removeKeepInfoForMergedClasses(prunedItems));
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
// sites, fields accesses, etc. are correctly transferred to the target classes.
@@ -142,12 +141,7 @@
}
appView.pruneItems(
- PrunedItems.builder()
- .setPrunedApp(appView.appInfo().app())
- .addRemovedClasses(mergedClasses.getSources())
- .addNoLongerSyntheticItems(mergedClasses.getSources())
- .build(),
- executorService);
+ prunedItems.toBuilder().setPrunedApp(appView.app()).build(), executorService);
}
private FieldAccessInfoCollectionModifier createFieldAccessInfoCollectionModifier(
@@ -220,13 +214,16 @@
}
/** Merges all class groups using {@link ClassMerger}. */
- private void applyClassMergers(
+ private PrunedItems applyClassMergers(
Collection<ClassMerger> classMergers,
SyntheticArgumentClass syntheticArgumentClass,
SyntheticInitializerConverter.Builder syntheticInitializerConverterBuilder) {
+ PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
for (ClassMerger merger : classMergers) {
- merger.mergeGroup(syntheticArgumentClass, syntheticInitializerConverterBuilder);
+ merger.mergeGroup(
+ prunedItemsBuilder, syntheticArgumentClass, syntheticInitializerConverterBuilder);
}
+ return prunedItemsBuilder.build();
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index e5a1aec..3ebd37a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -28,6 +28,7 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final FieldAssignmentTracker fieldAssignmentTracker;
private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
+ private final FieldReadForInvokeReceiverAnalysis fieldReadForInvokeReceiverAnalysis;
private final FieldReadForWriteAnalysis fieldReadForWriteAnalysis;
public FieldAccessAnalysis(AppView<AppInfoWithLiveness> appView) {
@@ -36,6 +37,7 @@
this.fieldBitAccessAnalysis =
options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
this.fieldAssignmentTracker = new FieldAssignmentTracker(appView);
+ this.fieldReadForInvokeReceiverAnalysis = new FieldReadForInvokeReceiverAnalysis(appView);
this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
}
@@ -44,10 +46,12 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
FieldAssignmentTracker fieldAssignmentTracker,
FieldBitAccessAnalysis fieldBitAccessAnalysis,
+ FieldReadForInvokeReceiverAnalysis fieldReadForInvokeReceiverAnalysis,
FieldReadForWriteAnalysis fieldReadForWriteAnalysis) {
this.appView = appView;
this.fieldAssignmentTracker = fieldAssignmentTracker;
this.fieldBitAccessAnalysis = fieldBitAccessAnalysis;
+ this.fieldReadForInvokeReceiverAnalysis = fieldReadForInvokeReceiverAnalysis;
this.fieldReadForWriteAnalysis = fieldReadForWriteAnalysis;
}
@@ -91,6 +95,10 @@
fieldBitAccessAnalysis.recordFieldAccess(
fieldInstruction, field.getDefinition(), feedback);
}
+ if (fieldReadForInvokeReceiverAnalysis != null) {
+ fieldReadForInvokeReceiverAnalysis.recordFieldAccess(
+ fieldInstruction, field, bytecodeMetadataProviderBuilder, code.context());
+ }
if (fieldReadForWriteAnalysis != null) {
fieldReadForWriteAnalysis.recordFieldAccess(
fieldInstruction, field, bytecodeMetadataProviderBuilder);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java
new file mode 100644
index 0000000..b2aded5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldReadForInvokeReceiverAnalysis.java
@@ -0,0 +1,90 @@
+// 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.analysis.fieldaccess;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+public class FieldReadForInvokeReceiverAnalysis {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ FieldReadForInvokeReceiverAnalysis(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public void recordFieldAccess(
+ FieldInstruction instruction,
+ ProgramField field,
+ BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
+ ProgramMethod context) {
+ if (!instruction.isStaticGet()) {
+ return;
+ }
+
+ StaticGet staticGet = instruction.asStaticGet();
+ Set<DexMethod> methods = getMethods(staticGet.outValue(), context);
+ if (methods == null || methods.isEmpty()) {
+ return;
+ }
+
+ bytecodeMetadataProviderBuilder.addMetadata(
+ instruction, builder -> builder.setIsReadForInvokeReceiver(methods));
+ }
+
+ private Set<DexMethod> getMethods(Value value, ProgramMethod context) {
+ WorkList<Instruction> users = WorkList.newIdentityWorkList();
+ if (!enqueueUsersForAnalysis(value, users)) {
+ return null;
+ }
+ Set<DexMethod> methods = Sets.newIdentityHashSet();
+ while (users.hasNext()) {
+ Instruction user = users.next();
+ if (user.isAssume()) {
+ if (enqueueUsersForAnalysis(user.outValue(), users)) {
+ // OK.
+ continue;
+ }
+ } else if (user.isInvokeMethodWithReceiver()) {
+ InvokeMethodWithReceiver invoke = user.asInvokeMethodWithReceiver();
+ for (int argumentIndex = 1; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+ if (invoke.getArgument(argumentIndex).getAliasedValue() == value) {
+ return null;
+ }
+ }
+ assert invoke.getReceiver().getAliasedValue() == value;
+
+ ProgramMethod singleTarget = invoke.lookupSingleProgramTarget(appView, context);
+ if (singleTarget == null) {
+ return null;
+ }
+
+ methods.add(singleTarget.getReference());
+ }
+ return null;
+ }
+ return methods;
+ }
+
+ private boolean enqueueUsersForAnalysis(Value value, WorkList<Instruction> users) {
+ if (value.hasDebugUsers() || value.hasPhiUsers()) {
+ return false;
+ }
+ users.addIfNotSeen(value.uniqueUsers());
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index f392dec..7f36c0c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.ir.analysis.fieldaccess;
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.code.CfOrDexInstanceFieldRead;
import com.android.tools.r8.code.CfOrDexStaticFieldRead;
@@ -53,6 +55,8 @@
private final AppView<AppInfoWithLiveness> appView;
private final PostMethodProcessor.Builder postMethodProcessorBuilder;
+ private final Map<DexEncodedField, ProgramMethodSet> dependencies = new ConcurrentHashMap<>();
+
/** Updated concurrently from {@link #processClass(DexProgramClass)}. */
private final Map<DexEncodedField, AbstractAccessContexts> readFields = new ConcurrentHashMap<>();
@@ -233,6 +237,7 @@
fieldAccessInfoCollection.get(field.getReference()).asMutable().clearReads();
methodsToReprocess.addAll(
contexts.asConcrete().getAccessesWithContexts().values().iterator().next());
+ methodsToReprocess.addAll(dependencies.getOrDefault(field, ProgramMethodSet.empty()));
});
}
@@ -250,6 +255,7 @@
assert !writtenFields.containsKey(field);
methodsToReprocess.addAll(
contexts.asConcrete().getAccessesWithContexts().values().iterator().next());
+ methodsToReprocess.addAll(dependencies.getOrDefault(field, ProgramMethodSet.empty()));
});
}
@@ -326,11 +332,20 @@
return;
}
- if (metadata != null && metadata.isReadForWrite()) {
- // Ignore this read. If the field ends up only being written, then we will still reprocess
- // the method with the read-for-write instruction, since the method contains a write that
- // requires reprocessing.
- return;
+ if (metadata != null) {
+ if (isUnusedReadAfterMethodStaticizing(metadata)) {
+ // Ignore this read.
+ dependencies
+ .computeIfAbsent(field.getDefinition(), ignoreKey(ProgramMethodSet::createConcurrent))
+ .add(getContext());
+ return;
+ }
+ if (metadata.isReadForWrite()) {
+ // Ignore this read. If the field ends up only being written, then we will still reprocess
+ // the method with the read-for-write instruction, since the method contains a write that
+ // requires reprocessing.
+ return;
+ }
}
// Record access.
@@ -352,6 +367,28 @@
}
}
+ private boolean isUnusedReadAfterMethodStaticizing(BytecodeInstructionMetadata metadata) {
+ if (!metadata.isReadForInvokeReceiver()) {
+ return false;
+ }
+ Set<DexMethod> readForInvokeReceiver = metadata.getReadForInvokeReceiver();
+ for (DexMethod methodReference : readForInvokeReceiver) {
+ DexMethod rewrittenMethodReference =
+ appView.graphLens().getRenamedMethodSignature(methodReference, appView.codeLens());
+ DexProgramClass holder =
+ asProgramClassOrNull(appView.definitionFor(rewrittenMethodReference.getHolderType()));
+ ProgramMethod method = rewrittenMethodReference.lookupOnProgramClass(holder);
+ if (method == null) {
+ assert false;
+ return false;
+ }
+ if (!method.getDefinition().isStatic()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void recordAccessThatCannotBeOptimized(
DexClassAndField field, DexEncodedField definition) {
constantFields.remove(definition);
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/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index 62cbb04..2ad1f65 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -275,6 +275,40 @@
}
@Override
+ public InvokeMethod insertNullCheckInstruction(
+ AppView<?> appView,
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Value value,
+ Position position) {
+ InternalOptions options = appView.options();
+
+ InvokeMethod invoke;
+ if (appView.options().canUseJavaUtilObjectsRequireNonNull()) {
+ DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
+ invoke =
+ InvokeStatic.builder()
+ .setMethod(requireNonNullMethod)
+ .setSingleArgument(value)
+ .setPosition(position)
+ .build();
+ } else {
+ DexMethod getClassMethod = appView.dexItemFactory().objectMembers.getClass;
+ invoke =
+ InvokeVirtual.builder()
+ .setMethod(getClassMethod)
+ .setSingleArgument(value)
+ .setPosition(position)
+ .build();
+ }
+ add(invoke);
+ if (block.hasCatchHandlers()) {
+ splitCopyCatchHandlers(code, blockIterator, options);
+ }
+ return invoke;
+ }
+
+ @Override
public boolean replaceCurrentInstructionByNullCheckIfPossible(
AppView<?> appView, ProgramMethod context) {
Instruction toBeReplaced = current;
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index f9bbcc8..a68da1a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -45,6 +45,17 @@
}
@Override
+ public InvokeMethod insertNullCheckInstruction(
+ AppView<?> appView,
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Value value,
+ Position position) {
+ return instructionIterator.insertNullCheckInstruction(
+ appView, code, blockIterator, value, position);
+ }
+
+ @Override
public boolean replaceCurrentInstructionByNullCheckIfPossible(
AppView<?> appView, ProgramMethod context) {
return instructionIterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index d08413d..bc6bec6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -100,6 +100,13 @@
Value insertConstStringInstruction(AppView<?> appView, IRCode code, DexString value);
+ InvokeMethod insertNullCheckInstruction(
+ AppView<?> appView,
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Value value,
+ Position position);
+
boolean replaceCurrentInstructionByNullCheckIfPossible(AppView<?> appView, ProgramMethod context);
boolean replaceCurrentInstructionByInitClassIfPossible(
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index cb20fd4..37d45df 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -69,6 +69,17 @@
}
@Override
+ public InvokeMethod insertNullCheckInstruction(
+ AppView<?> appView,
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Value value,
+ Position position) {
+ return currentBlockIterator.insertNullCheckInstruction(
+ appView, code, blockIterator, value, position);
+ }
+
+ @Override
public boolean replaceCurrentInstructionByNullCheckIfPossible(
AppView<?> appView, ProgramMethod context) {
return currentBlockIterator.replaceCurrentInstructionByNullCheckIfPossible(appView, context);
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/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index c4dba5e..45758d3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1505,6 +1505,9 @@
CodeRewriter.removeAssumeInstructions(appView, code);
timing.end();
assert code.isConsistentSSA();
+
+ // TODO(b/214496607): Remove when dynamic types are safe w.r.t. interface assignment rules.
+ codeRewriter.rewriteMoveResult(code);
}
// Assert that we do not have unremoved non-sense code in the output, e.g., v <- non-null NULL.
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/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index a3fc0e8..ee7f8e1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1091,27 +1091,44 @@
}
private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
- DexString name;
- DexProto proto;
- DexMethod method;
// sun.misc.Unsafe
+ {
+ // compareAndSwapObject(Object receiver, long offset, Object expect, Object update)
+ DexType type = factory.unsafeType;
+ DexString name = factory.createString("compareAndSwapObject");
+ DexProto proto =
+ factory.createProto(
+ factory.booleanType,
+ factory.objectType,
+ factory.longType,
+ factory.objectType,
+ factory.objectType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new StatifyingMethodWithForwardingGenerator(
+ method,
+ BackportedMethods::UnsafeMethods_compareAndSwapObject,
+ "compareAndSwapObject",
+ type));
+ }
- // compareAndSwapObject(Object receiver, long offset, Object expect, Object update)
- name = factory.createString("compareAndSwapObject");
- proto =
- factory.createProto(
- factory.booleanType,
- factory.objectType,
- factory.longType,
- factory.objectType,
- factory.objectType);
- method = factory.createMethod(factory.unsafeType, proto, name);
- addProvider(
- new StatifyingMethodWithForwardingGenerator(
- method,
- BackportedMethods::UnsafeMethods_compareAndSwapObject,
- "compareAndSwapObject",
- factory.unsafeType));
+ // java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+ {
+ // compareAndSet(Object object, Object expect, Object update)
+ DexType type =
+ factory.createType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
+ DexString name = factory.createString("compareAndSet");
+ DexProto proto =
+ factory.createProto(
+ factory.booleanType, factory.objectType, factory.objectType, factory.objectType);
+ DexMethod method = factory.createMethod(type, proto, name);
+ addProvider(
+ new StatifyingMethodWithForwardingGenerator(
+ method,
+ BackportedMethods::AtomicReferenceFieldUpdaterMethods_compareAndSet,
+ "compareAndSet",
+ type));
+ }
}
private void initializeJava9MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index b934c40..e6b793f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -97,6 +97,7 @@
factory.createSynthesizedType("Ljava/util/OptionalInt;");
factory.createSynthesizedType("Ljava/util/OptionalLong;");
factory.createSynthesizedType("Ljava/util/Set;");
+ factory.createSynthesizedType("Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;");
factory.createSynthesizedType("Ljava/util/function/Consumer;");
factory.createSynthesizedType("Ljava/util/function/DoubleConsumer;");
factory.createSynthesizedType("Ljava/util/function/IntConsumer;");
@@ -114,6 +115,85 @@
factory.createSynthesizedType("[Ljava/util/Map$Entry;");
}
+ public static CfCode AtomicReferenceFieldUpdaterMethods_compareAndSet(
+ InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initialized(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"),
+ options.itemFactory.createProto(
+ options.itemFactory.booleanType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType),
+ options.itemFactory.createString("compareAndSet")),
+ false),
+ new CfIf(If.Type.EQ, ValueType.INT, label2),
+ label1,
+ new CfConstNumber(1, ValueType.INT),
+ new CfReturn(ValueType.INT),
+ label2,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2, 3},
+ new FrameType[] {
+ FrameType.initialized(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.objectType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType(
+ "Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType, options.itemFactory.objectType),
+ options.itemFactory.createString("get")),
+ false),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+ label3,
+ new CfConstNumber(0, ValueType.INT),
+ new CfReturn(ValueType.INT),
+ label4),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode BooleanMethods_compare(InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java
new file mode 100644
index 0000000..c383dcd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/DerivedMethod.java
@@ -0,0 +1,52 @@
+// 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.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+
+/**
+ * A derived method is: - if the holderKind is null, a normal dexMethod; - if the holderKind is
+ * non-null, a method derived from the dexMethod In that case the method holder is used as the
+ * context to generate the holder type. The method may however differ (for example the method name
+ * may be different).
+ */
+public class DerivedMethod {
+
+ private final DexMethod method;
+ private final SyntheticKind holderKind;
+
+ public DerivedMethod(DexMethod method) {
+ this(method, null);
+ }
+
+ public DerivedMethod(DexMethod method, SyntheticKind holderKind) {
+ this.holderKind = holderKind;
+ this.method = method;
+ }
+
+ public SyntheticKind getHolderKind() {
+ return holderKind;
+ }
+
+ public DexType getHolderContext() {
+ return method.getHolderType();
+ }
+
+ public DexMethod getMethod() {
+ return method;
+ }
+
+ public DexString getName() {
+ return method.getName();
+ }
+
+ public DexProto getProto() {
+ return method.getProto();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
new file mode 100644
index 0000000..c5154cd
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/EmulatedDispatchMethodDescriptor.java
@@ -0,0 +1,71 @@
+// 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.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexType;
+import java.util.LinkedHashMap;
+
+public class EmulatedDispatchMethodDescriptor {
+
+ /**
+ * When resolving into the descriptor, if the resolution is used for a super-invoke or to generate
+ * a forwarding method, then the forwarding method should be used. If the resolution is used to
+ * rewrite an invoke, then it should be rewritten to an invoke-static to the emulated dispatch
+ * method.
+ *
+ * <p>Emulated dispatch method are generated as follows: <code>emulatedDispatchMethod {
+ * if (rcvr instanceof itf) {
+ * invoke-interface itfMethod
+ * }
+ * if (rcvr instanceof dispatchCases0) {
+ * invoke-static dispatchCases0
+ * }
+ * ...
+ * if (rcvr instanceof dispatchCasesN) {
+ * invoke-static dispatchCasesN
+ * }
+ * invoke-static forwardingMethod }</code>
+ *
+ * <p>For emulatedVirtualRetarget instances, the itfMethod holder and emulatedDispatchMethod
+ * holder are the itf and emulated dispatch holder types to synthesize. The forwardingMethod is
+ * the method to retarget to.
+ *
+ * <p>For emulated interface, itfMethod holder is the rewritten emulated interface type and the
+ * emulated dispatch method is on the $-EI class holding the dispatch methods. The forwarding
+ * method is the method on the companion class.
+ */
+ private final DerivedMethod interfaceMethod;
+
+ private final DerivedMethod emulatedDispatchMethod;
+ private final DerivedMethod forwardingMethod;
+ private final LinkedHashMap<DexType, DerivedMethod> dispatchCases;
+
+ public EmulatedDispatchMethodDescriptor(
+ DerivedMethod interfaceMethod,
+ DerivedMethod emulatedDispatchMethod,
+ DerivedMethod forwardingMethod,
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases) {
+ this.interfaceMethod = interfaceMethod;
+ this.emulatedDispatchMethod = emulatedDispatchMethod;
+ this.forwardingMethod = forwardingMethod;
+ this.dispatchCases = dispatchCases;
+ }
+
+ public DerivedMethod getInterfaceMethod() {
+ return interfaceMethod;
+ }
+
+ public DerivedMethod getEmulatedDispatchMethod() {
+ return emulatedDispatchMethod;
+ }
+
+ public DerivedMethod getForwardingMethod() {
+ return forwardingMethod;
+ }
+
+ public LinkedHashMap<DexType, DerivedMethod> getDispatchCases() {
+ return dispatchCases;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
new file mode 100644
index 0000000..2a88f9a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineDesugaredLibrarySpecification.java
@@ -0,0 +1,26 @@
+// 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.desugar.desugaredlibrary.machinespecification;
+
+/** TODO(b/184026720): Move the rest of the flags. */
+public class MachineDesugaredLibrarySpecification {
+
+ private final boolean libraryCompilation;
+ private final MachineRewritingFlags rewritingFlags;
+
+ public MachineDesugaredLibrarySpecification(
+ boolean libraryCompilation, MachineRewritingFlags rewritingFlags) {
+ this.libraryCompilation = libraryCompilation;
+ this.rewritingFlags = rewritingFlags;
+ }
+
+ public boolean isLibraryCompilation() {
+ return libraryCompilation;
+ }
+
+ public MachineRewritingFlags getRewritingFlags() {
+ return rewritingFlags;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
new file mode 100644
index 0000000..f73e1c9
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/machinespecification/MachineRewritingFlags.java
@@ -0,0 +1,82 @@
+// 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.desugar.desugaredlibrary.machinespecification;
+
+import com.android.tools.r8.graph.DexMethod;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public class MachineRewritingFlags {
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ MachineRewritingFlags(
+ Map<DexMethod, DexMethod> staticRetarget,
+ Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget) {
+ this.staticRetarget = staticRetarget;
+ this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
+ this.emulatedVirtualRetarget = emulatedVirtualRetarget;
+ }
+
+ // Static methods to retarget, duplicated to library boundaries.
+ private final Map<DexMethod, DexMethod> staticRetarget;
+
+ // Virtual methods to retarget, which are guaranteed not to require emulated dispatch.
+ // A method does not require emulated dispatch if two conditions are met:
+ // (1) the method does not override any other library method;
+ // (2) the method is final or installed in a final class.
+ // Any invoke resolving into the method will be rewritten into an invoke-static to the desugared
+ // code.
+ private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
+
+ // Virtual methods to retarget through emulated dispatch.
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
+
+ public Map<DexMethod, DexMethod> getStaticRetarget() {
+ return staticRetarget;
+ }
+
+ public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
+ return nonEmulatedVirtualRetarget;
+ }
+
+ public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
+ return emulatedVirtualRetarget;
+ }
+
+ public static class Builder {
+
+ Builder() {}
+
+ private final ImmutableMap.Builder<DexMethod, DexMethod> staticRetarget =
+ ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexMethod, DexMethod> nonEmulatedVirtualRetarget =
+ ImmutableMap.builder();
+ private final ImmutableMap.Builder<DexMethod, EmulatedDispatchMethodDescriptor>
+ emulatedVirtualRetarget = ImmutableMap.builder();
+
+ public void putStaticRetarget(DexMethod src, DexMethod dest) {
+ staticRetarget.put(src, dest);
+ }
+
+ public void putNonEmulatedVirtualRetarget(DexMethod src, DexMethod dest) {
+ nonEmulatedVirtualRetarget.put(src, dest);
+ }
+
+ public void putEmulatedVirtualRetarget(DexMethod src, EmulatedDispatchMethodDescriptor dest) {
+ emulatedVirtualRetarget.put(src, dest);
+ }
+
+ public MachineRewritingFlags build() {
+ return new MachineRewritingFlags(
+ staticRetarget.build(),
+ nonEmulatedVirtualRetarget.build(),
+ emulatedVirtualRetarget.build());
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
index c4a01f6..7c5d1c3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeter.java
@@ -9,14 +9,12 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
@@ -24,11 +22,10 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -40,22 +37,24 @@
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
private final RetargetingInfo retargetingInfo;
- private final Map<DexMethod, DexMethod> retargetLibraryMember;
- private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites;
- private final DexClassAndMethodSet emulatedDispatchMethods;
+ private final Map<DexMethod, DexMethod> staticRetarget;
+ private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
retargetingInfo = RetargetingInfo.get(appView);
- retargetLibraryMember = retargetingInfo.getRetargetLibraryMember();
- nonFinalHolderRewrites = retargetingInfo.getNonFinalHolderRewrites();
- emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
+ staticRetarget = retargetingInfo.getStaticRetarget();
+ nonEmulatedVirtualRetarget = retargetingInfo.getNonEmulatedVirtualRetarget();
+ emulatedVirtualRetarget = retargetingInfo.getEmulatedVirtualRetarget();
}
// Used by the ListOfBackportedMethods utility.
public void visit(Consumer<DexMethod> consumer) {
- retargetLibraryMember.keySet().forEach(consumer);
+ staticRetarget.keySet().forEach(consumer);
+ nonEmulatedVirtualRetarget.keySet().forEach(consumer);
+ emulatedVirtualRetarget.keySet().forEach(consumer);
}
public RetargetingInfo getRetargetingInfo() {
@@ -125,7 +124,7 @@
private InvokeRetargetingResult computeNewInvokeTarget(
CfInstruction instruction, ProgramMethod context) {
- if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
+ if (!instruction.isInvoke()) {
return NO_REWRITING;
}
if (appView
@@ -137,77 +136,56 @@
}
CfInvoke cfInvoke = instruction.asInvoke();
DexMethod invokedMethod = cfInvoke.getMethod();
- InvokeRetargetingResult retarget =
- computeRetargetedMethod(invokedMethod, cfInvoke.isInterface());
+ AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethod(invokedMethod, cfInvoke.isInterface());
+ if (!resolutionResult.isSingleResolution()) {
+ return NO_REWRITING;
+ }
+ DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ assert singleTarget != null;
+ if (cfInvoke.isInvokeStatic()) {
+ DexMethod retarget = staticRetarget.get(singleTarget.getReference());
+ return retarget == null
+ ? NO_REWRITING
+ : InvokeRetargetingResult.createInvokeRetargetingResult(retarget);
+ }
+ InvokeRetargetingResult retarget = computeNonStaticRetarget(singleTarget);
if (!retarget.hasNewInvokeTarget()) {
return NO_REWRITING;
}
- if (cfInvoke.isInvokeSuper(context.getHolderType())
- && matchesNonFinalHolderRewrite(invokedMethod)) {
- DexClassAndMethod superTarget =
- appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
- // Final methods can be rewritten as a normal invoke.
- if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
- return InvokeRetargetingResult.createInvokeRetargetingResult(
- appView.options().desugaredLibrarySpecification.retargetMethod(superTarget, appView));
+ if (cfInvoke.isInvokeSuper(context.getHolderType())) {
+ DexClassAndMethod superTarget = appInfo.lookupSuperTarget(invokedMethod, context);
+ if (superTarget != null) {
+ return computeSuperRetarget(superTarget.getDefinition());
}
}
return retarget;
}
- private InvokeRetargetingResult computeRetargetedMethod(
- DexMethod invokedMethod, boolean isInterface) {
- InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
- if (!invokeRetargetingResult.hasNewInvokeTarget()) {
- if (!matchesNonFinalHolderRewrite(invokedMethod)) {
- return NO_REWRITING;
- }
- // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
- MethodResolutionResult resolutionResult =
- appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
- if (resolutionResult.isFailedResolution()) {
- return NO_REWRITING;
- }
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
- assert singleTarget != null;
- invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
- }
- return invokeRetargetingResult;
- }
-
- private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
- DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
- if (emulatedMethod != null) {
- assert !emulatedMethod.getAccessFlags().isStatic();
+ private InvokeRetargetingResult computeNonStaticRetarget(DexEncodedMethod singleTarget) {
+ assert !singleTarget.isStatic();
+ DexMethod reference = singleTarget.getReference();
+ EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(reference);
+ if (descriptor != null) {
return new InvokeRetargetingResult(
true,
- eventConsumer -> {
- DexType newHolder =
- syntheticHelper.ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer)
- .type;
- return computeRetargetMethod(
- method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
- });
+ eventConsumer ->
+ syntheticHelper.ensureEmulatedHolderDispatchMethod(descriptor, eventConsumer));
}
- return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
+ return InvokeRetargetingResult.createInvokeRetargetingResult(
+ nonEmulatedVirtualRetarget.get(reference));
}
- private boolean matchesNonFinalHolderRewrite(DexMethod method) {
- List<DexMethod> dexMethods = nonFinalHolderRewrites.get(method.name);
- if (dexMethods == null) {
- return false;
+ private InvokeRetargetingResult computeSuperRetarget(DexEncodedMethod singleTarget) {
+ assert !singleTarget.isStatic();
+ DexMethod reference = singleTarget.getReference();
+ EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(reference);
+ if (descriptor != null) {
+ return InvokeRetargetingResult.createInvokeRetargetingResult(
+ syntheticHelper.ensureForwardingMethod(descriptor));
}
- for (DexMethod dexMethod : dexMethods) {
- if (method.match(dexMethod)) {
- return true;
- }
- }
- return false;
- }
-
- DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
- DexItemFactory factory = appView.dexItemFactory();
- DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
- return factory.createMethod(newHolder, newProto, method.getName());
+ return InvokeRetargetingResult.createInvokeRetargetingResult(
+ nonEmulatedVirtualRetarget.get(reference));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
index d757c94..11fcfb7 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterL8Synthesizer.java
@@ -4,21 +4,22 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import java.util.Map;
public class DesugaredLibraryRetargeterL8Synthesizer implements CfClassSynthesizerDesugaring {
private final AppView<?> appView;
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
- private final DexClassAndMethodSet emulatedDispatchMethods;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedDispatchMethods;
public static DesugaredLibraryRetargeterL8Synthesizer create(
AppView<?> appView, RetargetingInfo retargetingInfo) {
assert appView.options().isDesugaredLibraryCompilation();
- if (retargetingInfo == null || retargetingInfo.getEmulatedDispatchMethods().isEmpty()) {
+ if (retargetingInfo == null || retargetingInfo.getEmulatedVirtualRetarget().isEmpty()) {
assert appView.options().desugaredLibrarySpecification.getRetargetCoreLibMember().isEmpty();
return null;
}
@@ -29,13 +30,14 @@
AppView<?> appView, RetargetingInfo retargetingInfo) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
- emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
+ emulatedDispatchMethods = retargetingInfo.getEmulatedVirtualRetarget();
}
@Override
public void synthesizeClasses(CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
assert !emulatedDispatchMethods.isEmpty();
- for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ for (EmulatedDispatchMethodDescriptor emulatedDispatchMethod :
+ emulatedDispatchMethods.values()) {
syntheticHelper.ensureProgramEmulatedHolderDispatchMethod(
emulatedDispatchMethod, eventConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index aae08a7..414c32d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -5,7 +5,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
@@ -15,15 +14,16 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
import com.android.tools.r8.utils.OptionalBool;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.Maps;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -34,13 +34,13 @@
private final AppView<?> appView;
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
- private final DexClassAndMethodSet emulatedDispatchMethods;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedDispatchMethods;
public DesugaredLibraryRetargeterPostProcessor(
AppView<?> appView, RetargetingInfo retargetingInfo) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
- emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
+ emulatedDispatchMethods = retargetingInfo.getEmulatedVirtualRetarget();
}
@Override
@@ -57,11 +57,12 @@
Collection<DexProgramClass> programClasses,
DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer) {
assert !appView.options().isDesugaredLibraryCompilation();
- Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
- for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
- map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
- map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
- }
+ Map<DexType, List<DexMethod>> map = Maps.newIdentityHashMap();
+ emulatedDispatchMethods.forEach(
+ (method, descriptor) -> {
+ map.putIfAbsent(method.getHolderType(), new ArrayList<>(1));
+ map.get(method.getHolderType()).add(method);
+ });
for (DexProgramClass clazz : programClasses) {
if (clazz.superType == null) {
assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
@@ -84,7 +85,9 @@
}
private boolean inherit(
- DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
+ DexLibraryClass clazz,
+ DexType typeToInherit,
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> retarget) {
DexLibraryClass current = clazz;
while (current.type != appView.dexItemFactory().objectType) {
if (current.type == typeToInherit) {
@@ -92,7 +95,7 @@
}
DexClass dexClass = appView.definitionFor(current.superType);
if (dexClass == null || dexClass.isClasspathClass()) {
- reportInvalidLibrarySupertype(current, retarget);
+ reportInvalidLibrarySupertype(current, retarget.keySet());
return false;
} else if (dexClass.isProgramClass()) {
// If dexClass is a program class, then it is already correctly desugared.
@@ -106,7 +109,7 @@
private void ensureInterfacesAndForwardingMethodsSynthesized(
DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer,
DexProgramClass clazz,
- List<DexClassAndMethod> methods) {
+ List<DexMethod> methods) {
// DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
// methods.
// We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
@@ -114,9 +117,10 @@
if (appView.isAlreadyLibraryDesugared(clazz)) {
return;
}
- for (DexClassAndMethod method : methods) {
+ for (DexMethod method : methods) {
+ EmulatedDispatchMethodDescriptor descriptor = emulatedDispatchMethods.get(method);
DexClass newInterface =
- syntheticHelper.ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
+ syntheticHelper.ensureEmulatedInterfaceDispatchMethod(descriptor, eventConsumer);
if (clazz.interfaces.contains(newInterface.type)) {
// The class has already been desugared.
continue;
@@ -131,30 +135,35 @@
clazz.addExtraInterfaces(
Collections.singletonList(new ClassTypeSignature(newInterface.type)));
eventConsumer.acceptInterfaceInjection(clazz, newInterface);
- if (clazz.lookupVirtualMethod(method.getReference()) == null) {
- DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
+ DexMethod itfMethod =
+ syntheticHelper.getEmulatedInterfaceDispatchMethod(newInterface, descriptor);
+ if (clazz.lookupVirtualMethod(method) == null) {
+ DexEncodedMethod newMethod = createForwardingMethod(itfMethod, descriptor, clazz);
clazz.addVirtualMethod(newMethod);
eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
}
}
}
- private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
+ private DexEncodedMethod createForwardingMethod(
+ DexMethod target, EmulatedDispatchMethodDescriptor descriptor, DexClass clazz) {
// NOTE: Never add a forwarding method to methods of classes unknown or coming from android.jar
// even if this results in invalid code, these classes are never desugared.
// In desugared library, emulated interface methods can be overridden by retarget lib members.
- DexMethod forwardMethod =
- appView.options().desugaredLibrarySpecification.retargetMethod(target, appView);
- assert forwardMethod != null && forwardMethod != target.getReference();
+ DexMethod forwardMethod = syntheticHelper.ensureForwardingMethod(descriptor);
+ assert forwardMethod != null && forwardMethod != target;
+ DexEncodedMethod resolvedMethod =
+ appView.appInfoForDesugaring().resolveMethod(target, true).getResolvedMethod();
+ assert resolvedMethod != null;
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
- target, clazz, forwardMethod, appView.dexItemFactory());
+ resolvedMethod, clazz, forwardMethod, appView.dexItemFactory());
desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
return desugaringForwardingMethod;
}
private void reportInvalidLibrarySupertype(
- DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
+ DexLibraryClass libraryClass, Set<DexMethod> retarget) {
DexClass dexClass = appView.definitionFor(libraryClass.superType);
String message;
if (dexClass == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index 1a46480..5a5768f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -5,19 +5,21 @@
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
-import java.util.Collections;
+import java.util.LinkedHashMap;
public class DesugaredLibraryRetargeterSyntheticHelper {
@@ -27,54 +29,87 @@
this.appView = appView;
}
- public DexClass ensureEmulatedHolderDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
+ public DexMethod ensureForwardingMethod(EmulatedDispatchMethodDescriptor descriptor) {
+ // TODO(b/184026720): We may synthesize a stub on the classpath if absent.
+ assert descriptor.getForwardingMethod().getHolderKind() == null;
+ return descriptor.getForwardingMethod().getMethod();
+ }
+
+ private DexMethod emulatedHolderDispatchMethod(DexType holder, DerivedMethod method) {
+ assert method.getHolderKind() == SyntheticKind.RETARGET_CLASS;
+ return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
+ }
+
+ DexMethod emulatedInterfaceDispatchMethod(DexType holder, DerivedMethod method) {
+ assert method.getHolderKind() == SyntheticKind.RETARGET_INTERFACE;
+ return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
+ }
+
+ public DexMethod getEmulatedInterfaceDispatchMethod(
+ DexClass newInterface, EmulatedDispatchMethodDescriptor descriptor) {
+ DexMethod method =
+ emulatedInterfaceDispatchMethod(newInterface.type, descriptor.getInterfaceMethod());
+ assert newInterface.lookupMethod(method) != null;
+ return method;
+ }
+
+ public DexMethod ensureEmulatedHolderDispatchMethod(
+ EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert eventConsumer != null;
+ DerivedMethod emulatedDispatchMethod = descriptor.getEmulatedDispatchMethod();
+ DexClass holderContext =
+ appView.contextIndependentDefinitionFor(emulatedDispatchMethod.getHolderContext());
+ DexClass syntheticClass;
if (appView.options().isDesugaredLibraryCompilation()) {
- return appView
- .getSyntheticItems()
- .getExistingFixedClass(
- SyntheticKind.RETARGET_CLASS, emulatedDispatchMethod.getHolder(), appView);
+ syntheticClass =
+ appView
+ .getSyntheticItems()
+ .getExistingFixedClass(
+ emulatedDispatchMethod.getHolderKind(), holderContext, appView);
+ DexMethod dispatchMethod =
+ emulatedHolderDispatchMethod(syntheticClass.type, emulatedDispatchMethod);
+ assert syntheticClass.lookupMethod(dispatchMethod) != null;
+ return dispatchMethod;
+ } else {
+ DexClass itfClass = ensureEmulatedInterfaceDispatchMethod(descriptor, eventConsumer);
+ ClasspathOrLibraryClass context = holderContext.asClasspathOrLibraryClass();
+ assert context != null;
+ syntheticClass =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClass(
+ SyntheticKind.RETARGET_CLASS,
+ context,
+ appView,
+ classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
+ clazz -> {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+ rewriteType(clazz.type);
+ });
}
- DexClass interfaceClass =
- ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
- DexMethod itfMethod =
- interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
- ClasspathOrLibraryClass context =
- emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
- assert context != null;
- return appView
- .getSyntheticItems()
- .ensureFixedClasspathClass(
- SyntheticKind.RETARGET_CLASS,
- context,
- appView,
- classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
- clazz -> {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
- rewriteType(clazz.type);
- });
+ DexMethod dispatchMethod =
+ emulatedHolderDispatchMethod(syntheticClass.type, emulatedDispatchMethod);
+ assert syntheticClass.lookupMethod(dispatchMethod) != null;
+ return dispatchMethod;
}
public void ensureProgramEmulatedHolderDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
+ EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer eventConsumer) {
assert eventConsumer != null;
assert appView.options().isDesugaredLibraryCompilation();
- DexClass interfaceClass =
- ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
- DexMethod itfMethod =
- interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
+ DerivedMethod emulatedDispatchMethod = descriptor.getEmulatedDispatchMethod();
+ DexClass holderContext =
+ appView.contextIndependentDefinitionFor(emulatedDispatchMethod.getHolderContext());
+ DexClass itfClass = ensureEmulatedInterfaceDispatchMethod(descriptor, eventConsumer);
appView
.getSyntheticItems()
.ensureFixedClass(
- SyntheticKind.RETARGET_CLASS,
- emulatedDispatchMethod.getHolder(),
+ emulatedDispatchMethod.getHolderKind(),
+ holderContext,
appView,
- classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+ classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor),
clazz -> {
eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
rewriteType(clazz.type);
@@ -82,17 +117,17 @@
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
+ EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert eventConsumer != null;
+ DerivedMethod itfMethod = descriptor.getInterfaceMethod();
+ DexClass itfContext = appView.contextIndependentDefinitionFor(itfMethod.getHolderContext());
if (appView.options().isDesugaredLibraryCompilation()) {
return appView
.getSyntheticItems()
- .getExistingFixedClass(
- SyntheticKind.RETARGET_INTERFACE, emulatedDispatchMethod.getHolder(), appView);
+ .getExistingFixedClass(itfMethod.getHolderKind(), itfContext, appView);
}
- ClasspathOrLibraryClass context =
- emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
+ ClasspathOrLibraryClass context = itfContext.asClasspathOrLibraryClass();
assert context != null;
return appView
.getSyntheticItems()
@@ -100,7 +135,7 @@
SyntheticKind.RETARGET_INTERFACE,
context,
appView,
- classBuilder -> buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
clazz -> {
eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
rewriteType(clazz.type);
@@ -108,17 +143,19 @@
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
+ EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
assert eventConsumer != null;
+ DerivedMethod itfMethod = descriptor.getInterfaceMethod();
+ DexClass itfContext = appView.contextIndependentDefinitionFor(itfMethod.getHolderContext());
return appView
.getSyntheticItems()
.ensureFixedClass(
- SyntheticKind.RETARGET_INTERFACE,
- emulatedDispatchMethod.getHolder(),
+ itfMethod.getHolderKind(),
+ itfContext,
appView,
- classBuilder -> buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
clazz -> {
eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
rewriteType(clazz.type);
@@ -126,18 +163,21 @@
}
private void buildInterfaceDispatchMethod(
- SyntheticClassBuilder<?, ?> classBuilder, DexClassAndMethod emulatedDispatchMethod) {
+ SyntheticClassBuilder<?, ?> classBuilder, EmulatedDispatchMethodDescriptor descriptor) {
classBuilder
.setInterface()
.addMethod(
methodBuilder -> {
+ DexMethod itfMethod =
+ emulatedInterfaceDispatchMethod(
+ classBuilder.getType(), descriptor.getInterfaceMethod());
MethodAccessFlags flags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC,
false);
methodBuilder
- .setName(emulatedDispatchMethod.getName())
- .setProto(emulatedDispatchMethod.getProto())
+ .setName(itfMethod.getName())
+ .setProto(itfMethod.getProto())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.setAccessFlags(flags);
@@ -145,36 +185,36 @@
}
private <SCB extends SyntheticClassBuilder<?, ?>> void buildHolderDispatchMethod(
- SCB classBuilder, DexClassAndMethod emulatedDispatchMethod, DexMethod itfMethod) {
+ SCB classBuilder, DexClass itfClass, EmulatedDispatchMethodDescriptor descriptor) {
classBuilder.addMethod(
methodBuilder -> {
- DexMethod desugarMethod =
- appView
- .options()
- .desugaredLibrarySpecification
- .retargetMethod(emulatedDispatchMethod, appView);
- assert desugarMethod
- != null; // This method is reached only for retarget core lib members.
+ DexMethod dispatchMethod =
+ emulatedHolderDispatchMethod(
+ classBuilder.getType(), descriptor.getEmulatedDispatchMethod());
methodBuilder
- .setName(emulatedDispatchMethod.getName())
- .setProto(desugarMethod.proto)
+ .setName(dispatchMethod.getName())
+ .setProto(dispatchMethod.getProto())
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.setCode(
methodSig ->
appView.options().isDesugaredLibraryCompilation()
- ? new EmulateDispatchSyntheticCfCodeProvider(
- methodSig.getHolderType(),
- desugarMethod,
- itfMethod,
- Collections.emptyList(),
- appView)
- .generateCfCode()
+ ? generateEmulatedDispatchCfCode(descriptor, itfClass, methodSig)
: null);
});
}
+ private CfCode generateEmulatedDispatchCfCode(
+ EmulatedDispatchMethodDescriptor descriptor, DexClass itfClass, DexMethod methodSig) {
+ DexMethod forwardingMethod = ensureForwardingMethod(descriptor);
+ DexMethod itfMethod = getEmulatedInterfaceDispatchMethod(itfClass, descriptor);
+ assert descriptor.getDispatchCases().isEmpty();
+ return new EmulateDispatchSyntheticCfCodeProvider(
+ methodSig.getHolderType(), forwardingMethod, itfMethod, new LinkedHashMap<>(), appView)
+ .generateCfCode();
+ }
+
private void rewriteType(DexType type) {
String newName =
appView.options().desugaredLibrarySpecification.convertJavaNameToDesugaredLibrary(type);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
index d9656fe..eb38e9e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/RetargetingInfo.java
@@ -12,55 +12,64 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class RetargetingInfo {
- private final Map<DexMethod, DexMethod> retargetLibraryMember;
- // Map nonFinalRewrite hold a methodName -> method mapping for methods which are rewritten while
- // the holder is non final. In this case d8 needs to force resolution of given methods to see if
- // the invoke needs to be rewritten.
- private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites;
- // Non final virtual library methods requiring generation of emulated dispatch.
- private final DexClassAndMethodSet emulatedDispatchMethods;
+ private final Map<DexMethod, DexMethod> staticRetarget;
+ private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
RetargetingInfo(
- Map<DexMethod, DexMethod> retargetLibraryMember,
- Map<DexString, List<DexMethod>> nonFinalHolderRewrites,
- DexClassAndMethodSet emulatedDispatchMethods) {
- this.retargetLibraryMember = retargetLibraryMember;
- this.nonFinalHolderRewrites = nonFinalHolderRewrites;
- this.emulatedDispatchMethods = emulatedDispatchMethods;
+ Map<DexMethod, DexMethod> staticRetarget,
+ Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget,
+ Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget) {
+ this.staticRetarget = staticRetarget;
+ this.nonEmulatedVirtualRetarget = nonEmulatedVirtualRetarget;
+ this.emulatedVirtualRetarget = emulatedVirtualRetarget;
}
- public static synchronized RetargetingInfo get(AppView<?> appView) {
+ public static RetargetingInfo get(AppView<?> appView) {
+ if (appView.options().testing.machineDesugaredLibrarySpecification != null) {
+ MachineRewritingFlags rewritingFlags =
+ appView.options().testing.machineDesugaredLibrarySpecification.getRewritingFlags();
+ return new RetargetingInfo(
+ rewritingFlags.getStaticRetarget(),
+ rewritingFlags.getNonEmulatedVirtualRetarget(),
+ rewritingFlags.getEmulatedVirtualRetarget());
+ }
return new RetargetingInfoBuilder(appView).computeRetargetingInfo();
}
- public Map<DexMethod, DexMethod> getRetargetLibraryMember() {
- return retargetLibraryMember;
+ public Map<DexMethod, DexMethod> getStaticRetarget() {
+ return staticRetarget;
}
- public Map<DexString, List<DexMethod>> getNonFinalHolderRewrites() {
- return nonFinalHolderRewrites;
+ public Map<DexMethod, DexMethod> getNonEmulatedVirtualRetarget() {
+ return nonEmulatedVirtualRetarget;
}
- public DexClassAndMethodSet getEmulatedDispatchMethods() {
- return emulatedDispatchMethods;
+ public Map<DexMethod, EmulatedDispatchMethodDescriptor> getEmulatedVirtualRetarget() {
+ return emulatedVirtualRetarget;
}
private static class RetargetingInfoBuilder {
private final AppView<?> appView;
- private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
- private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
- private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
+ private final Map<DexMethod, DexMethod> staticRetarget = new IdentityHashMap<>();
+ private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget = new IdentityHashMap<>();
+ private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget =
+ new IdentityHashMap<>();
public RetargetingInfoBuilder(AppView<?> appView) {
this.appView = appView;
@@ -72,8 +81,7 @@
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
desugaredLibrarySpecification.getRetargetCoreLibMember();
if (retargetCoreLibMember.isEmpty()) {
- return new RetargetingInfo(
- ImmutableMap.of(), ImmutableMap.of(), DexClassAndMethodSet.empty());
+ return new RetargetingInfo(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
}
for (DexString methodName : retargetCoreLibMember.keySet()) {
for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
@@ -82,28 +90,41 @@
DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
for (DexClassAndMethod method : found) {
- boolean emulatedDispatch = false;
DexMethod methodReference = method.getReference();
- if (!typeClass.isFinal()) {
- nonFinalHolderRewrites.putIfAbsent(method.getName(), new ArrayList<>());
- nonFinalHolderRewrites.get(method.getName()).add(methodReference);
- if (!method.getAccessFlags().isStatic()) {
- if (isEmulatedInterfaceDispatch(method)) {
- // In this case interface method rewriter takes care of it.
- continue;
- } else if (!method.getAccessFlags().isFinal()) {
- // Virtual rewrites require emulated dispatch for inheritance.
- // The call is rewritten to the dispatch holder class instead.
- emulatedDispatchMethods.add(method);
- emulatedDispatch = true;
- }
- }
- }
- if (!emulatedDispatch) {
- retargetLibraryMember.put(
+ if (method.getAccessFlags().isStatic()) {
+ staticRetarget.put(
methodReference,
computeRetargetMethod(
methodReference, method.getAccessFlags().isStatic(), newHolder));
+ continue;
+ }
+ if (isEmulatedInterfaceDispatch(method)) {
+ continue;
+ }
+ if (typeClass.isFinal() || method.getAccessFlags().isFinal()) {
+ nonEmulatedVirtualRetarget.put(
+ methodReference,
+ computeRetargetMethod(
+ methodReference, method.getAccessFlags().isStatic(), newHolder));
+ } else {
+ // Virtual rewrites require emulated dispatch for inheritance.
+ // The call is rewritten to the dispatch holder class instead.
+ DexProto newProto = appView.dexItemFactory().prependHolderToProto(methodReference);
+ DexMethod forwardingDexMethod =
+ appView.dexItemFactory().createMethod(newHolder, newProto, methodName);
+ DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
+ DerivedMethod interfaceMethod =
+ new DerivedMethod(methodReference, SyntheticKind.RETARGET_INTERFACE);
+ DexMethod dispatchDexMethod =
+ appView
+ .dexItemFactory()
+ .createMethod(methodReference.getHolderType(), newProto, methodName);
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
+ emulatedVirtualRetarget.put(
+ methodReference,
+ new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, new LinkedHashMap<>()));
}
}
}
@@ -123,7 +144,7 @@
DexMethod target =
computeRetargetMethod(
source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
- retargetLibraryMember.put(source, target);
+ staticRetarget.put(source, target);
// TODO(b/181629049): This is only a workaround rewriting invokes of
// j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
name = itemFactory.createString("getTimeZone");
@@ -136,12 +157,12 @@
target =
computeRetargetMethod(
source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
- retargetLibraryMember.put(source, target);
+ staticRetarget.put(source, target);
}
return new RetargetingInfo(
- ImmutableMap.copyOf(retargetLibraryMember),
- ImmutableMap.copyOf(nonFinalHolderRewrites),
- emulatedDispatchMethods);
+ ImmutableMap.copyOf(staticRetarget),
+ ImmutableMap.copyOf(nonEmulatedVirtualRetarget),
+ emulatedVirtualRetarget);
}
DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
new file mode 100644
index 0000000..d2227cb
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/HumanToMachineSpecificationConverter.java
@@ -0,0 +1,211 @@
+// 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.desugar.desugaredlibrary.specificationconversion;
+
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineRewritingFlags;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.function.BiConsumer;
+
+public class HumanToMachineSpecificationConverter {
+
+ public MachineDesugaredLibrarySpecification convert(
+ HumanDesugaredLibrarySpecification humanSpec, Path androidLib, InternalOptions options)
+ throws IOException {
+ DexApplication app = readApp(androidLib, options);
+ AppView<?> appView = AppView.createForD8(AppInfo.createInitialAppInfo(app));
+ MachineRewritingFlags machineRewritingFlags =
+ convertRewritingFlags(humanSpec.getRewritingFlags(), appView.appInfoForDesugaring());
+ return new MachineDesugaredLibrarySpecification(
+ humanSpec.isLibraryCompilation(), machineRewritingFlags);
+ }
+
+ private MachineRewritingFlags convertRewritingFlags(
+ HumanRewritingFlags rewritingFlags, AppInfoWithClassHierarchy appInfo) {
+ MachineRewritingFlags.Builder builder = MachineRewritingFlags.builder();
+ SubtypingInfo subtypingInfo = new SubtypingInfo(appInfo);
+ rewritingFlags
+ .getRetargetCoreLibMember()
+ .forEach(
+ (method, type) ->
+ convertRetargetCoreLibMemberFlag(
+ builder, rewritingFlags, method, type, appInfo, subtypingInfo));
+ return builder.build();
+ }
+
+ private void convertRetargetCoreLibMemberFlag(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexMethod method,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo) {
+ DexClass holder = appInfo.definitionFor(method.holder);
+ DexEncodedMethod foundMethod = holder.lookupMethod(method);
+ assert foundMethod != null;
+ if (foundMethod.isStatic()) {
+ convertStaticRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
+ return;
+ }
+ if (holder.isFinal() || foundMethod.isFinal()) {
+ convertNonEmulatedVirtualRetarget(builder, foundMethod, type, appInfo, subtypingInfo);
+ return;
+ }
+ convertEmulatedVirtualRetarget(
+ builder, rewritingFlags, foundMethod, type, appInfo, subtypingInfo);
+ }
+
+ private void convertEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder,
+ HumanRewritingFlags rewritingFlags,
+ DexEncodedMethod src,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo) {
+ if (isEmulatedInterfaceDispatch(src, appInfo, rewritingFlags)) {
+ // Handled by emulated interface dispatch.
+ return;
+ }
+ // TODO(b/184026720): Implement library boundaries.
+ DexProto newProto = appInfo.dexItemFactory().prependHolderToProto(src.getReference());
+ DexMethod forwardingDexMethod =
+ appInfo.dexItemFactory().createMethod(type, newProto, src.getName());
+ DerivedMethod forwardingMethod = new DerivedMethod(forwardingDexMethod);
+ DerivedMethod interfaceMethod =
+ new DerivedMethod(src.getReference(), SyntheticKind.RETARGET_INTERFACE);
+ DexMethod dispatchDexMethod =
+ appInfo.dexItemFactory().createMethod(src.getHolderType(), newProto, src.getName());
+ DerivedMethod dispatchMethod =
+ new DerivedMethod(dispatchDexMethod, SyntheticKind.RETARGET_CLASS);
+ LinkedHashMap<DexType, DerivedMethod> dispatchCases = new LinkedHashMap<>();
+ assert validateNoOverride(src, appInfo, subtypingInfo);
+ builder.putEmulatedVirtualRetarget(
+ src.getReference(),
+ new EmulatedDispatchMethodDescriptor(
+ interfaceMethod, dispatchMethod, forwardingMethod, dispatchCases));
+ }
+
+ private boolean validateNoOverride(
+ DexEncodedMethod src, AppInfoWithClassHierarchy appInfo, SubtypingInfo subtypingInfo) {
+ for (DexType subtype : subtypingInfo.subtypes(src.getHolderType())) {
+ DexClass subclass = appInfo.definitionFor(subtype);
+ MethodResolutionResult resolutionResult =
+ appInfo.resolveMethodOn(subclass, src.getReference());
+ if (resolutionResult.isSuccessfulMemberResolutionResult()
+ && resolutionResult.getResolvedMethod().getReference() != src.getReference()) {
+ assert false; // Unsupported.
+ }
+ }
+ return true;
+ }
+
+ private boolean isEmulatedInterfaceDispatch(
+ DexEncodedMethod method,
+ AppInfoWithClassHierarchy appInfo,
+ HumanRewritingFlags humanRewritingFlags) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ humanRewritingFlags.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.getReference();
+ // Look-up all superclass and interfaces, if an emulated interface is found,
+ // and it implements the method, answers true.
+ DexClass dexClass = appInfo.definitionFor(method.getHolderType());
+ // Cannot retarget a method on a virtual method on an emulated interface.
+ assert !emulateLibraryInterface.containsKey(dexClass.getType());
+ return appInfo
+ .traverseSuperTypes(
+ dexClass,
+ (supertype, subclass, isSupertypeAnInterface) ->
+ TraversalContinuation.breakIf(
+ subclass.isInterface()
+ && emulateLibraryInterface.containsKey(subclass.getType())
+ && subclass.lookupMethod(methodToFind) != null))
+ .shouldBreak();
+ }
+
+ private void convertNonEmulatedRetarget(
+ DexEncodedMethod foundMethod,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo,
+ BiConsumer<DexMethod, DexMethod> consumer) {
+ DexMethod src = foundMethod.getReference();
+ DexMethod dest = src.withHolder(type, appInfo.dexItemFactory());
+ consumer.accept(src, dest);
+ for (DexType subtype : subtypingInfo.subtypes(foundMethod.getHolderType())) {
+ DexClass subclass = appInfo.definitionFor(subtype);
+ MethodResolutionResult resolutionResult = appInfo.resolveMethodOn(subclass, src);
+ if (resolutionResult.isSuccessfulMemberResolutionResult()
+ && resolutionResult.getResolvedMethod().getReference() == src) {
+ consumer.accept(src.withHolder(subtype, appInfo.dexItemFactory()), dest);
+ }
+ }
+ }
+
+ private void convertNonEmulatedVirtualRetarget(
+ MachineRewritingFlags.Builder builder,
+ DexEncodedMethod foundMethod,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo) {
+ convertNonEmulatedRetarget(
+ foundMethod,
+ type,
+ appInfo,
+ subtypingInfo,
+ (src, dest) ->
+ builder.putNonEmulatedVirtualRetarget(
+ src,
+ dest.withExtraArgumentPrepended(
+ foundMethod.getHolderType(), appInfo.dexItemFactory())));
+ }
+
+ private void convertStaticRetarget(
+ MachineRewritingFlags.Builder builder,
+ DexEncodedMethod foundMethod,
+ DexType type,
+ AppInfoWithClassHierarchy appInfo,
+ SubtypingInfo subtypingInfo) {
+ convertNonEmulatedRetarget(
+ foundMethod, type, appInfo, subtypingInfo, builder::putStaticRetarget);
+ }
+
+ private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
+ AndroidApp androidApp = AndroidApp.builder().addProgramFile(androidLib).build();
+ ApplicationReader applicationReader =
+ new ApplicationReader(androidApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ return applicationReader.read(executorService).toDirect();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
index 14898fc..34bf5fa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/LegacyToHumanSpecificationConverter.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanRewritingFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanTopLevelFlags;
@@ -43,9 +42,8 @@
import java.util.Map;
import java.util.concurrent.ExecutorService;
-public class LegacyToHumanSpecificationConverter implements SpecificationConverter {
+public class LegacyToHumanSpecificationConverter {
- @Override
public void convertAllAPILevels(
StringResource inputSpecification, Path androidLib, StringConsumer output)
throws IOException {
@@ -136,8 +134,7 @@
libraryFlags.put(level, builder.build());
}
- private DirectMappedDexApplication readApp(Path androidLib, InternalOptions options)
- throws IOException {
+ private DexApplication readApp(Path androidLib, InternalOptions options) throws IOException {
AndroidApp androidApp = AndroidApp.builder().addLibraryFile(androidLib).build();
ApplicationReader applicationReader =
new ApplicationReader(androidApp, options, Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java
deleted file mode 100644
index 85a8c8b..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/specificationconversion/SpecificationConverter.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright (c) 2021, 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.desugar.desugaredlibrary.specificationconversion;
-
-import com.android.tools.r8.StringConsumer;
-import com.android.tools.r8.StringResource;
-import java.io.IOException;
-import java.nio.file.Path;
-
-public interface SpecificationConverter {
-
- void convertAllAPILevels(
- StringResource inputSpecification, Path androidLib, StringConsumer output) throws IOException;
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 0d79ac1..3a91fef 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -853,7 +853,7 @@
: appView.options().desugaredLibrarySpecification.retargetMethod(target, appView);
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
- target, clazz, forwardMethod, dexItemFactory);
+ target.getDefinition(), clazz, forwardMethod, dexItemFactory);
if (!target.isProgramDefinition()
|| target.getDefinition().isLibraryMethodOverride().isTrue()) {
desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 3d91f5c..55cf273 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -28,6 +27,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -143,7 +143,7 @@
.withHolder(helper.getEmulatedInterface(theInterface.type), appView.dexItemFactory());
DexMethod companionMethod =
helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
- List<Pair<DexType, DexMethod>> extraDispatchCases =
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
return new EmulateDispatchSyntheticCfCodeProvider(
emulatedInterfaceMethod.getHolderType(),
@@ -154,14 +154,14 @@
.generateCfCode();
}
- private List<Pair<DexType, DexMethod>> getDispatchCases(
+ private LinkedHashMap<DexType, DexMethod> getDispatchCases(
ProgramMethod method, DexProgramClass theInterface, DexMethod companionMethod) {
// To properly emulate the library interface call, we need to compute the interfaces
// inheriting from the interface and manually implement the dispatch with instance of.
// The list guarantees that an interface is always after interfaces it extends,
// hence reverse iteration.
List<DexType> subInterfaces = emulatedInterfacesHierarchy.get(theInterface.type);
- List<Pair<DexType, DexMethod>> extraDispatchCases = new ArrayList<>();
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
// In practice, there is usually a single case (except for tests),
// so we do not bother to make the following loop more clever.
Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
@@ -171,17 +171,16 @@
for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
DexClass inClass = appView.definitionFor(inType);
if (inClass != null && implementsInterface(inClass, theInterface.type)) {
- extraDispatchCases.add(
- new Pair<>(
- inType,
- appView
- .dexItemFactory()
- .createMethod(
- retargetCoreLibMember.get(methodName).get(inType),
- appView
- .dexItemFactory()
- .protoWithDifferentFirstParameter(companionMethod.proto, inType),
- method.getName())));
+ extraDispatchCases.put(
+ inType,
+ appView
+ .dexItemFactory()
+ .createMethod(
+ retargetCoreLibMember.get(methodName).get(inType),
+ appView
+ .dexItemFactory()
+ .protoWithDifferentFirstParameter(companionMethod.proto, inType),
+ method.getName()));
}
}
}
@@ -196,11 +195,10 @@
DexEncodedMethod result = subInterfaceClass.lookupVirtualMethod(method.getReference());
if (result != null && !result.isAbstract()) {
assert result.isDefaultMethod();
- extraDispatchCases.add(
- new Pair<>(
- subInterfaceClass.type,
- InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
- result.getReference(), appView.dexItemFactory())));
+ extraDispatchCases.put(
+ subInterfaceClass.type,
+ InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass(
+ result.getReference(), appView.dexItemFactory()));
}
}
} else {
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/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 9997cc0..862840b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -43,6 +43,7 @@
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
import com.android.tools.r8.ir.optimize.enums.classification.CheckNotNullEnumUnboxerMethodClassification;
import com.android.tools.r8.ir.optimize.enums.code.CheckNotZeroCode;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
@@ -189,10 +190,10 @@
.setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> new CheckNotZeroCode(checkNotNullMethod))
.setOptimizationInfo(
- checkNotNullMethod
- .getOptimizationInfo()
- .asMutableMethodOptimizationInfo()
- .mutableCopy())
+ DefaultMethodOptimizationInfo.getInstance()
+ .toMutableOptimizationInfo()
+ .setEnumUnboxerMethodClassification(
+ checkNotNullClassification))
.setProto(newProto));
checkNotNullToCheckNotZeroMapping.put(
checkNotNullMethod.getReference(), checkNotZeroMethod.getReference());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index 6356bbf..b651b1f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -24,7 +24,7 @@
// The index exactly matches with in values of invocation, i.e., even including receiver.
public DynamicType getDynamicType(int argIndex) {
- return null;
+ return DynamicType.unknown();
}
// The index exactly matches with in values of invocation, i.e., even including receiver.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 2cb900b..e07fee5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -282,12 +282,13 @@
return enumUnboxerMethodClassification;
}
- void setEnumUnboxerMethodClassification(
+ public MutableMethodOptimizationInfo setEnumUnboxerMethodClassification(
EnumUnboxerMethodClassification enumUnboxerMethodClassification) {
// Check monotonicity.
assert !this.enumUnboxerMethodClassification.isCheckNotNullClassification()
|| enumUnboxerMethodClassification.isCheckNotNullClassification();
this.enumUnboxerMethodClassification = enumUnboxerMethodClassification;
+ return this;
}
public void unsetEnumUnboxerMethodClassification() {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index 4847e78..e356796 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -21,29 +21,30 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import java.util.ArrayList;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import org.objectweb.asm.Opcodes;
public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
- private final DexType receiverType;
private final DexMethod forwardingMethod;
+ private final DexType receiverType;
private final DexMethod interfaceMethod;
- private final List<Pair<DexType, DexMethod>> extraDispatchCases;
+ private final LinkedHashMap<DexType, DexMethod> extraDispatchCases;
public EmulateDispatchSyntheticCfCodeProvider(
DexType holder,
DexMethod forwardingMethod,
DexMethod interfaceMethod,
- List<Pair<DexType, DexMethod>> extraDispatchCases,
+ LinkedHashMap<DexType, DexMethod> extraDispatchCases,
AppView<?> appView) {
super(appView, holder);
- this.receiverType = forwardingMethod.getParameter(0);
this.forwardingMethod = forwardingMethod;
+ this.receiverType = forwardingMethod.getParameter(0);
this.interfaceMethod = interfaceMethod;
this.extraDispatchCases = extraDispatchCases;
}
@@ -78,19 +79,19 @@
addReturn(instructions);
// SubInterface dispatch (subInterfaces are ordered).
- for (Pair<DexType, DexMethod> dispatch : extraDispatchCases) {
+ for (Map.Entry<DexType, DexMethod> dispatch : extraDispatchCases.entrySet()) {
// Type check basic block.
instructions.add(labels[nextLabel++]);
instructions.add(new CfFrame(locals, ImmutableDeque.of()));
instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
- instructions.add(new CfInstanceOf(dispatch.getFirst()));
+ instructions.add(new CfInstanceOf(dispatch.getKey()));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
// Call basic block.
instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
- instructions.add(new CfCheckCast(dispatch.getFirst()));
+ instructions.add(new CfCheckCast(dispatch.getKey()));
loadExtraParameters(instructions);
- instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getSecond(), false));
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getValue(), false));
addReturn(instructions);
}
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 ea756b5..dce1aee 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -143,6 +143,9 @@
return iterator;
}
Value in = instruction.value();
+ if (in.isDexItemBasedConstString()) {
+ return iterator;
+ }
if (!in.isConstString()) {
warnUndeterminedIdentifierIfNecessary(field, code.context(), instruction, null);
return iterator;
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index cd6c271..8a8d804 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessInfoCollection;
import com.android.tools.r8.graph.MethodResolutionResult;
-import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThreadUtils;
@@ -123,7 +123,6 @@
}
private final AppView<AppInfoWithLiveness> appView;
- private final SubtypingInfo subtypingInfo;
private final MemberNamingStrategy strategy;
private final Map<DexMethod, DexString> renaming = new IdentityHashMap<>();
@@ -141,12 +140,11 @@
MethodNameMinifier(
AppView<AppInfoWithLiveness> appView,
- SubtypingInfo subtypingInfo,
MemberNamingStrategy strategy) {
this.appView = appView;
- this.subtypingInfo = subtypingInfo;
this.strategy = strategy;
rootReservationState = MethodReservationState.createRoot(getReservationKeyTransform());
+ reservationStates.put(null, rootReservationState);
rootNamingState =
MethodNamingState.createRoot(getNamingKeyTransform(), strategy, rootReservationState);
namingStates.put(null, rootNamingState);
@@ -202,7 +200,8 @@
timing.end();
// Phase 4: Assign names top-down by traversing the subtype hierarchy.
timing.begin("Phase 4");
- assignNamesToClassesMethods(appView.dexItemFactory().objectType, rootNamingState);
+ assignNamesToClassesMethods();
+ renameMethodsInUnrelatedClasspathClasses();
timing.end();
timing.begin("Phase 5: non-rebound references");
renameNonReboundReferences(executorService);
@@ -211,21 +210,46 @@
return new MethodRenaming(renaming);
}
- private void assignNamesToClassesMethods(DexType type, MethodNamingState<?> parentNamingState) {
- MethodReservationState<?> reservationState =
- reservationStates.get(frontiers.getOrDefault(type, type));
- assert reservationState != null : "Could not find reservation state for " + type.toString();
- MethodNamingState<?> namingState =
- namingStates.computeIfAbsent(
- type, ignore -> parentNamingState.createChild(reservationState));
- DexClass holder = appView.definitionFor(type);
- if (holder != null && strategy.allowMemberRenaming(holder)) {
- for (DexEncodedMethod method : holder.allMethodsSorted()) {
- assignNameToMethod(holder, method, namingState);
- }
- }
- for (DexType subType : subtypingInfo.allImmediateExtendsSubtypes(type)) {
- assignNamesToClassesMethods(subType, namingState);
+ private void assignNamesToClassesMethods() {
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .excludeInterfaces()
+ .visit(
+ appView.appInfo().classes(),
+ clazz -> {
+ DexType type = clazz.type;
+ MethodReservationState<?> reservationState =
+ reservationStates.get(frontiers.getOrDefault(type, type));
+ assert reservationState != null
+ : "Could not find reservation state for " + type.toString();
+ MethodNamingState<?> namingState =
+ namingStates.computeIfAbsent(
+ type,
+ ignore ->
+ namingStates
+ .getOrDefault(clazz.superType, rootNamingState)
+ .createChild(reservationState));
+ DexClass holder = appView.definitionFor(type);
+ if (holder != null && strategy.allowMemberRenaming(holder)) {
+ for (DexEncodedMethod method : holder.allMethodsSorted()) {
+ assignNameToMethod(holder, method, namingState);
+ }
+ }
+ });
+ }
+
+ private void renameMethodsInUnrelatedClasspathClasses() {
+ if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
+ appView
+ .appInfo()
+ .forEachReferencedClasspathClass(
+ clazz -> {
+ for (DexEncodedMethod method : clazz.methods()) {
+ DexString reservedName = strategy.getReservedName(method, clazz);
+ if (reservedName != null && reservedName != method.getReference().name) {
+ renaming.put(method.getReference(), reservedName);
+ }
+ }
+ });
}
}
@@ -248,42 +272,30 @@
}
private void reserveNamesInClasses() {
- reserveNamesInClasses(
- appView.dexItemFactory().objectType,
- appView.dexItemFactory().objectType,
- rootReservationState);
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .visit(
+ appView.appInfo().classes(),
+ clazz -> {
+ DexType type = clazz.type;
+ DexType frontier = frontiers.getOrDefault(clazz.superType, type);
+ if (frontier != type || clazz.isProgramClass()) {
+ DexType existingValue = frontiers.put(clazz.type, frontier);
+ assert existingValue == null;
+ }
+ // If this is not a program class (or effectively a library class as it is missing)
+ // move the frontier forward. This will ensure all reservations are put on the library
+ // or classpath frontier for the program path.
+ allocateReservationStateAndReserve(
+ type,
+ frontier,
+ reservationStates.getOrDefault(clazz.superType, rootReservationState));
+ });
}
- private void reserveNamesInClasses(
- DexType type, DexType libraryFrontier, MethodReservationState<?> parentReservationState) {
- assert !appView.isInterface(type).isTrue();
-
- MethodReservationState<?> reservationState =
- allocateReservationStateAndReserve(type, libraryFrontier, parentReservationState);
-
- // If this is not a program class (or effectively a library class as it is missing) move the
- // frontier forward. This will ensure all reservations are put on the library or classpath
- // frontier for the program path.
- DexClass holder = appView.definitionFor(type);
- for (DexType subtype : subtypingInfo.allImmediateExtendsSubtypes(type)) {
- reserveNamesInClasses(
- subtype,
- holder == null || holder.isNotProgramClass() ? subtype : libraryFrontier,
- reservationState);
- }
- }
-
- private MethodReservationState<?> allocateReservationStateAndReserve(
+ private void allocateReservationStateAndReserve(
DexType type, DexType frontier, MethodReservationState<?> parent) {
- assert parent != null;
-
- if (frontier != type) {
- frontiers.put(type, frontier);
- }
-
MethodReservationState<?> state =
reservationStates.computeIfAbsent(frontier, ignore -> parent.createChild());
-
DexClass holder = appView.definitionFor(type);
if (holder != null) {
for (DexEncodedMethod method : shuffleMethods(holder.methods(), appView.options())) {
@@ -293,8 +305,6 @@
}
}
}
-
- return state;
}
private MethodNamingState<?> getOrAllocateMethodNamingStates(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/naming/Minifier.java b/src/main/java/com/android/tools/r8/naming/Minifier.java
index e7106a6..738be08 100644
--- a/src/main/java/com/android/tools/r8/naming/Minifier.java
+++ b/src/main/java/com/android/tools/r8/naming/Minifier.java
@@ -66,7 +66,7 @@
MemberNamingStrategy minifyMembers = new MinifierMemberNamingStrategy(appView);
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appView, subtypingInfo, minifyMembers)
+ new MethodNameMinifier(appView, minifyMembers)
.computeRenaming(interfaces, executorService, timing);
timing.end();
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 5c07e4a..7ee0681 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapMinifier.java
@@ -147,7 +147,7 @@
new ApplyMappingMemberNamingStrategy(appView, memberNames);
timing.begin("MinifyMethods");
MethodRenaming methodRenaming =
- new MethodNameMinifier(appView, subtypingInfo, nameStrategy)
+ new MethodNameMinifier(appView, nameStrategy)
.computeRenaming(interfaces, executorService, timing);
// Amend the method renamings with the default interface methods.
methodRenaming.renaming.putAll(defaultInterfaceMethodImplementationNames);
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index de68c75..32bc033 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -127,6 +127,7 @@
return resolvedMethod.isLibraryMethod()
&& isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts)
&& !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType)
+ && !isInvokeSuperToAbstractMethod(resolvedMethod, invokeType)
&& isApiSafeForMemberRebinding(
resolvedMethod.asLibraryMethod(), androidApiLevelCompute, options);
}
@@ -147,6 +148,10 @@
return method.getHolder().isInterface() && invokeType.isSuper();
}
+ private boolean isInvokeSuperToAbstractMethod(DexClassAndMethod method, Type invokeType) {
+ return method.getAccessFlags().isAbstract() && invokeType.isSuper();
+ }
+
public static DexField validMemberRebindingTargetFor(
DexDefinitionSupplier definitions, DexClassAndField field, DexField original) {
if (field.isProgramField()) {
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/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 570ed16..62529ed 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -249,6 +249,12 @@
if (prunedItems.hasRemovedClasses()) {
keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
}
+ if (prunedItems.hasRemovedFields()) {
+ keepFieldInfo.keySet().removeAll(prunedItems.getRemovedFields());
+ }
+ if (prunedItems.hasRemovedMembers()) {
+ keepMethodInfo.keySet().removeAll(prunedItems.getRemovedMethods());
+ }
}
public void removeKeepInfoForPrunedItems(PrunedItems prunedItems) {
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index c882cf4..a7362e5 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1335,7 +1335,8 @@
}
List<FormalTypeParameter> formals = source.getClassSignature().getFormalTypeParameters();
if (genericArgumentsToSuperType.size() != formals.size()) {
- assert false : "Invalid argument count to formals";
+ // TODO(b/214509535): Correctly rewrite signature when type arguments is empty.
+ assert genericArgumentsToSuperType.isEmpty() : "Invalid argument count to formals";
return null;
}
Map<String, FieldTypeSignature> substitutionMap = new HashMap<>();
diff --git a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
index 06fdc4c..13f630e 100644
--- a/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ArrayUtils.java
@@ -47,39 +47,6 @@
return results;
}
- /**
- * Filters the input array based on the given predicate.
- *
- * @param clazz target type's Class to cast
- * @param original an array of original elements
- * @param filter a predicate that tells us what to keep
- * @param <T> target type
- * @return a partial copy of the original array
- */
- public static <T> T[] filter(Class<T[]> clazz, T[] original, Predicate<T> filter) {
- ArrayList<T> filtered = null;
- for (int i = 0; i < original.length; i++) {
- T elt = original[i];
- if (filter.test(elt)) {
- if (filtered != null) {
- filtered.add(elt);
- }
- } else {
- if (filtered == null) {
- filtered = new ArrayList<>(original.length);
- for (int j = 0; j < i; j++) {
- filtered.add(original[j]);
- }
- }
- }
- }
- if (filtered == null) {
- return original;
- }
- return filtered.toArray(
- clazz.cast(Array.newInstance(clazz.getComponentType(), filtered.size())));
- }
-
public static <T> boolean isEmpty(T[] array) {
return array.length == 0;
}
@@ -131,8 +98,23 @@
return results != null ? results.toArray(emptyArray) : original;
}
- public static <T> T[] filter(T[] original, Predicate<T> test, T[] emptyArray) {
- return map(original, e -> test.test(e) ? e : null, emptyArray);
+ public static <T> T[] filter(T[] original, Predicate<T> predicate, T[] emptyArray) {
+ return map(original, e -> predicate.test(e) ? e : null, emptyArray);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T[] filter(T[] original, Predicate<T> predicate, T[] emptyArray, int newSize) {
+ T[] result = (T[]) Array.newInstance(emptyArray.getClass().getComponentType(), newSize);
+ int newIndex = 0;
+ for (int originalIndex = 0; originalIndex < original.length; originalIndex++) {
+ T element = original[originalIndex];
+ if (predicate.test(element)) {
+ result[newIndex] = element;
+ newIndex++;
+ }
+ }
+ assert newIndex == newSize;
+ return result;
}
public static int[] createIdentityArray(int size) {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index f3dcf92..c1dc2c9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -54,6 +54,7 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.desugar.desugaredlibrary.legacyspecification.LegacyDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
@@ -74,7 +75,6 @@
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.IROrdering.IdentityIROrdering;
import com.android.tools.r8.utils.IROrdering.NondeterministicIROrdering;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
import com.google.common.annotations.VisibleForTesting;
@@ -1037,7 +1037,7 @@
DexType libraryType,
DexType invalidSuperType,
String message,
- DexClassAndMethodSet retarget) {
+ Set<DexMethod> retarget) {
if (invalidLibraryClasses.add(invalidSuperType)) {
reporter.warning(
new InvalidLibrarySuperclassDiagnostic(
@@ -1046,8 +1046,7 @@
Reference.classFromDescriptor(invalidSuperType.toDescriptorString()),
message,
Lists.newArrayList(
- Iterables.transform(
- retarget, method -> method.getReference().asMethodReference()))));
+ Iterables.transform(retarget, method -> method.asMethodReference()))));
}
}
@@ -1615,6 +1614,9 @@
public Consumer<Deque<ProgramMethodSet>> waveModifier = waves -> {};
+ // Meant to replace desugaredLibrarySpecification, set only from tests at the moment.
+ public MachineDesugaredLibrarySpecification machineDesugaredLibrarySpecification = null;
+
/**
* If this flag is enabled, we will also compute the set of possible targets for invoke-
* interface and invoke-virtual instructions that target a library method, and add the
diff --git a/src/test/examples/inlining/NoHorizontalClassMerging.java b/src/test/examples/inlining/NoHorizontalClassMerging.java
new file mode 100644
index 0000000..fcd446d
--- /dev/null
+++ b/src/test/examples/inlining/NoHorizontalClassMerging.java
@@ -0,0 +1,10 @@
+// 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 inlining;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.TYPE})
+public @interface NoHorizontalClassMerging {}
diff --git a/src/test/examples/inlining/NoMethodStaticizing.java b/src/test/examples/inlining/NoMethodStaticizing.java
new file mode 100644
index 0000000..1ffa6f0
--- /dev/null
+++ b/src/test/examples/inlining/NoMethodStaticizing.java
@@ -0,0 +1,10 @@
+// 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 inlining;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoMethodStaticizing {}
diff --git a/src/test/examples/inlining/Nullability.java b/src/test/examples/inlining/Nullability.java
index 4c8dcb0..472e06a 100644
--- a/src/test/examples/inlining/Nullability.java
+++ b/src/test/examples/inlining/Nullability.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package inlining;
+@NoHorizontalClassMerging
class Nullability {
private final int f;
public final int publicField;
@@ -48,6 +49,7 @@
return a != null ? a.b() : -1;
}
+ @NoMethodStaticizing
int notInlinableOnThrow(Throwable t) throws Throwable {
// NPE is not preserved if t is not a NullPointerException.
throw t;
@@ -61,6 +63,7 @@
}
}
+ @NoMethodStaticizing
public int notInlinableDueToMissingNpeBeforeThrow(Throwable t) throws Throwable {
try {
throw t;
diff --git a/src/test/examples/inlining/keep-rules.txt b/src/test/examples/inlining/keep-rules.txt
index 2c0f288..bf9bc45 100644
--- a/src/test/examples/inlining/keep-rules.txt
+++ b/src/test/examples/inlining/keep-rules.txt
@@ -17,3 +17,7 @@
-keepconstantarguments class * {
@inlining.KeepConstantArguments <methods>;
}
+-nohorizontalclassmerging @inlining.NoHorizontalClassMerging class *
+-nomethodstaticizing class * {
+ @inlining.NoMethodStaticizing <methods>;
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
index 9f88bbb..420bf8d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -11,6 +11,7 @@
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -50,6 +51,7 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
// We are testing that we do not inline/merge higher api-levels
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .enableNoMethodStaticizingAnnotations()
.noMinification()
.compile()
.inspect(
@@ -94,6 +96,8 @@
}
public interface ApiCaller {
+
+ @NoMethodStaticizing
default void callApiLevel() {
System.out.println("ApiCaller::callApiLevel");
Api.apiLevel22();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index da967e8..d105c17 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -54,6 +55,7 @@
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.addVerticallyMergedClassesInspector(
inspector -> {
if (parameters.isDexRuntime()
@@ -105,6 +107,7 @@
public static class Sub extends Base {
@NeverInline
+ @NoMethodStaticizing
public void callCallApi() {
System.out.println("Sub::callCallApi");
callApi();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index 7276937..352cd3a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -54,6 +55,7 @@
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.addVerticallyMergedClassesInspector(
inspector -> {
if (parameters.isDexRuntime()
@@ -105,6 +107,7 @@
public static class Sub extends Base {
@NeverInline
+ @NoMethodStaticizing
public void callCallApi() {
System.out.println("Sub::callCallApi");
callApi();
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index b9cf537..581fca7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -91,7 +91,7 @@
};
}
- static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+ public static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
ThrowableConsumer<T> setMockApiLevelForClass(Class<?> clazz, AndroidApiLevel apiLevel) {
return compilerBuilder -> {
compilerBuilder.addOptionsModification(
@@ -104,7 +104,8 @@
};
}
- static void enableApiCallerIdentification(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ public static void enableApiCallerIdentification(
+ TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
options.apiModelingOptions().enableApiCallerIdentification = true;
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 036dd46..fb4f27e 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -501,6 +501,7 @@
}
private void configure(InternalOptions options) {
+ options.callSiteOptimizationOptions().setEnableMethodStaticizing(false);
// Callees are invoked with a simple constant, e.g., "Bar". Propagating it into the callees
// bothers what the tests want to check, such as exact instructions in the body that include
// invocation kinds, like virtual call to a bridge.
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index bf166ba..1eb5247 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -75,6 +76,7 @@
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
// TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
@@ -125,6 +127,7 @@
// in TestClass.main().
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
/*bridge*/ String bridgeB(Object o) {
return (String) m((String) o);
}
@@ -138,6 +141,7 @@
// access bridgeC() via class C. From B, the bridge can be hoisted again to A.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String bridgeC(Object o) {
return (String) m((String) o);
}
@@ -150,6 +154,7 @@
// in TestClass.main().
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
/*bridge*/ String bridgeB(Object o, int a, int b, int c, int d, int e) {
return (String) m((String) o, a, b, c, d, e);
}
@@ -163,6 +168,7 @@
// access bridgeC() via class C. From B, the bridge can be hoisted again to A.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String bridgeC(Object o, int a, int b, int c, int d, int e) {
return (String) m((String) o, a, b, c, d, e);
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index bdd55fc..120d7b4 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -55,6 +56,7 @@
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
// TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
.noMinification()
@@ -112,11 +114,13 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public Object m(String arg) {
return System.currentTimeMillis() >= 0 ? arg : null;
}
@NeverInline
+ @NoMethodStaticizing
public Object m2(String arg) {
return System.currentTimeMillis() >= 0 ? arg : null;
}
@@ -129,6 +133,7 @@
// invoke-virtual instruction.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String superBridge(Object o) {
return (String) super.m((String) o);
}
@@ -136,6 +141,7 @@
// This bridge can be hoisted to A.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String virtualBridge(Object o) {
return (String) m((String) o);
}
@@ -148,6 +154,7 @@
// By hoisting B1.superBridge() to A this method bridge redundant.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String superBridge(Object o) {
return (String) super.m((String) o);
}
@@ -155,6 +162,7 @@
// By hoisting B1.virtualBridge() to A this method bridge redundant.
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String virtualBridge(Object o) {
return (String) m((String) o);
}
@@ -182,12 +190,14 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public String superBridge(Object o) {
return System.currentTimeMillis() >= 0 ? ((String) o) : null;
}
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public String virtualBridge(Object o) {
return System.currentTimeMillis() >= 0 ? ((String) o) : null;
}
@@ -201,12 +211,14 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String superBridge(Object o) {
return (String) super.m2((String) o);
}
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public /*bridge*/ String virtualBridge(Object o) {
return (String) m2((String) o);
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
index 36cc8be..c26b1c6 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.bridgeremoval.hoisting.BridgeHoistingAccessibilityTest;
import com.android.tools.r8.bridgeremoval.hoisting.BridgeHoistingAccessibilityTest.CWithRangedInvoke;
@@ -18,6 +19,7 @@
public static class A {
@NeverInline
+ @NoMethodStaticizing
public Object m(String arg) {
return System.currentTimeMillis() > 0 ? arg : null;
}
@@ -37,6 +39,7 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public Object m(String arg, int a, int b, int c, int d, int e) {
return System.currentTimeMillis() > 0 ? arg + a + b + c + d + e : null;
}
diff --git a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
index 43d02cb..792f793 100644
--- a/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
+++ b/src/test/java/com/android/tools/r8/cf/MissingClassJoinsToObjectTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.cf;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -52,6 +53,7 @@
.addProgramClasses(TestClass.class, A.class)
.addKeepMainRule(TestClass.class)
.addDontWarn(B.class)
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(getRuntimeClasspath())
@@ -66,6 +68,7 @@
static class A {
@NeverInline
+ @NoMethodStaticizing
public void foo() {
System.out.println("A::foo");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
index 48e6535..d276d0c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
@@ -44,6 +44,8 @@
.addHorizontallyMergedClassesInspector(
inspector ->
inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .addOptionsModification(
+ options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableUnusedArgumentAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
index 508b10b..ef4dacd 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.testclasses.InterfacesVisibilityTestClasses;
import com.android.tools.r8.classmerging.horizontal.testclasses.InterfacesVisibilityTestClasses.ImplementingPackagePrivateInterface;
@@ -32,6 +33,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -59,6 +61,7 @@
@NeverClassInline
public static class A {
@NeverInline
+ @NoMethodStaticizing
public void bar() {
System.out.println("bar");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
index 4ebbd51..621a805 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -26,6 +27,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
@@ -47,6 +49,7 @@
@NeverClassInline
public static class B {
@NeverInline
+ @NoMethodStaticizing
public void bar() {
System.out.println("bar");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java
index f540d9d..d96c16c 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonPublicOverrideOfPublicMethodAfterAbstractClassMergingTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -48,6 +49,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -84,6 +86,7 @@
abstract static class A {
@NeverInline
+ @NoMethodStaticizing
void m() {
System.out.println("A.m()");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
index 6529d20..440ccde 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -70,6 +71,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -110,6 +112,7 @@
@NoVerticalClassMerging
interface J {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("J");
}
@@ -120,6 +123,7 @@
@NoVerticalClassMerging
interface K {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("K");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
index 5ee0365..dc48b45 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -60,6 +61,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -100,6 +102,7 @@
@NoVerticalClassMerging
interface J {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("J");
}
@@ -110,6 +113,7 @@
@NoVerticalClassMerging
interface K {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("K");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
index a1dbbaf..9fa9260 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -55,6 +56,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -107,6 +109,7 @@
@NoVerticalClassMerging
interface J {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("J");
}
@@ -117,6 +120,7 @@
@NoVerticalClassMerging
interface K {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("K");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
index 2c40a58..a544c7e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -50,6 +51,7 @@
})
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -80,6 +82,7 @@
@NoVerticalClassMerging
interface I {
@NeverInline
+ @NoMethodStaticizing
default void f() {
System.out.println("I");
}
@@ -89,6 +92,7 @@
@NoVerticalClassMerging
interface J {
@NeverInline
+ @NoMethodStaticizing
default void g() {
System.out.println("J");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
index e9cd1c1..f58252e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalOverrideAfterInterfaceMergingTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -48,6 +49,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -96,6 +98,7 @@
// Intentionally package private. If J is merged into I then this is an illegal override of
// I.m().
@NeverInline
+ @NoMethodStaticizing
void m() {
System.out.println("A.m()");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
index 00a1394..420ccaa 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/IllegalSiblingAfterInterfaceMergingTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -49,6 +50,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -101,6 +103,7 @@
// Intentionally package private. If J is merged into I then this is an illegal override of
// I.m().
@NeverInline
+ @NoMethodStaticizing
void m() {
System.out.println("A.m()");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
index f68dd90..9d18660 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoUnusedInterfaceRemoval;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -48,6 +49,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoUnusedInterfaceRemovalAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -91,6 +93,7 @@
@NoVerticalClassMerging
interface I {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("I");
}
@@ -100,6 +103,7 @@
@NoVerticalClassMerging
interface J {
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println("J");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsTest.java
new file mode 100644
index 0000000..00d8557
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithMissingTypeArgsTest.java
@@ -0,0 +1,60 @@
+// 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.classmerging.vertical;
+
+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 VerticalClassMergingWithMissingTypeArgsTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributes("Signature, InnerClasses, EnclosingMethod, *Annotation*")
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("B.m()");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ new B().m();
+ }
+ }
+
+ static class A<T> {}
+
+ interface I<T> {}
+
+ static class B extends A implements I<B> {
+ @NeverInline
+ void m() {
+ System.out.println("B.m()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
new file mode 100644
index 0000000..1e66d40
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AtomicReferenceFieldUpdaterTest.java
@@ -0,0 +1,47 @@
+// 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AtomicReferenceFieldUpdaterTest extends AbstractBackportTest {
+
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+ .withAllApiLevels()
+ .build();
+ }
+
+ public AtomicReferenceFieldUpdaterTest(TestParameters parameters) {
+ super(parameters, AtomicReferenceFieldUpdater.class, Main.class);
+
+ ignoreInvokes("newUpdater");
+
+ // java.util.concurrent.atomic.AtomicReferenceFieldUpdater issue is on API 31, see b/211646483.
+ registerTarget(AndroidApiLevel.Sv2, 3);
+ }
+
+ public static class Main extends MiniAssert {
+ public volatile String field;
+
+ public static void main(String[] args) throws Exception {
+ AtomicReferenceFieldUpdater<Main, String> updater =
+ AtomicReferenceFieldUpdater.newUpdater(Main.class, String.class, "field");
+ Main x = new Main();
+ assertTrue(updater.compareAndSet(x, null, "A"));
+ assertTrue(updater.compareAndSet(x, "A", "B"));
+ assertFalse(updater.compareAndSet(x, "A", "B"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index bd338b6..8db2a9c 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -5,14 +5,19 @@
package com.android.tools.r8.desugar.backports;
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.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -20,13 +25,38 @@
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 com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.stream.Collectors;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+@RunWith(Parameterized.class)
public class TestBackportedNotPresentInAndroidJar extends TestBase {
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ private Set<DexMethod> expectedToAlwaysBePresentInAndroidJar(DexItemFactory factory)
+ throws Exception {
+ MethodReference compareAndSet =
+ Reference.methodFromMethod(
+ AtomicReferenceFieldUpdater.class.getDeclaredMethod(
+ "compareAndSet", Object.class, Object.class, Object.class));
+ assert compareAndSet.getReturnType().getTypeName().equals("boolean");
+ return ImmutableSet.of(factory.createMethod(compareAndSet));
+ }
+
@Test
public void testBackportedMethodsPerAPILevel() throws Exception {
for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
@@ -43,6 +73,7 @@
List<DexMethod> backportedMethods =
BackportedMethodRewriter.generateListOfBackportedMethods(
AndroidApp.builder().build(), options, ThreadUtils.getExecutorService(options));
+ Set<DexMethod> alwaysPresent = expectedToAlwaysBePresentInAndroidJar(options.itemFactory);
for (DexMethod method : backportedMethods) {
// Two different DexItemFactories are in play, but as toSourceString is used for lookup
// that is not an issue.
@@ -55,7 +86,9 @@
.map(DexType::toSourceString)
.collect(Collectors.toList()));
assertThat(
- foundInAndroidJar + " present in " + apiLevel, foundInAndroidJar, not(isPresent()));
+ foundInAndroidJar + " present in " + apiLevel,
+ foundInAndroidJar,
+ notIf(isPresent(), !alwaysPresent.contains(method)));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
index dbe597d..00134b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.java
@@ -39,7 +39,7 @@
ignoreInvokes("objectFieldOffset");
- // sun.misc.Unsafe issue is on API 31.
+ // sun.misc.Unsafe issue is on API 31, see b/211646483..
registerTarget(AndroidApiLevel.Sv2, 3);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
index 61a1bf8..6bf49a0 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetOverrideTest.java
@@ -5,7 +5,13 @@
package com.android.tools.r8.desugar.desugaredlibrary;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.humanspecification.HumanDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.HumanToMachineSpecificationConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.specificationconversion.LegacyToHumanSpecificationConverter;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import java.time.ZonedDateTime;
@@ -25,15 +31,19 @@
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
+ private final boolean machineSpec;
- @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ @Parameters(name = "machine: {0}, {2}, shrink: {1}")
public static List<Object[]> data() {
return buildParameters(
BooleanUtils.values(),
+ BooleanUtils.values(),
getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
}
- public RetargetOverrideTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public RetargetOverrideTest(
+ boolean machineSpec, boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.machineSpec = machineSpec;
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -44,6 +54,7 @@
Path desugaredTwice =
testForD8(Backend.CF)
.addLibraryFiles(getLibraryFile())
+ .addOptionsModification(this::setMachineSpec)
.addProgramFiles(
testForD8(Backend.CF)
.addLibraryFiles(getLibraryFile())
@@ -65,6 +76,7 @@
stdout =
testForD8(Backend.DEX)
.addProgramFiles(desugaredTwice)
+ .addOptionsModification(this::setMachineSpec)
.setMinApi(parameters.getApiLevel())
.disableDesugaring()
.compile()
@@ -95,6 +107,22 @@
assertLines2By2Correct(stdout);
}
+ private void setMachineSpec(InternalOptions opt) {
+ if (!machineSpec) {
+ return;
+ }
+ try {
+ HumanDesugaredLibrarySpecification human =
+ new LegacyToHumanSpecificationConverter()
+ .convert(opt.desugaredLibrarySpecification, getLibraryFile(), opt);
+ MachineDesugaredLibrarySpecification machine =
+ new HumanToMachineSpecificationConverter().convert(human, getLibraryFile(), opt);
+ opt.testing.machineDesugaredLibrarySpecification = machine;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
@Test
public void testRetargetOverrideD8() throws Exception {
Assume.assumeTrue(parameters.getRuntime().isDex());
@@ -102,6 +130,7 @@
String stdout =
testForD8()
.addLibraryFiles(getLibraryFile())
+ .addOptionsModification(this::setMachineSpec)
.addInnerClasses(RetargetOverrideTest.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.setMinApi(parameters.getApiLevel())
@@ -127,6 +156,7 @@
String stdout =
testForR8(Backend.DEX)
.addLibraryFiles(getLibraryFile())
+ .addOptionsModification(this::setMachineSpec)
.addKeepMainRule(Executor.class)
.addInnerClasses(RetargetOverrideTest.class)
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
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/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
index 093f902..9dcd9cb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/VirtualMethodOverrideEnumUnboxingTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -49,6 +50,7 @@
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addOptionsModification(options -> enableEnumOptions(options, enumValueOptimization))
.addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(MyEnum.class))
@@ -93,6 +95,7 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
void m(int x, MyEnum y) {
System.out.println("B.m(" + x + " : int, " + y.toString() + " : MyEnum)");
}
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
index 644fc9c..cb43c01 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
@@ -6,6 +6,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -146,6 +147,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(MainClassFailing.class)
.addOptionsModification(o -> o.testing.allowTypeErrors = true)
+ .enableNoMethodStaticizingAnnotations()
.run(parameters.getRuntime(), MainClassFailing.class)
.apply(r -> checkNonVerifyingResult(r, true));
}
@@ -190,6 +192,7 @@
static class SubClassOfInvokerClass extends InvokerClass {
+ @NoMethodStaticizing
public void subLevel2Method() {
System.out.println("subLevel2Method in SubClassOfInvokerClass");
}
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
index 99ab7b3..d05122a 100644
--- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertEquals;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -50,6 +51,7 @@
.addProgramClasses(I.class)
.addProgramClassFileData(getClassWithTransformedInvoked())
.addKeepMainRule(Main.class)
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class)
@@ -75,6 +77,7 @@
public interface I {
+ @NoMethodStaticizing
default void foo() {
System.out.println("Hello World!");
}
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index 6b593fd..128f780 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -58,6 +58,7 @@
testForR8(Backend.CF)
.addProgramFiles(outDirectory.resolve("program.jar"))
.apply(this::configure)
+ .apply(this::configureCf)
.compile();
testForR8(Backend.DEX)
.addProgramFiles(compileResult.writeToZip())
@@ -79,6 +80,7 @@
testForR8Compat(Backend.CF)
.addProgramFiles(outDirectory.resolve("program.jar"))
.apply(this::configure)
+ .apply(this::configureCf)
.compile();
testForR8Compat(Backend.DEX)
.addProgramFiles(compileResult.writeToZip())
@@ -93,7 +95,12 @@
.addKeepRuleFiles(outDirectory.resolve("proguard.config"))
.setMinApi(AndroidApiLevel.M)
.allowDiagnosticMessages()
+ .allowUnnecessaryDontWarnWildcards()
.allowUnusedDontWarnPatterns()
.allowUnusedProguardConfigurationRules();
}
+
+ private void configureCf(R8TestBuilder<?> testBuilder) {
+ testBuilder.addOptionsModification(options -> options.horizontalClassMergerOptions().disable());
+ }
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 82bc605..2e3512d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -86,7 +86,7 @@
OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
FieldAccessAnalysis fieldAccessAnalysis =
- new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis, null);
+ new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis, null, null);
DexProgramClass clazz = appView.appInfo().classes().iterator().next();
assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString());
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java
new file mode 100644
index 0000000..b3f658c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/AtomicReferenceFieldUpdaterMethods.java
@@ -0,0 +1,23 @@
+// 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.desugar.backports;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+public final class AtomicReferenceFieldUpdaterMethods {
+ // Workaround Android S issue with AtomicReferenceFieldUpdater.compareAndSet (b/211646483).
+ public static boolean compareAndSet(
+ AtomicReferenceFieldUpdater<Object, Object> updater,
+ Object object,
+ Object expect,
+ Object update) {
+ do {
+ if (updater.compareAndSet(object, expect, update)) {
+ return true;
+ }
+ } while (updater.get(object) == expect);
+ return false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index f955ca3..c6e3eea 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -34,6 +34,7 @@
factory.createType("Lcom/android/tools/r8/ir/desugar/backports/BackportedMethods;");
private final List<Class<?>> METHOD_TEMPLATE_CLASSES =
ImmutableList.of(
+ AtomicReferenceFieldUpdaterMethods.class,
BooleanMethods.class,
ByteMethods.class,
CharSequenceMethods.class,
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/LibraryMethodOverridesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
index 5fd1825..e4b5197 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/LibraryMethodOverridesTest.java
@@ -8,12 +8,14 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -34,6 +36,7 @@
.withCfRuntimes()
// java.util.function.Predicate is not available prior to API level 24 (V7.0).
.withDexRuntimesStartingFromIncluding(Version.V7_0_0)
+ .withApiLevelsStartingAtIncluding(AndroidApiLevel.N)
.build();
}
@@ -44,10 +47,10 @@
public void testR8() throws Exception {
R8TestCompileResult libraryCompileResult =
testForR8(parameters.getBackend())
- .addProgramClasses(LibClass.class)
- .addKeepClassAndMembersRules(LibClass.class)
- .setMinApi(parameters.getRuntime())
- .compile();
+ .addProgramClasses(LibClass.class)
+ .addKeepClassAndMembersRules(LibClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile();
testForR8(parameters.getBackend())
.addProgramClasses(TestClass.class, CustomPredicate.class)
.addClasspathClasses(LibClass.class)
@@ -56,7 +59,8 @@
o ->
o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect)
.enableInliningAnnotations()
- .setMinApi(parameters.getRuntime())
+ .enableNoMethodStaticizingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryCompileResult.writeToZip())
.run(parameters.getRuntime(), MAIN)
@@ -107,11 +111,13 @@
}
@NeverInline
+ @NoMethodStaticizing
private void live() {
System.out.println("Live");
}
@NeverInline
+ @NoMethodStaticizing
private void alsoLive() {
System.out.println("Also live");
}
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/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 0cd06dc..ef373cd 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -44,6 +45,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeDirectPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoMethodStaticizingAnnotations()
.enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
@@ -109,6 +111,7 @@
}
@NeverInline
+ @NoMethodStaticizing
@NoParameterTypeStrengthening
private void test(Base arg) {
if (arg instanceof Sub1) {
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 0372989..3a3a62d 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
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -42,6 +43,7 @@
.addKeepMainRule(MAIN)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.addOptionsModification(
o ->
o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect)
@@ -74,6 +76,7 @@
}
@NeverInline
+ @NoMethodStaticizing
private void test(Object arg) {
if (arg != null) {
System.out.println("non-null");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 6ea5d98..1884e5e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -87,6 +87,7 @@
.addProgramClasses(classes)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableSideEffectAnnotations()
.addKeepMainRule(main)
.addKeepAttributes("LineNumberTable")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
index 0390712..f56779e 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceAB.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
@NoHorizontalClassMerging
public class CycleReferenceAB {
@@ -14,6 +15,7 @@
this.a = a;
}
+ @NoMethodStaticizing
public void foo(int depth) {
CycleReferenceBA ba = new CycleReferenceBA("depth=" + depth);
System.out.println("CycleReferenceAB::foo(" + depth + ")");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceBA.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceBA.java
index 1c5d147..6d22b16 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceBA.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/CycleReferenceBA.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.classinliner.trivial;
+import com.android.tools.r8.NoMethodStaticizing;
+
public class CycleReferenceBA {
private String a;
@@ -11,6 +13,7 @@
this.a = a;
}
+ @NoMethodStaticizing
public void foo(int depth) {
CycleReferenceAB ab = new CycleReferenceAB("depth=" + depth);
System.out.println("CycleReferenceBA::foo(" + depth + ")");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
index 86cf25e..820a5de 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/trivial/TrivialTestClass.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
public class TrivialTestClass {
private static int ID = 0;
@@ -88,6 +89,7 @@
}
@NeverInline
+ @NoMethodStaticizing
private void testCycles() {
new CycleReferenceAB("first").foo(3);
new CycleReferenceBA("second").foo(4);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
index 3d48fb9..b21bcab 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/InvokeSuperToInvokeVirtualTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -42,6 +43,7 @@
.addInnerClasses(InvokeSuperToInvokeVirtualTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters.getApiLevel())
@@ -98,6 +100,7 @@
}
@NeverInline
+ @NoMethodStaticizing
void world() {
System.out.println(" world!");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
index f3db3a7..ca2d49a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoReturnTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -44,6 +45,7 @@
// Keep B to ensure that we will treat it as being instantiated.
.addKeepClassRulesWithAllowObfuscation(B.class)
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoReturnTypeStrengtheningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -155,6 +157,7 @@
static class A implements I {
@NeverInline
+ @NoMethodStaticizing
@Override
public void hello() {
System.out.print("Hello");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
index 3a0b304..883c62a 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/classtypes/B134462736.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -66,6 +67,7 @@
.addInnerClasses(B134462736.class)
.addKeepMainRule(TestClass.class)
.enableConstantArgumentAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.noMinification()
.addOptionsModification(
@@ -83,6 +85,7 @@
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
public void consumer(String arg1, String arg2) {
System.out.println(arg1 + " " + arg2);
}
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/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
index 58dc4e5..2c8e8ed 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/PrivateInstanceMethodCollisionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -64,6 +65,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.minification(minification)
.allowAccessModification(allowAccessModification)
.setMinApi(parameters.getApiLevel())
@@ -113,11 +115,13 @@
@NeverClassInline
static class A {
@NeverInline
+ @NoMethodStaticizing
private void foo(B instantiated) {
System.out.println("A#foo(" + instantiated + ")");
}
@NeverInline
+ @NoMethodStaticizing
void foo(B instantiated, C uninstantiated) {
System.out.println("A#foo(" + instantiated + ", Object)");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
index 472ec24..f8dd8d9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/uninstantiatedtypes/VoidReturnTypeRewritingTest.java
@@ -10,9 +10,11 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
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.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -20,20 +22,18 @@
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 VoidReturnTypeRewritingTest extends TestBase {
- private final Backend backend;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public VoidReturnTypeRewritingTest(Backend backend) {
- this.backend = backend;
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
@@ -48,15 +48,17 @@
testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expected);
CodeInspector inspector =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addInnerClasses(VoidReturnTypeRewritingTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.addKeepRules("-dontobfuscate")
.addOptionsModification(options -> options.enableClassInlining = false)
- .run(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expected)
.inspector();
@@ -116,6 +118,7 @@
}
@NeverInline
+ @NoMethodStaticizing
public Uninstantiated createVirtual() {
System.out.print("Factory.createVirtual()");
return null;
@@ -127,6 +130,7 @@
@Override
@NeverInline
+ @NoMethodStaticizing
public Uninstantiated createVirtual() {
System.out.print("SubFactory.createVirtual()");
return null;
@@ -138,6 +142,7 @@
@Override
@NeverInline
+ @NoMethodStaticizing
public SubUninstantiated createVirtual() {
System.out.print("SubSubFactory.createVirtual()");
return null;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
index e990830..9cdc693 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/CollisionWithLibraryMethodsTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -50,6 +51,7 @@
.addKeepMainRule(TestClass.class)
.enableMemberValuePropagationAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableInliningAnnotations()
.minification(minification)
.setMinApi(parameters.getApiLevel())
@@ -87,6 +89,7 @@
@NeverInline
@NeverPropagateValue
+ @NoMethodStaticizing
public String toString(Object unused) {
System.out.print("Hello ");
return "world!";
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/PrivateInstanceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/PrivateInstanceMethodCollisionTest.java
index ed2d01d..abc1bd6 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/PrivateInstanceMethodCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/PrivateInstanceMethodCollisionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.KeepConstantArguments;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -35,7 +36,7 @@
@Parameters(name = "{0}, minification: {1}, allowaccessmodification: {2}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimes().build(),
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
BooleanUtils.values(),
BooleanUtils.values());
}
@@ -64,9 +65,10 @@
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.minification(minification)
.allowAccessModification(allowAccessModification)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyUnusedArgumentsRemovedAndNoCollisions)
.run(parameters.getRuntime(), TestClass.class)
@@ -113,12 +115,14 @@
static class A {
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
private void foo(String used) {
System.out.println("A#foo(" + used + ")");
}
@KeepConstantArguments
@NeverInline
+ @NoMethodStaticizing
void foo(String used, Object unused) {
System.out.println("A#foo(" + used + ", Object)");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
index c59eb85..7527598 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsCollisionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -59,6 +60,7 @@
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.minification(minification)
.setMinApi(parameters.getApiLevel())
@@ -119,6 +121,7 @@
static class A {
@NeverInline
+ @NoMethodStaticizing
public void method1() {
System.out.print("Hello");
}
@@ -138,6 +141,7 @@
}
@NeverInline
+ @NoMethodStaticizing
public void method2(Object unused) {
System.out.println(" world");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
index c06823f..5497d76 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsObjectTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -58,31 +59,37 @@
}
@NeverInline
+ @NoMethodStaticizing
private Object privateMethod(Object a) {
return a;
}
@NeverInline
+ @NoMethodStaticizing
private Object privateMethod(Object a, Object b) {
return a;
}
@NeverInline
+ @NoMethodStaticizing
private Object privateMethod(Object a, Object b, Object c) {
return a;
}
@NeverInline
+ @NoMethodStaticizing
public Object publicMethod(Object a) {
return a;
}
@NeverInline
+ @NoMethodStaticizing
public Object publicMethod(Object a, Object b) {
return a;
}
@NeverInline
+ @NoMethodStaticizing
public Object publicMethod(Object a, Object b, Object c) {
return a;
}
@@ -106,7 +113,10 @@
@Override
public void configure(R8FullTestBuilder builder) {
- builder.enableNeverClassInliningAnnotations().enableInliningAnnotations();
+ builder
+ .enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations();
}
@Override
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/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index f3db67c..b05928d 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -19,10 +19,13 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Move;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -65,6 +68,16 @@
}
@Override
+ public InvokeMethod insertNullCheckInstruction(
+ AppView<?> appView,
+ IRCode code,
+ BasicBlockIterator blockIterator,
+ Value value,
+ Position position) {
+ throw new Unimplemented();
+ }
+
+ @Override
public boolean replaceCurrentInstructionByNullCheckIfPossible(
AppView<?> appView, ProgramMethod context) {
throw new Unimplemented();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java
index fea8bfa..ee3a60f 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexUnusedArgumentRewriteWithLensTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -49,6 +50,7 @@
.addInnerClasses(getClass())
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.addKeepClassRules(Dependency.class)
.addMainDexRules(
@@ -101,6 +103,7 @@
// Will be rewritten because it has an unused argument
@NeverInline
+ @NoMethodStaticizing
public void foo(Object obj, int argumentUnused) {
B.foo(obj);
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
new file mode 100644
index 0000000..24c31c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
@@ -0,0 +1,108 @@
+// 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.memberrebinding;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+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;
+
+// This is a reproduction of b/213581039.
+@RunWith(Parameterized.class)
+public class MemberRebindingInvokeSuperAbstractTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ private final List<Class<?>> libraryClasses =
+ ImmutableList.of(LibraryBase.class, LibrarySub.class, LibrarySubSub.class);
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addLibraryClasses(libraryClasses)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .apply(
+ builder ->
+ libraryClasses.forEach(clazz -> setMockApiLevelForClass(clazz, AndroidApiLevel.B)))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryBase.class.getDeclaredMethod("getSystemService"), AndroidApiLevel.B))
+ .apply(
+ setMockApiLevelForMethod(
+ LibrarySub.class.getDeclaredMethod("getSystemService"), AndroidApiLevel.B))
+ .compile()
+ .addRunClasspathClasses(libraryClasses)
+ .inspect(
+ inspector -> {
+ MethodSubject getSystemService =
+ inspector.clazz(Main.class).uniqueMethodWithName("getSystemService");
+ assertThat(getSystemService, isPresent());
+ // We should never rebind this call to LibraryBase::getSystemService since this can
+ // cause errors when verifying the code on a device where the image has a definition
+ // but it is abstract. For more information, see b/213581039.
+ assertThat(
+ getSystemService,
+ CodeMatchers.invokesMethodWithHolderAndName(
+ typeName(LibrarySub.class), "getSystemService"));
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("LibrarySub::getSystemService");
+ }
+
+ public abstract static class LibraryBase {
+
+ public abstract void getSystemService();
+ }
+
+ public static class LibrarySub extends LibraryBase {
+
+ @Override
+ public void getSystemService() {
+ System.out.println("LibrarySub::getSystemService");
+ }
+ }
+
+ public static class LibrarySubSub extends LibrarySub {}
+
+ public static class Main extends LibrarySubSub {
+
+ public static void main(String[] args) {
+ new Main().getSystemService();
+ }
+
+ @Override
+ @NeverInline
+ public void getSystemService() {
+ super.getSystemService();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringReprocessingTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringReprocessingTest.java
new file mode 100644
index 0000000..4041e32
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringReprocessingTest.java
@@ -0,0 +1,72 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.ReprocessClassInitializer;
+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 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 IdentifierNameStringReprocessingTest 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)
+ .addKeepClassRulesWithAllowObfuscation(A.class)
+ .addKeepRules(
+ "-identifiernamestring class " + Main.class.getTypeName() + " {",
+ " static java.lang.String f;",
+ "}")
+ .enableMemberValuePropagationAnnotations()
+ .enableReprocessClassInitializerAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .apply(
+ runResult -> {
+ ClassSubject aClassSubject = runResult.inspector().clazz(A.class);
+ assertThat(aClassSubject, isPresentAndRenamed());
+ runResult.assertSuccessWithOutputLines(aClassSubject.getFinalName());
+ });
+ }
+
+ @ReprocessClassInitializer
+ static class Main {
+
+ @NeverPropagateValue static String f;
+
+ static {
+ // Prevent class initializer defaults optimization.
+ System.out.print("");
+ f = "com.android.tools.r8.naming.IdentifierNameStringReprocessingTest$A";
+ }
+
+ public static void main(String[] args) {
+ System.out.println(f);
+ }
+ }
+
+ static class A {}
+}
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..99554bf 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
@@ -4,18 +4,24 @@
package com.android.tools.r8.naming.applymapping;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoMethodStaticizing;
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 {
@@ -35,19 +41,22 @@
public static class LibClassA implements LibInterfaceA {
@Override
+ @NoMethodStaticizing
public void foo() {
System.out.println("LibClassA::foo");
}
}
- // LibClassB should be devirtualized into LibInterfaceB
+ // LibInterfaceB should be devirtualized into LibClassB
public static class LibClassB implements LibInterfaceB {
@Override
+ @NoMethodStaticizing
public void foo() {
System.out.println("LibClassB::foo");
}
+ @NoMethodStaticizing
public void bar() {
System.out.println("LibClassB::bar");
}
@@ -71,92 +80,94 @@
}
}
- private static final Class<?>[] LIBRARY_CLASSES = {
+ private static final Class<?>[] CLASSPATH_CLASSES = {
LibInterfaceA.class, LibInterfaceB.class, LibClassA.class, LibClassB.class
};
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(CLASSPATH_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)
- .addProgramClasses(LIBRARY_CLASSES)
+ testForR8(parameters.getBackend())
+ .addProgramClasses(CLASSPATH_CLASSES)
.addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class)
.addKeepMainRule(LibClassB.class)
+ .addKeepClassAndDefaultConstructor(LibClassB.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .enableNoMethodStaticizingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
- assertThat(inspector.clazz(LibClassA.class), isPresent());
- assertThat(inspector.clazz(LibClassB.class), isPresent());
+ assertThat(inspector.clazz(LibClassA.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(LibClassB.class), isPresentAndNotRenamed());
// LibInterfaceX should have been moved into LibClassX.
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))
+ .addClasspathClasses(CLASSPATH_CLASSES)
+ .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)
- .addProgramClasses(LIBRARY_CLASSES)
- .addKeepClassAndMembersRulesWithAllowObfuscation(LibClassA.class, LibInterfaceA.class)
- .addKeepMainRule(LibClassB.class)
+ testForR8(parameters.getBackend())
+ .addProgramClasses(CLASSPATH_CLASSES)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(
+ LibClassA.class, LibClassB.class, LibInterfaceA.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
+ .enableNoMethodStaticizingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile();
CodeInspector inspector = libraryResult.inspector();
- assertThat(inspector.clazz(LibClassA.class), isPresent());
- assertThat(inspector.clazz(LibClassB.class), isPresent());
+ assertThat(inspector.clazz(LibClassA.class), isPresentAndRenamed());
+ assertThat(inspector.clazz(LibClassB.class), isPresentAndRenamed());
// LibInterfaceA is now kept.
- assertThat(inspector.clazz(LibInterfaceA.class), isPresent());
+ assertThat(inspector.clazz(LibInterfaceA.class), isPresentAndRenamed());
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))
+ .addClasspathClasses(CLASSPATH_CLASSES)
+ .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/applymapping/ApplyMappingInterfaceInvokeTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
index a1ef87f..b4b6807 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingInterfaceInvokeTest.java
@@ -4,13 +4,10 @@
package com.android.tools.r8.naming.applymapping;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -57,7 +54,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingInterfaceInvokeTest(TestParameters parameters) {
@@ -65,14 +62,13 @@
}
@Test
- public void testInvokeVirtual()
- throws IOException, CompilationFailedException, ExecutionException {
+ public void testInvokeVirtual() throws Exception {
Class<?>[] classPathClasses = {I.class, A.class, B.class, C.class};
R8TestCompileResult libraryResult =
testForR8(parameters.getBackend())
.addProgramClasses(classPathClasses)
.addKeepAllClassesRuleWithAllowObfuscation()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile();
testForR8(parameters.getBackend())
.addClasspathClasses(classPathClasses)
@@ -80,7 +76,7 @@
.noMinification()
.noTreeShaking()
.addApplyMapping(libraryResult.getProguardMap())
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.addRunClasspathFiles(libraryResult.writeToZip())
.run(parameters.getRuntime(), TestApp.class)
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckFollowingImplicitReceiverNullCheckTest.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckFollowingImplicitReceiverNullCheckTest.java
new file mode 100644
index 0000000..b5cd101
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceInlineeWithNullCheckFollowingImplicitReceiverNullCheckTest.java
@@ -0,0 +1,95 @@
+// 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.naming.retrace;
+
+import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Before;
+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 RetraceInlineeWithNullCheckFollowingImplicitReceiverNullCheckTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public StackTrace expectedStackTrace;
+ public StackTrace unexpectedStackTrace;
+
+ @Before
+ public void setup() throws Exception {
+ expectedStackTrace = getStackTrace();
+ unexpectedStackTrace = getStackTrace("foo");
+ }
+
+ private StackTrace getStackTrace(String... args) throws Exception {
+ SingleTestRunResult<?> runResult =
+ testForRuntime(parameters)
+ .addProgramClasses(Caller.class, Foo.class)
+ .run(parameters.getRuntime(), Caller.class, args);
+ return parameters.isCfRuntime()
+ ? runResult.map(StackTrace::extractFromJvm)
+ : StackTrace.extractFromArt(runResult.getStdErr(), parameters.asDexRuntime().getVm());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Caller.class)
+ .addKeepAttributeLineNumberTable()
+ .addKeepAttributeSourceFile()
+ .enableExperimentalMapFileVersion()
+ .enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Caller.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class)
+ // TODO(b/214377135): Should retrace to expectedStackTrace.
+ .inspectStackTrace(
+ (stackTrace, codeInspector) -> assertThat(stackTrace, isSame(unexpectedStackTrace)));
+ }
+
+ static class Foo {
+
+ @NeverInline
+ @NoMethodStaticizing
+ void checkNull() {
+ System.out.println("Hello, world!");
+ }
+
+ void inlinable(Foo foo) {
+ checkNull();
+ foo.checkNull();
+ }
+ }
+
+ static class Caller {
+
+ @NeverInline
+ static void caller(Foo f) {
+ f.inlinable(null);
+ }
+
+ public static void main(String[] args) {
+ caller(args.length == 0 ? new Foo() : null);
+ }
+ }
+}
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/optimize/argumentpropagation/CollisionWithLibraryMethodAfterConstantParameterRemovalTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CollisionWithLibraryMethodAfterConstantParameterRemovalTest.java
index a0c58ac..a8f51d2 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CollisionWithLibraryMethodAfterConstantParameterRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CollisionWithLibraryMethodAfterConstantParameterRemovalTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -41,6 +42,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -77,6 +79,7 @@
static class A {
@NeverInline
+ @NoMethodStaticizing
public String toString(String whichOne) {
return System.currentTimeMillis() > 0 ? whichOne : null;
}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
new file mode 100644
index 0000000..f5fb4f5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/CompanionConstructorShakingTest.java
@@ -0,0 +1,87 @@
+// 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;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+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 CompanionConstructorShakingTest 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)
+ .addOptionsModification(options -> options.enableClassStaticizer = false)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject hostClassSubject = inspector.clazz(Host.class);
+ assertThat(hostClassSubject, isAbsent());
+
+ ClassSubject companionClassSubject = inspector.clazz(Host.Companion.class);
+ assertThat(companionClassSubject, isPresent());
+ assertEquals(1, companionClassSubject.allMethods().size());
+
+ MethodSubject greetMethodSubject =
+ companionClassSubject.uniqueMethodWithName("greet");
+ assertThat(greetMethodSubject, isStatic());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ Host.companion.greet();
+ }
+ }
+
+ static class Host {
+
+ static final Companion companion = new Companion();
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class Companion {
+
+ @NeverInline
+ void greet() {
+ System.out.println("Hello world!");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/finalize/FinalizeVirtualMethodTest.java b/src/test/java/com/android/tools/r8/optimize/finalize/FinalizeVirtualMethodTest.java
index b8f5492..115b180 100644
--- a/src/test/java/com/android/tools/r8/optimize/finalize/FinalizeVirtualMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/finalize/FinalizeVirtualMethodTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -39,6 +40,7 @@
.addKeepClassAndMembersRules(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(
@@ -63,6 +65,7 @@
// Should be made final.
@NeverInline
+ @NoMethodStaticizing
public void m() {
System.out.println("A.m()");
}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress214340258.java b/src/test/java/com/android/tools/r8/regress/Regress214340258.java
new file mode 100644
index 0000000..bce5a5b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress214340258.java
@@ -0,0 +1,105 @@
+// 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.regress;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress214340258 extends TestBase {
+ // Generate this many classes to not overflow instruction limit.
+ static final int NUMBER_OF_FILES = 50;
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public Regress214340258(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ Path compiledJumbo = getZipWithJumboString();
+ R8TestRunResult r8TestRunResult =
+ testForR8(parameters.getBackend())
+ .addDontOptimize()
+ .addKeepAllClassesRule()
+ .addProgramFiles(compiledJumbo)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), "TestClass0");
+ r8TestRunResult.assertSuccessWithOutputLines("foobar");
+ assertTrue(hasJumboString(r8TestRunResult));
+ }
+
+ private boolean hasJumboString(R8TestRunResult r8TestRunResult)
+ throws IOException, ExecutionException {
+ for (FoundClassSubject classSubject : r8TestRunResult.inspector().allClasses()) {
+ for (FoundMethodSubject foundMethodSubject : classSubject.allMethods()) {
+ for (InstructionSubject instruction : foundMethodSubject.instructions()) {
+ if (instruction.isJumboString()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public Path getZipWithJumboString() throws IOException {
+ List<Path> javaFiles = new ArrayList<>();
+ for (int i = 0; i < NUMBER_OF_FILES; i++) {
+ String name = "TestClass" + i;
+ Path file = temp.newFile(name + ".java").toPath();
+ Files.write(file, getClassWithManyStrings(name, i).getBytes(StandardCharsets.UTF_8));
+ javaFiles.add(file);
+ }
+ Path compiledJumbo = javac(CfRuntime.getCheckedInJdk9()).addSourceFiles(javaFiles).compile();
+ return compiledJumbo;
+ }
+
+ private String getClassWithManyStrings(String className, int index) {
+ String file =
+ ""
+ + "public class "
+ + className
+ + " {\n"
+ + " public static void use(String s) { }\n"
+ + "\n"
+ + " public static void main(String[] args) {\n"
+ + " String s = \"foobar\";\n";
+
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < Constants.MAX_NON_JUMBO_INDEX / NUMBER_OF_FILES; i++) {
+ builder.append(" s = \"foobar" + i + "_" + index + "\";\n");
+ builder.append(" System.getenv(s);\n");
+ }
+ file += builder.toString();
+
+ file += "" + " s = \"foobar\";\n" + " System.out.println(s);\n" + " }\n" + "}";
+ return file;
+ }
+}
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/EventuallyNonTargetedMethodTest.java b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
index 24d4436..cb750b5 100644
--- a/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/EventuallyNonTargetedMethodTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -19,27 +20,26 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
@RunWith(Parameterized.class)
public class EventuallyNonTargetedMethodTest extends TestBase {
static final String EXPECTED = StringUtils.lines("A::foo", "C::bar");
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public EventuallyNonTargetedMethodTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
@@ -78,11 +78,13 @@
// Non-targeted override.
@Override
+ @NoMethodStaticizing
public void foo() {
System.out.println("C::foo");
}
@NeverInline
+ @NoMethodStaticizing
public void bar() {
System.out.println("C::bar");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
index 7c9c834..163a63f 100644
--- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -8,16 +8,15 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.ByteDataView;
import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
@@ -93,50 +92,31 @@
memoizeFunction(NonVirtualOverrideTest::compile);
public static String getExpectedResult(boolean isOldVm) throws Exception {
- if (isOldVm) {
- return String.join(
- System.lineSeparator(),
- "In A.m1()",
- "In A.m2()",
- "In A.m3()",
- "In A.m4()",
- "In C.m1()",
- "In A.m2()",
- "In C.m3()",
- "In A.m4()",
- "In A.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "Caught IncompatibleClassChangeError when calling B.m3()",
- "In C.m1()", // With Java: Caught IllegalAccessError when calling B.m1()
- "Caught IncompatibleClassChangeError when calling B.m3()",
- "In C.m1()",
- "In C.m3()",
- "");
- } else {
- Path referenceJar = staticTemp.getRoot().toPath().resolve("input.jar");
- ArchiveConsumer inputConsumer = new ArchiveConsumer(referenceJar);
- inputConsumer.accept(
- ByteDataView.of(NonVirtualOverrideTestClassDump.dump()),
- DescriptorUtils.javaTypeToDescriptor(NonVirtualOverrideTestClass.class.getName()),
- null);
- inputConsumer.accept(
- ByteDataView.of(ADump.dump()),
- DescriptorUtils.javaTypeToDescriptor(A.class.getName()),
- null);
- inputConsumer.accept(
- ByteDataView.of(BDump.dump()),
- DescriptorUtils.javaTypeToDescriptor(B.class.getName()),
- null);
- inputConsumer.accept(
- ByteDataView.of(CDump.dump()),
- DescriptorUtils.javaTypeToDescriptor(C.class.getName()),
- null);
- inputConsumer.finished(null);
+ Path referenceJar = staticTemp.getRoot().toPath().resolve("input.jar");
+ ArchiveConsumer inputConsumer = new ArchiveConsumer(referenceJar);
+ inputConsumer.accept(
+ ByteDataView.of(NonVirtualOverrideTestClassDump.dump()),
+ DescriptorUtils.javaTypeToDescriptor(NonVirtualOverrideTestClass.class.getName()),
+ null);
+ inputConsumer.accept(
+ ByteDataView.of(ADump.dump()),
+ DescriptorUtils.javaTypeToDescriptor(A.class.getName()),
+ null);
+ inputConsumer.accept(
+ ByteDataView.of(BDump.dump()),
+ DescriptorUtils.javaTypeToDescriptor(B.class.getName()),
+ null);
+ inputConsumer.accept(
+ ByteDataView.of(CDump.dump()),
+ DescriptorUtils.javaTypeToDescriptor(C.class.getName()),
+ null);
+ inputConsumer.finished(null);
- ProcessResult javaResult =
- ToolHelper.runJava(referenceJar, NonVirtualOverrideTestClass.class.getName());
- assertEquals(javaResult.exitCode, 0);
- return javaResult.stdout;
- }
+ return testForJvm(getStaticTemp())
+ .addProgramFiles(referenceJar)
+ .run(CfRuntime.getDefaultCfRuntime(), NonVirtualOverrideTestClass.class)
+ .assertSuccess()
+ .getStdOut();
}
public static boolean isDexVmBetween5_1_1and7_0_0(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
index 51bd0cf..dda29d7 100644
--- a/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ServiceLoaderTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -92,6 +93,7 @@
})
.enableGraphInspector()
.enableMemberValuePropagationAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -189,6 +191,7 @@
new DataResourceConsumerForTesting(options.dataResourceConsumer);
options.dataResourceConsumer = dataResourceConsumer;
})
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), OtherTestClass.class)
.assertSuccessWithOutput(expectedOutput)
@@ -230,6 +233,7 @@
public static class HelloGreeter implements Greeter {
@NeverPropagateValue
+ @NoMethodStaticizing
@Override
public String greeting() {
return "Hello";
@@ -238,6 +242,7 @@
public static class WorldGreeter implements Greeter {
+ @NoMethodStaticizing
@Override
public String greeting() {
return " world!";
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
index 3f68961..dcb962b 100644
--- a/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/clinit/InterfaceInitializedByImplementationTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -43,6 +44,7 @@
.addInnerClasses(InterfaceInitializedByImplementationTest.class)
.addKeepMainRule(TestClass.class)
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
.setMinApi(parameters.getApiLevel())
.noMinification()
.compile()
@@ -81,6 +83,7 @@
// TODO(b/144266257): If tree shaking removes this method, then I.<clinit>() won't be run when
// A is being class initialized.
@NeverInline
+ @NoMethodStaticizing
default void m() {
System.out.println(" world!");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxationTest.java
index 6518cfe..882076c 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/accessrelaxation/IfRuleWithAccessRelaxationTest.java
@@ -12,32 +12,32 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoMethodStaticizing;
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.codeinspector.CodeInspector;
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 IfRuleWithAccessRelaxationTest extends TestBase {
- private final Backend backend;
+ @Parameter(0)
+ public TestParameters parameters;
- public IfRuleWithAccessRelaxationTest(Backend backend) {
- this.backend = backend;
- }
-
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@Test
public void r8Test() throws Exception {
CodeInspector inspector =
- testForR8(backend)
+ testForR8(parameters.getBackend())
.addInnerClasses(IfRuleWithAccessRelaxationTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -54,6 +54,8 @@
"-keep class " + Unused3.class.getTypeName())
.allowAccessModification()
.enableInliningAnnotations()
+ .enableNoMethodStaticizingAnnotations()
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspector();
@@ -80,6 +82,7 @@
protected int field = 42;
@NeverInline
+ @NoMethodStaticizing
private void privateMethod() {
System.out.println("In privateMethod()");
}
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());
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
index b97fd41..93cc98f 100644
--- a/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
+++ b/src/test/java/com/android/tools/r8/utils/ArrayUtilsTest.java
@@ -113,7 +113,7 @@
public void testFilter_identity() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.filter(Integer[].class, input, x -> true);
+ Integer[] output = ArrayUtils.filter(input, x -> true, new Integer[0]);
assertEquals(input, output);
}
@@ -121,7 +121,7 @@
public void testFilter_dropOdd() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.filter(Integer[].class, input, x -> x % 2 == 0);
+ Integer[] output = ArrayUtils.filter(input, x -> x % 2 == 0, new Integer[0]);
assertNotEquals(input, output);
assertEquals(2, output.length);
assertEquals(0, (int) output[0]);
@@ -132,7 +132,7 @@
public void testFilter_dropAll() {
int size = 3;
Integer[] input = createInputData(size);
- Integer[] output = ArrayUtils.filter(Integer[].class, input, x -> false);
+ Integer[] output = ArrayUtils.filter(input, x -> false, new Integer[0]);
assertNotEquals(input, output);
assertEquals(0, output.length);
}