Merge commit 'f79fefd7dff4f054ec8b8f1991ce4e7bf9c828c0' into dev-release
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
index fbeda56..f8b10e0 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfFrame.java
@@ -105,6 +105,24 @@
return false;
}
+ FrameType map(java.util.function.Function<DexType, DexType> func) {
+ if (isInitialized()) {
+ DexType type = getInitializedType();
+ DexType newType = func.apply(type);
+ if (type != newType) {
+ return initialized(newType);
+ }
+ }
+ if (isUninitializedNew()) {
+ DexType type = getUninitializedNewType();
+ DexType newType = func.apply(type);
+ if (type != newType) {
+ return uninitializedNew(getUninitializedLabel(), newType);
+ }
+ }
+ return this;
+ }
+
private FrameType() {}
public static FrameType fromMemberType(MemberType memberType, DexItemFactory factory) {
@@ -512,4 +530,16 @@
}
return other;
}
+
+ public CfFrame map(java.util.function.Function<DexType, DexType> func) {
+ Int2ReferenceSortedMap<FrameType> newLocals = new Int2ReferenceAVLTreeMap<>();
+ for (int var : locals.keySet()) {
+ newLocals.put(var, locals.get(var).map(func));
+ }
+ Deque<FrameType> newStack = new ArrayDeque<>();
+ for (FrameType frameType : stack) {
+ newStack.addLast(frameType.map(func));
+ }
+ return new CfFrame(newLocals, newStack);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
index c4bbada..644ed7d 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvokeDynamic.java
@@ -75,9 +75,6 @@
NamingLens namingLens,
LensCodeRewriterUtils rewriter,
MethodVisitor visitor) {
- if (context.getName().toSourceString().equals("recordNewFieldSignature")) {
- System.currentTimeMillis();
- }
DexCallSite rewrittenCallSite = rewriter.rewriteCallSite(callSite, context);
DexMethodHandle bootstrapMethod = rewrittenCallSite.bootstrapMethod;
List<DexValue> bootstrapArgs = rewrittenCallSite.bootstrapArgs;
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 8cd6162..a3fc0e8 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
@@ -34,6 +34,7 @@
import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeter;
import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -90,7 +91,10 @@
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
return instruction.isInvoke()
- && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null;
+ && getMethodProviderOrNull(instruction.asInvoke().getMethod()) != null
+ && !appView
+ .getSyntheticItems()
+ .isSyntheticOfKind(context.getContextType(), SyntheticKind.BACKPORT_WITH_FORWARDING);
}
public static List<DexMethod> generateListOfBackportedMethods(
@@ -121,7 +125,6 @@
BackportedMethods.registerSynthesizedCodeReferences(options.itemFactory);
}
-
private MethodProvider getMethodProviderOrNull(DexMethod method) {
DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
assert original != null;
@@ -182,6 +185,9 @@
if (options.getMinApiLevel().isLessThan(AndroidApiLevel.S)) {
initializeAndroidSMethodProviders(factory);
}
+ if (options.getMinApiLevel().isLessThan(AndroidApiLevel.Sv2)) {
+ initializeAndroidSv2MethodProviders(factory);
+ }
// The following providers are currently not implemented at any API level in Android.
// They however require the Optional/Stream class to be present, either through desugared
@@ -1084,6 +1090,30 @@
factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
}
+ private void initializeAndroidSv2MethodProviders(DexItemFactory factory) {
+ DexString name;
+ DexProto proto;
+ DexMethod method;
+ // sun.misc.Unsafe
+
+ // 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));
+ }
+
private void initializeJava9MethodProviders(DexItemFactory factory) {
// Integer
DexType type = factory.boxedIntType;
@@ -1446,6 +1476,10 @@
this.methodName = methodName;
}
+ protected SyntheticKind getSyntheticKind() {
+ return SyntheticNaming.SyntheticKind.BACKPORT;
+ }
+
@Override
public Collection<CfInstruction> rewriteInvoke(
CfInvoke invoke,
@@ -1463,7 +1497,7 @@
return appView
.getSyntheticItems()
.createMethod(
- SyntheticNaming.SyntheticKind.BACKPORT,
+ getSyntheticKind(),
methodProcessingContext.createUniqueContext(),
appView,
builder ->
@@ -1502,6 +1536,20 @@
}
}
+ // Version of StatifyingMethodGenerator for backports which will call the method they backport.
+ // Such backports will not go through backporting again as that would cause infinite recursion.
+ private static class StatifyingMethodWithForwardingGenerator extends StatifyingMethodGenerator {
+ StatifyingMethodWithForwardingGenerator(
+ DexMethod method, TemplateMethodFactory factory, String methodName, DexType receiverType) {
+ super(method, factory, methodName, receiverType);
+ }
+
+ @Override
+ protected SyntheticKind getSyntheticKind() {
+ return SyntheticKind.BACKPORT_WITH_FORWARDING;
+ }
+ }
+
private interface TemplateMethodFactory {
CfCode create(InternalOptions options, DexMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
index da44c03..8e2f9fb 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/apimodel/ApiInvokeOutlinerDesugaring.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -95,7 +96,8 @@
ComputedApiLevel methodApiLevel =
apiLevelCompute.computeApiLevelForLibraryReference(
cfInvoke.getMethod(), ComputedApiLevel.unknown());
- if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)) {
+ if (appView.computedMinApiLevel().isGreaterThanOrEqualTo(methodApiLevel)
+ || isApiLevelLessThanOrEqualTo9(methodApiLevel)) {
return appView.computedMinApiLevel();
}
// Compute the api level of the holder to see if the method will be stubbed.
@@ -106,6 +108,11 @@
: appView.computedMinApiLevel();
}
+ private boolean isApiLevelLessThanOrEqualTo9(ComputedApiLevel apiLevel) {
+ return apiLevel.isKnownApiLevel()
+ && apiLevel.asKnownApiLevel().getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.G);
+ }
+
private Collection<CfInstruction> desugarLibraryCall(
UniqueContext context,
CfInvoke invoke,
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 b2fd06d..d7f7109 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
@@ -105,6 +105,7 @@
factory.createSynthesizedType("Ljava/util/stream/IntStream;");
factory.createSynthesizedType("Ljava/util/stream/LongStream;");
factory.createSynthesizedType("Ljava/util/stream/Stream;");
+ factory.createSynthesizedType("Lsun/misc/Unsafe;");
factory.createSynthesizedType("[Ljava/lang/CharSequence;");
factory.createSynthesizedType("[Ljava/lang/Class;");
factory.createSynthesizedType("[Ljava/lang/Object;");
@@ -9077,4 +9078,84 @@
ImmutableList.of(),
ImmutableList.of());
}
+
+ public static CfCode UnsafeMethods_compareAndSwapObject(
+ 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,
+ 6,
+ 6,
+ ImmutableList.of(
+ label0,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2, 4, 5},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.longType),
+ 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.LONG, 2),
+ new CfLoad(ValueType.OBJECT, 4),
+ new CfLoad(ValueType.OBJECT, 5),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Lsun/misc/Unsafe;"),
+ options.itemFactory.createProto(
+ options.itemFactory.booleanType,
+ options.itemFactory.objectType,
+ options.itemFactory.longType,
+ options.itemFactory.objectType,
+ options.itemFactory.objectType),
+ options.itemFactory.createString("compareAndSwapObject")),
+ 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, 4, 5},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.createType("Lsun/misc/Unsafe;")),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.longType),
+ 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.LONG, 2),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Lsun/misc/Unsafe;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.longType),
+ options.itemFactory.createString("getObject")),
+ false),
+ new CfLoad(ValueType.OBJECT, 4),
+ new CfIfCmp(If.Type.EQ, ValueType.OBJECT, label0),
+ label3,
+ new CfConstNumber(0, ValueType.INT),
+ new CfReturn(ValueType.INT),
+ label4),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 0e3dfdb..50bea08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -29,6 +29,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
@@ -243,13 +244,12 @@
}
} else if (instruction.isInvokeDirect()) {
handleInvokeDirect(instruction.asInvokeDirect());
+ } else if (instruction.isInvokeStatic()) {
+ handleInvokeStatic(instruction.asInvokeStatic());
} else if (instruction.isInvokeMethod() || instruction.isInvokeCustom()) {
killAllNonFinalActiveFields();
} else if (instruction.isNewInstance()) {
- NewInstance newInstance = instruction.asNewInstance();
- if (newInstance.clazz.classInitializationMayHaveSideEffectsInContext(appView, method)) {
- killAllNonFinalActiveFields();
- }
+ handleNewInstance(instruction.asNewInstance());
} else {
// If the current instruction could trigger a method invocation, it could also cause
// field values to change. In that case, it must be handled above.
@@ -258,6 +258,7 @@
// Clear the field writes.
if (instruction.instructionInstanceCanThrow(appView, method)) {
activeState.clearMostRecentFieldWrites();
+ activeState.clearMostRecentInitClass();
}
// If this assertion fails for a new instruction we need to determine if that
@@ -394,6 +395,23 @@
});
}
+ private void handleInvokeStatic(InvokeStatic invoke) {
+ if (appView.hasClassHierarchy()) {
+ ProgramMethod resolvedMethod =
+ appView
+ .appInfo()
+ .withClassHierarchy()
+ .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod())
+ .getResolvedProgramMethod();
+ if (resolvedMethod != null) {
+ markClassAsInitialized(resolvedMethod.getHolderType());
+ markMostRecentInitClassForRemoval(resolvedMethod.getHolderType());
+ }
+ }
+
+ killAllNonFinalActiveFields();
+ }
+
private void handleInitClass(InstructionListIterator instructionIterator, InitClass initClass) {
assert !initClass.outValue().hasAnyUsers();
@@ -406,11 +424,28 @@
}
DexType clazz = initClass.getClassValue();
- if (!activeState.markClassAsInitialized(clazz)) {
+ if (markClassAsInitialized(clazz)) {
+ if (release) {
+ activeState.setMostRecentInitClass(initClass);
+ }
+ } else {
instructionIterator.removeOrReplaceByDebugLocalRead();
}
}
+ private boolean markClassAsInitialized(DexType type) {
+ return activeState.markClassAsInitialized(type);
+ }
+
+ private void markMostRecentInitClassForRemoval(DexType initializedType) {
+ InitClass mostRecentInitClass = activeState.getMostRecentInitClass();
+ if (mostRecentInitClass != null && mostRecentInitClass.getClassValue() == initializedType) {
+ instructionsToRemove
+ .computeIfAbsent(mostRecentInitClass.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentInitClass);
+ }
+ }
+
private void handleInstanceGet(
InstructionListIterator it,
InstanceGet instanceGet,
@@ -431,9 +466,18 @@
}
activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(instanceGet.value()));
+ activeState.clearMostRecentInitClass();
clearMostRecentInstanceFieldWrite(instanceGet, field);
}
+ private void handleNewInstance(NewInstance newInstance) {
+ markClassAsInitialized(newInstance.getType());
+ markMostRecentInitClassForRemoval(newInstance.getType());
+ if (newInstance.getType().classInitializationMayHaveSideEffectsInContext(appView, method)) {
+ killAllNonFinalActiveFields();
+ }
+ }
+
private void clearMostRecentInstanceFieldWrite(InstanceGet instanceGet, DexClassAndField field) {
// If the instruction can throw, we need to clear all most-recent-writes, since subsequent field
// writes (if any) are not guaranteed to be executed.
@@ -494,6 +538,8 @@
}
}
}
+
+ activeState.clearMostRecentInitClass();
}
private void handleStaticGet(
@@ -501,6 +547,8 @@
StaticGet staticGet,
DexClassAndField field,
AssumeRemover assumeRemover) {
+ markClassAsInitialized(field.getHolderType());
+
if (staticGet.outValue().hasLocalInfo()) {
killNonFinalActiveFields(staticGet);
clearMostRecentStaticFieldWrite(staticGet, field);
@@ -532,6 +580,9 @@
applyObjectState(staticGet.outValue(), singleFieldValue.getObjectState());
}
}
+
+ markMostRecentInitClassForRemoval(field.getHolderType());
+ activeState.clearMostRecentInitClass();
}
private void clearMostRecentStaticFieldWrite(StaticGet staticGet, DexClassAndField field) {
@@ -545,6 +596,8 @@
}
private void handleStaticPut(StaticPut staticPut, DexClassAndField field) {
+ markClassAsInitialized(field.getHolderType());
+
// A field put on a different class can cause <clinit> to run and change static field values.
killNonFinalActiveFields(staticPut);
@@ -573,6 +626,9 @@
}
}
}
+
+ markMostRecentInitClassForRemoval(field.getHolderType());
+ activeState.clearMostRecentInitClass();
}
private void applyObjectState(Value value, ObjectState objectState) {
@@ -593,6 +649,7 @@
activeState.clearNonFinalInstanceFields();
activeState.clearNonFinalStaticFields();
activeState.clearMostRecentFieldWrites();
+ activeState.clearMostRecentInitClass();
}
private void killNonFinalActiveFields(Instruction instruction) {
@@ -714,6 +771,7 @@
}
if (!block.hasUniqueSuccessorWithUniquePredecessor()) {
state.clearMostRecentFieldWrites();
+ state.clearMostRecentInitClass();
}
ensureCapacity(state);
activeStateAtExit.put(block, state);
@@ -753,6 +811,8 @@
private LinkedHashMap<DexField, FieldValue> nonFinalStaticFieldValues;
+ private InitClass mostRecentInitClass;
+
private LinkedHashMap<FieldAndObject, InstancePut> mostRecentInstanceFieldWrites;
private LinkedHashMap<DexField, StaticPut> mostRecentStaticFieldWrites;
@@ -787,6 +847,7 @@
nonFinalStaticFieldValues = new LinkedHashMap<>();
nonFinalStaticFieldValues.putAll(state.nonFinalStaticFieldValues);
}
+ mostRecentInitClass = state.mostRecentInitClass;
if (state.mostRecentInstanceFieldWrites != null
&& !state.mostRecentInstanceFieldWrites.isEmpty()) {
mostRecentInstanceFieldWrites = new LinkedHashMap<>();
@@ -885,6 +946,7 @@
} else {
nonFinalStaticFieldValues = null;
}
+ assert mostRecentInitClass == null;
assert mostRecentInstanceFieldWrites == null;
assert mostRecentStaticFieldWrites == null;
}
@@ -899,10 +961,6 @@
initializedClasses.removeIf(not(other::contains));
}
- public boolean isClassInitialized(DexType clazz) {
- return initializedClasses != null && initializedClasses.contains(clazz);
- }
-
public boolean isEmpty() {
return isEmpty(finalInstanceFieldValues)
&& isEmpty(finalStaticFieldValues)
@@ -1082,6 +1140,20 @@
nonFinalStaticFieldValues.put(field, value);
}
+ public InitClass getMostRecentInitClass() {
+ return mostRecentInitClass;
+ }
+
+ public void setMostRecentInitClass(InitClass initClass) {
+ mostRecentInitClass = initClass;
+ }
+
+ public InitClass clearMostRecentInitClass() {
+ InitClass result = mostRecentInitClass;
+ mostRecentInitClass = null;
+ return result;
+ }
+
public int size() {
return size(finalInstanceFieldValues)
+ size(finalStaticFieldValues)
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 834e879..de68c75 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -197,11 +197,12 @@
}
private static DexType firstLibraryClass(DexDefinitionSupplier definitions, DexType bottom) {
- DexClass searchClass = definitions.definitionFor(bottom);
- while (searchClass.isProgramClass()) {
- searchClass = definitions.definitionFor(searchClass.superType);
+ DexClass searchClass = definitions.contextIndependentDefinitionFor(bottom);
+ while (searchClass != null && searchClass.isProgramClass()) {
+ searchClass =
+ definitions.definitionFor(searchClass.getSuperType(), searchClass.asProgramClass());
}
- return searchClass.type;
+ return searchClass != null ? searchClass.getType() : null;
}
private MethodResolutionResult resolveMethodOnClass(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index fc487f7..c80a94b 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -43,6 +43,7 @@
ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD("CheckNotZero", 27, true),
RECORD_HELPER("Record", 9, true),
BACKPORT("Backport", 10, true),
+ BACKPORT_WITH_FORWARDING("BackportWithForwarding", 34, true),
STATIC_INTERFACE_CALL("StaticInterfaceCall", 11, true),
TO_STRING_IF_NOT_NULL("ToStringIfNotNull", 12, true),
THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", 13, true),
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 7ce8109..0bfc079 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1469,10 +1469,14 @@
public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
public BiConsumer<MethodReference, ComputedApiLevel> tracedMethodApiLevelCallback = null;
- public boolean enableApiCallerIdentification = true;
- public boolean checkAllApiReferencesAreSet = true;
- public boolean enableStubbingOfClasses = false;
- public boolean enableOutliningOfMethods = false;
+ public boolean enableApiCallerIdentification =
+ System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+ public boolean checkAllApiReferencesAreSet =
+ System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+ public boolean enableStubbingOfClasses =
+ System.getProperty("com.android.tools.r8.disableApiModeling") == null;
+ public boolean enableOutliningOfMethods =
+ System.getProperty("com.android.tools.r8.disableApiModeling") == null;
public void visitMockedApiLevelsForReferences(
DexItemFactory factory, Consumer<AndroidApiForHashingClass> consumer) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
index 0eeb405..5730230c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
@@ -52,6 +52,8 @@
}
})
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.compile()
.addRunClasspathClasses(Api.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
index 8d4e2f1..f351779 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineInSameClassTest.java
@@ -52,6 +52,8 @@
.addKeepMainRule(Main.class)
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
index 22ecf86..013d98f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoClassInliningMethodTest.java
@@ -48,6 +48,8 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
index 45d2bec..6e8d3b0 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoDesugaredLibraryReferenceTest.java
@@ -47,6 +47,8 @@
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
(reference, apiLevel) -> {
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 a580fb1..9f88bbb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -48,6 +48,8 @@
.addKeepMainRule(Main.class)
.apply(setMockApiLevelForMethod(apiMethod, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.noMinification()
.compile()
.inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
index d39b038..22a7321 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -50,6 +50,8 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callInterfaceMethod")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
index 98aa240..84ce168 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
@@ -46,6 +46,8 @@
.apply(setMockApiLevelForMethod(apiLevel21, AndroidApiLevel.L))
.apply(setMockApiLevelForMethod(apiLevel22, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::apiLevel21", "B::apiLevel22")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
index 16f24fa..1b7713d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelStaticTest.java
@@ -48,6 +48,8 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index 2311f12..c4a6c58 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -53,6 +53,8 @@
.apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.compile()
.inspect(
inspector ->
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
index 498ef4c..22714a3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -50,6 +50,8 @@
.apply(setMockApiLevelForMethod(apiMethod22, L_MR1))
.apply(setMockApiLevelForMethod(apiMethod26, O))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.noMinification()
.enableInliningAnnotations()
.compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
deleted file mode 100644
index a4cbd4b..0000000
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoMockForOutlineTest.java
+++ /dev/null
@@ -1,118 +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.apimodel;
-
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
-
-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.ToolHelper.DexVm.Version;
-import com.android.tools.r8.testing.AndroidBuildVersion;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.lang.reflect.Method;
-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 ApiModelNoMockForOutlineTest extends TestBase {
-
- private final AndroidApiLevel classApiLevel = AndroidApiLevel.K;
- private final AndroidApiLevel methodApiLevel = AndroidApiLevel.M;
-
- @Parameter public TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- @Test
- public void testR8() throws Exception {
- assumeFalse(
- parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isEqualTo(Version.V12_0_0));
- boolean isMethodApiLevel =
- parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(methodApiLevel);
- Method methodOn23 = LibraryClass.class.getDeclaredMethod("methodOn23");
- Method mainMethod = Main.class.getDeclaredMethod("main", String[].class);
- testForR8(parameters.getBackend())
- .addProgramClasses(Main.class)
- .addLibraryClasses(LibraryClass.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .addAndroidBuildVersion()
- .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForMethod(methodOn23, methodApiLevel))
- .apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
- .enableInliningAnnotations()
- .compile()
- .applyIf(
- parameters.isDexRuntime()
- && parameters
- .getRuntime()
- .maxSupportedApiLevel()
- .isGreaterThanOrEqualTo(classApiLevel),
- b -> b.addBootClasspathClasses(LibraryClass.class))
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLinesIf(!isMethodApiLevel, "Hello World")
- .assertSuccessWithOutputLinesIf(isMethodApiLevel, "LibraryClass::methodOn23", "Hello World")
- .inspect(
- inspector -> {
- assertThat(inspector.method(mainMethod), isPresent());
- verifyThat(inspector, parameters, methodOn23)
- .isOutlinedFromUntil(mainMethod, methodApiLevel);
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(classApiLevel);
- if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(classApiLevel)) {
- // We never trace outlined method for stubs so this holds by default.
- ClassSubject mockedLibraryClass = inspector.clazz(LibraryClass.class);
- assertThat(mockedLibraryClass, isPresent());
- MethodSubject mockedMethodOn23 =
- mockedLibraryClass.uniqueMethodWithName("methodOn23");
- assertThat(mockedMethodOn23, isAbsent());
- }
- });
- }
-
- // Only present from api level 19.
- public static class LibraryClass {
-
- public void methodOn23() {
- System.out.println("LibraryClass::methodOn23");
- }
- }
-
- public static class Main {
-
- @NeverInline
- public static Object create() {
- return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
- }
-
- public static void main(String[] args) {
- Object libraryClass = create();
- if (libraryClass != null) {
- ((LibraryClass) libraryClass).methodOn23();
- }
- System.out.println("Hello World");
- }
- }
-}
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 98b4e92..da967e8 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -50,6 +50,8 @@
.addKeepMainRule(Main.class)
.apply(setMockApiLevelForMethod(apiMethod, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addVerticallyMergedClassesInspector(
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 b7cd0b1..7276937 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -50,6 +50,8 @@
.addKeepMainRule(Main.class)
.apply(setMockApiLevelForMethod(apiMethod, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.addVerticallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 0760cad..c4dcd42 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -64,6 +64,7 @@
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForMethod(adeddOn23, methodApiLevel))
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableInliningAnnotations()
.compile()
.applyIf(
@@ -79,7 +80,6 @@
isMethodApiLevel, "LibraryClass::addedOn23", "LibraryClass::addedOn23", "Hello World")
.inspect(
inspector -> {
- // No need to check further on CF.
int classCount =
parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(methodApiLevel)
? 4
@@ -90,7 +90,7 @@
.isOutlinedFromUntil(testMethod, methodApiLevel);
if (parameters.isDexRuntime()
&& parameters.getApiLevel().isLessThan(methodApiLevel)) {
- // Verify that we invoke the synthesized outline addedOn23 twice.
+ // Verify that we invoke the synthesized outline, addedOn23, twice.
Optional<FoundMethodSubject> synthesizedAddedOn23 =
inspector.allClasses().stream()
.flatMap(clazz -> clazz.allMethods().stream())
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 2426afb..ce21ee4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -80,6 +80,7 @@
setMockApiLevelForMethod(
OtherLibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableInliningAnnotations()
.compile()
.applyIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 7755822..8531ceb 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -71,6 +71,7 @@
.apply(setMockApiLevelForMethod(addedOn23, initialLibraryMockLevel))
.apply(setMockApiLevelForMethod(addedOn27, finalLibraryMethodLevel))
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableInliningAnnotations()
.compile()
.applyIf(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
index 25f0c3a..79df3a4 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeReferenceInvokeTest.java
@@ -47,6 +47,8 @@
.apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.M))
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.M))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.addAndroidBuildVersion()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
index d4127e8..f28c3c6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVirtualDispatchLinkInterfaceTest.java
@@ -52,6 +52,8 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ // We are testing that we do not inline/merge higher api-levels
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.apply(
addTracedApiReferenceLevelCallBack(
(method, apiLevel) -> {
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 7077a21..b9cf537 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -133,6 +133,22 @@
});
}
+ public static void disableOutliningAndStubbing(
+ TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ disableStubbingOfClasses(compilerBuilder);
+ disableOutlining(compilerBuilder);
+ }
+
+ public static void disableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ compilerBuilder.addOptionsModification(
+ options -> options.apiModelingOptions().enableStubbingOfClasses = false);
+ }
+
+ public static void disableOutlining(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ compilerBuilder.addOptionsModification(
+ options -> options.apiModelingOptions().enableOutliningOfMethods = false);
+ }
+
static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
BiConsumer<MethodReference, AndroidApiLevel> consumer) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 407165c..3f95a85 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -327,7 +328,8 @@
runTest(
testForR8(parameters.getBackend())
.addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
+ .allowUnusedProguardConfigurationRules()
+ .apply(ApiModelingTestHelper::disableOutlining),
main,
programFiles,
preservedClassNames::contains);
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 7f77265..0c6d576 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -42,26 +42,34 @@
private final Set<String> ignoredInvokes = new HashSet<>();
private static class ClassInfo {
+ private final String name;
private final Class<?> clazz;
private final List<byte[]> classFileData;
+ private ClassInfo(String name) {
+ this.name = name;
+ this.clazz = null;
+ this.classFileData = null;
+ }
+
private ClassInfo(Class<?> clazz) {
+ this.name = clazz.getName();
this.clazz = clazz;
this.classFileData = null;
}
private ClassInfo(byte[] classFileData) {
- this.clazz = null;
- this.classFileData = ImmutableList.of(classFileData);
+ this(ImmutableList.of(classFileData));
}
private ClassInfo(List<byte[]> classFileData) {
+ this.name = extractClassName(classFileData.get(0));
this.clazz = null;
this.classFileData = classFileData;
}
String getName() {
- return clazz != null ? clazz.getName() : extractClassName(classFileData.get(0));
+ return name;
}
TestBuilder<?, ?> addAsProgramClass(TestBuilder<?, ?> builder) throws IOException {
@@ -79,6 +87,11 @@
}
AbstractBackportTest(
+ TestParameters parameters, String className, List<byte[]> testClassFileData) {
+ this(parameters, new ClassInfo(className), new ClassInfo(testClassFileData), null, null);
+ }
+
+ AbstractBackportTest(
TestParameters parameters, byte[] targetClassFileData, List<byte[]> testClassFileData) {
this(
parameters,
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
new file mode 100644
index 0000000..dbe597d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/UnsafeBackportTest.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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class UnsafeBackportTest extends AbstractBackportTest {
+
+ private static final String UNSAFE_TYPE_NAME = "sun.misc.Unsafe";
+ private static final String UNSAFE_DESCRIPTOR =
+ DescriptorUtils.javaTypeToDescriptor(UNSAFE_TYPE_NAME);
+
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters()
+ .withDexRuntimesStartingFromExcluding(Version.V4_0_4)
+ .withAllApiLevels()
+ .build();
+ }
+
+ public UnsafeBackportTest(TestParameters parameters) throws IOException {
+ super(
+ parameters,
+ "sun.misc.Unsafe",
+ ImmutableList.of(UnsafeBackportTest.getTestRunner(), UnsafeBackportTest.getA()));
+
+ ignoreInvokes("objectFieldOffset");
+
+ // sun.misc.Unsafe issue is on API 31.
+ registerTarget(AndroidApiLevel.Sv2, 3);
+ }
+
+ public static class UnsafeStub {
+
+ boolean compareAndSwapObject(Object receiver, long offset, Object expect, Object update) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public long objectFieldOffset(Field field) {
+ throw new RuntimeException("Stub called.");
+ }
+ }
+
+ private static byte[] getTestRunner() throws IOException {
+ return transformer(TestRunner.class)
+ .setReturnType(MethodPredicate.onName("getUnsafe"), UNSAFE_TYPE_NAME)
+ .replaceClassDescriptorInMethodInstructions(descriptor(UnsafeStub.class), UNSAFE_DESCRIPTOR)
+ .transform();
+ }
+
+ private static byte[] getA() throws IOException {
+ return transformer(A.class).transform();
+ }
+
+ public static class A {
+ public String field;
+ }
+
+ public static class TestRunner extends MiniAssert {
+
+ private static UnsafeStub getUnsafe() throws Exception {
+ Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+ Field f = unsafeClass.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ return (UnsafeStub) f.get(null);
+ }
+
+ public static void main(String[] args) throws Exception {
+ UnsafeStub theUnsafe = getUnsafe();
+ A x = new A();
+ long offset = theUnsafe.objectFieldOffset(A.class.getField("field"));
+ assertTrue(theUnsafe.compareAndSwapObject(x, offset, null, "A"));
+ assertTrue(theUnsafe.compareAndSwapObject(x, offset, "A", "B"));
+ assertFalse(theUnsafe.compareAndSwapObject(x, offset, "A", "B"));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
index 61b0fd7..3e86cc9 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/SyntheticDistributionTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
@@ -98,6 +99,8 @@
"keptApplyLambda", MyFunction.class, String.class)))
.enableInliningAnnotations()
.noMinification()
+ // BaseDexClassLoader was introduced at api level 14.
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.setMinApi(parameters.getApiLevel());
}
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 5e79870..ce75bd9 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
@@ -50,7 +50,8 @@
OptionalMethods.class,
ShortMethods.class,
StreamMethods.class,
- StringMethods.class);
+ StringMethods.class,
+ UnsafeMethods.class);
protected final TestParameters parameters;
@@ -101,6 +102,34 @@
}
}
+ private static CfInstruction rewriteToUnsafe(
+ DexItemFactory itemFactory, CfInstruction instruction) {
+ // Rewrite references to UnsafeStub to sun.misc.Unsafe.
+ if (instruction.isInvoke()) {
+ String name = instruction.asInvoke().getMethod().getName().toString();
+ if (name.equals("compareAndSwapObject") || name.equals("getObject")) {
+ CfInvoke invoke = instruction.asInvoke();
+ return new CfInvoke(
+ invoke.getOpcode(),
+ itemFactory.createMethod(
+ itemFactory.createType("Lsun/misc/Unsafe;"),
+ invoke.getMethod().getProto(),
+ itemFactory.createString(name)),
+ invoke.isInterface());
+ }
+ }
+ if (instruction.isFrame()) {
+ return instruction
+ .asFrame()
+ .map(
+ type ->
+ (type.getTypeName().endsWith("$UnsafeStub"))
+ ? itemFactory.createType("Lsun/misc/Unsafe;")
+ : type);
+ }
+ return instruction;
+ }
+
@Override
protected CfCode getCode(String holderName, String methodName, CfCode code) {
if (methodName.endsWith("Stub")) {
@@ -113,6 +142,12 @@
.map(instruction -> rewriteToJava9API(factory, instruction))
.collect(Collectors.toList()));
}
+ if (holderName.equals("UnsafeMethods") && methodName.equals("compareAndSwapObject")) {
+ code.setInstructions(
+ code.getInstructions().stream()
+ .map(instruction -> rewriteToUnsafe(factory, instruction))
+ .collect(Collectors.toList()));
+ }
return code;
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java
new file mode 100644
index 0000000..95dab48
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/UnsafeMethods.java
@@ -0,0 +1,31 @@
+// 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;
+
+public final class UnsafeMethods {
+ // Stub out sun.misc.Unsafe to avoid compiler issues with referring to sun.misc.Unsafe.
+ private static class UnsafeStub {
+
+ public boolean compareAndSwapObject(
+ Object receiver, long offset, Object expect, Object update) {
+ throw new RuntimeException("Stub called.");
+ }
+
+ public Object getObject(Object receiver, long offset) {
+ throw new RuntimeException("Stub called.");
+ }
+ }
+
+ // Workaround Android S issue with compareAndSwapObject (b/211646483).
+ public static boolean compareAndSwapObject(
+ UnsafeStub unsafe, Object receiver, long offset, Object expect, Object update) {
+ do {
+ if (unsafe.compareAndSwapObject(receiver, offset, expect, update)) {
+ return true;
+ }
+ } while (unsafe.getObject(receiver, offset) == expect);
+ return false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
index cc1ded1..d74eb4b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InterfaceInvokeWithObjectReceiverInliningTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
import java.io.IOException;
import java.util.List;
import org.junit.Test;
@@ -23,6 +24,8 @@
@RunWith(Parameterized.class)
public class InterfaceInvokeWithObjectReceiverInliningTest extends TestBase {
+ private static final String EXPECTED = StringUtils.lines("0", "0");
+
@Parameter(0)
public boolean enableInlining;
@@ -48,7 +51,7 @@
.addProgramClasses(I.class, A.class)
.addProgramClassFileData(getTransformedMain())
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("0");
+ .assertSuccessWithOutput(EXPECTED);
}
@Test
@@ -75,7 +78,7 @@
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("0", "0");
+ .assertSuccessWithOutput(EXPECTED);
}
private static byte[] getTransformedMain() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java
new file mode 100644
index 0000000..d7e4296
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantInitClassBeforeInvokeStaticTest.java
@@ -0,0 +1,78 @@
+// 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.redundantfieldloadelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+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 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 RedundantInitClassBeforeInvokeStaticTest 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 greeterClassSubject = inspector.clazz(Greeter.class);
+ assertThat(greeterClassSubject, isPresent());
+ assertThat(greeterClassSubject.uniqueMethodWithName("hello"), isAbsent());
+ assertThat(greeterClassSubject.uniqueMethodWithName("world"), isPresent());
+ assertEquals(0, greeterClassSubject.allFields().size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ // The inlining of hello() will materialize an init-class instruction for Greeter. This
+ // instruction should be removed since it immediately precedes another instruction that
+ // initializes Greeter.
+ Greeter.hello();
+ Greeter.world();
+ }
+ }
+
+ static class Greeter {
+
+ static {
+ System.out.print("Hello");
+ }
+
+ static void hello() {}
+
+ @NeverInline
+ static void world() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
index 1183c1f..87fe5f0 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithFeatureSplitTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.apimodel.ApiModelingTestHelper;
import com.android.tools.r8.dexsplitter.SplitterTestBase.RunInterface;
import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -45,6 +46,8 @@
.addFeatureSplitRuntime()
.addKeepFeatureMainRule(FeatureMain.class)
.apply(this::configureRepackaging)
+ // BaseDexClassLoader was introduced at api level 14.
+ .apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()