Merge commit '348064ccc3ffcfff8c51db26b1c98e3396d46529' into dev-release
diff --git a/build.gradle b/build.gradle
index 7f0d08f..1dc777d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -492,21 +492,29 @@
}
}
-task downloadDeps {
+task downloadCloudDeps() {
cloudDependencies.each { entry ->
entry.value.each { entryFile ->
dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
}
}
- if (!project.hasProperty('no_internal')) {
- x20Dependencies.each { entry ->
- entry.value.each { entryFile ->
- dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
- }
+}
+
+task downloadX20Deps() {
+ x20Dependencies.each { entry ->
+ entry.value.each { entryFile ->
+ dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
}
}
}
+task downloadDeps {
+ dependsOn downloadCloudDeps
+ if (!project.hasProperty('no_internal')) {
+ dependsOn downloadX20Deps
+ }
+}
+
allprojects {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
@@ -538,6 +546,8 @@
}
}
+compileJava.dependsOn downloadCloudDeps
+
sourceSets.configureEach { sourceSet ->
tasks.named(sourceSet.compileJavaTaskName).configure {
// Default disable errorprone (enabled and setup below).
@@ -727,6 +737,7 @@
}
task repackageDepsNew(type: ShadowJar) {
+ dependsOn downloadCloudDeps
configurations = [project.configurations.runtimeClasspath]
mergeServiceFiles(it)
exclude { it.getRelativePath().getPathString().endsWith("module-info.class") }
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 2fadb7d..ac5cd11 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -44,7 +44,6 @@
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.LineNumberOptimizer;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
@@ -348,8 +347,6 @@
private static ProguardMapSupplier finalizeApplication(
AndroidApp inputApp, AppView<AppInfo> appView, NamingLens namingLens) {
SyntheticFinalization.finalize(appView);
- // TODO(b/37830524): Once D8 supports PC mapping this will need to be run for that too.
- assert appView.options().lineNumberOptimization == LineNumberOptimization.OFF;
if (appView.options().proguardMapConsumer == null) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
index f33f5bd..ee0ffb2 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiClass.java
@@ -26,6 +26,8 @@
public abstract AndroidApiLevel getApiLevel();
+ public abstract int getMemberCount();
+
public abstract TraversalContinuation visitFields(
BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor);
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
new file mode 100644
index 0000000..124ea3f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiReferenceLevelCache.java
@@ -0,0 +1,150 @@
+// 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.androidapi;
+
+import com.android.tools.r8.apimodel.AndroidApiDatabaseBuilder;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.TraversalContinuation;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+public class AndroidApiReferenceLevelCache {
+
+ private static final int BUILD_CACHE_TRESHOLD = 20;
+
+ private final Map<DexType, AndroidApiClass> apiTypeLookup;
+ private final Map<DexReference, AndroidApiLevel> apiMemberLookup = new IdentityHashMap<>();
+ private final AppView<?> appView;
+
+ private AndroidApiReferenceLevelCache(AppView<?> appView) {
+ this.appView = appView;
+ this.apiTypeLookup = new IdentityHashMap<>();
+ }
+
+ private AndroidApiReferenceLevelCache(
+ AppView<?> appView, Map<DexType, AndroidApiClass> apiTypeLookup) {
+ this.appView = appView;
+ this.apiTypeLookup = apiTypeLookup;
+ }
+
+ public static AndroidApiReferenceLevelCache create(AppView<?> appView) {
+ if (!appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ // If enableApiCallerIdentification is not enabled then override lookup to always return
+ // AndroidApiLevel.B.
+ return new AndroidApiReferenceLevelCache(appView) {
+ @Override
+ public AndroidApiLevel lookup(DexReference reference) {
+ return AndroidApiLevel.B;
+ }
+ };
+ }
+ // The apiTypeLookup is build lazily except for the mocked api types that we define in tests
+ // externally.
+ Map<DexType, AndroidApiClass> apiTypeLookup = new IdentityHashMap<>();
+ appView
+ .options()
+ .apiModelingOptions()
+ .visitMockedApiReferences(
+ (classReference, androidApiClass) ->
+ apiTypeLookup.put(
+ appView.dexItemFactory().createType(classReference.getDescriptor()),
+ androidApiClass));
+ return new AndroidApiReferenceLevelCache(appView, apiTypeLookup);
+ }
+
+ public AndroidApiLevel lookupMax(DexReference reference, AndroidApiLevel minApiLevel) {
+ return lookup(reference).max(minApiLevel);
+ }
+
+ public AndroidApiLevel lookup(DexReference reference) {
+ DexType contextType = reference.getContextType();
+ assert !contextType.isArrayType();
+ if (contextType.isPrimitiveType() || contextType.isVoidType()) {
+ return AndroidApiLevel.B;
+ }
+ DexClass clazz = appView.definitionFor(contextType);
+ if (clazz == null) {
+ return AndroidApiLevel.UNKNOWN;
+ }
+ if (!clazz.isLibraryClass()) {
+ return appView.options().minApiLevel;
+ }
+ AndroidApiClass androidApiClass =
+ apiTypeLookup.computeIfAbsent(
+ contextType, type -> AndroidApiDatabaseBuilder.buildClass(type.asClassReference()));
+ if (androidApiClass == null) {
+ // This is a library class but we have no api model for it. This happens if using an older
+ // version of R8 to compile a new target. We simply have to disallow inlining of methods
+ // that has such references.
+ return AndroidApiLevel.UNKNOWN;
+ }
+ if (reference.isDexType()) {
+ return androidApiClass.getApiLevel();
+ }
+ return androidApiClass.getMemberCount() > BUILD_CACHE_TRESHOLD
+ ? findMemberByCaching(reference, androidApiClass)
+ : findMemberByIteration(reference.asDexMember(), androidApiClass);
+ }
+
+ private AndroidApiLevel findMemberByIteration(
+ DexMember<?, ?> reference, AndroidApiClass apiClass) {
+ DexItemFactory factory = appView.dexItemFactory();
+ // Similar to the case for api classes we are unable to find, if the member
+ // is unknown we have to be conservative.
+ Box<AndroidApiLevel> apiLevelBox = new Box<>(AndroidApiLevel.UNKNOWN);
+ reference.apply(
+ field ->
+ apiClass.visitFields(
+ (fieldReference, apiLevel) -> {
+ if (factory.createField(fieldReference) == field) {
+ apiLevelBox.set(apiLevel);
+ return TraversalContinuation.BREAK;
+ }
+ return TraversalContinuation.CONTINUE;
+ }),
+ method ->
+ apiClass.visitMethods(
+ (methodReference, apiLevel) -> {
+ if (factory.createMethod(methodReference) == method) {
+ apiLevelBox.set(apiLevel);
+ return TraversalContinuation.BREAK;
+ }
+ return TraversalContinuation.CONTINUE;
+ }));
+ return apiLevelBox.get();
+ }
+
+ private AndroidApiLevel findMemberByCaching(DexReference reference, AndroidApiClass apiClass) {
+ buildCacheForMembers(reference.getContextType(), apiClass);
+ return apiMemberLookup.getOrDefault(reference, AndroidApiLevel.UNKNOWN);
+ }
+
+ private void buildCacheForMembers(DexType context, AndroidApiClass apiClass) {
+ assert apiClass.getMemberCount() > BUILD_CACHE_TRESHOLD;
+ // Use the context type as a token for us having build a cache for it.
+ if (apiMemberLookup.containsKey(context)) {
+ return;
+ }
+ DexItemFactory factory = appView.dexItemFactory();
+ apiClass.visitFields(
+ (fieldReference, apiLevel) -> {
+ apiMemberLookup.put(factory.createField(fieldReference), apiLevel);
+ return TraversalContinuation.CONTINUE;
+ });
+ apiClass.visitMethods(
+ (methodReference, apiLevel) -> {
+ apiMemberLookup.put(factory.createMethod(methodReference), apiLevel);
+ return TraversalContinuation.CONTINUE;
+ });
+ apiMemberLookup.put(context, AndroidApiLevel.UNKNOWN);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index 271fd68..e054ffa 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -69,7 +69,7 @@
public abstract <T> T apply(
Function<DexEncodedField, T> fieldConsumer, Function<DexEncodedMethod, T> methodConsumer);
- public void apply(
+ public void accept(
Consumer<DexEncodedField> fieldConsumer, Consumer<DexEncodedMethod> methodConsumer) {
apply(
field -> {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index ae9d9fb..bffc842 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -5,7 +5,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.google.common.collect.Iterables;
-import java.util.Map;
+import java.util.function.BiFunction;
import java.util.function.Function;
public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
@@ -64,12 +64,11 @@
}
public AndroidApiLevel computeApiLevelForReferencedTypes(
- AppView<?> appView, Map<DexReference, AndroidApiLevel> apiLevelMap) {
- AndroidApiLevel minApiLevel = appView.options().minApiLevel;
- AndroidApiLevel apiLevel = minApiLevel;
+ AppView<?> appView, BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> computeMax) {
+ AndroidApiLevel computedLevel = appView.options().minApiLevel;
for (DexType type : getReferencedBaseTypes(appView.dexItemFactory())) {
- apiLevel = apiLevel.max(apiLevelMap.getOrDefault(type, minApiLevel));
+ computedLevel = computeMax.apply(type, computedLevel);
}
- return apiLevel;
+ return computedLevel;
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0ebd1da..d46c103 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -15,11 +15,13 @@
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
import com.android.tools.r8.kotlin.KotlinClassLevelInfo;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticMarker;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.Ordered;
import com.android.tools.r8.utils.structural.StructuralItem;
@@ -32,6 +34,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -834,4 +837,26 @@
public ChecksumSupplier getChecksumSupplier() {
return checksumSupplier;
}
+
+ public AndroidApiLevel getApiReferenceLevel(
+ AndroidApiLevel minApiLevel,
+ BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelLookup) {
+ // The api level of a class is the max level of it's members, super class and interfaces.
+ AndroidApiLevel computedApiLevel = minApiLevel;
+ for (DexType superType : allImmediateSupertypes()) {
+ computedApiLevel = apiLevelLookup.apply(superType, computedApiLevel);
+ if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
+ return AndroidApiLevel.UNKNOWN;
+ }
+ }
+ for (DexEncodedMember<?, ?> member : members()) {
+ MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
+ assert optimizationInfo.hasApiReferenceLevel();
+ computedApiLevel = optimizationInfo.getApiReferenceLevel(computedApiLevel);
+ if (computedApiLevel == AndroidApiLevel.UNKNOWN) {
+ return AndroidApiLevel.UNKNOWN;
+ }
+ }
+ return computedApiLevel;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index a3cf3a1..03f4259 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -236,7 +236,7 @@
// We represent no signature as object.
} else if (context.superType
!= appView.graphLens().lookupClassType(classSignature.superClassSignature().type())) {
- assert mode.doNotVerify();
+ assert mode.doNotVerify() : "Super type inconsistency in generic signature";
return INVALID_SUPER_TYPE;
}
signatureEvaluationResult =
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
index f93437e..65840c5 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ApiModelAnalysis.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.graph.analysis;
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
@@ -16,19 +16,17 @@
import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
import com.android.tools.r8.shaking.DefaultEnqueuerUseRegistry;
import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
public class ApiModelAnalysis extends EnqueuerAnalysis {
private final AppView<?> appView;
private final AndroidApiLevel minApiLevel;
- private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+ private final AndroidApiReferenceLevelCache referenceLevelCache;
- public ApiModelAnalysis(
- AppView<?> appView, Map<DexReference, AndroidApiLevel> referenceToApiLevelMap) {
+ public ApiModelAnalysis(AppView<?> appView, AndroidApiReferenceLevelCache referenceLevelCache) {
this.appView = appView;
this.minApiLevel = appView.options().minApiLevel;
- this.referenceToApiLevelMap = referenceToApiLevelMap;
+ this.referenceLevelCache = referenceLevelCache;
}
@Override
@@ -46,6 +44,13 @@
@Override
public void processTracedCode(ProgramMethod method, DefaultEnqueuerUseRegistry registry) {
assert registry.getMaxApiReferenceLevel().isGreaterThanOrEqualTo(minApiLevel);
+ if (appView.options().apiModelingOptions().tracedMethodApiLevelCallback != null) {
+ appView
+ .options()
+ .apiModelingOptions()
+ .tracedMethodApiLevelCallback
+ .accept(method.getMethodReference(), registry.getMaxApiReferenceLevel());
+ }
setApiLevelForMember(method.getDefinition(), registry.getMaxApiReferenceLevel());
}
@@ -54,7 +59,7 @@
// swap the default optimization info for one with that marks the api level to be min api.
MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
if (!optimizationInfo.isMutableOptimizationInfo() && apiLevel == minApiLevel) {
- member.apply(
+ member.accept(
field -> {
field.setMinApiOptimizationInfo(DefaultFieldOptimizationWithMinApiInfo.getInstance());
},
@@ -66,7 +71,7 @@
optimizationInfo.hasApiReferenceLevel()
? apiLevel.max(optimizationInfo.getApiReferenceLevel(minApiLevel))
: apiLevel;
- member.apply(
+ member.accept(
field -> {
field.getMutableOptimizationInfo().setApiReferenceLevel(maxApiLevel);
},
@@ -77,6 +82,6 @@
}
private AndroidApiLevel computeApiLevelForReferencedTypes(DexMember<?, ?> member) {
- return member.computeApiLevelForReferencedTypes(appView, referenceToApiLevelMap);
+ return member.computeApiLevelForReferencedTypes(appView, referenceLevelCache::lookupMax);
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 0d3bf0d..4abddc6 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoDeadLocks;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.NoDefaultInterfaceMethodMerging;
+import com.android.tools.r8.horizontalclassmerging.policies.NoDifferentApiReferenceLevel;
import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
import com.android.tools.r8.horizontalclassmerging.policies.NoIllegalInlining;
@@ -199,6 +200,7 @@
new SameParentClass(),
new SyntheticItemsPolicy(appView, mode),
new RespectPackageBoundaries(appView),
+ new NoDifferentApiReferenceLevel(appView),
new PreventClassMethodAndDefaultMethodCollisions(appView));
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
new file mode 100644
index 0000000..97d8d21
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDifferentApiReferenceLevel.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
+import com.android.tools.r8.utils.AndroidApiLevel;
+
+public class NoDifferentApiReferenceLevel extends MultiClassSameReferencePolicy<AndroidApiLevel> {
+
+ private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
+ private final AndroidApiLevel minApi;
+ // TODO(b/188388130): Remove when stabilized.
+ private final boolean enableApiCallerIdentification;
+
+ public NoDifferentApiReferenceLevel(AppView<?> appView) {
+ apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
+ minApi = appView.options().minApiLevel;
+ enableApiCallerIdentification =
+ appView.options().apiModelingOptions().enableApiCallerIdentification;
+ }
+
+ @Override
+ public boolean shouldSkipPolicy() {
+ return !enableApiCallerIdentification;
+ }
+
+ @Override
+ public String getName() {
+ return "NoDifferentApiReferenceLevel";
+ }
+
+ @Override
+ public AndroidApiLevel getMergeKey(DexProgramClass clazz) {
+ assert enableApiCallerIdentification;
+ return clazz.getApiReferenceLevel(minApi, apiReferenceLevelCache::lookupMax);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index 2dce46e..19607ee 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -28,9 +28,13 @@
@Override
public boolean isSatisfied(InvokeMethod invoke) {
- Value argument = getArgument(invoke);
- assert argument.getType().isReferenceType();
- return argument.getType().isDefinitelyNull();
+ // Take the root value to also deal with the following case, which may happen in dead code,
+ // where v1 is actually guaranteed to be null, despite the value's type being non-null:
+ // v0 <- ConstNumber 0
+ // v1 <- AssumeNotNull v0
+ Value argumentRoot = getArgument(invoke).getAliasedValue();
+ assert argumentRoot.getType().isReferenceType();
+ return argumentRoot.getType().isDefinitelyNull();
}
@Override
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 f8aae86..ecd958d 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
@@ -20,7 +20,7 @@
import com.android.tools.r8.shaking.EnqueuerUseRegistryFactory;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
public class ProtoEnqueuerUseRegistry extends DefaultEnqueuerUseRegistry {
@@ -32,7 +32,7 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod currentMethod,
Enqueuer enqueuer,
- Map<DexReference, AndroidApiLevel> apiLevelReferenceMap) {
+ BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap) {
super(appView, currentMethod, enqueuer, apiLevelReferenceMap);
this.references = appView.protoShrinker().references;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 4674e14..9c70c92 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -240,6 +240,16 @@
}
@Override
+ public Value getNonNullInput() {
+ return src();
+ }
+
+ @Override
+ public boolean throwsOnNullInput() {
+ return true;
+ }
+
+ @Override
public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
assert super.verifyTypes(appView, verifyTypesHelper);
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index df8f00e..a6a4704 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
@@ -1499,6 +1500,19 @@
return block;
}
+ public boolean isInstructionBeforeThrowingInstruction(Instruction instruction) {
+ assert instruction.getBlock() == this;
+ for (Instruction candidate : getInstructions()) {
+ if (candidate == instruction) {
+ return true;
+ }
+ if (candidate.instructionTypeCanThrow()) {
+ return false;
+ }
+ }
+ throw new Unreachable();
+ }
+
public boolean isTrivialGoto() {
return instructions.size() == 1 && exit().isGoto();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
index 95ee7e0..6005e1f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CatchHandlers.java
@@ -25,6 +25,10 @@
this.guard = guard;
this.target = target;
}
+
+ public T getTarget() {
+ return target;
+ }
}
private final List<DexType> guards;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 3f52f3d..a9e34e2 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -1168,7 +1168,8 @@
ProgramMethod method,
CfInstructionDesugaringEventConsumer desugaringEventConsumer,
MethodProcessingContext methodProcessingContext) {
- if (options.desugarState.isOff() || !method.getDefinition().getCode().isCfCode()) {
+ // Due to some mandatory desugarings, we need to run desugaring even if desugaring is disabled.
+ if (!method.getDefinition().getCode().isCfCode()) {
return false;
}
instructionDesugaring.scan(method, desugaringEventConsumer);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 78291d7..148ea56 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -28,7 +28,7 @@
if (appView.options().isGeneratingClassFiles()) {
return NonEmptyCfInstructionDesugaringCollection.createForCfToCfNonDesugar(appView);
}
- return empty();
+ return NonEmptyCfInstructionDesugaringCollection.createForCfToDexNonDesugar(appView);
}
public static CfInstructionDesugaringCollection empty() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index ba7cbc0b..723e8b1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -43,6 +43,12 @@
NonEmptyCfInstructionDesugaringCollection(AppView<?> appView) {
this.appView = appView;
+ if (appView.options().desugarState.isOff()) {
+ this.nestBasedAccessDesugaring = null;
+ this.recordRewriter = null;
+ this.desugaredLibraryRetargeter = null;
+ return;
+ }
this.nestBasedAccessDesugaring = NestBasedAccessDesugaring.create(appView);
BackportedMethodRewriter backportedMethodRewriter = null;
desugaredLibraryRetargeter =
@@ -85,22 +91,25 @@
}
}
- // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
- // This should be removed once we can represent invoke-special instructions in the IR.
- private NonEmptyCfInstructionDesugaringCollection(
- AppView<?> appView, InvokeSpecialToSelfDesugaring invokeSpecialToSelfDesugaring) {
- this.appView = appView;
- this.nestBasedAccessDesugaring = null;
- this.desugaredLibraryRetargeter = null;
- this.recordRewriter = null;
- desugarings.add(invokeSpecialToSelfDesugaring);
- }
-
static NonEmptyCfInstructionDesugaringCollection createForCfToCfNonDesugar(AppView<?> appView) {
assert appView.options().desugarState.isOff();
assert appView.options().isGeneratingClassFiles();
- return new NonEmptyCfInstructionDesugaringCollection(
- appView, new InvokeSpecialToSelfDesugaring(appView));
+ NonEmptyCfInstructionDesugaringCollection desugaringCollection =
+ new NonEmptyCfInstructionDesugaringCollection(appView);
+ // TODO(b/145775365): special constructor for cf-to-cf compilations with desugaring disabled.
+ // This should be removed once we can represent invoke-special instructions in the IR.
+ desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+ return desugaringCollection;
+ }
+
+ static NonEmptyCfInstructionDesugaringCollection createForCfToDexNonDesugar(AppView<?> appView) {
+ assert appView.options().desugarState.isOff();
+ assert appView.options().isGeneratingDex();
+ NonEmptyCfInstructionDesugaringCollection desugaringCollection =
+ new NonEmptyCfInstructionDesugaringCollection(appView);
+ desugaringCollection.desugarings.add(new InvokeSpecialToSelfDesugaring(appView));
+ desugaringCollection.desugarings.add(new InvokeToPrivateRewriter());
+ return desugaringCollection;
}
private void ensureCfCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 97af733..55598d1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -735,10 +735,18 @@
// method is expected to be in the current class since it is private, but desugaring
// may move some methods or their code into other classes.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
- return rewriteInvoke.apply(
- directTarget.getDefinition().isPrivateMethod()
- ? privateAsMethodOfCompanionClass(directTarget)
- : defaultAsMethodOfCompanionClass(directTarget));
+ DexMethod companionMethod;
+ if (directTarget.getDefinition().isPrivateMethod()) {
+ companionMethod =
+ directTarget.isProgramMethod()
+ ? ensurePrivateAsMethodOfProgramCompanionClassStub(directTarget.asProgramMethod())
+ .getReference()
+ // TODO(b/183998768): Why does this not create a stub on the class path?
+ : privateAsMethodOfCompanionClass(directTarget);
+ } else {
+ companionMethod = defaultAsMethodOfCompanionClass(directTarget);
+ }
+ return rewriteInvoke.apply(companionMethod);
} else {
// The method can be a default method in the interface hierarchy.
DexClassAndMethod virtualTarget =
@@ -1196,6 +1204,35 @@
}
}
+ ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexMethod companionMethod =
+ privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ DexEncodedMethod definition = method.getDefinition();
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethod.getName(),
+ companionMethod.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = definition.getAccessFlags().copy();
+ assert newFlags.isPrivate();
+ newFlags.promoteToPublic();
+ newFlags.promoteToStatic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(definition.getGenericSignature())
+ .setAnnotations(definition.annotations())
+ .setParameterAnnotationsList(definition.getParameterAnnotations())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ ignored ->
+ appView.enableWholeProgramOptimizations()
+ ? definition.getCode()
+ : InvalidCode.getInstance());
+ });
+ }
+
// Represent a static interface method as a method of companion class.
final DexMethod staticAsMethodOfCompanionClass(DexClassAndMethod method) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
@@ -1299,7 +1336,7 @@
// TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
// code to ensure it is never used before desugared and installed.
.setCode(
- m ->
+ ignored ->
appView.enableWholeProgramOptimizations()
? definition.getCode()
: InvalidCode.getInstance());
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index ab61be0..799b584 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -288,71 +288,41 @@
getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods();
- MethodAccessFlags originalFlags = method.getAccessFlags();
- MethodAccessFlags newFlags = originalFlags.copy();
- if (originalFlags.isPrivate()) {
- newFlags.promoteToPublic();
- }
-
- DexMethod oldMethod = method.getReference();
+ ProgramMethod companion;
if (isStaticMethod(definition)) {
- assert originalFlags.isPrivate() || originalFlags.isPublic()
+ assert definition.isPrivate() || definition.isPublic()
: "Static interface method "
+ method.toSourceString()
+ " is expected to "
+ "either be public or private in "
+ iface.origin;
- ProgramMethod companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
- // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
- // moves.
- assert appView.enableWholeProgramOptimizations()
- || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
- if (definition.hasClassFileVersion()) {
- companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
+ companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
+ } else {
+ assert definition.isPrivate();
+ companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
+ Code code = definition.getCode();
+ if (code == null) {
+ throw new CompilationError(
+ "Code is missing for private instance "
+ + "interface method: "
+ + method.getReference().toSourceString(),
+ iface.origin);
}
- companion.getDefinition().setCode(definition.getCode(), appView);
- getPostProcessingInterfaceInfo(iface).moveMethod(oldMethod, companion.getReference());
- definition.setCode(InvalidCode.getInstance(), appView);
- continue;
+ DexEncodedMethod.setDebugInfoWithFakeThisParameter(
+ code, companion.getReference().getArity(), appView);
}
- assert originalFlags.isPrivate();
-
- newFlags.promoteToStatic();
-
- DexMethod companionMethod =
- InterfaceMethodRewriter.privateAsMethodOfCompanionClass(
- oldMethod, appView.dexItemFactory());
-
- Code code = definition.getCode();
- if (code == null) {
- throw new CompilationError(
- "Code is missing for private instance "
- + "interface method: "
- + oldMethod.toSourceString(),
- iface.origin);
+ // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
+ // moves.
+ assert appView.enableWholeProgramOptimizations()
+ || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+ if (definition.hasClassFileVersion()) {
+ companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
}
-
- DexEncodedMethod.setDebugInfoWithFakeThisParameter(code, companionMethod.getArity(), appView);
-
- ensureCompanionMethod(
- iface,
- companionMethod.getName(),
- companionMethod.getProto(),
- appView,
- methodBuilder ->
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(definition.getGenericSignature())
- .setAnnotations(definition.annotations())
- .setParameterAnnotationsList(definition.getParameterAnnotations())
- .setCode(ignored -> definition.getCode())
- .setOnBuildConsumer(
- implMethod -> {
- implMethod.copyMetadata(definition);
- getPostProcessingInterfaceInfo(iface)
- .moveMethod(oldMethod, companionMethod);
- }));
+ companion.getDefinition().setCode(definition.getCode(), appView);
+ getPostProcessingInterfaceInfo(iface)
+ .moveMethod(method.getReference(), companion.getReference());
+ definition.setCode(InvalidCode.getInstance(), appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 36d70df..2a1ec57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -44,6 +44,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.Binop;
import com.android.tools.r8.ir.code.CatchHandlers;
+import com.android.tools.r8.ir.code.CatchHandlers.CatchHandler;
import com.android.tools.r8.ir.code.CheckCast;
import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
@@ -3108,6 +3109,7 @@
Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet();
ListIterator<BasicBlock> blockIterator = code.listIterator();
ProgramMethod context = code.context();
+ boolean hasUnlinkedCatchHandlers = false;
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
if (block.getNumber() != 0 && block.getPredecessors().isEmpty()) {
@@ -3123,7 +3125,19 @@
Value inValue = instruction.getNonNullInput();
if (inValue.isAlwaysNull(appView)) {
// Insert `throw null` after the instruction if it is not guaranteed to throw an NPE.
- if (instruction.isInstanceFieldInstruction()) {
+ if (instruction.isAssume()) {
+ // If this assume is in a block with catch handlers, then the out-value can have
+ // usages in the catch handler if the block's throwing instruction comes after the
+ // assume instruction. In this case, the catch handler is also guaranteed to be dead,
+ // so we detach it from the current block.
+ if (block.hasCatchHandlers()
+ && block.isInstructionBeforeThrowingInstruction(instruction)) {
+ for (CatchHandler<BasicBlock> catchHandler : block.getCatchHandlers()) {
+ catchHandler.getTarget().unlinkCatchHandler();
+ }
+ hasUnlinkedCatchHandlers = true;
+ }
+ } else if (instruction.isInstanceFieldInstruction()) {
InstanceFieldInstruction instanceFieldInstruction =
instruction.asInstanceFieldInstruction();
if (instanceFieldInstruction.instructionInstanceCanThrow(
@@ -3183,6 +3197,9 @@
}
}
code.removeBlocks(blocksToRemove);
+ if (hasUnlinkedCatchHandlers) {
+ affectedValues.addAll(code.removeUnreachableBlocks());
+ }
assert code.getUnreachableBlocks().isEmpty();
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 5f6ad1a..e653838 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -742,8 +742,12 @@
Set<BasicBlock> seen = Sets.newIdentityHashSet();
Set<Instruction> users = eligibleInstance.uniqueUsers();
for (Instruction user : users) {
+ if (!user.hasBlock()) {
+ continue;
+ }
+
BasicBlock block = user.getBlock();
- if (block == null || !seen.add(block)) {
+ if (!seen.add(block)) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index 26516df..ede372a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -29,7 +29,7 @@
default void fixup(DexEncodedMember<?, ?> member) {
MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
if (optimizationInfo.isMutableOptimizationInfo()) {
- member.apply(
+ member.accept(
field -> {
fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
},
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 da86fe5..8a3a7e3 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -19,21 +19,21 @@
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.util.ListIterator;
-import java.util.Map;
+import java.util.function.BiFunction;
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 final BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping;
private AndroidApiLevel maxApiReferenceLevel;
public DefaultEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod context,
Enqueuer enqueuer,
- Map<DexReference, AndroidApiLevel> apiReferenceMapping) {
+ BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiReferenceMapping) {
super(appView.dexItemFactory());
this.appView = appView;
this.context = context;
@@ -188,14 +188,13 @@
private void setMaxApiReferenceLevel(DexReference reference) {
if (reference.isDexMember()) {
- this.maxApiReferenceLevel =
+ maxApiReferenceLevel =
maxApiReferenceLevel.max(
reference
.asDexMember()
.computeApiLevelForReferencedTypes(appView, apiReferenceMapping));
}
- this.maxApiReferenceLevel =
- maxApiReferenceLevel.max(apiReferenceMapping.getOrDefault(reference, maxApiReferenceLevel));
+ maxApiReferenceLevel = apiReferenceMapping.apply(reference, maxApiReferenceLevel);
}
public AndroidApiLevel getMaxApiReferenceLevel() {
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 abb6523..ea9b2d7 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -15,6 +15,7 @@
import static java.util.Collections.emptySet;
import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.code.CfOrDexInstruction;
@@ -120,7 +121,6 @@
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;
@@ -255,7 +255,7 @@
private final Set<DexReference> identifierNameStrings = Sets.newIdentityHashSet();
- private final Map<DexReference, AndroidApiLevel> referenceToApiLevelMap;
+ private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
/**
* Tracks the dependency between a method and the super-method it calls, if any. Used to make
@@ -473,10 +473,7 @@
} else {
desugaredLibraryWrapperAnalysis = null;
}
- referenceToApiLevelMap = new IdentityHashMap<>();
- if (options.apiModelingOptions().enableApiCallerIdentification) {
- options.apiModelingOptions().appendToApiLevelMap(referenceToApiLevelMap, dexItemFactory);
- }
+ apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
}
private AppInfoWithClassHierarchy appInfo() {
@@ -3028,7 +3025,7 @@
registerAnalysis(new GenericSignatureEnqueuerAnalysis(enqueuerDefinitionSupplier));
}
if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
- registerAnalysis(new ApiModelAnalysis(appView, referenceToApiLevelMap));
+ registerAnalysis(new ApiModelAnalysis(appView, apiReferenceLevelCache));
}
if (mode.isInitialTreeShaking()) {
// This is simulating the effect of the "root set" applied rules.
@@ -3944,7 +3941,7 @@
void traceCode(ProgramMethod method) {
DefaultEnqueuerUseRegistry registry =
- useRegistryFactory.create(appView, method, this, referenceToApiLevelMap);
+ useRegistryFactory.create(appView, method, this, apiReferenceLevelCache::lookupMax);
method.registerCodeReferences(registry);
// Notify analyses.
analyses.forEach(analysis -> analysis.processTracedCode(method, registry));
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 6bd8473..def9a41 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerUseRegistryFactory.java
@@ -9,7 +9,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.AndroidApiLevel;
-import java.util.Map;
+import java.util.function.BiFunction;
public interface EnqueuerUseRegistryFactory {
@@ -17,5 +17,5 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod currentMethod,
Enqueuer enqueuer,
- Map<DexReference, AndroidApiLevel> apiLevelReferenceMap);
+ BiFunction<DexReference, AndroidApiLevel, AndroidApiLevel> apiLevelReferenceMap);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index d43f32a..230af5f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
+import com.android.tools.r8.androidapi.AndroidApiReferenceLevelCache;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -53,6 +54,7 @@
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
@@ -117,7 +119,8 @@
UNHANDLED_INVOKE_DIRECT,
UNHANDLED_INVOKE_SUPER,
UNSAFE_INLINING,
- UNSUPPORTED_ATTRIBUTES;
+ UNSUPPORTED_ATTRIBUTES,
+ API_REFERENCE_LEVEL;
public void printLogMessageForClass(DexClass clazz) {
Log.info(VerticalClassMerger.class, getMessageForClass(clazz));
@@ -180,6 +183,9 @@
case UNSUPPORTED_ATTRIBUTES:
message = "since inner-class attributes are not supported";
break;
+ case API_REFERENCE_LEVEL:
+ message = "since source class references a higher api-level than target";
+ break;
default:
assert false;
}
@@ -201,6 +207,7 @@
private final MethodPoolCollection methodPoolCollection;
private final Timing timing;
private Collection<DexMethod> invokes;
+ private final AndroidApiReferenceLevelCache apiReferenceLevelCache;
private final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
@@ -235,6 +242,7 @@
this.executorService = executorService;
this.methodPoolCollection = new MethodPoolCollection(appView, subtypingInfo);
this.lensBuilder = new VerticalClassMergerGraphLens.Builder(appView.dexItemFactory());
+ this.apiReferenceLevelCache = AndroidApiReferenceLevelCache.create(appView);
this.timing = timing;
Iterable<DexProgramClass> classes = application.classesWithDeterministicOrder();
@@ -419,6 +427,21 @@
}
return false;
}
+ // Only merge if api reference level of source class is equal to target class.
+ if (appView.options().apiModelingOptions().enableApiCallerIdentification) {
+ AndroidApiLevel sourceApiLevel =
+ sourceClass.getApiReferenceLevel(
+ appView.options().minApiLevel, apiReferenceLevelCache::lookupMax);
+ AndroidApiLevel targetApiLevel =
+ targetClass.getApiReferenceLevel(
+ appView.options().minApiLevel, apiReferenceLevelCache::lookupMax);
+ if (sourceApiLevel != targetApiLevel) {
+ if (Log.ENABLED) {
+ AbortReason.API_REFERENCE_LEVEL.printLogMessageForClass(sourceClass);
+ }
+ return false;
+ }
+ }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/utils/EntryUtils.java b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
new file mode 100644
index 0000000..e1d28b1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/EntryUtils.java
@@ -0,0 +1,15 @@
+// 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.utils;
+
+import java.util.Map.Entry;
+import java.util.function.BiFunction;
+
+public class EntryUtils {
+
+ public static <K, V, R> R accept(Entry<K, V> entry, BiFunction<K, V, R> consumer) {
+ return consumer.apply(entry.getKey(), entry.getValue());
+ }
+}
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 0a1fcc7..ff1643f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
+import com.android.tools.r8.androidapi.AndroidApiClass;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Backend;
@@ -37,7 +38,6 @@
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexMethod;
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.classmerging.VerticallyMergedClasses;
@@ -52,10 +52,10 @@
import com.android.tools.r8.naming.MapVersion;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
+import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.repackaging.Repackaging.DefaultRepackagingConfiguration;
import com.android.tools.r8.repackaging.Repackaging.RepackagingConfiguration;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -84,10 +84,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -1319,21 +1321,68 @@
// A mapping from references to the api-level introducing them.
public Map<MethodReference, AndroidApiLevel> methodApiMapping = new HashMap<>();
public Map<FieldReference, AndroidApiLevel> fieldApiMapping = new HashMap<>();
- public Map<TypeReference, AndroidApiLevel> typeApiMapping = new HashMap<>();
+ public Map<ClassReference, AndroidApiLevel> classApiMapping = new HashMap<>();
+ public BiConsumer<MethodReference, AndroidApiLevel> tracedMethodApiLevelCallback = null;
public boolean enableApiCallerIdentification = false;
- public void appendToApiLevelMap(
- Map<DexReference, AndroidApiLevel> apiLevelMap, DexItemFactory factory) {
- methodApiMapping.forEach(
- (methodReference, apiLevel) ->
- apiLevelMap.put(factory.createMethod(methodReference), apiLevel));
- fieldApiMapping.forEach(
- (fieldReference, apiLevel) ->
- apiLevelMap.put(factory.createField(fieldReference), apiLevel));
- typeApiMapping.forEach(
- (typeReference, apiLevel) ->
- apiLevelMap.put(factory.createType(typeReference.getDescriptor()), apiLevel));
+ public void visitMockedApiReferences(BiConsumer<ClassReference, AndroidApiClass> consumer) {
+ if (methodApiMapping.isEmpty() && fieldApiMapping.isEmpty() && classApiMapping.isEmpty()) {
+ return;
+ }
+ Set<ClassReference> classReferences = new HashSet<>(classApiMapping.keySet());
+ methodApiMapping
+ .keySet()
+ .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+ fieldApiMapping
+ .keySet()
+ .forEach(methodReference -> classReferences.add(methodReference.getHolderClass()));
+ classReferences.forEach(
+ classReference -> {
+ consumer.accept(
+ classReference,
+ new AndroidApiClass(classReference) {
+ @Override
+ public AndroidApiLevel getApiLevel() {
+ return classApiMapping.getOrDefault(classReference, AndroidApiLevel.B);
+ }
+
+ @Override
+ public int getMemberCount() {
+ return 0;
+ }
+
+ @Override
+ public TraversalContinuation visitFields(
+ BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ for (Entry<FieldReference, AndroidApiLevel> entry :
+ fieldApiMapping.entrySet()) {
+ if (!entry.getKey().getHolderClass().equals(classReference)) {
+ continue;
+ }
+ if (EntryUtils.accept(entry, visitor).shouldBreak()) {
+ return TraversalContinuation.BREAK;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
+
+ @Override
+ public TraversalContinuation visitMethods(
+ BiFunction<MethodReference, AndroidApiLevel, TraversalContinuation> visitor) {
+ for (Entry<MethodReference, AndroidApiLevel> entry :
+ methodApiMapping.entrySet()) {
+ if (!entry.getKey().getHolderClass().equals(classReference)) {
+ continue;
+ }
+ if (EntryUtils.accept(entry, visitor).shouldBreak()) {
+ return TraversalContinuation.BREAK;
+ }
+ }
+ return TraversalContinuation.CONTINUE;
+ }
+ });
+ });
}
}
@@ -1652,7 +1701,7 @@
}
public boolean canUseDexPcAsDebugInformation() {
- return !debug && hasMinApi(AndroidApiLevel.O);
+ return lineNumberOptimization == LineNumberOptimization.ON && hasMinApi(AndroidApiLevel.O);
}
public boolean isInterfaceMethodDesugaringEnabled() {
diff --git a/src/test/java/com/android/tools/r8/L8TestBuilder.java b/src/test/java/com/android/tools/r8/L8TestBuilder.java
index 567d471..072da5a 100644
--- a/src/test/java/com/android/tools/r8/L8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/L8TestBuilder.java
@@ -134,6 +134,11 @@
return this;
}
+ public L8TestBuilder setDesugaredLibraryConfiguration(StringResource configuration) {
+ this.desugaredLibraryConfiguration = configuration;
+ return this;
+ }
+
public L8TestBuilder setDisableL8AnnotationRemoval(boolean disableL8AnnotationRemoval) {
return addOptionsModifier(
options -> options.testing.disableL8AnnotationRemoval = disableL8AnnotationRemoval);
diff --git a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
index 79dc2c9..737b0ff 100644
--- a/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
+++ b/src/test/java/com/android/tools/r8/LibraryDesugaringTestConfiguration.java
@@ -227,10 +227,12 @@
}
String finalGeneratedKeepRules = generatedKeepRules;
try {
+ assert desugaredLibraryConfigurationResources.size() == 1 : "There can be only one";
return L8TestBuilder.create(minApiLevel, Backend.DEX, state)
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
.setDesugarJDKLibs(desugarJdkLibs)
.setDesugarJDKLibsConfiguration(customConversions)
+ .setDesugaredLibraryConfiguration(desugaredLibraryConfigurationResources.get(0))
.applyIf(
mode == CompilationMode.RELEASE,
builder -> {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index e51cf99..d41987d 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1758,6 +1758,10 @@
return AndroidApiLevel.N;
}
+ public static AndroidApiLevel apiLevelWithPrivateInterfaceMethodsSupport() {
+ return AndroidApiLevel.N;
+ }
+
public static AndroidApiLevel apiLevelWithInvokeCustomSupport() {
return AndroidApiLevel.O;
}
diff --git a/src/test/java/com/android/tools/r8/TestBuilder.java b/src/test/java/com/android/tools/r8/TestBuilder.java
index 8f5466c..656955e 100644
--- a/src/test/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBuilder.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
import com.android.tools.r8.TestBase.Backend;
import com.android.tools.r8.debug.DebugTestConfig;
import com.android.tools.r8.errors.Unimplemented;
@@ -158,6 +159,25 @@
public abstract T addClasspathFiles(Collection<Path> files);
+ public T addClasspathClassFileData(byte[]... classes) {
+ return addClasspathClassFileData(Arrays.asList(classes));
+ }
+
+ public T addClasspathClassFileData(Collection<byte[]> classes) {
+ Path out;
+ try {
+ out = getState().getNewTempFolder().resolve("cp.jar");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ ArchiveConsumer consumer = new ArchiveConsumer(out);
+ for (byte[] bytes : classes) {
+ consumer.accept(ByteDataView.of(bytes), TestBase.extractClassDescriptor(bytes), null);
+ }
+ consumer.finished(null);
+ return addClasspathFiles(out);
+ }
+
public final T addTestingAnnotationsAsProgramClasses() {
return addProgramClasses(getTestingAnnotations());
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
index bc25cd4..9aa8f4d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGenerator.java
@@ -94,10 +94,12 @@
.setClassDescriptor(getApiClassDescriptor(apiClass))
.addMethodTransformer(getInitTransformer(apiClass))
.addMethodTransformer(getApiLevelTransformer(apiClass))
+ .addMethodTransformer(getGetMemberCountTransformer(apiClass))
.addMethodTransformer(getVisitFieldsTransformer(apiClass))
.addMethodTransformer(getVisitMethodsTransformer(apiClass))
.removeMethods(MethodPredicate.onName("placeHolderForInit"))
.removeMethods(MethodPredicate.onName("placeHolderForGetApiLevel"))
+ .removeMethods(MethodPredicate.onName("placeHolderForGetMemberCount"))
.removeMethods(MethodPredicate.onName("placeHolderForVisitFields"))
.removeMethods(MethodPredicate.onName("placeHolderForVisitMethods"))
.transform());
@@ -170,6 +172,16 @@
});
}
+ // The transformer below changes AndroidApiDatabaseClassTemplate.getMemberCount from:
+ // return placeHolderForGetMemberCount();
+ // into
+ // return <memberCount>;
+ private static MethodTransformer getGetMemberCountTransformer(ParsedApiClass apiClass) {
+ return replaceCode(
+ "placeHolderForGetMemberCount",
+ transformer -> transformer.visitLdcInsn(apiClass.getMemberCount()));
+ }
+
// The transformer below changes AndroidApiDatabaseClassTemplate.visitFields from:
// placeHolder();
// return TraversalContinuation.CONTINUE;
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
index d85e3e5..6d46bfd 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -225,12 +225,7 @@
ZipUtils.unzip(
ToolHelper.DEPS.toString(),
tempDeps.toFile(),
- entry -> {
- if (entry.getName().startsWith("com/android/tools/r8/apimodel/")) {
- return false;
- }
- return true;
- });
+ entry -> !entry.getName().startsWith("com/android/tools/r8/apimodel/"));
Path modifiedDeps = Files.createTempFile("modified_deps", ".jar");
ZipUtils.zip(modifiedDeps, tempDeps);
return modifiedDeps;
@@ -242,6 +237,7 @@
apiClass -> {
expected.add(apiClass.getClassReference().getDescriptor());
expected.add(apiClass.getApiLevel().getName());
+ expected.add(apiClass.getMemberCount() + "");
BooleanBox added = new BooleanBox(false);
apiClass.visitFieldReferences(
(apiLevel, fieldReferences) -> {
@@ -292,6 +288,7 @@
if (apiClass != null) {
System.out.println(descriptor);
System.out.println(apiClass.getApiLevel().getName());
+ System.out.println(apiClass.getMemberCount());
apiClass.visitFields(
(reference, apiLevel) -> {
System.out.println(reference.getFieldType().getDescriptor());
@@ -322,6 +319,7 @@
if (apiClass != null) {
System.out.println(descriptor);
System.out.println(apiClass.getApiLevel().getName());
+ System.out.println(apiClass.getMemberCount());
apiClass.visitFields(
(reference, apiLevel) -> {
System.out.println(reference.getFieldType().getDescriptor());
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
index 2598898..faefd62 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseClassTemplate.java
@@ -27,6 +27,12 @@
}
@Override
+ public int getMemberCount() {
+ // Code added dynamically in AndroidApiDatabaseBuilderGenerator.
+ return placeHolderForGetMemberCount();
+ }
+
+ @Override
public TraversalContinuation visitFields(
BiFunction<FieldReference, AndroidApiLevel, TraversalContinuation> visitor) {
// Code added dynamically in AndroidApiDatabaseBuilderGenerator.
@@ -50,6 +56,10 @@
return null;
}
+ private static int placeHolderForGetMemberCount() {
+ return 0;
+ }
+
private static void placeHolderForVisitFields() {}
private static void placeHolderForVisitMethods() {}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index c904dff..dbb71d3 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -135,6 +135,11 @@
private final TreeMap<AndroidApiLevel, List<FieldReference>> fieldReferences = new TreeMap<>();
private final Map<AndroidApiLevel, List<MethodReference>> methodReferences = new TreeMap<>();
+ private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
+ this.classReference = classReference;
+ this.apiLevel = apiLevel;
+ }
+
public ClassReference getClassReference() {
return classReference;
}
@@ -143,9 +148,8 @@
return apiLevel;
}
- private ParsedApiClass(ClassReference classReference, AndroidApiLevel apiLevel) {
- this.classReference = classReference;
- this.apiLevel = apiLevel;
+ public int getMemberCount() {
+ return fieldReferences.size() + methodReferences.size();
}
private void register(FieldReference reference, AndroidApiLevel apiLevel) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
index 5d0c0b8..cb837ac 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiFieldsTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.apimodel;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
@@ -41,11 +41,17 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
- // TODO(b/138781768): Should not be merged
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertClassesMerged(A.class, B.class))
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+ inspector.assertClassesMerged(A.class, B.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
- .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
.compile()
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
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 f174546..0eeb405 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingWithDifferentApiMethodsTest.java
@@ -42,9 +42,15 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
- // TODO(b/138781768): Should not be merged
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertClassesMerged(A.class, B.class))
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+ inspector.assertClassesMerged(A.class, B.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
.compile()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
index f6996e9..03b2f3e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMethodWithApiTypeTest.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.apimodel;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
@@ -46,7 +46,7 @@
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableNoHorizontalClassMergingAnnotations()
- .apply(setMockApiLevelForType(ApiType.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForClass(ApiType.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.addRunClasspathClasses(ApiType.class)
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 a19ecec..cd86ade 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -53,9 +53,9 @@
assertThat(aSubject, isPresent());
aSubject.forAllMethods(
method -> {
- // TODO(b/191013385): callApiLevel is merged into A, but not with method
- // implementation.
- // TODO(b/191013233): Check that the bridge is supposed to stay in R8.
+ // TODO(b/191013385): noApiCall is inlined into Main, but the synthesized
+ // A.callApiLevel that dispatches to $CC is not. This should change after
+ // desugaring is moved up to the enqueuer.
if (method
.getOriginalName()
.equals(ApiCaller.class.getTypeName() + ".callApiLevel")
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
index c7b2b1c..a1ecb6a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelInstanceFieldTest.java
@@ -4,15 +4,16 @@
package com.android.tools.r8.apimodel;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForField;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.junit.Test;
@@ -47,12 +48,11 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
- .apply(setMockApiLevelForField(apiField, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForField(apiField, L_MR1))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
- .inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+ .inspect(verifyThat(parameters, apiCaller).inlinedIntoFromApiLevel(apiCallerCaller, L_MR1))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("Hello World!");
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
index 7d6230b..e1cf2e2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelSuperTest.java
@@ -4,16 +4,19 @@
package com.android.tools.r8.apimodel;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
+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 org.junit.Assert.assertEquals;
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;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.lang.reflect.Method;
import org.junit.Test;
@@ -41,25 +44,36 @@
Method apiCaller = ApiCaller.class.getDeclaredMethod("apiLevel22");
Method apiCallerCaller = A.class.getDeclaredMethod("noApiCall");
testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
+ .addProgramClasses(ApiCaller.class, A.class, Main.class)
+ .addLibraryClasses(Api.class)
+ .addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNoVerticalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(
+ addTracedApiReferenceLevelCallBack(
+ (method, apiLevel) -> {
+ if (Reference.methodFromMethod(apiCaller).equals(method)) {
+ if (parameters.isCfRuntime()) {
+ assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+ } else {
+ assertEquals(AndroidApiLevel.L_MR1.max(parameters.getApiLevel()), apiLevel);
+ }
+ }
+ }))
.compile()
- .inspect(
- verifyThat(parameters, apiCaller)
- .inlinedIntoFromApiLevel(apiCallerCaller, AndroidApiLevel.L_MR1))
+ // We do not inline overrides calling super.
+ .inspect(verifyThat(parameters, apiCaller).notInlinedInto(apiCallerCaller))
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::apiLevel22", "Api::apiLevel22");
}
- @NoVerticalClassMerging
public static class Api {
void apiLevel22() {
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 d87ed82..f09f09d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfHigherApiLevelVirtualTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelNoInliningOfHigherApiLevelVirtualTest.ApiCaller.callVirtualMethod;
+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;
@@ -48,6 +49,7 @@
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.apply(setMockApiLevelForMethod(apiMethod, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.compile()
.inspect(
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 5ed1708..1e2fd44 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -5,17 +5,18 @@
package com.android.tools.r8.apimodel;
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.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.AndroidApiLevel.O;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
-import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import java.lang.reflect.Method;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,7 +30,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
}
public ApiModelNoInliningOfStaticInterfaceMethodsTest(TestParameters parameters) {
@@ -38,38 +39,52 @@
@Test
public void testR8() throws Exception {
- Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
- // Method apiCaller = ApiCaller.class.getDeclaredMethod("callApiLevel");
+ Method apiMethod22 = Api.class.getDeclaredMethod("apiLevel22");
+ Method apiMethod26 = Api.class.getDeclaredMethod("apiLevel26");
testForR8(parameters.getBackend())
.addProgramClasses(Main.class, A.class, ApiCaller.class)
.addLibraryClasses(Api.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Main.class)
- .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
+ .apply(setMockApiLevelForMethod(apiMethod22, L_MR1))
+ .apply(setMockApiLevelForMethod(apiMethod26, O))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.noMinification()
+ .enableInliningAnnotations()
.compile()
.inspect(
inspector -> {
// The call to the api is moved to $-CC (or stays) and is then merged if allowed.
- if (parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
- assertEquals(1, inspector.allClasses().size());
+ Method callApiLevel22 = ApiCaller.class.getDeclaredMethod("callApiLevel22");
+ Method callApiLevel26 = ApiCaller.class.getDeclaredMethod("callApiLevel26");
+ Method noApiCallTo22 = A.class.getDeclaredMethod("noApiCallTo22");
+ Method noApiCallTo26 = A.class.getDeclaredMethod("noApiCallTo26");
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject companion = inspector.companionClassFor(ApiCaller.class);
+ assertThat(companion, isPresent());
+ FoundClassSubject foundCompanion = companion.asFoundClassSubject();
+ verifyThat(parameters, callApiLevel22)
+ .setHolder(foundCompanion)
+ .inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
+ verifyThat(parameters, callApiLevel26)
+ .setHolder(foundCompanion)
+ .inlinedIntoFromApiLevel(noApiCallTo26, O);
} else {
- assertEquals(2, inspector.allClasses().size());
- ClassSubject aSubject = inspector.clazz(A.class);
- assertThat(aSubject, isPresent());
- // TODO(b/191008231): Should not invoke api here but stay on the CC class.
- assertEquals(1, aSubject.allMethods().size());
- MethodSubject callApiLevel = aSubject.uniqueMethodWithName("callApiLevel");
- assertThat(callApiLevel, isPresent());
- assertThat(callApiLevel, CodeMatchers.invokesMethodWithName("apiLevel22"));
+ verifyThat(parameters, callApiLevel22)
+ .inlinedIntoFromApiLevel(noApiCallTo22, L_MR1);
+ verifyThat(parameters, callApiLevel26).inlinedIntoFromApiLevel(noApiCallTo26, O);
}
})
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutputLines("A::noApiCall", "ApiCaller::callApiLevel", "Api::apiLevel22");
+ .assertSuccessWithOutputLines(
+ "A::noApiCallTo22",
+ "ApiCaller::callApiLevel22",
+ "Api::apiLevel22",
+ "A::noApiCallTo26",
+ "ApiCaller::callApiLevel26",
+ "Api::apiLevel26");
}
public static class Api {
@@ -77,27 +92,44 @@
public static void apiLevel22() {
System.out.println("Api::apiLevel22");
}
- }
- public interface ApiCaller {
- static void callApiLevel() {
- System.out.println("ApiCaller::callApiLevel");
- Api.apiLevel22();
+ public static void apiLevel26() {
+ System.out.println("Api::apiLevel26");
}
}
- public static class A implements ApiCaller {
+ public interface ApiCaller {
+ static void callApiLevel22() {
+ System.out.println("ApiCaller::callApiLevel22");
+ Api.apiLevel22();
+ }
- public static void noApiCall() {
- System.out.println("A::noApiCall");
- ApiCaller.callApiLevel();
+ static void callApiLevel26() {
+ System.out.println("ApiCaller::callApiLevel26");
+ Api.apiLevel26();
+ }
+ }
+
+ public static class A {
+
+ @NeverInline
+ public static void noApiCallTo22() {
+ System.out.println("A::noApiCallTo22");
+ ApiCaller.callApiLevel22();
+ }
+
+ @NeverInline
+ public static void noApiCallTo26() {
+ System.out.println("A::noApiCallTo26");
+ ApiCaller.callApiLevel26();
}
}
public static class Main {
public static void main(String[] args) {
- A.noApiCall();
+ A.noApiCallTo22();
+ A.noApiCallTo26();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
new file mode 100644
index 0000000..f01e1b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -0,0 +1,119 @@
+// 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.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertEquals;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import java.lang.reflect.Method;
+import java.util.List;
+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 ApiModelNoVerticalMergingSubReferenceApiTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ApiModelNoVerticalMergingSubReferenceApiTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test()
+ public void testR8() throws Exception {
+ Method apiMethod = Api.class.getDeclaredMethod("apiLevel22");
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, Base.class, Sub.class)
+ .addLibraryClasses(Api.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .apply(setMockApiLevelForMethod(apiMethod, L_MR1))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .addVerticallyMergedClassesInspector(
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+ inspector.assertMergedIntoSubtype(Base.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
+ .noMinification()
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject base = inspector.clazz(Base.class);
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+ assertThat(base, not(isPresent()));
+ ClassSubject sub = inspector.clazz(Sub.class);
+ List<FoundMethodSubject> callApis =
+ sub.allMethods(
+ method ->
+ method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
+ // TODO(b/191013233): Remove synthetic bridge. Remove noMinification after fixed.
+ assertEquals(2, callApis.size());
+ } else {
+ assertThat(base, isPresent());
+ }
+ })
+ .addRunClasspathClasses(Api.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Sub::callCallApi", "Base::callApi", "Api::apiLevel22");
+ }
+
+ public static class Api {
+
+ public static void apiLevel22() {
+ System.out.println("Api::apiLevel22");
+ }
+ }
+
+ public static class Base {
+
+ public void callApi() {
+ System.out.println("Base::callApi");
+ }
+ }
+
+ @NeverClassInline
+ public static class Sub extends Base {
+
+ @NeverInline
+ public void callCallApi() {
+ System.out.println("Sub::callCallApi");
+ callApi();
+ Api.apiLevel22();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new Sub().callCallApi();
+ }
+ }
+}
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 e7c41d8..b29e694 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -52,20 +52,33 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .addVerticallyMergedClassesInspector(
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+ inspector.assertMergedIntoSubtype(Base.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.noMinification()
.compile()
.inspect(
inspector -> {
- // TODO(b/191013385): Prevent Base merging with Sub.
ClassSubject base = inspector.clazz(Base.class);
- assertThat(base, not(isPresent()));
- ClassSubject sub = inspector.clazz(Sub.class);
- List<FoundMethodSubject> callApis =
- sub.allMethods(
- method ->
- method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
- // TODO(b/191013233): Remove synthetic bridge.
- assertEquals(2, callApis.size());
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+ assertThat(base, not(isPresent()));
+ ClassSubject sub = inspector.clazz(Sub.class);
+ List<FoundMethodSubject> callApis =
+ sub.allMethods(
+ method ->
+ method.getOriginalName().equals(Base.class.getTypeName() + ".callApi"));
+ // TODO(b/191013233): Remove synthetic bridge. Remove noMinification after fixed.
+ assertEquals(2, callApis.size());
+ } else {
+ assertThat(base, isPresent());
+ }
})
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
index 80e213e..db7dc06 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelVerticalMergingOfSuperClassTest.java
@@ -4,16 +4,21 @@
package com.android.tools.r8.apimodel;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForType;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.addTracedApiReferenceLevelCallBack;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
+import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,13 +49,37 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .apply(setMockApiLevelForType(Api.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForClass(Api.class, AndroidApiLevel.L_MR1))
+ .apply(setMockApiLevelForDefaultInstanceInitializer(Api.class, AndroidApiLevel.L_MR1))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(
+ addTracedApiReferenceLevelCallBack(
+ (methodReference, apiLevel) -> {
+ if (methodReference.getMethodName().equals("<init>")
+ && methodReference
+ .getHolderClass()
+ .equals(Reference.classFromClass(Api.class))) {
+ assertEquals(AndroidApiLevel.L_MR1, apiLevel);
+ }
+ }))
+ .addVerticallyMergedClassesInspector(
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(L_MR1)) {
+ inspector.assertMergedIntoSubtype(A.class);
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.compile()
.inspect(
inspector -> {
- // TODO(b/138781768): We should not merge A into B.
- assertThat(inspector.clazz(A.class), not(isPresent()));
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L_MR1)) {
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ } else {
+ assertThat(inspector.clazz(A.class), isPresent());
+ }
})
.addRunClasspathClasses(Api.class)
.run(parameters.getRuntime(), Main.class)
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 33d1deb..4e02aff 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -11,14 +11,18 @@
import com.android.tools.r8.TestCompilerBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.function.BiConsumer;
public abstract class ApiModelingTestHelper {
@@ -36,6 +40,23 @@
}
static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+ ThrowableConsumer<T> setMockApiLevelForDefaultInstanceInitializer(
+ Class<?> clazz, AndroidApiLevel apiLevel) {
+ return compilerBuilder -> {
+ compilerBuilder.addOptionsModification(
+ options -> {
+ options
+ .apiModelingOptions()
+ .methodApiMapping
+ .put(
+ Reference.method(
+ Reference.classFromClass(clazz), "<init>", ImmutableList.of(), null),
+ apiLevel);
+ });
+ };
+ }
+
+ static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
ThrowableConsumer<T> setMockApiLevelForField(Field field, AndroidApiLevel apiLevel) {
return compilerBuilder -> {
compilerBuilder.addOptionsModification(
@@ -48,14 +69,14 @@
};
}
- static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>> ThrowableConsumer<T> setMockApiLevelForType(
- Class<?> clazz, AndroidApiLevel apiLevel) {
+ static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+ ThrowableConsumer<T> setMockApiLevelForClass(Class<?> clazz, AndroidApiLevel apiLevel) {
return compilerBuilder -> {
compilerBuilder.addOptionsModification(
options -> {
options
.apiModelingOptions()
- .typeApiMapping
+ .classApiMapping
.put(Reference.classFromClass(clazz), apiLevel);
});
};
@@ -68,28 +89,50 @@
});
}
+ static <T extends TestCompilerBuilder<?, ?, ?, ?, ?>>
+ ThrowableConsumer<T> addTracedApiReferenceLevelCallBack(
+ BiConsumer<MethodReference, AndroidApiLevel> consumer) {
+ return compilerBuilder -> {
+ compilerBuilder.addOptionsModification(
+ options -> {
+ options.apiModelingOptions().tracedMethodApiLevelCallback = consumer;
+ });
+ };
+ }
+
static ApiModelingMethodVerificationHelper verifyThat(TestParameters parameters, Method method) {
- return new ApiModelingMethodVerificationHelper(parameters, method);
+ return new ApiModelingMethodVerificationHelper(parameters, Reference.methodFromMethod(method));
}
public static class ApiModelingMethodVerificationHelper {
- private final Method methodOfInterest;
+ private final MethodReference methodOfInterest;
private final TestParameters parameters;
- public ApiModelingMethodVerificationHelper(TestParameters parameters, Method methodOfInterest) {
+ private ApiModelingMethodVerificationHelper(
+ TestParameters parameters, MethodReference methodOfInterest) {
this.methodOfInterest = methodOfInterest;
this.parameters = parameters;
}
- protected ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
+ public ApiModelingMethodVerificationHelper setHolder(FoundClassSubject classSubject) {
+ return new ApiModelingMethodVerificationHelper(
+ parameters,
+ Reference.method(
+ classSubject.getFinalReference(),
+ methodOfInterest.getMethodName(),
+ methodOfInterest.getFormalTypes(),
+ methodOfInterest.getReturnType()));
+ }
+
+ ThrowingConsumer<CodeInspector, Exception> inlinedIntoFromApiLevel(
Method method, AndroidApiLevel apiLevel) {
return parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevel)
? inlinedInto(method)
: notInlinedInto(method);
}
- private ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
+ ThrowingConsumer<CodeInspector, Exception> notInlinedInto(Method method) {
return inspector -> {
MethodSubject candidate = inspector.method(methodOfInterest);
assertThat(candidate, isPresent());
@@ -99,7 +142,7 @@
};
}
- public ThrowingConsumer<CodeInspector, Exception> inlinedInto(Method method) {
+ ThrowingConsumer<CodeInspector, Exception> inlinedInto(Method method) {
return inspector -> {
MethodSubject candidate = inspector.method(methodOfInterest);
if (!candidate.isPresent()) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index c2f1811..a91f006 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.debuginfo;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
+import static com.android.tools.r8.utils.InternalOptions.LineNumberOptimization.ON;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -72,6 +73,11 @@
.addProgramClasses(MAIN)
.setMinApi(parameters.getApiLevel())
.internalEnableMappingOutput()
+ // TODO(b/191038746): Enable LineNumberOptimization for release builds for DEX PC Output.
+ .applyIf(
+ apiLevelSupportsPcOutput(),
+ builder ->
+ builder.addOptionsModification(options -> options.lineNumberOptimization = ON))
.run(parameters.getRuntime(), MAIN)
.inspectFailure(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
index a81de0a..8713609 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ReleasedVersionsSmokeTest.java
@@ -35,6 +35,14 @@
"GMT",
"1000",
"Hello, world");
+ private static final String expectedOutput_1_0_9 =
+ StringUtils.lines(
+ "true",
+ "Caught java.time.format.DateTimeParseException",
+ "true",
+ "1970-01-02T10:17:36.789Z",
+ "1000",
+ "Hello, world");
@Parameters(name = "{0}, {1}")
public static List<Object[]> data() {
@@ -61,12 +69,9 @@
.withKeepRuleConsumer()
.setMode(CompilationMode.DEBUG)
.build())
- .compile()
- .run(parameters.getRuntime(), TestClass.class)
- .applyIf(
- configuration.equals(Configuration.RELEASED_1_1_5),
- r -> r.assertSuccessWithOutput(expectedOutput),
- r -> r.assertFailureWithErrorThatThrows(NoClassDefFoundError.class));
+ .run(parameters.getRuntime(), TestClass.class, configuration.name())
+ .assertSuccessWithOutput(
+ configuration != Configuration.RELEASED_1_0_9 ? expectedOutput : expectedOutput_1_0_9);
}
@Test
@@ -82,14 +87,15 @@
.withKeepRuleConsumer()
.setMode(CompilationMode.RELEASE)
.build())
- .compile()
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(expectedOutput);
+ .run(parameters.getRuntime(), TestClass.class, configuration.name())
+ .assertSuccessWithOutput(
+ configuration != Configuration.RELEASED_1_0_9 ? expectedOutput : expectedOutput_1_0_9);
}
static class TestClass {
public static void main(String[] args) {
+ String configurationVersion = args[0];
System.out.println(Clock.systemDefaultZone().getZone().equals(ZoneId.systemDefault()));
try {
java.time.LocalDate.parse("");
@@ -100,9 +106,12 @@
System.out.println(
java.util.Date.from(new java.util.Date(123456789).toInstant()).toInstant());
- java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(ZoneId.of("GMT"));
- System.out.println(timeZone.getID());
- System.out.println(timeZone.toZoneId().getId());
+ // Support for this was added in 1.0.10.
+ if (!configurationVersion.equals("RELEASED_1_0_9")) {
+ java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(ZoneId.of("GMT"));
+ System.out.println(timeZone.getID());
+ System.out.println(timeZone.toZoneId().getId());
+ }
System.out.println(Duration.ofMillis(1000).toMillis());
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
index ae2b6ae..52bc03e 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestPrivateInterfaceMethodsTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.StringUtils;
+import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -47,7 +48,7 @@
.addProgramClasses(TestClass.class)
.addProgramClassFileData(getClassWithNest(I.class), getClassWithNest(J.class))
.run(parameters.getRuntime(), TestClass.class)
- // TODO(191115349): Nest desugar does not downgrade the classfile version.
+ // TODO(b/191115349): Nest desugar does not downgrade the classfile version.
.applyIf(
c ->
DesugarTestConfiguration.isNotJavac(c)
@@ -56,6 +57,38 @@
r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class));
}
+ @Test
+ public void testOnClasspath() throws Exception {
+ byte[] bytesI = getClassWithNest(I.class);
+ byte[] bytesJ = getClassWithNest(J.class);
+ Path outI =
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(bytesI)
+ .addClasspathClassFileData(bytesJ)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ Path outJ =
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(bytesJ)
+ .addClasspathClassFileData(bytesI)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ Path outTestClass =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(TestClass.class)
+ .addClasspathClassFileData(bytesI, bytesJ)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .writeToZip();
+ testForD8(parameters.getBackend())
+ .addProgramFiles(outI, outJ, outTestClass)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
interface I {
default /* will be private */ void foo() {
System.out.println("Hello world!");
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
new file mode 100644
index 0000000..b8d02f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureVerticalMergeTest.java
@@ -0,0 +1,102 @@
+// 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.graph.genericsignature;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.lang.reflect.Type;
+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 GenericSignatureVerticalMergeTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public GenericSignatureVerticalMergeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test(expected = CompilationFailedException.class)
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(I.class, J.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(Base.class)
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addKeepAttributeSignature()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(A.class))
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertErrorMessageThatMatches(
+ containsString("Super type inconsistency in generic signature"));
+ });
+ }
+
+ public interface I<T> {
+
+ T t();
+ }
+
+ @NoVerticalClassMerging
+ public static class Base<T> {}
+
+ public static class A<S, T> extends Base<S> implements I<T> {
+
+ @Override
+ @NeverInline
+ public T t() {
+ System.out.println("I::t");
+ return null;
+ }
+ }
+
+ public interface J<R> {
+
+ void r(R r);
+ }
+
+ @NeverClassInline
+ public static class B<X> extends A<String, X> implements J<X> {
+
+ @Override
+ @NeverInline
+ public void r(X x) {
+ System.out.println("B::r");
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(B.class.getGenericSuperclass());
+ for (Type genericInterface : B.class.getGenericInterfaces()) {
+ System.out.println(genericInterface);
+ }
+ B<String> b = new B<>();
+ b.r(b.t());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/A.java b/src/test/java/com/android/tools/r8/regress/b191296688/A.java
new file mode 100644
index 0000000..8b6b5fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/A.java
@@ -0,0 +1,10 @@
+// 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.regress.b191296688;
+
+public class A {
+ public void doIt(Runnable r) {
+ r.run();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/B.kt b/src/test/java/com/android/tools/r8/regress/b191296688/B.kt
new file mode 100644
index 0000000..a0ed49a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/B.kt
@@ -0,0 +1,16 @@
+// 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.regress.b191296688
+
+class B {
+ fun doIt() {
+ A().doIt(this::proceed)
+ }
+
+ private fun proceed() {
+ println("hep")
+ }
+}
+
+fun main() = B().doIt()
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
new file mode 100644
index 0000000..dc7458c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/b191296688/Regress191296688.java
@@ -0,0 +1,95 @@
+// 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.regress.b191296688;
+
+import static com.android.tools.r8.ToolHelper.getKotlinAnnotationJar;
+import static com.android.tools.r8.ToolHelper.getKotlinStdlibJar;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.isInvokeWithTarget;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.TestRuntime.CfRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.nio.file.Path;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress191296688 extends KotlinTestBase {
+
+ private static final String PKG = Regress191296688.class.getPackage().getName();
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Collection<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(),
+ getKotlinTestParameters()
+ .withTargetVersion(KotlinTargetVersion.JAVA_8)
+ .withCompiler(ToolHelper.getKotlinC_1_5_0_m2())
+ .build());
+ }
+
+ public Regress191296688(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRegress191296688() throws Exception {
+ Path aLib = temp.newFolder().toPath().resolve("alib.jar");
+ writeClassesToJar(aLib, A.class);
+ String folder = DescriptorUtils.getBinaryNameFromJavaType(PKG);
+ CfRuntime cfRuntime = TestRuntime.getCheckedInJdk9();
+ Path ktClasses =
+ kotlinc(cfRuntime, kotlinc, targetVersion)
+ .addSourceFiles(getKotlinFileInTest(folder, "B"))
+ .addClasspathFiles(aLib)
+ .compile();
+ Path desugaredJar =
+ testForD8(Backend.CF)
+ .addLibraryFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc))
+ .addProgramFiles(ktClasses)
+ .addProgramClasses(A.class)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::verifyVirtualCallToPrivate)
+ .writeToZip();
+ testForD8()
+ .addProgramFiles(desugaredJar)
+ .setMinApi(parameters.getApiLevel())
+ .disableDesugaring()
+ .run(parameters.getRuntime(), PKG + ".BKt")
+ .assertSuccessWithOutputLines("hep");
+ }
+
+ private void verifyVirtualCallToPrivate(CodeInspector inspector) {
+ ClassSubject bClassSubject = inspector.clazz(PKG + ".B");
+ MethodSubject proceedMethodSubject = bClassSubject.uniqueMethodWithName("proceed");
+ assertThat(proceedMethodSubject, isPresent());
+ assertTrue(
+ bClassSubject.allMethods().stream()
+ .anyMatch(
+ method ->
+ method
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeVirtual)
+ .anyMatch(isInvokeWithTarget(proceedMethodSubject))));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
index 8179e48..689fce0 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -80,6 +80,8 @@
public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) {
assertEquals(
horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from)), toDexType(target));
+ seen.add(toDexType(from).asClassReference());
+ seen.add(toDexType(target).asClassReference());
return this;
}
diff --git a/third_party/android_jar/api-database.tar.gz.sha1 b/third_party/android_jar/api-database.tar.gz.sha1
index 84abf49..6336e23 100644
--- a/third_party/android_jar/api-database.tar.gz.sha1
+++ b/third_party/android_jar/api-database.tar.gz.sha1
@@ -1 +1 @@
-e4da4b29079ac393e0012e7676dcca0799841e29
\ No newline at end of file
+be72aeca006f1aba8b1fe4d9c3ff4c0e76259960
\ No newline at end of file