Add api reference level to method optimization info
Bug: 188388130
Bug: 188498051
Change-Id: I7abb04ded8b2a78e113f9bb140a356c868cb343d
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 7c26ce2..1cd35e6 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -56,6 +56,7 @@
import com.android.tools.r8.ir.optimize.NestUtils;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationWithMinApiInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.info.UpdatableMethodOptimizationInfo;
@@ -1455,10 +1456,7 @@
public synchronized UpdatableMethodOptimizationInfo getMutableOptimizationInfo() {
checkIfObsolete();
- if (optimizationInfo == DefaultMethodOptimizationInfo.DEFAULT_INSTANCE) {
- optimizationInfo = optimizationInfo.mutableCopy();
- }
- return (UpdatableMethodOptimizationInfo) optimizationInfo;
+ return optimizationInfo.asUpdatableMethodOptimizationInfo();
}
public void setOptimizationInfo(UpdatableMethodOptimizationInfo info) {
@@ -1466,6 +1464,11 @@
optimizationInfo = info;
}
+ public void setMinApiOptimizationInfo(DefaultMethodOptimizationWithMinApiInfo info) {
+ checkIfObsolete();
+ optimizationInfo = info;
+ }
+
public synchronized void abandonCallSiteOptimizationInfo() {
checkIfObsolete();
callSiteOptimizationInfo = CallSiteOptimizationInfo.abandoned();
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 7d95e87..646c9a0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -29,10 +29,12 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.kotlin.Kotlin;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.LRUCacheTable;
+import com.android.tools.r8.utils.ListUtils;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -2498,6 +2500,22 @@
return createMethod(holder, proto, createString(name));
}
+ public DexMethod createMethod(MethodReference methodReference) {
+ DexString[] formals = new DexString[methodReference.getFormalTypes().size()];
+ ListUtils.forEachWithIndex(
+ methodReference.getFormalTypes(),
+ (formal, index) -> {
+ formals[index] = createString(formal.getDescriptor());
+ });
+ return createMethod(
+ createString(methodReference.getHolderClass().getDescriptor()),
+ createString(methodReference.getMethodName()),
+ methodReference.getReturnType() == null
+ ? voidDescriptor
+ : createString(methodReference.getReturnType().getDescriptor()),
+ formals);
+ }
+
public DexMethodHandle createMethodHandle(
MethodHandleType type,
DexMember<? extends DexItem, ? extends DexMember<?, ?>> fieldOrMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
index 17758fa..f8aae86 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoEnqueuerUseRegistry.java
@@ -11,13 +11,16 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoEnqueuerExtension;
import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerUseRegistryFactory;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.ListIterator;
+import java.util.Map;
public class ProtoEnqueuerUseRegistry extends DefaultEnqueuerUseRegistry {
@@ -28,8 +31,9 @@
public ProtoEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod currentMethod,
- Enqueuer enqueuer) {
- super(appView, currentMethod, enqueuer);
+ Enqueuer enqueuer,
+ Map<DexReference, AndroidApiLevel> apiLevelReferenceMap) {
+ super(appView, currentMethod, enqueuer, apiLevelReferenceMap);
this.references = appView.protoShrinker().references;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 8e66601..ececbe3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo.isApiSafeForInlining;
import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.addMonitorEnterValue;
import static com.android.tools.r8.ir.optimize.inliner.InlinerUtils.collectAllMonitorEnterValues;
@@ -33,6 +34,7 @@
import com.android.tools.r8.ir.optimize.Inliner.InlineAction;
import com.android.tools.r8.ir.optimize.Inliner.InlineeWithReason;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.inliner.InliningReasonStrategy;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
@@ -121,11 +123,22 @@
Reason reason,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
DexEncodedMethod singleTargetMethod = singleTarget.getDefinition();
- if (singleTargetMethod.getOptimizationInfo().neverInline()) {
+ MethodOptimizationInfo targetOptimizationInfo = singleTargetMethod.getOptimizationInfo();
+ if (targetOptimizationInfo.neverInline()) {
whyAreYouNotInliningReporter.reportMarkedAsNeverInline();
return false;
}
+ // Do not inline if the inlinee is greater than the api caller level.
+ MethodOptimizationInfo callerOptimizationInfo = method.getDefinition().getOptimizationInfo();
+ // TODO(b/188498051): We should not force inline lower api method calls.
+ if (reason != Reason.FORCE
+ && isApiSafeForInlining(callerOptimizationInfo, targetOptimizationInfo, appView.options())
+ .isPossiblyFalse()) {
+ whyAreYouNotInliningReporter.reportInlineeHigherApiCall();
+ return false;
+ }
+
// We don't inline into constructors when producing class files since this can mess up
// the stackmap, see b/136250031
if (method.getDefinition().isInstanceInitializer()
@@ -138,7 +151,7 @@
if (method.getDefinition() == singleTargetMethod) {
// Cannot handle recursive inlining at this point.
// Force inlined method should never be recursive.
- assert !singleTargetMethod.getOptimizationInfo().forceInline();
+ assert !targetOptimizationInfo.forceInline();
whyAreYouNotInliningReporter.reportRecursiveMethod();
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
index d49189f..2b7ef1a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationInfo.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.DefaultInstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.ImmutableSet;
import java.util.BitSet;
import java.util.Set;
@@ -38,8 +39,9 @@
static boolean UNKNOWN_RETURN_VALUE_ONLY_DEPENDS_ON_ARGUMENTS = false;
static BitSet NO_NULL_PARAMETER_OR_THROW_FACTS = null;
static BitSet NO_NULL_PARAMETER_ON_NORMAL_EXITS_FACTS = null;
+ static AndroidApiLevel UNKNOWN_API_REFERENCE_LEVEL = null;
- private DefaultMethodOptimizationInfo() {}
+ protected DefaultMethodOptimizationInfo() {}
public static DefaultMethodOptimizationInfo getInstance() {
return DEFAULT_INSTANCE;
@@ -57,7 +59,7 @@
@Override
public UpdatableMethodOptimizationInfo asUpdatableMethodOptimizationInfo() {
- return null;
+ return mutableCopy();
}
@Override
@@ -192,6 +194,16 @@
}
@Override
+ public AndroidApiLevel getApiReferenceLevel(AndroidApiLevel minApi) {
+ return UNKNOWN_API_REFERENCE_LEVEL;
+ }
+
+ @Override
+ public boolean hasApiReferenceLevel() {
+ return false;
+ }
+
+ @Override
public UpdatableMethodOptimizationInfo mutableCopy() {
return new UpdatableMethodOptimizationInfo();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java
new file mode 100644
index 0000000..fc61ba6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/DefaultMethodOptimizationWithMinApiInfo.java
@@ -0,0 +1,35 @@
+// 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.optimize.info;
+
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class DefaultMethodOptimizationWithMinApiInfo extends DefaultMethodOptimizationInfo {
+
+ private static final DefaultMethodOptimizationWithMinApiInfo DEFAULT_MIN_API_INSTANCE =
+ new DefaultMethodOptimizationWithMinApiInfo();
+
+ public static DefaultMethodOptimizationWithMinApiInfo getInstance() {
+ return DEFAULT_MIN_API_INSTANCE;
+ }
+
+ @Override
+ public boolean hasApiReferenceLevel() {
+ return true;
+ }
+
+ @Override
+ public AndroidApiLevel getApiReferenceLevel(AndroidApiLevel minApi) {
+ return minApi;
+ }
+
+ @Override
+ public UpdatableMethodOptimizationInfo mutableCopy() {
+ UpdatableMethodOptimizationInfo updatableMethodOptimizationInfo = super.mutableCopy();
+ // Use null to specify that the min api is set to minApi.
+ updatableMethodOptimizationInfo.setApiReferenceLevel(null);
+ return updatableMethodOptimizationInfo;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
index bdc35f3..c006e6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfo.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.ir.optimize.info;
+import static com.android.tools.r8.utils.OptionalBool.UNKNOWN;
+
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -13,6 +15,9 @@
import com.android.tools.r8.ir.optimize.classinliner.constraint.ClassInlinerMethodConstraint;
import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.OptionalBool;
import java.util.BitSet;
import java.util.Set;
@@ -95,5 +100,20 @@
public abstract boolean returnValueHasBeenPropagated();
+ public abstract AndroidApiLevel getApiReferenceLevel(AndroidApiLevel minApi);
+
+ public abstract boolean hasApiReferenceLevel();
+
public abstract UpdatableMethodOptimizationInfo mutableCopy();
+
+ public static OptionalBool isApiSafeForInlining(
+ MethodOptimizationInfo caller, MethodOptimizationInfo inlinee, InternalOptions options) {
+ if (!caller.hasApiReferenceLevel() || !inlinee.hasApiReferenceLevel()) {
+ return UNKNOWN;
+ }
+ return OptionalBool.of(
+ caller
+ .getApiReferenceLevel(options.minApiLevel)
+ .isGreaterThanOrEqualTo(inlinee.getApiReferenceLevel(options.minApiLevel)));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
index 0d2e43b..85e2576 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/UpdatableMethodOptimizationInfo.java
@@ -22,8 +22,10 @@
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.BitSet;
+import java.util.Optional;
import java.util.Set;
public class UpdatableMethodOptimizationInfo extends MethodOptimizationInfo {
@@ -66,6 +68,8 @@
private SimpleInliningConstraint simpleInliningConstraint =
NeverSimpleInliningConstraint.getInstance();
+ private Optional<AndroidApiLevel> apiReferenceLevel = null;
+
// To reduce the memory footprint of UpdatableMethodOptimizationInfo, all the boolean fields are
// merged into a flag int field. The various static final FLAG fields indicate which bit is
// used by each boolean. DEFAULT_FLAGS encodes the default value for efficient instantiation and
@@ -146,6 +150,7 @@
nonNullParamOrThrow = template.nonNullParamOrThrow;
nonNullParamOnNormalExits = template.nonNullParamOnNormalExits;
classInlinerConstraint = template.classInlinerConstraint;
+ apiReferenceLevel = template.apiReferenceLevel;
}
public UpdatableMethodOptimizationInfo fixupClassTypeReferences(
@@ -495,6 +500,23 @@
}
@Override
+ public AndroidApiLevel getApiReferenceLevel(AndroidApiLevel minApi) {
+ assert hasApiReferenceLevel();
+ return apiReferenceLevel.orElse(minApi);
+ }
+
+ @SuppressWarnings("OptionalAssignedToNull")
+ @Override
+ public boolean hasApiReferenceLevel() {
+ return apiReferenceLevel != null;
+ }
+
+ public UpdatableMethodOptimizationInfo setApiReferenceLevel(AndroidApiLevel apiReferenceLevel) {
+ this.apiReferenceLevel = Optional.ofNullable(apiReferenceLevel);
+ return this;
+ }
+
+ @Override
public UpdatableMethodOptimizationInfo mutableCopy() {
return new UpdatableMethodOptimizationInfo(this);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
index e398dce..218a8fc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -58,6 +58,9 @@
public void reportInlineeNotSimple() {}
@Override
+ public void reportInlineeHigherApiCall() {}
+
+ @Override
public void reportInlineeRefersToClassesNotInMainDex() {}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index 81c6a35..8063674 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -69,6 +69,8 @@
public abstract void reportInlineeNotSimple();
+ public abstract void reportInlineeHigherApiCall();
+
public abstract void reportInlineeRefersToClassesNotInMainDex();
public abstract void reportInliningAcrossFeatureSplit();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 257cc15..f9418b4 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -120,6 +120,11 @@
}
@Override
+ public void reportInlineeHigherApiCall() {
+ print("inlinee having a higher api call than caller context.");
+ }
+
+ @Override
public void reportInlineeRefersToClassesNotInMainDex() {
print(
"inlining could increase the main dex size "
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index f8302d7..cb49580 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -13,25 +13,33 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.ListIterator;
+import java.util.Map;
public class DefaultEnqueuerUseRegistry extends UseRegistry {
protected final AppView<? extends AppInfoWithClassHierarchy> appView;
private final ProgramMethod context;
protected final Enqueuer enqueuer;
+ private final Map<DexReference, AndroidApiLevel> apiReferenceMapping;
+ private AndroidApiLevel maxApiReferenceLevel;
public DefaultEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod context,
- Enqueuer enqueuer) {
+ Enqueuer enqueuer,
+ Map<DexReference, AndroidApiLevel> apiReferenceMapping) {
super(appView.dexItemFactory());
this.appView = appView;
this.context = context;
this.enqueuer = enqueuer;
+ this.apiReferenceMapping = apiReferenceMapping;
+ this.maxApiReferenceLevel = appView.options().minApiLevel;
}
public ProgramMethod getContext() {
@@ -53,26 +61,31 @@
@Override
public void registerInvokeVirtual(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
enqueuer.traceInvokeVirtual(invokedMethod, context);
}
@Override
public void registerInvokeDirect(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
enqueuer.traceInvokeDirect(invokedMethod, context);
}
@Override
public void registerInvokeStatic(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
enqueuer.traceInvokeStatic(invokedMethod, context);
}
@Override
public void registerInvokeInterface(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
enqueuer.traceInvokeInterface(invokedMethod, context);
}
@Override
public void registerInvokeSuper(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
enqueuer.traceInvokeSuper(invokedMethod, context);
}
@@ -158,4 +171,14 @@
super.registerCallSite(callSite);
enqueuer.traceCallSite(callSite, context);
}
+
+ private void setMaxApiReferenceLevel(DexMethod invokedMethod) {
+ this.maxApiReferenceLevel =
+ maxApiReferenceLevel.max(
+ apiReferenceMapping.getOrDefault(invokedMethod, maxApiReferenceLevel));
+ }
+
+ public AndroidApiLevel getMaxApiReferenceLevel() {
+ return maxApiReferenceLevel;
+ }
}
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 f6d9e6e..6d9bc15 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -102,6 +102,8 @@
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.DefaultMethodOptimizationWithMinApiInfo;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -120,6 +122,7 @@
import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult;
import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle;
import com.android.tools.r8.utils.Action;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.ListUtils;
@@ -254,6 +257,8 @@
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
+ private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+
/**
* Tracks the dependency between a method and the super-method it calls, if any. Used to make
* super methods become live when they become reachable from a live sub-method.
@@ -463,6 +468,17 @@
} else {
desugaredLibraryWrapperAnalysis = null;
}
+ referenceToApiLevelMap = new IdentityHashMap<>();
+ if (options.apiModelingOptions().enableApiCallerIdentification) {
+ options
+ .apiModelingOptions()
+ .methodApiMapping
+ .forEach(
+ (methodReference, apiLevel) -> {
+ referenceToApiLevelMap.put(
+ options.dexItemFactory().createMethod(methodReference), apiLevel);
+ });
+ }
}
private AppInfoWithClassHierarchy appInfo() {
@@ -3895,7 +3911,22 @@
}
void traceCode(ProgramMethod method) {
- method.registerCodeReferences(useRegistryFactory.create(appView, method, this));
+ DefaultEnqueuerUseRegistry registry =
+ useRegistryFactory.create(appView, method, this, referenceToApiLevelMap);
+ method.registerCodeReferences(registry);
+ DexEncodedMethod methodDefinition = method.getDefinition();
+ AndroidApiLevel maxApiReferenceLevel = registry.getMaxApiReferenceLevel();
+ assert maxApiReferenceLevel.isGreaterThanOrEqualTo(options.minApiLevel);
+ // To not have mutable update information for all methods that all has min api level we
+ // swap the default optimization info for one with that marks the api level to be min api.
+ if (methodDefinition.getOptimizationInfo() == DefaultMethodOptimizationInfo.getInstance()
+ && maxApiReferenceLevel == options.minApiLevel) {
+ methodDefinition.setMinApiOptimizationInfo(
+ DefaultMethodOptimizationWithMinApiInfo.getInstance());
+ return;
+ }
+ methodDefinition.setOptimizationInfo(
+ methodDefinition.getMutableOptimizationInfo().setApiReferenceLevel(maxApiReferenceLevel));
}
private void checkMemberForSoftPinning(ProgramMember<?, ?> member) {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
index c15f8d7..6bd8473 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -6,13 +6,16 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Map;
public interface EnqueuerUseRegistryFactory {
- UseRegistry create(
+ DefaultEnqueuerUseRegistry create(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod currentMethod,
- Enqueuer enqueuer);
+ Enqueuer enqueuer,
+ Map<DexReference, AndroidApiLevel> apiLevelReferenceMap);
}
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 de01d36..0263e28 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -348,9 +348,6 @@
// Contain the contents of the build properties file from the compiler command.
public DumpOptions dumpOptions;
- // A mapping from methods to the api-level introducing them.
- public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
-
// Hidden marker for classes.dex
private boolean hasMarker = false;
private Marker marker;
@@ -675,6 +672,7 @@
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
+ private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
private final DesugarSpecificOptions desugarSpecificOptions = new DesugarSpecificOptions();
public final TestingOptions testing = new TestingOptions();
@@ -707,6 +705,10 @@
return kotlinOptimizationOptions;
}
+ public ApiModelTestingOptions apiModelingOptions() {
+ return apiModelTestingOptions;
+ }
+
public DesugarSpecificOptions desugarSpecificOptions() {
return desugarSpecificOptions;
}
@@ -1290,6 +1292,14 @@
}
}
+ public static class ApiModelTestingOptions {
+
+ // A mapping from methods to the api-level introducing them.
+ public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
+
+ public boolean enableApiCallerIdentification = false;
+ }
+
public static class ProtoShrinkingOptions {
public boolean enableGeneratedExtensionRegistryShrinking = false;
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
similarity index 60%
copy from src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java
copy to src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
index 7130631..4e5c39d 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelInterfaceTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
-import static org.junit.Assert.assertThrows;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -21,23 +20,23 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelStaticTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelInterfaceTest extends TestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
}
- public ApiModelingNoInliningOfHigherApiLevelStaticTest(TestParameters parameters) {
+ public ApiModelNoInliningOfHigherApiLevelInterfaceTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testR8() throws Exception {
Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
- Method apiCaller = ApiCaller.class.getDeclaredMethod("callStaticMethod");
+ Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Api.class);
Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
testForR8(parameters.getBackend())
.addProgramClasses(Main.class, A.class, ApiCaller.class)
@@ -48,41 +47,27 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
- .compile()
- .inspect(
- inspector -> {
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector);
- } else {
- // TODO(b/188388130): Should only inline on minApi >= 22.
- assertThrows(
- AssertionError.class,
- () ->
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector));
- }
- })
- .addRunClasspathClasses(Api.class)
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines(
- "A::noApiCall", "ApiCaller::callStaticMethod", "Api::apiLevel22");
+ .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callInterfaceMethod")
+ .inspect(
+ verifyThat(parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1));
}
- public static class Api {
+ public interface Api {
- public static void apiLevel22() {
- System.out.println("Api::apiLevel22");
- }
+ void apiLevel22();
}
@NoHorizontalClassMerging
public static class ApiCaller {
- public static void callStaticMethod() {
- System.out.println("ApiCaller::callStaticMethod");
- Api.apiLevel22();
+
+ public static void callInterfaceMethod(Api api) {
+ System.out.println("ApiCaller::callInterfaceMethod");
+ if (api != null) {
+ api.apiLevel22();
+ }
}
}
@@ -92,7 +77,7 @@
@NeverInline
public static void noApiCall() {
System.out.println("A::noApiCall");
- ApiCaller.callStaticMethod();
+ ApiCaller.callInterfaceMethod(null);
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelIntoLowerDirectTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
similarity index 88%
rename from src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelIntoLowerDirectTest.java
rename to src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
index 174dcf7..048874d 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelIntoLowerDirectTest.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest.java
@@ -20,16 +20,16 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelIntoLowerDirectTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest extends TestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public ApiModelingNoInliningOfHigherApiLevelIntoLowerDirectTest(TestParameters parameters) {
+ public ApiModelNoInliningOfHigherApiLevelIntoLowerDirectTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -45,6 +45,7 @@
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiLevel21, AndroidApiLevel.L))
.apply(setMockApiLevelForMethod(apiLevel22, AndroidApiLevel.L_MR1))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::apiLevel21", "B::apiLevel22")
.inspect(verifyThat(parameters, apiLevel22).inlinedInto(apiLevel21));
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticTest.java
similarity index 72%
rename from src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java
rename to src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticTest.java
index 7130631..dfb8628 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelStaticTest.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelStaticTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
-import static org.junit.Assert.assertThrows;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
@@ -21,16 +20,16 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelStaticTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelStaticTest extends TestBase {
private final TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public ApiModelingNoInliningOfHigherApiLevelStaticTest(TestParameters parameters) {
+ public ApiModelNoInliningOfHigherApiLevelStaticTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -48,23 +47,11 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- inspector -> {
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector);
- } else {
- // TODO(b/188388130): Should only inline on minApi >= 22.
- assertThrows(
- AssertionError.class,
- () ->
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector));
- }
- })
+ verifyThat(parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelSuperTest.java
similarity index 74%
rename from src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelSuperTest.java
rename to src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index 7f96dbc9..415a163 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -6,11 +6,11 @@
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
-import static org.junit.Assert.assertThrows;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -22,7 +22,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelSuperTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelSuperTest extends TestBase {
private final TestParameters parameters;
@@ -31,7 +31,7 @@
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
- public ApiModelingNoInliningOfHigherApiLevelSuperTest(TestParameters parameters) {
+ public ApiModelNoInliningOfHigherApiLevelSuperTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -47,30 +47,19 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- inspector -> {
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector);
- } else {
- // TODO(b/188388130): Should only inline on minApi >= 22.
- assertThrows(
- AssertionError.class,
- () ->
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector));
- }
- })
+ verifyThat(parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::apiLevel22", "Api::apiLevel22");
}
- @NoHorizontalClassMerging
+ @NoVerticalClassMerging
public static class Api {
void apiLevel22() {
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelVirtualTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
similarity index 70%
rename from src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelVirtualTest.java
rename to src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
index fdf184e..31af9e3 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -4,11 +4,10 @@
package com.android.tools.r8.apimodeling;
+import static com.android.tools.r8.apimodeling.ApiModelNoInliningOfHigherApiLevelVirtualTest.ApiCaller.callVirtualMethod;
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
-import static org.junit.Assert.assertThrows;
-import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
@@ -22,7 +21,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelVirtualTest extends TestBase {
+public class ApiModelNoInliningOfHigherApiLevelVirtualTest extends TestBase {
private final TestParameters parameters;
@@ -31,7 +30,7 @@
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
- public ApiModelingNoInliningOfHigherApiLevelVirtualTest(TestParameters parameters) {
+ public ApiModelNoInliningOfHigherApiLevelVirtualTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -48,25 +47,12 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
- .enableNeverClassInliningAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
- inspector -> {
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector);
- } else {
- // TODO(b/188388130): Should only inline on minApi >= 22.
- assertThrows(
- AssertionError.class,
- () ->
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)
- .accept(inspector));
- }
- })
+ verifyThat(parameters, apiCaller)
+ .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
@@ -81,9 +67,9 @@
}
@NoHorizontalClassMerging
- @NeverClassInline
public static class ApiCaller {
- public void callVirtualMethod() {
+
+ public static void callVirtualMethod() {
System.out.println("ApiCaller::callVirtualMethod");
new Api().apiLevel22();
}
@@ -95,7 +81,7 @@
@NeverInline
public static void noApiCall() {
System.out.println("A::noApiCall");
- new ApiCaller().callVirtualMethod();
+ callVirtualMethod();
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelInterfaceTest.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelInterfaceTest.java
deleted file mode 100644
index e56d36e..0000000
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingNoInliningOfHigherApiLevelInterfaceTest.java
+++ /dev/null
@@ -1,102 +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.apimodeling;
-
-import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.setMockApiLevelForMethod;
-import static com.android.tools.r8.apimodeling.ApiModelingTestHelper.verifyThat;
-import static org.junit.Assert.assertThrows;
-
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
-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.utils.AndroidApiLevel;
-import java.lang.reflect.Method;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class ApiModelingNoInliningOfHigherApiLevelInterfaceTest extends TestBase {
-
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
- }
-
- public ApiModelingNoInliningOfHigherApiLevelInterfaceTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testR8() throws Exception {
- Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
- Method apiCaller = ApiCaller.class.getDeclaredMethod("callInterfaceMethod", Api.class);
- Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClasses(Main.class, A.class, ApiCaller.class)
- .addLibraryClasses(Api.class)
- .addDefaultRuntimeLibrary(parameters)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(Main.class)
- .enableInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
- .apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
- .run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callInterfaceMethod");
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
- runResult.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1));
- } else {
- // TODO(b/188388130): Should only inline on minApi >= 22.
- assertThrows(
- AssertionError.class,
- () ->
- runResult.inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1)));
- }
- }
-
- public interface Api {
-
- void apiLevel22();
- }
-
- @NoHorizontalClassMerging
- public static class ApiCaller {
-
- public static void callInterfaceMethod(Api api) {
- System.out.println("ApiCaller::callInterfaceMethod");
- if (api != null) {
- api.apiLevel22();
- }
- }
- }
-
- @NoHorizontalClassMerging
- public static class A {
-
- @NeverInline
- public static void noApiCall() {
- System.out.println("A::noApiCall");
- ApiCaller.callInterfaceMethod(null);
- }
- }
-
- public static class Main {
-
- public static void main(String[] args) {
- A.noApiCall();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
index d22c4bb..5b1a2f7 100644
--- a/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodeling/ApiModelingTestHelper.java
@@ -26,11 +26,21 @@
return compilerBuilder -> {
compilerBuilder.addOptionsModification(
options -> {
- options.methodApiMapping.put(Reference.methodFromMethod(method), apiLevel);
+ options
+ .apiModelingOptions()
+ .methodApiMapping
+ .put(Reference.methodFromMethod(method), apiLevel);
});
};
}
+ static void enableApiCallerIdentification(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ compilerBuilder.addOptionsModification(
+ options -> {
+ options.apiModelingOptions().enableApiCallerIdentification = true;
+ });
+ }
+
static ApiModelingMethodVerificationHelper verifyThat(TestParameters parameters, Method method) {
return new ApiModelingMethodVerificationHelper(parameters, method);
}
@@ -47,7 +57,7 @@
protected ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
Method method, AndroidApiLevel apiLevel) {
- return parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)
+ return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)
? inlinedInto(method)
: notInlinedInto(method);
}