Merge commit '7801f105b2a5da2f62bbc2d7ac5b2c27f7c84dbc' into dev-release
Change-Id: Ia034cddb95e49de7774a50ba221bd261e4a12c9f
diff --git a/.gitignore b/.gitignore
index d3eb304..1559ec0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -357,6 +357,8 @@
tools/*/host/art-14.0.0-beta3.tar.gz
tools/*/host/art-15.0.0-beta2
tools/*/host/art-15.0.0-beta2.tar.gz
+tools/*/host/art-16.0.0
+tools/*/host/art-16.0.0.tar.gz
tools/*/host/art-master
tools/*/host/art-master.tar.gz
tools/*/art.tar.gz
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 5dfc0a5..3c25a8d 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -1064,6 +1064,7 @@
fun getThirdPartyAndroidVms(): List<ThirdPartyDependency> {
return listOf(
listOf("host", "art-master"),
+ listOf("host", "art-16.0.0"),
listOf("host", "art-15.0.0-beta2"),
listOf("host", "art-14.0.0-beta3"),
listOf("host", "art-13.0.0"),
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg
index 76170b1..9882315 100644
--- a/infra/config/global/generated/cr-buildbucket.cfg
+++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -622,6 +622,82 @@
}
}
builders {
+ name: "linux-android-16"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cpu:x86-64"
+ dimensions: "normal:true"
+ dimensions: "os:Ubuntu-22.04"
+ dimensions: "pool:luci.r8.ci"
+ exe {
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+ cipd_version: "refs/heads/main"
+ cmd: "luciexe"
+ }
+ properties:
+ '{'
+ ' "builder_group": "internal.client.r8",'
+ ' "recipe": "rex",'
+ ' "test_options": ['
+ ' "--dex_vm=16.0.0",'
+ ' "--all_tests",'
+ ' "--command_cache_dir=/tmp/ccache",'
+ ' "--tool=r8",'
+ ' "--print-times",'
+ ' "--no_internal",'
+ ' "--one_line_per_test",'
+ ' "--archive_failures"'
+ ' ]'
+ '}'
+ priority: 26
+ execution_timeout_secs: 21600
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ }
+ builders {
+ name: "linux-android-16_release"
+ swarming_host: "chrome-swarming.appspot.com"
+ swarming_tags: "vpython:native-python-wrapper"
+ dimensions: "cpu:x86-64"
+ dimensions: "normal:true"
+ dimensions: "os:Ubuntu-22.04"
+ dimensions: "pool:luci.r8.ci"
+ exe {
+ cipd_package: "infra_internal/recipe_bundles/chrome-internal.googlesource.com/chrome/tools/build"
+ cipd_version: "refs/heads/main"
+ cmd: "luciexe"
+ }
+ properties:
+ '{'
+ ' "builder_group": "internal.client.r8",'
+ ' "recipe": "rex",'
+ ' "test_options": ['
+ ' "--dex_vm=16.0.0",'
+ ' "--all_tests",'
+ ' "--command_cache_dir=/tmp/ccache",'
+ ' "--tool=r8",'
+ ' "--print-times",'
+ ' "--no_internal",'
+ ' "--one_line_per_test",'
+ ' "--archive_failures"'
+ ' ]'
+ '}'
+ priority: 26
+ execution_timeout_secs: 21600
+ expiration_secs: 126000
+ build_numbers: YES
+ service_account: "r8-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
+ experiments {
+ key: "luci.recipes.use_python3"
+ value: 100
+ }
+ }
+ builders {
name: "linux-android-4.0"
swarming_host: "chrome-swarming.appspot.com"
swarming_tags: "vpython:native-python-wrapper"
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg
index e6c8157..5a6d3a2 100644
--- a/infra/config/global/generated/luci-milo.cfg
+++ b/infra/config/global/generated/luci-milo.cfg
@@ -116,6 +116,11 @@
short_name: "15"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux-android-16"
+ category: "R8"
+ short_name: "16"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/win"
category: "R8"
short_name: "win"
@@ -271,6 +276,11 @@
short_name: "15"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux-android-16_release"
+ category: "Release|R8"
+ short_name: "16"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/win_release"
category: "Release|R8"
short_name: "win"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg
index a13297c..01cd1bd 100644
--- a/infra/config/global/generated/luci-notify.cfg
+++ b/infra/config/global/generated/luci-notify.cfg
@@ -180,6 +180,30 @@
}
builders {
bucket: "ci"
+ name: "linux-android-16"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
+ name: "linux-android-16_release"
+ repository: "https://r8.googlesource.com/r8"
+ }
+}
+notifiers {
+ notifications {
+ on_failure: true
+ on_new_failure: true
+ notify_blamelist {}
+ }
+ builders {
+ bucket: "ci"
name: "linux-android-4.0"
repository: "https://r8.googlesource.com/r8"
}
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg
index ef9103f..2323ed2 100644
--- a/infra/config/global/generated/luci-scheduler.cfg
+++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -254,6 +254,35 @@
}
}
job {
+ id: "linux-android-16"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "ci"
+ builder: "linux-android-16"
+ }
+}
+job {
+ id: "linux-android-16_release"
+ realm: "ci"
+ acl_sets: "ci"
+ triggering_policy {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 1
+ max_batch_size: 1
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "ci"
+ builder: "linux-android-16_release"
+ }
+}
+job {
id: "linux-android-4.0"
realm: "ci"
acl_sets: "ci"
@@ -846,6 +875,7 @@
id: "branch-gitiles-9.0-forward"
realm: "ci"
acl_sets: "ci"
+ triggers: "linux-android-16_release"
triggers: "linux-jdk25_release"
gitiles {
repo: "https://r8.googlesource.com/r8"
@@ -896,6 +926,7 @@
triggers: "linux-android-13"
triggers: "linux-android-14"
triggers: "linux-android-15"
+ triggers: "linux-android-16"
triggers: "linux-android-4.0"
triggers: "linux-android-4.4"
triggers: "linux-android-5"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star
index 5760b29..30d367e 100755
--- a/infra/config/global/main.star
+++ b/infra/config/global/main.star
@@ -445,6 +445,11 @@
["--dex_vm=15.0.0", "--all_tests", "--command_cache_dir=/tmp/ccache"],
release_trigger = ["branch-gitiles-8.5-forward"],
)
+r8_tester_with_default(
+ "linux-android-16",
+ ["--dex_vm=16.0.0", "--all_tests", "--command_cache_dir=/tmp/ccache"],
+ release_trigger = ["branch-gitiles-9.0-forward"],
+)
r8_tester_with_default(
"win",
diff --git a/scripts/update-host-art.sh b/scripts/update-host-art.sh
index a84c946..59ea813 100755
--- a/scripts/update-host-art.sh
+++ b/scripts/update-host-art.sh
@@ -180,8 +180,7 @@
if [ -f $DEST/product/$ANDROID_PRODUCT/system/framework/boot.vdex ]; then
for VDEXFILE in $DEST/product/$ANDROID_PRODUCT/system/framework/*.vdex; do
VDEXNAME=$(basename ${VDEXFILE});
- # Maybe remove arm here.
- for ARCH in arm arm64; do
+ for ARCH in arm64; do
rm $DEST/product/$ANDROID_PRODUCT/system/framework/$ARCH/${VDEXNAME};
# This relative link command will create a symbolic link of the form
# ../${VDEXNAME} for each architecture.
@@ -204,5 +203,5 @@
strip $DEST/framework/x86_64/* 2> /dev/null
echo "Now run"
-echo "(cd $DEST_ROOT; upload_to_google_storage.py -a --bucket r8-deps $ART_DIR)"
+echo "(cd $DEST_ROOT/host; upload_to_google_storage.py -a --bucket r8-deps $ART_DIR)"
echo "NOTE; If $ART_DIR has several directory elements adjust accordingly."
diff --git a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
index d6f1ead..137faa3 100644
--- a/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
+++ b/src/assistant/java/com/android/tools/r8/assistant/runtime/ReflectiveOperationLogger.java
@@ -26,6 +26,9 @@
}
private String printParameters(Class<?>... parameters) {
+ if (parameters == null) {
+ return "(null)";
+ }
if (parameters.length == 0) {
return "()";
}
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index 41bc7e2..0b93eb9 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -17,7 +17,6 @@
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ImmediateAppSubtypingInfo;
import com.android.tools.r8.keepanno.annotations.KeepForApi;
-import com.android.tools.r8.profile.rewriting.ProfileCollectionAdditions;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerFactory;
import com.android.tools.r8.shaking.MainDexInfo;
@@ -79,11 +78,8 @@
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
ImmediateAppSubtypingInfo subtypingInfo = ImmediateAppSubtypingInfo.create(appView);
-
- ProfileCollectionAdditions profileCollectionAdditions = ProfileCollectionAdditions.nop();
MainDexRootSet mainDexRootSet =
- MainDexRootSet.builder(
- appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
+ MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules)
.evaluateRulesAndBuild(executor);
appView.setMainDexRootSet(mainDexRootSet);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 8f40057..90e1e84 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -379,8 +379,7 @@
assert appView.graphLens().isIdentityLens();
// Find classes which may have code executed before secondary dex files installation.
MainDexRootSet mainDexRootSet =
- MainDexRootSet.builder(
- appView, profileCollectionAdditions, subtypingInfo, options.mainDexKeepRules)
+ MainDexRootSet.builder(appView, subtypingInfo, options.mainDexKeepRules)
.evaluateRulesAndBuild(executorService);
appView.setMainDexRootSet(mainDexRootSet);
appView.appInfo().unsetObsolete();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
index 65a5c1a..2f7bce3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
@@ -93,7 +93,21 @@
TypeElement instructionUseType =
computeUseTypeForInstruction(appView, returnType, instruction, item.value, meet, users);
useType = meet.apply(useType, instructionUseType);
- if (useType.isTop() || useType.equalUpToNullability(staticType)) {
+ if (useType.isTop()) {
+ // Bail-out.
+ if (staticType.isInt()
+ && appView.options().canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug()) {
+ return useType;
+ }
+ return staticType;
+ }
+ if (useType.equalUpToNullability(staticType)) {
+ if (staticType.isInt()
+ && appView.options().canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug()) {
+ // We need to consider all usages. We need to return TOP if we find a use typed as
+ // boolean/byte/char/short and compile to Dalvik.
+ continue;
+ }
// Bail-out.
return staticType;
}
@@ -152,7 +166,7 @@
private static TypeElement computeUseTypeForInstanceGet(
AppView<?> appView, InstanceGet instanceGet) {
- return instanceGet.getField().getHolderType().toTypeElement(appView);
+ return toReferenceTypeElement(instanceGet.getField().getHolderType(), appView);
}
private static TypeElement computeUseTypeForInstancePut(
@@ -163,10 +177,10 @@
DexField field = instancePut.getField();
TypeElement useType = TypeElement.getBottom();
if (instancePut.object() == value) {
- useType = meet.apply(useType, field.getHolderType().toTypeElement(appView));
+ useType = meet.apply(useType, toReferenceTypeElement(field.getHolderType(), appView));
}
if (instancePut.value() == value) {
- useType = meet.apply(useType, field.getType().toTypeElement(appView));
+ useType = meet.apply(useType, toTypeElementOrTop(field.getType(), appView));
}
return useType;
}
@@ -183,10 +197,9 @@
continue;
}
TypeElement useTypeForArgument =
- invoke
- .getInvokedMethod()
- .getArgumentType(argumentIndex, invoke.isInvokeStatic())
- .toTypeElement(appView);
+ toTypeElementOrTop(
+ invoke.getInvokedMethod().getArgumentType(argumentIndex, invoke.isInvokeStatic()),
+ appView);
useType = meet.apply(useType, useTypeForArgument);
}
assert !useType.isBottom();
@@ -194,11 +207,11 @@
}
private static TypeElement computeUseTypeForReturn(AppView<?> appView, DexType returnType) {
- return returnType.toTypeElement(appView);
+ return toTypeElementOrTop(returnType, appView);
}
private static TypeElement computeUseTypeForStaticPut(AppView<?> appView, StaticPut staticPut) {
- return staticPut.getField().getType().toTypeElement(appView);
+ return toTypeElementOrTop(staticPut.getField().getType(), appView);
}
private static TypeElement meet(
@@ -249,4 +262,24 @@
}
return TypeElement.getTop();
}
+
+ private static TypeElement toReferenceTypeElement(DexType type, AppView<?> appView) {
+ assert type.isReferenceType();
+ return type.toTypeElement(appView);
+ }
+
+ private static TypeElement toTypeElementOrTop(DexType type, AppView<?> appView) {
+ if (appView.options().canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug()) {
+ switch (type.getDescriptor().getFirstByteAsChar()) {
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'Z':
+ return TypeElement.getTop();
+ default:
+ break;
+ }
+ }
+ return type.toTypeElement(appView);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 003e17f..2287bbc 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -96,10 +96,19 @@
return prev;
}
+ public boolean hasNext() {
+ return next != null;
+ }
+
public Instruction getNext() {
return next;
}
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ public <T extends Instruction> T nextUntilExclusive(Predicate<Instruction> predicate) {
+ return hasNext() ? getNext().nextUntilInclusive(predicate) : null;
+ }
+
@SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
public <T extends Instruction> T nextUntilInclusive(Predicate<Instruction> predicate) {
Instruction current = this;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
index 77bbc33..64a5d69 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfToLirConverter.java
@@ -7,27 +7,29 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysisCollection;
import com.android.tools.r8.graph.analysis.FinishedEnqueuerAnalysis;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
+import com.android.tools.r8.ir.code.FieldGet;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.ir.conversion.passes.BranchSimplifier;
import com.android.tools.r8.ir.conversion.passes.CodeRewriterPassCollection;
import com.android.tools.r8.ir.conversion.passes.ConstResourceNumberRewriter;
import com.android.tools.r8.ir.conversion.passes.StringSwitchConverter;
import com.android.tools.r8.ir.optimize.DeadCodeRemover;
-import com.android.tools.r8.ir.optimize.membervaluepropagation.D8MemberValuePropagation;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.AssumePropagator;
import com.android.tools.r8.lightir.IR2LirConverter;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirStrategy;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
+import com.android.tools.r8.shaking.AssumeInfoCollection;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -122,8 +124,8 @@
}
IRCode code = method.buildIR(appView, MethodConversionOptions.forLirPhase(appView));
codeRewriterPassCollection.run(code, null, null, Timing.empty(), null, appView.options());
- if (appView.options().isGeneratingDex() && isCodeReadingSdkInt(code)) {
- new D8MemberValuePropagation(appView).run(code);
+ if (appView.options().isGeneratingDex() && hasApplicableAssumeValuesRule(code)) {
+ new AssumePropagator(appView).run(code);
new BranchSimplifier(appView).simplifyIf(code);
new DeadCodeRemover(appView).run(code, Timing.empty());
}
@@ -136,10 +138,21 @@
method.setCode(lirCode, appView);
}
- private boolean isCodeReadingSdkInt(IRCode code) {
- DexField SDK_INT = appView.dexItemFactory().androidOsBuildVersionMembers.SDK_INT;
- for (StaticGet staticGet : code.<StaticGet>instructions(Instruction::isStaticGet)) {
- if (staticGet.getField().isIdenticalTo(SDK_INT)) {
+ private boolean hasApplicableAssumeValuesRule(IRCode code) {
+ AssumeInfoCollection assumeInfoCollection = appView.getAssumeInfoCollection();
+ for (Instruction instruction : code.instructions()) {
+ DexEncodedMember<?, ?> resolvedMember;
+ if (instruction.isFieldGet()) {
+ FieldGet fieldGet = instruction.asFieldGet();
+ resolvedMember = fieldGet.resolveField(appView, code.context()).getResolvedField();
+ } else if (instruction.isInvokeMethod()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ resolvedMember = invoke.resolveMethod(appView, code.context()).getResolvedMethod();
+ } else {
+ continue;
+ }
+ if (resolvedMember != null
+ && !assumeInfoCollection.get(resolvedMember).getAssumeValue().isUnknown()) {
return true;
}
}
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 b84e744..0718bd7 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
@@ -38,6 +38,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.IfType;
@@ -691,6 +692,20 @@
}
}
}
+
+ // Null checks are removed above. If a call to Object#getClass() remains then it must be due
+ // to having an out value.
+ if (invoke.getInvokedMethod().match(dexItemFactory.objectMembers.getClass)) {
+ ConstClass replacement =
+ ConstClass.builder()
+ .setType(eligibleClass.getType())
+ .setFreshOutValue(
+ code, dexItemFactory.classType.toNonNullClassTypeElement(appView))
+ .setPosition(invoke)
+ .build();
+ invoke.replace(replacement, affectedValues);
+ continue;
+ }
}
if (user.isIf()) {
@@ -1302,7 +1317,14 @@
.appInfo()
.resolveMethodOnLegacy(eligibleClass, invokedMethod)
.asSingleResolution();
- if (resolutionResult == null || !resolutionResult.getResolvedHolder().isProgramClass()) {
+ if (resolutionResult == null) {
+ return false;
+ }
+ if (!resolutionResult.getResolvedHolder().isProgramClass()) {
+ DexMethod method = resolutionResult.getResolvedMethod().getReference();
+ if (method.isIdenticalTo(dexItemFactory.objectMembers.getClass)) {
+ continue;
+ }
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumePropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumePropagator.java
new file mode 100644
index 0000000..3d19511
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/AssumePropagator.java
@@ -0,0 +1,108 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.membervaluepropagation;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.ArrayGet;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
+import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
+import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfoLookup;
+import java.util.Set;
+
+public class AssumePropagator extends MemberValuePropagation<AppInfoWithClassHierarchy> {
+
+ public AssumePropagator(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ }
+
+ @Override
+ InstructionListIterator rewriteInvokeMethod(
+ IRCode code,
+ ProgramMethod context,
+ Set<Value> affectedValues,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ InvokeMethod invoke) {
+ if (invoke.hasUsedOutValue() && invoke.getInvokedMethod().getHolderType().isClassType()) {
+ SingleResolutionResult<?> resolutionResult =
+ invoke.resolveMethod(appView, context).asSingleResolution();
+ if (resolutionResult != null) {
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ AssumeInfo lookup =
+ AssumeInfoLookup.lookupAssumeInfo(appView, resolutionResult, singleTarget);
+ applyAssumeInfo(code, affectedValues, blocks, iterator, invoke, lookup);
+ }
+ }
+ return iterator;
+ }
+
+ @Override
+ InstructionListIterator rewriteInstanceGet(
+ IRCode code,
+ Set<Value> affectedValues,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ InstanceGet current) {
+ return rewriteFieldGet(code, affectedValues, blocks, iterator, current);
+ }
+
+ @Override
+ InstructionListIterator rewriteStaticGet(
+ IRCode code,
+ Set<Value> affectedValues,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ StaticGet current) {
+ return rewriteFieldGet(code, affectedValues, blocks, iterator, current);
+ }
+
+ private InstructionListIterator rewriteFieldGet(
+ IRCode code,
+ Set<Value> affectedValues,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ FieldInstruction current) {
+ DexClassAndField resolvedField =
+ current.resolveField(appView, code.context()).getResolutionPair();
+ if (resolvedField != null) {
+ AssumeInfo lookup = appView.getAssumeInfoCollection().get(resolvedField);
+ applyAssumeInfo(code, affectedValues, blocks, iterator, current, lookup);
+ }
+ return iterator;
+ }
+
+ @Override
+ void rewriteArrayGet(
+ IRCode code,
+ Set<Value> affectedValues,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ ArrayGet arrayGet) {
+ // Intentionally empty.
+ }
+
+ @Override
+ void rewriteInstancePut(IRCode code, InstructionListIterator iterator, InstancePut current) {
+ // Intentionally empty.
+ }
+
+ @Override
+ void rewriteStaticPut(IRCode code, InstructionListIterator iterator, StaticPut current) {
+ // Intentionally empty.
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/OutlineCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/OutlineCollection.java
new file mode 100644
index 0000000..64bd487
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/OutlineCollection.java
@@ -0,0 +1,76 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner.exceptions;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.lightir.LirCode;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+public class OutlineCollection {
+
+ private final AppView<?> appView;
+ private final ClassToFeatureSplitMap classToFeatureSplitMap;
+
+ private final Map<FeatureSplit, Map<Wrapper<LirCode<?>>, ThrowBlockOutline>> outlines =
+ new ConcurrentHashMap<>();
+
+ OutlineCollection(AppView<?> appView) {
+ this.appView = appView;
+ this.classToFeatureSplitMap = appView.appInfo().getClassToFeatureSplitMap();
+ }
+
+ public ThrowBlockOutline add(LirCode<?> lirCode, DexProto proto, ProgramMethod context) {
+ // Get the outlines in the current feature.
+ FeatureSplit feature = classToFeatureSplitMap.getFeatureSplit(context.getHolder(), appView);
+ Map<Wrapper<LirCode<?>>, ThrowBlockOutline> outlinesInFeature =
+ outlines.computeIfAbsent(feature, ignoreKey(ConcurrentHashMap::new));
+ // Add the outline.
+ Wrapper<LirCode<?>> lirCodeWrapper = ThrowBlockOutlinerLirCodeEquivalence.get().wrap(lirCode);
+ return outlinesInFeature.computeIfAbsent(
+ lirCodeWrapper, w -> new ThrowBlockOutline(w.get(), proto));
+ }
+
+ public Collection<ThrowBlockOutline> getOutlines() {
+ mergeOutlinesFromFeaturesIntoBase();
+ return outlines.values().stream()
+ .flatMap(x -> x.values().stream())
+ .collect(Collectors.toList());
+ }
+
+ private void mergeOutlinesFromFeaturesIntoBase() {
+ Map<Wrapper<LirCode<?>>, ThrowBlockOutline> outlinesInBase = outlines.get(FeatureSplit.BASE);
+ if (outlinesInBase == null) {
+ return;
+ }
+ for (var entry : outlines.entrySet()) {
+ FeatureSplit feature = entry.getKey();
+ if (feature.isBase()) {
+ continue;
+ }
+ Map<Wrapper<LirCode<?>>, ThrowBlockOutline> outlinesInFeature = entry.getValue();
+ var innerIterator = outlinesInFeature.entrySet().iterator();
+ while (innerIterator.hasNext()) {
+ var innerEntry = innerIterator.next();
+ Wrapper<LirCode<?>> lirCodeWrapper = innerEntry.getKey();
+ ThrowBlockOutline outlineInBase = outlinesInBase.get(lirCodeWrapper);
+ if (outlineInBase == null) {
+ continue;
+ }
+ ThrowBlockOutline outlineInFeature = innerEntry.getValue();
+ outlineInBase.merge(outlineInFeature);
+ innerIterator.remove();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java
index f868bc8..353aba6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirConstant;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.structural.CompareToVisitor;
import com.android.tools.r8.utils.structural.HashingVisitor;
import com.google.common.collect.ConcurrentHashMultiset;
@@ -47,11 +48,20 @@
public class ThrowBlockOutline implements LirConstant {
- private final List<AbstractValue> arguments;
- private final LirCode<?> lirCode;
- private final DexProto proto;
+ private List<AbstractValue> arguments;
+ private LirCode<?> lirCode;
+ private DexProto proto;
+
private final Multiset<DexMethod> users = ConcurrentHashMultiset.create();
+ // If this is an outline in base, and there are equivalent outlines in features, then the outlines
+ // from features will be removed and merged into the outline in base. This field stores the merged
+ // outlines.
+ //
+ // This is always null in D8.
+ private List<ThrowBlockOutline> children;
+ private ThrowBlockOutline parent;
+
private ProgramMethod materializedOutlineMethod;
ThrowBlockOutline(LirCode<?> lirCode, DexProto proto) {
@@ -86,6 +96,14 @@
}
}
+ public void clear() {
+ // Clear the state of this outline since it has been merged with its parent.
+ assert parent != null;
+ arguments = null;
+ lirCode = null;
+ proto = null;
+ }
+
private AbstractValue encodeArgumentValue(Value value, AbstractValueFactory valueFactory) {
if (value.isConstNumber()) {
ConstNumber constNumber = value.getDefinition().asConstNumber();
@@ -102,11 +120,24 @@
return AbstractValue.unknown();
}
+ // Returns this outline and all outlines that have been merged into this outline.
+ public Iterable<ThrowBlockOutline> getAllOutlines() {
+ if (children == null) {
+ return Collections.singletonList(this);
+ }
+ return IterableUtils.append(children, this);
+ }
+
public List<AbstractValue> getArguments() {
return arguments;
}
+ public List<ThrowBlockOutline> getChildren() {
+ return children != null ? children : Collections.emptyList();
+ }
+
public LirCode<?> getLirCode() {
+ assert verifyNotMerged();
return lirCode;
}
@@ -120,14 +151,26 @@
}
public int getNumberOfUsers() {
- return users.size();
+ int result = users.size();
+ if (children != null) {
+ for (ThrowBlockOutline child : children) {
+ result += child.users.size();
+ }
+ }
+ return result;
+ }
+
+ public ThrowBlockOutline getParentOrSelf() {
+ return parent != null ? parent : this;
}
public DexProto getProto() {
+ assert verifyNotMerged();
return proto;
}
public RewrittenPrototypeDescription getProtoChanges() {
+ assert verifyNotMerged();
assert hasConstantArgument();
ArgumentInfoCollection.Builder argumentsInfoBuilder =
ArgumentInfoCollection.builder().setArgumentInfosSize(proto.getArity());
@@ -145,10 +188,12 @@
}
public DexProto getOptimizedProto(DexItemFactory factory) {
+ assert verifyNotMerged();
return proto.withoutParameters((i, p) -> isArgumentConstant(i), factory);
}
public ProgramMethod getSynthesizingContext(AppView<?> appView) {
+ assert verifyNotMerged();
DexMethod shortestUser = null;
for (DexMethod user : users) {
if (shortestUser == null) {
@@ -227,6 +272,7 @@
}
public boolean hasConstantArgument() {
+ assert verifyNotMerged();
return Iterables.any(arguments, argument -> !argument.isUnknown());
}
@@ -241,6 +287,7 @@
}
public boolean isArgumentConstant(int index) {
+ assert verifyNotMerged();
return !arguments.get(index).isUnknown();
}
@@ -248,7 +295,16 @@
return materializedOutlineMethod != null;
}
+ public boolean isStringBuilderToStringOutline() {
+ return !isThrowOutline();
+ }
+
+ public boolean isThrowOutline() {
+ return proto.getReturnType().isVoidType();
+ }
+
public void materialize(AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ assert verifyNotMerged();
SyntheticItems syntheticItems = appView.getSyntheticItems();
materializedOutlineMethod =
syntheticItems.createMethod(
@@ -265,6 +321,9 @@
appView.apiLevelCompute().computeInitialMinApiLevel(appView.options()))
.setCode(methodSig -> lirCode)
.setProto(getOptimizedProto(appView.dexItemFactory())));
+ for (ThrowBlockOutline child : getChildren()) {
+ child.materializedOutlineMethod = materializedOutlineMethod;
+ }
}
private DexAnnotationSet createAnnotations(AppView<?> appView) {
@@ -278,4 +337,23 @@
}
return DexAnnotationSet.empty();
}
+
+ public void merge(ThrowBlockOutline outline) {
+ for (int i = 0; i < arguments.size(); i++) {
+ if (!arguments.get(i).equals(outline.arguments.get(i))) {
+ arguments.set(i, AbstractValue.unknown());
+ }
+ }
+ if (children == null) {
+ children = new ArrayList<>();
+ }
+ children.add(outline);
+ outline.parent = this;
+ outline.clear();
+ }
+
+ public boolean verifyNotMerged() {
+ assert parent == null;
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
index e9de503..04e8567 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlineMarkerRewriter.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
@@ -18,7 +19,6 @@
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.ThrowBlockOutlineMarker;
import com.android.tools.r8.ir.code.UnusedArgument;
import com.android.tools.r8.ir.code.Value;
@@ -41,10 +41,12 @@
private final AppView<?> appView;
private final DeadCodeRemover deadCodeRemover;
+ private final DexItemFactory factory;
ThrowBlockOutlineMarkerRewriter(AppView<?> appView) {
this.appView = appView;
this.deadCodeRemover = new DeadCodeRemover(appView);
+ this.factory = appView.dexItemFactory();
}
public void processOutlineMethod(
@@ -109,80 +111,92 @@
private void processOutlineMarkers(IRCode code) {
for (BasicBlock block : code.getBlocks()) {
- Throw throwInstruction = block.exit().asThrow();
- if (throwInstruction != null) {
- ThrowBlockOutlineMarker outlineMarker =
- block.entry().nextUntilInclusive(Instruction::isThrowBlockOutlineMarker);
- if (outlineMarker != null) {
- ThrowBlockOutline outline = outlineMarker.getOutline();
- outlineMarker.detachConstantOutlineArguments(outline);
- if (outline.isMaterialized()) {
- // Insert a call to the materialized outline method and load the return value.
- BasicBlockInstructionListIterator instructionIterator =
- block.listIterator(block.exit());
- InvokeStatic invoke =
- InvokeStatic.builder()
- .setArguments(outlineMarker.inValues())
- .setIsInterface(false)
- .setMethod(outline.getMaterializedOutlineMethod())
- .setPosition(throwInstruction)
- .build();
+ ThrowBlockOutlineMarker outlineMarker =
+ block.entry().nextUntilInclusive(Instruction::isThrowBlockOutlineMarker);
+ while (outlineMarker != null) {
+ ThrowBlockOutline outline = outlineMarker.getOutline().getParentOrSelf();
+ Instruction outlineEnd = getOutlineEnd(block, outline, outlineMarker);
+ outlineMarker.detachConstantOutlineArguments(outline);
+ if (outline.isMaterialized()) {
+ // Insert a call to the materialized outline method and load the return value.
+ BasicBlockInstructionListIterator instructionIterator = block.listIterator(outlineEnd);
+ InvokeStatic.Builder invokeBuilder =
+ InvokeStatic.builder()
+ .setArguments(outlineMarker.inValues())
+ .setIsInterface(false)
+ .setMethod(outline.getMaterializedOutlineMethod())
+ .setPosition(outlineEnd);
+ if (outline.isStringBuilderToStringOutline()) {
+ invokeBuilder.setFreshOutValue(
+ code, factory.stringType.toTypeElement(appView), outlineEnd.getLocalInfo());
+ }
+ InvokeStatic invoke = invokeBuilder.build();
+ if (outline.isStringBuilderToStringOutline()) {
+ outlineEnd.replace(invoke);
+ outlineEnd = invoke;
+ } else {
+ assert outline.isThrowOutline();
instructionIterator.add(invoke);
- Value returnValue = addReturnOrThrowValue(code, instructionIterator);
+ Value returnOrThrowValue = addReturnOrThrowValue(code, instructionIterator);
// Replace the throw instruction by a normal return, but throw null in initializers.
if (code.context().getDefinition().isInstanceInitializer()) {
- throwInstruction.replaceValue(0, returnValue);
+ outlineEnd.replaceValue(0, returnOrThrowValue);
} else {
Return returnInstruction =
Return.builder()
.setPositionForNonThrowingInstruction(
- throwInstruction.getPosition(), appView.options())
- .setReturnValue(returnValue)
+ outlineEnd.getPosition(), appView.options())
+ .setReturnValue(returnOrThrowValue)
.build();
block.replaceLastInstruction(returnInstruction);
+ outlineEnd = returnInstruction;
}
+ }
- // Remove all outlined instructions bottom up.
- instructionIterator = block.listIterator(invoke);
- for (Instruction instruction = instructionIterator.previous();
- instruction != outlineMarker;
- instruction = instructionIterator.previous()) {
- Value outValue = instruction.outValue();
- if (outValue == null || !outValue.hasNonDebugUsers()) {
- // Remove all debug users of the out-value.
- if (outValue != null && outValue.hasDebugUsers()) {
- for (Instruction debugUser : outValue.debugUsers()) {
- debugUser.getDebugValues().remove(outValue);
- if (debugUser.isDebugLocalRead() && debugUser.getDebugValues().isEmpty()) {
- debugUser.remove();
- }
+ // Remove all outlined instructions bottom up.
+ instructionIterator = block.listIterator(invoke);
+ for (Instruction outlinedInstruction = instructionIterator.previous();
+ outlinedInstruction != outlineMarker;
+ outlinedInstruction = instructionIterator.previous()) {
+ assert !outlinedInstruction.isThrowBlockOutlineMarker();
+ Value outValue = outlinedInstruction.outValue();
+ if (outValue == null || !outValue.hasNonDebugUsers()) {
+ // Remove all debug users of the out-value.
+ if (outValue != null && outValue.hasDebugUsers()) {
+ for (Instruction debugUser : outValue.debugUsers()) {
+ debugUser.getDebugValues().remove(outValue);
+ if (debugUser.isDebugLocalRead() && debugUser.getDebugValues().isEmpty()) {
+ debugUser.remove();
}
- outValue.clearDebugUsers();
}
- // We are not using `removeOrReplaceByDebugLocalRead` here due to the backwards
- // iteration.
- if (instruction.getDebugValues().isEmpty()) {
- instruction.remove();
- } else {
- DebugLocalRead replacement = new DebugLocalRead();
- instruction.replace(replacement);
- Instruction previous = instructionIterator.previous();
- assert previous == replacement;
- }
+ outValue.clearDebugUsers();
+ }
+ // We are not using `removeOrReplaceByDebugLocalRead` here due to the backwards
+ // iteration.
+ if (outlinedInstruction.getDebugValues().isEmpty()) {
+ outlinedInstruction.remove();
+ } else {
+ DebugLocalRead replacement = new DebugLocalRead();
+ outlinedInstruction.replace(replacement);
+ Instruction previous = instructionIterator.previous();
+ assert previous == replacement;
}
}
}
-
- // Finally delete the outline marker.
- outlineMarker.removeOrReplaceByDebugLocalRead();
-
- // Blocks cannot start with DebugLocalRead.
- while (block.entry().isDebugLocalRead()) {
- block.entry().moveDebugValues(block.entry().getNext());
- block.entry().remove();
- }
}
+
+ // Finally delete the outline marker.
+ outlineMarker.removeOrReplaceByDebugLocalRead();
+
+ // Blocks cannot start with DebugLocalRead.
+ while (block.entry().isDebugLocalRead()) {
+ block.entry().moveDebugValues(block.entry().getNext());
+ block.entry().remove();
+ }
+
+ // Continue searching for outline markers from the end of the current outline.
+ outlineMarker = outlineEnd.nextUntilExclusive(Instruction::isThrowBlockOutlineMarker);
}
assert block.streamInstructions().noneMatch(Instruction::isThrowBlockOutlineMarker);
}
@@ -216,4 +230,15 @@
return null;
}
}
+
+ private Instruction getOutlineEnd(
+ BasicBlock block, ThrowBlockOutline outline, ThrowBlockOutlineMarker outlineMarker) {
+ if (outline.isThrowOutline()) {
+ return block.exit();
+ } else {
+ // The end of a StringBuilder#toString outline is the call to StringBuilder#toString.
+ return outlineMarker.nextUntilExclusive(
+ i -> ThrowBlockOutlinerScanner.isStringBuilderToString(i, factory));
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutliner.java
index 004281a..91aefd2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutliner.java
@@ -227,17 +227,19 @@
// Estimate the savings from this outline.
int estimatedSavingsInBytes = 0;
- for (Multiset.Entry<DexMethod> entry : outline.getUsers().entrySet()) {
- // For each call we save the outlined instructions at the cost of an invoke + return.
- int estimatedSavingsForUser = codeSizeInBytes - (DexInvokeStatic.SIZE + DexReturn.SIZE);
- if (entry.getElement().getReturnType().isWideType()) {
- estimatedSavingsForUser -= DexConstWide16.SIZE;
- } else if (!entry.getElement().getReturnType().isVoidType()) {
- estimatedSavingsForUser -= DexConst4.SIZE;
- }
- estimatedSavingsInBytes += estimatedSavingsForUser * entry.getCount();
- if (estimatedSavingsInBytes > estimatedCostInBytes) {
- return true;
+ for (ThrowBlockOutline outlineOrMergedOutline : outline.getAllOutlines()) {
+ for (Multiset.Entry<DexMethod> entry : outlineOrMergedOutline.getUsers().entrySet()) {
+ // For each call we save the outlined instructions at the cost of an invoke + return.
+ int estimatedSavingsForUser = codeSizeInBytes - (DexInvokeStatic.SIZE + DexReturn.SIZE);
+ if (entry.getElement().getReturnType().isWideType()) {
+ estimatedSavingsForUser -= DexConstWide16.SIZE;
+ } else if (!entry.getElement().getReturnType().isVoidType()) {
+ estimatedSavingsForUser -= DexConst4.SIZE;
+ }
+ estimatedSavingsInBytes += estimatedSavingsForUser * entry.getCount();
+ if (estimatedSavingsInBytes > estimatedCostInBytes) {
+ return true;
+ }
}
}
return false;
@@ -269,10 +271,12 @@
ProgramMethodMap<ThrowBlockOutline> methodsToReprocess = ProgramMethodMap.create();
Set<DexMethod> seenUsers = Sets.newIdentityHashSet();
for (ThrowBlockOutline outline : outlines) {
- for (DexMethod user : outline.getUsers()) {
- if (seenUsers.add(user)) {
- ProgramMethod methodToReprocess = appView.definitionFor(user).asProgramMethod();
- methodsToReprocess.put(methodToReprocess, null);
+ for (ThrowBlockOutline outlineOrMergedOutline : outline.getAllOutlines()) {
+ for (DexMethod user : outlineOrMergedOutline.getUsers().elementSet()) {
+ if (seenUsers.add(user)) {
+ ProgramMethod methodToReprocess = appView.definitionFor(user).asProgramMethod();
+ methodsToReprocess.put(methodToReprocess, null);
+ }
}
}
if (outline.getMaterializedOutlineMethod() != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java
index 7f6ea7d..ce48c06 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java
@@ -15,6 +15,10 @@
SystemPropertyUtils.parseSystemPropertyOrDefault(
"com.android.tools.r8.throwblockoutliner.enable", false);
+ public boolean enableStringBuilderOutlining =
+ SystemPropertyUtils.parseSystemPropertyOrDefault(
+ "com.android.tools.r8.throwblockoutliner.enablestringbuilder", false);
+
public final int costInBytesForTesting =
SystemPropertyUtils.parseSystemPropertyOrDefault(
"com.android.tools.r8.throwblockoutliner.cost", -1);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerScanner.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerScanner.java
index 840483a..c3427e0e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerScanner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerScanner.java
@@ -13,7 +13,6 @@
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.MOVE;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
-import static java.util.Collections.emptyList;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
@@ -43,6 +42,7 @@
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
+import com.android.tools.r8.ir.code.Return;
import com.android.tools.r8.ir.code.Throw;
import com.android.tools.r8.ir.code.ThrowBlockOutlineMarker;
import com.android.tools.r8.ir.code.Value;
@@ -50,9 +50,9 @@
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.lightir.LirCode;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.timing.Timing;
-import com.google.common.base.Equivalence.Wrapper;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -61,7 +61,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class ThrowBlockOutlinerScanner {
@@ -70,13 +69,13 @@
private final AppView<?> appView;
private final DexItemFactory factory;
+ private final OutlineCollection outlines;
private final AbstractValueFactory valueFactory;
- private final Map<Wrapper<LirCode<?>>, ThrowBlockOutline> outlines = new ConcurrentHashMap<>();
-
ThrowBlockOutlinerScanner(AppView<?> appView) {
this.appView = appView;
this.factory = appView.dexItemFactory();
+ this.outlines = new OutlineCollection(appView);
this.valueFactory =
appView.enableWholeProgramOptimizations()
? appView.abstractValueFactory()
@@ -85,8 +84,37 @@
public void run(IRCode code) {
assert !code.metadata().mayHaveThrowBlockOutlineMarker();
- for (BasicBlock block : getThrowBlocks(code)) {
- new ThrowBlockOutlinerScannerForBlock(code, block).processThrowBlock();
+ if (IterableUtils.none(code.getBlocks(), BasicBlock::isReturnBlock)) {
+ // Don't outline from methods that unconditionally throw.
+ // They may be manually created throw block outlines.
+ return;
+ }
+ for (BasicBlock block : code.getBlocks()) {
+ if (block.exit().isThrow()) {
+ new ThrowBlockOutlinerScannerForThrow(code, block).tryBuildOutline();
+ }
+ if (!appView.options().getThrowBlockOutlinerOptions().enableStringBuilderOutlining) {
+ continue;
+ }
+ Instruction previousOutlineEnd = null;
+ for (Instruction instruction : block.getInstructions()) {
+ // If we encounter an outline marker it is because we were able to outline the tail of the
+ // block above. Since the remainder of the block has been outlined, abort further outlining.
+ if (instruction.isThrowBlockOutlineMarker()) {
+ assert block.exit().isThrow();
+ break;
+ }
+ if (isStringBuilderToString(instruction, factory)) {
+ InvokeVirtual invoke = instruction.asInvokeVirtual();
+ ThrowBlockOutline outline =
+ new ThrowBlockOutlinerScannerForStringBuilderToString(
+ code, block, invoke, previousOutlineEnd)
+ .tryBuildOutline();
+ if (outline != null) {
+ previousOutlineEnd = instruction;
+ }
+ }
+ }
}
if (code.metadata().mayHaveThrowBlockOutlineMarker()) {
if (appView.enableWholeProgramOptimizations()) {
@@ -105,136 +133,71 @@
}
public Collection<ThrowBlockOutline> getOutlines() {
- return outlines.values();
+ return outlines.getOutlines();
}
- private List<BasicBlock> getThrowBlocks(IRCode code) {
- boolean seenReturn = false;
- List<BasicBlock> throwBlocks = new ArrayList<>();
- for (BasicBlock block : code.getBlocks()) {
- if (block.exit().isReturn()) {
- seenReturn = true;
- } else if (block.exit().isThrow()) {
- throwBlocks.add(block);
- }
- }
- // Never outline from methods that always throw.
- return seenReturn ? throwBlocks : emptyList();
+ static boolean isStringBuilderToString(Instruction instruction, DexItemFactory factory) {
+ InvokeVirtual invoke = instruction.asInvokeVirtual();
+ return invoke != null
+ && invoke.getInvokedMethod().match(factory.objectMembers.toString)
+ && invoke.getReceiver().getType().isClassType(factory.stringBuilderType)
+ && invoke.hasOutValue();
}
- private class ThrowBlockOutlinerScannerForBlock {
+ private abstract class ThrowBlockOutlinerScannerForInstruction {
- private final IRCode code;
- private final Value exceptionValue;
- private final BasicBlock throwBlock;
- private final Throw throwInstruction;
+ final IRCode code;
+ final BasicBlock block;
+ final Instruction previousOutlineEnd;
private boolean hasRunPrefixer;
- ThrowBlockOutlinerScannerForBlock(IRCode code, BasicBlock throwBlock) {
+ ThrowBlockOutlinerScannerForInstruction(
+ IRCode code, BasicBlock block, Instruction previousOutlineEnd) {
this.code = code;
- this.throwBlock = throwBlock;
- this.throwInstruction = throwBlock.exit().asThrow();
- this.exceptionValue = throwInstruction.exception();
+ this.block = block;
+ this.previousOutlineEnd = previousOutlineEnd;
}
- private void processThrowBlock() {
- // Recursively build up the outline method. On successful outline creation, the resulting
- // LirCode is passed to the continuation function.
- processThrowInstruction(
- outlineBuilder -> {
- // On successful outline creation, store the outline for later processing.
- DexProto proto = outlineBuilder.getProto(appView);
- if (proto == null) {
+ void processInstruction(Instruction instruction, Consumer<OutlineBuilder> continuation) {
+ if (instruction != previousOutlineEnd) {
+ switch (instruction.opcode()) {
+ case ASSUME:
+ processAssume(instruction.asAssume(), continuation);
+ return;
+ case CONST_NUMBER:
+ case CONST_STRING:
+ processConstInstruction(instruction.asConstInstruction(), continuation);
+ return;
+ case DEBUG_LOCAL_READ:
+ case DEBUG_POSITION:
+ processNonMaterializingDebugInstruction(instruction, continuation);
+ return;
+ case INVOKE_DIRECT:
+ if (instruction.isInvokeConstructor(factory)) {
+ processStringBuilderConstructorCall(instruction.asInvokeDirect(), continuation);
return;
}
- LirCode<?> lirCode = outlineBuilder.buildLirCode(appView, code.context());
- Wrapper<LirCode<?>> lirCodeWrapper =
- ThrowBlockOutlinerLirCodeEquivalence.get().wrap(lirCode);
- ThrowBlockOutline outline =
- outlines.computeIfAbsent(
- lirCodeWrapper, w -> new ThrowBlockOutline(w.get(), proto));
- assert proto.isIdenticalTo(outline.getProto());
- List<Value> arguments = outlineBuilder.buildArguments();
- outline.addUser(code.reference(), arguments, getAbstractValueFactory());
-
- // Insert a synthetic marker instruction that references the outline so that we know
- // where to materialize the outline call.
- Instruction insertionPoint = outlineBuilder.getFirstOutlinedInstruction();
- assert insertionPoint.getBlock() == throwBlock;
- ThrowBlockOutlineMarker marker =
- ThrowBlockOutlineMarker.builder()
- .setArguments(arguments)
- .setOutline(outline)
- .setPosition(Position.none())
- .build();
- throwBlock.listIterator(insertionPoint).add(marker);
- });
- }
-
- private void processThrowInstruction(Consumer<OutlineBuilder> continuation) {
- if (!exceptionValue.isDefinedByInstructionSatisfying(
- i -> i.isNewInstance() && i.getBlock() == throwBlock)) {
- // Exception is not created in the throw block.
- return;
- }
- assert throwInstruction.hasPrev();
- // We always expect the constructor call corresponding to the thrown exception to be last.
- processExceptionConstructorCall(
- throwInstruction.getPrev(),
- outlineBuilder -> {
- Value outlinedExceptionValue = outlineBuilder.getOutlinedValue(exceptionValue);
- if (outlinedExceptionValue == null) {
- // Fail as we were unable to outline the corresponding new-instance instruction.
+ break;
+ case INVOKE_STATIC:
+ processStringFormatOrValueOf(instruction.asInvokeStatic(), continuation);
+ return;
+ case INVOKE_VIRTUAL:
+ processStringBuilderAppendOrToString(instruction.asInvokeVirtual(), continuation);
+ return;
+ case MOVE:
+ if (instruction.isDebugLocalWrite()) {
+ processDebugLocalWrite(instruction.asDebugLocalWrite(), continuation);
return;
}
- outlineBuilder.add(
- Throw.builder()
- .setExceptionValue(outlinedExceptionValue)
- .setPosition(Position.syntheticNone())
- .build());
- continuation.accept(outlineBuilder);
- });
- }
-
- private void processInstruction(
- Instruction instruction, Consumer<OutlineBuilder> continuation) {
- switch (instruction.opcode()) {
- case ASSUME:
- processAssume(instruction.asAssume(), continuation);
- return;
- case CONST_NUMBER:
- case CONST_STRING:
- processConstInstruction(instruction.asConstInstruction(), continuation);
- return;
- case DEBUG_LOCAL_READ:
- case DEBUG_POSITION:
- processNonMaterializingDebugInstruction(instruction, continuation);
- return;
- case INVOKE_DIRECT:
- if (instruction.isInvokeConstructor(factory)) {
- processStringBuilderConstructorCall(instruction.asInvokeDirect(), continuation);
+ assert false;
+ break;
+ case NEW_INSTANCE:
+ processNewInstanceInstruction(instruction.asNewInstance(), continuation);
return;
- }
- break;
- case INVOKE_STATIC:
- processStringFormatOrValueOf(instruction.asInvokeStatic(), continuation);
- return;
- case INVOKE_VIRTUAL:
- processStringBuilderAppendOrToString(instruction.asInvokeVirtual(), continuation);
- return;
- case MOVE:
- if (instruction.isDebugLocalWrite()) {
- processDebugLocalWrite(instruction.asDebugLocalWrite(), continuation);
- return;
- }
- assert false;
- break;
- case NEW_INSTANCE:
- processNewInstanceInstruction(instruction.asNewInstance(), continuation);
- return;
- default:
- break;
+ default:
+ break;
+ }
}
// Unhandled instruction. Start the outline at the successor instruction.
startOutline(instruction.getNext(), continuation);
@@ -264,7 +227,7 @@
processPredecessorInstructionOrStartOutline(instruction, continuation);
}
- private void processPredecessorInstructionOrFail(
+ void processPredecessorInstructionOrFail(
Instruction instruction, Consumer<OutlineBuilder> continuation) {
if (instruction.hasPrev()) {
processInstruction(instruction.getPrev(), continuation);
@@ -284,12 +247,16 @@
private void processNewInstanceInstruction(
NewInstance newInstance, Consumer<OutlineBuilder> continuation) {
- if (newInstance.outValue() != exceptionValue
- && newInstance.getType().isNotIdenticalTo(appView.dexItemFactory().stringBuilderType)) {
+ if (newInstance.getType().isNotIdenticalTo(appView.dexItemFactory().stringBuilderType)) {
// Unhandled instruction.
startOutline(newInstance.getNext(), continuation);
return;
}
+ internalProcessNewInstanceInstruction(newInstance, continuation);
+ }
+
+ void internalProcessNewInstanceInstruction(
+ NewInstance newInstance, Consumer<OutlineBuilder> continuation) {
processPredecessorInstructionOrStartOutline(
newInstance,
outlineBuilder -> {
@@ -298,8 +265,7 @@
});
}
- private void outlineNewInstanceInstruction(
- NewInstance newInstance, OutlineBuilder outlineBuilder) {
+ void outlineNewInstanceInstruction(NewInstance newInstance, OutlineBuilder outlineBuilder) {
NewInstance outlinedNewInstance =
NewInstance.builder()
.setFreshOutValue(
@@ -312,51 +278,6 @@
outlineBuilder.map(newInstance.outValue(), outlinedNewInstance.outValue());
}
- private void processExceptionConstructorCall(
- Instruction instruction, Consumer<OutlineBuilder> continuation) {
- InvokeDirect invoke = instruction.asInvokeConstructor(factory);
- if (invoke == null) {
- // Not a constructor call.
- return;
- }
- assert !exceptionValue.hasDebugUsers();
- assert !exceptionValue.hasPhiUsers();
- if (invoke.getReceiver() != exceptionValue) {
- // Not the constructor call corresponding to the thrown exception.
- return;
- }
- // This instruction is guaranteed to have a predecessor since the handling of the throw
- // instruction checks if the new-instance instruction is in the throw block.
- assert instruction.hasPrev();
- processPredecessorInstructionOrFail(
- invoke,
- outlineBuilder -> {
- if (outlineBuilder.getOutlinedValue(exceptionValue) == null) {
- // We were unable to outline the corresponding new-instance instruction. Check if we
- // can insert it here, right before the constructor call.
- NewInstance newInstance = exceptionValue.getDefinition().asNewInstance();
- if (exceptionValue.uniqueUsers().size() == 2
- && !newInstance.instructionMayHaveSideEffects(appView, code.context())) {
- // The exception value is only used by the constructor call and the throw.
- // By construction, it cannot have debug users nor phi users.
- outlineNewInstanceInstruction(newInstance, outlineBuilder);
- } else {
- // Fail as we were unable to outline the corresponding new-instance instruction.
- return;
- }
- }
- outlineBuilder.add(
- InvokeDirect.builder()
- .setArguments(
- ListUtils.map(
- invoke.arguments(), outlineBuilder::getOutlinedValueOrCreateArgument))
- .setMethod(invoke.getInvokedMethod())
- .setPosition(Position.syntheticNone())
- .build());
- continuation.accept(outlineBuilder);
- });
- }
-
private void processStringBuilderConstructorCall(
InvokeDirect invoke, Consumer<OutlineBuilder> continuation) {
if (!factory.stringBuilderMethods.isConstructorMethod(invoke.getInvokedMethod())) {
@@ -415,7 +336,7 @@
});
}
- private void processStringBuilderAppendOrToString(
+ void processStringBuilderAppendOrToString(
InvokeVirtual invoke, Consumer<OutlineBuilder> continuation) {
DexMethod invokedMethod = invoke.getInvokedMethod();
if (!factory.stringBuilderMethods.isAppendMethod(invokedMethod)
@@ -459,7 +380,7 @@
newFirstOutlinedInstruction = firstOutlinedInstruction;
} else {
newFirstOutlinedInstruction =
- new ThrowBlockOutlinerPrefixer(factory, throwBlock)
+ new ThrowBlockOutlinerPrefixer(factory, block)
.tryMoveNonOutlinedStringBuilderInstructionsToOutline(firstOutlinedInstruction);
hasRunPrefixer = true;
}
@@ -472,6 +393,194 @@
}
}
+ private class ThrowBlockOutlinerScannerForStringBuilderToString
+ extends ThrowBlockOutlinerScannerForInstruction {
+
+ private final InvokeVirtual stringBuilderToStringInstruction;
+
+ private ThrowBlockOutline outline;
+
+ ThrowBlockOutlinerScannerForStringBuilderToString(
+ IRCode code,
+ BasicBlock block,
+ InvokeVirtual stringBuilderToStringInstruction,
+ Instruction previousOutlineEnd) {
+ super(code, block, previousOutlineEnd);
+ this.stringBuilderToStringInstruction = stringBuilderToStringInstruction;
+ }
+
+ ThrowBlockOutline tryBuildOutline() {
+ // Recursively build up the outline method. On successful outline creation, the resulting
+ // LirCode is passed to the continuation function.
+ processStringBuilderToString(
+ outlineBuilder -> {
+ // On successful outline creation, store the outline for later processing.
+ DexProto proto = outlineBuilder.getProto(appView, factory.stringType);
+ if (proto == null) {
+ return;
+ }
+ LirCode<?> lirCode = outlineBuilder.buildLirCode(appView, code.context());
+ outline = outlines.add(lirCode, proto, code.context());
+ assert proto.isIdenticalTo(outline.getProto());
+ List<Value> arguments = outlineBuilder.buildArguments();
+ outline.addUser(code.reference(), arguments, getAbstractValueFactory());
+
+ // Insert a synthetic marker instruction that references the outline so that we know
+ // where to materialize the outline call.
+ Instruction insertionPoint = outlineBuilder.getFirstOutlinedInstruction();
+ assert insertionPoint.getBlock() == block;
+ ThrowBlockOutlineMarker marker =
+ ThrowBlockOutlineMarker.builder()
+ .setArguments(arguments)
+ .setOutline(outline)
+ .setPosition(Position.none())
+ .build();
+ block.listIterator(insertionPoint).add(marker);
+ });
+ return outline;
+ }
+
+ private void processStringBuilderToString(Consumer<OutlineBuilder> continuation) {
+ processStringBuilderAppendOrToString(
+ stringBuilderToStringInstruction,
+ outlineBuilder -> {
+ Value outlinedStringValue =
+ outlineBuilder.getOutlinedValue(stringBuilderToStringInstruction.outValue());
+ outlineBuilder.add(
+ Return.builder()
+ .setPosition(Position.syntheticNone())
+ .setReturnValue(outlinedStringValue)
+ .build());
+ continuation.accept(outlineBuilder);
+ });
+ }
+ }
+
+ private class ThrowBlockOutlinerScannerForThrow extends ThrowBlockOutlinerScannerForInstruction {
+
+ private final Throw throwInstruction;
+
+ ThrowBlockOutlinerScannerForThrow(IRCode code, BasicBlock block) {
+ super(code, block, null);
+ this.throwInstruction = block.exit().asThrow();
+ }
+
+ void tryBuildOutline() {
+ // Recursively build up the outline method. On successful outline creation, the resulting
+ // LirCode is passed to the continuation function.
+ processThrowInstruction(
+ outlineBuilder -> {
+ // On successful outline creation, store the outline for later processing.
+ DexProto proto = outlineBuilder.getProto(appView, factory.voidType);
+ if (proto == null) {
+ return;
+ }
+ LirCode<?> lirCode = outlineBuilder.buildLirCode(appView, code.context());
+ ThrowBlockOutline outline = outlines.add(lirCode, proto, code.context());
+ assert proto.isIdenticalTo(outline.getProto());
+ List<Value> arguments = outlineBuilder.buildArguments();
+ outline.addUser(code.reference(), arguments, getAbstractValueFactory());
+
+ // Insert a synthetic marker instruction that references the outline so that we know
+ // where to materialize the outline call.
+ Instruction insertionPoint = outlineBuilder.getFirstOutlinedInstruction();
+ assert insertionPoint.getBlock() == block;
+ ThrowBlockOutlineMarker marker =
+ ThrowBlockOutlineMarker.builder()
+ .setArguments(arguments)
+ .setOutline(outline)
+ .setPosition(Position.none())
+ .build();
+ block.listIterator(insertionPoint).add(marker);
+ });
+ }
+
+ private void processThrowInstruction(Consumer<OutlineBuilder> continuation) {
+ Throw throwInstruction = block.exit().asThrow();
+ Value exceptionValue = throwInstruction.exception();
+ if (!exceptionValue.isDefinedByInstructionSatisfying(
+ i -> i.isNewInstance() && i.getBlock() == block)) {
+ // Exception is not created in the throw block.
+ return;
+ }
+ assert throwInstruction.hasPrev();
+ // We always expect the constructor call corresponding to the thrown exception to be last.
+ processExceptionConstructorCall(
+ throwInstruction.getPrev(),
+ outlineBuilder -> {
+ Value outlinedExceptionValue = outlineBuilder.getOutlinedValue(exceptionValue);
+ if (outlinedExceptionValue == null) {
+ // Fail as we were unable to outline the corresponding new-instance instruction.
+ return;
+ }
+ outlineBuilder.add(
+ Throw.builder()
+ .setExceptionValue(outlinedExceptionValue)
+ .setPosition(Position.syntheticNone())
+ .build());
+ continuation.accept(outlineBuilder);
+ });
+ }
+
+ private void processExceptionConstructorCall(
+ Instruction instruction, Consumer<OutlineBuilder> continuation) {
+ InvokeDirect invoke = instruction.asInvokeConstructor(factory);
+ if (invoke == null) {
+ // Not a constructor call.
+ return;
+ }
+ Value exceptionValue = block.exit().asThrow().exception();
+ assert !exceptionValue.hasDebugUsers();
+ assert !exceptionValue.hasPhiUsers();
+ if (invoke.getReceiver() != exceptionValue) {
+ // Not the constructor call corresponding to the thrown exception.
+ return;
+ }
+ // This instruction is guaranteed to have a predecessor since the handling of the throw
+ // instruction checks if the new-instance instruction is in the throw block.
+ assert instruction.hasPrev();
+ processPredecessorInstructionOrFail(
+ invoke,
+ outlineBuilder -> {
+ if (outlineBuilder.getOutlinedValue(exceptionValue) == null) {
+ // We were unable to outline the corresponding new-instance instruction. Check if we
+ // can insert it here, right before the constructor call.
+ NewInstance newInstance = exceptionValue.getDefinition().asNewInstance();
+ if (exceptionValue.uniqueUsers().size() == 2
+ && !newInstance.instructionMayHaveSideEffects(appView, code.context())) {
+ // The exception value is only used by the constructor call and the throw.
+ // By construction, it cannot have debug users nor phi users.
+ outlineNewInstanceInstruction(newInstance, outlineBuilder);
+ } else {
+ // Fail as we were unable to outline the corresponding new-instance instruction.
+ return;
+ }
+ }
+ outlineBuilder.add(
+ InvokeDirect.builder()
+ .setArguments(
+ ListUtils.map(
+ invoke.arguments(), outlineBuilder::getOutlinedValueOrCreateArgument))
+ .setMethod(invoke.getInvokedMethod())
+ .setPosition(Position.syntheticNone())
+ .build());
+ continuation.accept(outlineBuilder);
+ });
+ }
+
+ @Override
+ void processInstruction(Instruction instruction, Consumer<OutlineBuilder> continuation) {
+ if (instruction.isNewInstance()) {
+ NewInstance newInstance = instruction.asNewInstance();
+ if (newInstance.outValue() == throwInstruction.exception()) {
+ internalProcessNewInstanceInstruction(newInstance, continuation);
+ return;
+ }
+ }
+ super.processInstruction(instruction, continuation);
+ }
+ }
+
private static class OutlineBuilder {
private static final AliasedValueConfiguration aliasing =
@@ -545,9 +654,8 @@
return addArgument(root).outValue();
}
- DexProto getProto(AppView<?> appView) {
+ DexProto getProto(AppView<?> appView, DexType returnType) {
DexItemFactory factory = appView.dexItemFactory();
- DexType returnType = factory.voidType;
List<DexType> parameters = new ArrayList<>(outlinedArguments.size());
for (Argument outlinedArgument : outlinedArguments) {
TypeElement useType =
@@ -556,6 +664,10 @@
// Instead of returning null here we could consider removing the parameter.
return null;
}
+ if (useType.isTop()) {
+ assert appView.options().canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug();
+ return null;
+ }
parameters.add(DexTypeUtils.toDexType(factory, useType));
}
return factory.createProto(returnType, parameters);
diff --git a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
index 016d1a0..c327aa2 100644
--- a/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/AssumeInfoCollection.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -46,6 +47,10 @@
return get(member.getReference());
}
+ public AssumeInfo get(DexEncodedMember<?, ?> member) {
+ return get(member.getReference());
+ }
+
public boolean isEmpty() {
return backing.isEmpty();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 3906ea2..2212b9b 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -44,7 +44,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.ImmediateAppSubtypingInfo;
-import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramField;
@@ -55,8 +54,6 @@
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringMode;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.optimize.membervaluepropagation.assume.AssumeInfo;
import com.android.tools.r8.partial.R8PartialResourceUseCollector;
@@ -129,7 +126,6 @@
private final AppView<? extends AppInfoWithClassHierarchy> appView;
private AssumeInfoCollection.Builder assumeInfoCollectionBuilder;
- private final RootSetBuilderEventConsumer eventConsumer;
private final ImmediateAppSubtypingInfo subtypingInfo;
private final DirectMappedDexApplication application;
private final Set<DexType> rootNonProgramTypes = Sets.newIdentityHashSet();
@@ -160,7 +156,6 @@
private final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance();
- private final InterfaceDesugaringSyntheticHelper interfaceDesugaringSyntheticHelper;
private final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse =
ProgramMethodMap.create();
@@ -172,22 +167,13 @@
private RootSetBuilder(
AppView<? extends AppInfoWithClassHierarchy> appView,
- RootSetBuilderEventConsumer eventConsumer,
ImmediateAppSubtypingInfo subtypingInfo,
Iterable<? extends ProguardConfigurationRule> rules) {
this.appView = appView;
- this.eventConsumer = eventConsumer;
this.subtypingInfo = subtypingInfo;
this.application = appView.appInfo().app().asDirect();
this.rules = rules;
this.options = appView.options();
- interfaceDesugaringSyntheticHelper =
- options.isInterfaceMethodDesugaringEnabled()
- ? new InterfaceDesugaringSyntheticHelper(
- appView,
- InterfaceMethodDesugaringMode.createForInterfaceMethodDesugaringInRootSetBuilder(
- options))
- : null;
attributesConfig =
options.getProguardConfiguration() != null
? options.getProguardConfiguration().getKeepAttributes()
@@ -208,11 +194,9 @@
private RootSetBuilder(
AppView<? extends AppInfoWithClassHierarchy> appView,
- Enqueuer enqueuer,
ImmediateAppSubtypingInfo subtypingInfo) {
this(
appView,
- RootSetBuilderEventConsumer.create(enqueuer.getProfileCollectionAdditions()),
subtypingInfo,
null);
}
@@ -1910,35 +1894,9 @@
preconditionEvent = UnconditionalKeepInfoEvent.get();
}
- if (isInterfaceMethodNeedingDesugaring(item)) {
- ProgramMethod method = item.asMethod();
- ProgramMethod companion =
- interfaceDesugaringSyntheticHelper.ensureMethodOfProgramCompanionClassStub(
- method, eventConsumer);
- // Add the method to the inverse map as tracing will now directly target the CC method.
- if (InvalidCode.isInvalidCode(companion.getDefinition().getCode())) {
- pendingMethodMoveInverse.put(companion, method);
- }
-
- LazyBox<Joiner<?, ?, ?>> companionJoiner =
- new LazyBox<>(
- () ->
- dependentMinimumKeepInfo.getOrCreateMinimumKeepInfoFor(
- preconditionEvent, companion.getReference()));
-
- // Only shrinking and optimization are transferred for interface companion methods.
- if (appView.options().isOptimizationEnabled() && !modifiers.allowsOptimization) {
- companionJoiner.computeIfAbsent().disallowOptimization();
- markAsUsed.execute();
- }
- if (appView.options().isShrinking() && !modifiers.allowsShrinking) {
- companionJoiner.computeIfAbsent().addRule(whyAreYouKeepingKeepRule).disallowShrinking();
- markAsUsed.execute();
- }
- if (!item.asMethod().isDefaultMethod()) {
- // Static and private methods do not apply to the original item.
- return;
- }
+ if (isInterfaceMethodNeedingDesugaring(item) && !item.asMethod().isDefaultMethod()) {
+ // Static and private methods do not apply to the original item.
+ return;
}
// Memoize the joiner to avoid repeated lookups and to validate it as non-bottom if set.
@@ -2546,7 +2504,7 @@
AppView<? extends AppInfoWithClassHierarchy> appView,
Enqueuer enqueuer,
ImmediateAppSubtypingInfo subtypingInfo) {
- return new RootSetBuilder(appView, enqueuer, subtypingInfo);
+ return new RootSetBuilder(appView, subtypingInfo);
}
public static RootSetBuilder builder(
@@ -2556,7 +2514,6 @@
Iterable<? extends ProguardConfigurationRule> rules) {
return new RootSetBuilder(
appView,
- RootSetBuilderEventConsumer.create(profileCollectionAdditions),
subtypingInfo,
rules);
}
@@ -2572,7 +2529,6 @@
ImmediateAppSubtypingInfo subtypingInfo) {
super(
appView,
- RootSetBuilderEventConsumer.create(enqueuer.getProfileCollectionAdditions()),
subtypingInfo,
null);
this.enqueuer = enqueuer;
@@ -2614,12 +2570,10 @@
private MainDexRootSetBuilder(
AppView<? extends AppInfoWithClassHierarchy> appView,
- ProfileCollectionAdditions profileCollectionAdditions,
ImmediateAppSubtypingInfo subtypingInfo,
Iterable<? extends ProguardConfigurationRule> rules) {
super(
appView,
- RootSetBuilderEventConsumer.create(profileCollectionAdditions),
subtypingInfo,
rules);
}
@@ -2667,10 +2621,9 @@
public static MainDexRootSetBuilder builder(
AppView<? extends AppInfoWithClassHierarchy> appView,
- ProfileCollectionAdditions profileCollectionAdditions,
ImmediateAppSubtypingInfo subtypingInfo,
Iterable<? extends ProguardConfigurationRule> rules) {
- return new MainDexRootSetBuilder(appView, profileCollectionAdditions, subtypingInfo, rules);
+ return new MainDexRootSetBuilder(appView, subtypingInfo, rules);
}
@Override
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 2cf24ca..982ec3f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -3169,10 +3169,10 @@
return true;
}
- // Art 7 and up can fail access check for array clone calls from within interface methods.
+ // Art 7 and up to 15 can fail access check for array clone calls from within interface methods.
// See b/342802978.
public boolean canHaveArtArrayCloneFromInterfaceMethodBug() {
- return true;
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.BAKLAVA);
}
// The dalvik verifier will crash the program if there is a try catch block with an exception
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index acd31a4..bf62ee6 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -143,6 +143,15 @@
return min;
}
+ public static <T> boolean none(Iterable<T> iterable, Predicate<T> predicate) {
+ for (T element : iterable) {
+ if (predicate.test(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@SuppressWarnings("UnusedVariable")
public static <T> int size(Iterable<T> iterable) {
int result = 0;
diff --git a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
index 471dffb..3383085 100644
--- a/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
+++ b/src/test/examplesJava9/twr/twrcloseresourceduplication/TwrCloseResourceDuplicationTest.java
@@ -175,10 +175,12 @@
Reference.classFromTypeName(BAR), 1)
.getTypeName());
}
- classOutputWithSynthetics.add(
- SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
- Reference.classFromTypeName(BAR), 0)
- .getTypeName());
+ if (!parameters.corelibWithExecutorServiceImplementingAutoClosable()) {
+ classOutputWithSynthetics.add(
+ SyntheticItemsTestUtils.syntheticAutoCloseableDispatcherClass(
+ Reference.classFromTypeName(BAR), 0)
+ .getTypeName());
+ }
assertEquals(classOutputWithSynthetics, foundClasses);
}
});
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 535c25f..cc5e538 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -101,6 +101,10 @@
Version.V15_0_0,
// TODO(120402963) Triage.
ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
+ .put(
+ Version.V16_0_0,
+ // TODO(120402963) Triage.
+ ImmutableList.of("invokecustom-with-shrinking", "invokecustom2-with-shrinking"))
.put(Version.DEFAULT, ImmutableList.of())
.build();
diff --git a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
index 17e87f0..cd24893 100644
--- a/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunSmaliTestsTest.java
@@ -131,56 +131,38 @@
"sparse-switch", "regression/33846227"));
// Tests where the output has a different output than the original on certain VMs.
+ private static final Map<String, String> dalvikExpectations =
+ ImmutableMap.of(
+ "bad-codegen", "java.lang.NullPointerException\n",
+ "type-confusion-regression2", "java.lang.NullPointerException\n",
+ "type-confusion-regression3", "java.lang.NullPointerException\n",
+ "merge-blocks-regression", "java.lang.NullPointerException\n");
+ private static final Map<String, String> art13PlusExpectations =
+ ImmutableMap.of(
+ "bad-codegen",
+ StringUtils.lines(
+ "java.lang.NullPointerException: Attempt to read from field 'Test Test.a'"
+ + " on a null object reference in method 'Test TestObject.a(Test,"
+ + " Test, Test, Test, boolean)'"),
+ "type-confusion-regression3",
+ StringUtils.lines(
+ "java.lang.NullPointerException: Attempt to read from field 'byte[]"
+ + " Test.a' on a null object reference in method 'int"
+ + " TestObject.a(Test, Test)'"));
private static final Map<DexVm.Version, Map<String, String>> customProcessedOutputExpectation =
ImmutableMap.of(
Version.V4_4_4,
- ImmutableMap.of(
- "bad-codegen", "java.lang.NullPointerException\n",
- "type-confusion-regression2", "java.lang.NullPointerException\n",
- "type-confusion-regression3", "java.lang.NullPointerException\n",
- "merge-blocks-regression", "java.lang.NullPointerException\n"),
+ dalvikExpectations,
Version.V4_0_4,
- ImmutableMap.of(
- "bad-codegen", "java.lang.NullPointerException\n",
- "type-confusion-regression2", "java.lang.NullPointerException\n",
- "type-confusion-regression3", "java.lang.NullPointerException\n",
- "merge-blocks-regression", "java.lang.NullPointerException\n"),
+ dalvikExpectations,
Version.V13_0_0,
- ImmutableMap.of(
- "bad-codegen",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'Test Test.a'"
- + " on a null object reference in method 'Test TestObject.a(Test,"
- + " Test, Test, Test, boolean)'"),
- "type-confusion-regression3",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'byte[]"
- + " Test.a' on a null object reference in method 'int"
- + " TestObject.a(Test, Test)'")),
+ art13PlusExpectations,
Version.V14_0_0,
- ImmutableMap.of(
- "bad-codegen",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'Test Test.a'"
- + " on a null object reference in method 'Test TestObject.a(Test,"
- + " Test, Test, Test, boolean)'"),
- "type-confusion-regression3",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'byte[]"
- + " Test.a' on a null object reference in method 'int"
- + " TestObject.a(Test, Test)'")),
+ art13PlusExpectations,
Version.V15_0_0,
- ImmutableMap.of(
- "bad-codegen",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'Test Test.a'"
- + " on a null object reference in method 'Test TestObject.a(Test,"
- + " Test, Test, Test, boolean)'"),
- "type-confusion-regression3",
- StringUtils.lines(
- "java.lang.NullPointerException: Attempt to read from field 'byte[]"
- + " Test.a' on a null object reference in method 'int"
- + " TestObject.a(Test, Test)'")));
+ art13PlusExpectations,
+ Version.V16_0_0,
+ art13PlusExpectations);
// Tests where the input fails with a verification error on Dalvik instead of the
// expected runtime exception.
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 0b3baa3..b3ba2d3 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -322,6 +322,11 @@
ImmutableList.of(
// TODO(b/120402963): Triage.
"invokecustom", "invokecustom2"))
+ .put(
+ Version.V16_0_0,
+ ImmutableList.of(
+ // TODO(b/120402963): Triage.
+ "invokecustom", "invokecustom2"))
.put(DexVm.Version.DEFAULT, ImmutableList.of());
failsOn = builder.build();
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java
index f72e4ac..c9315a2 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningAboveMinApiTest.java
@@ -9,6 +9,7 @@
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;
@@ -47,6 +48,7 @@
.apply(setMockApiLevelForClass(ApiLevel23.class, AndroidApiLevel.M))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .enableInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
@@ -85,6 +87,12 @@
if (Version.getSdkInt(sdk) >= 22) {
FIELD = new ApiLevel22();
}
+ print();
+ }
+
+ // So that we don't eliminate the field as a result of redundant field load elimination.
+ @NeverInline
+ static void print() {
System.out.println(FIELD);
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java
index 1f218a2..13ea62a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelTypeStrengtheningTest.java
@@ -9,6 +9,7 @@
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;
@@ -50,6 +51,7 @@
.apply(setMockApiLevelForClass(ApiLevel23.class, AndroidApiLevel.M))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
+ .enableInliningAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
@@ -89,6 +91,12 @@
if (Version.getSdkInt(sdk) >= 23) {
FIELD = new ApiLevel23();
}
+ print();
+ }
+
+ // So that we don't eliminate the field as a result of redundant field load elimination.
+ @NeverInline
+ static void print() {
System.out.println(FIELD);
}
}
diff --git a/src/test/java/com/android/tools/r8/assistant/R8AssistentReflectiveInstrumentationTest.java b/src/test/java/com/android/tools/r8/assistant/R8AssistantReflectiveInstrumentationTest.java
similarity index 98%
rename from src/test/java/com/android/tools/r8/assistant/R8AssistentReflectiveInstrumentationTest.java
rename to src/test/java/com/android/tools/r8/assistant/R8AssistantReflectiveInstrumentationTest.java
index bcf3dc6..31fe370 100644
--- a/src/test/java/com/android/tools/r8/assistant/R8AssistentReflectiveInstrumentationTest.java
+++ b/src/test/java/com/android/tools/r8/assistant/R8AssistantReflectiveInstrumentationTest.java
@@ -29,7 +29,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class R8AssistentReflectiveInstrumentationTest extends TestBase {
+public class R8AssistantReflectiveInstrumentationTest extends TestBase {
@Parameter(0)
public TestParameters parameters;
@@ -134,7 +134,7 @@
}
String topOfStack = stack.getStackTraceElements()[0].toString();
String secondToTopOfStack = stack.getStackTraceElements()[1].toString();
- String sourceFile = "R8AssistentReflectiveInstrumentationTest";
+ String sourceFile = "R8AssistantReflectiveInstrumentationTest";
if (!topOfStack.contains("reflectOn(" + sourceFile)) {
throw new RuntimeException("reflectOn must be top of stack, got " + topOfStack);
}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckSubclassDiscardedEntirelyTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckSubclassDiscardedEntirelyTest.java
index 516998b..f14b254 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckSubclassDiscardedEntirelyTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckSubclassDiscardedEntirelyTest.java
@@ -121,11 +121,17 @@
public static void main(String[] args) {
Public.printPublic();
Public.printPublicAllowInlining();
- if (!shrink()) {
+ if (!callShrink()) {
Secret.printSecret();
}
}
+ // Outline call to shrink() to avoid that the call to printSecret() is removed in the first
+ // round of tree shaking.
+ static boolean callShrink() {
+ return shrink();
+ }
+
static boolean shrink() {
throw new RuntimeException();
}
diff --git a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
index 6778a46..5f45f46 100644
--- a/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExceptionTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.debug.classes.Exceptions;
import com.android.tools.r8.utils.AndroidApiLevel;
import org.junit.Test;
@@ -53,8 +54,10 @@
@Test
public void testStepOnCatchD8() throws Throwable {
parameters.assumeDexRuntime();
- // ART/Dalvik jumps to 'move-exception' which initializes the local variable with the pending
- // exception. Thus it is "attached" to the line declaring the exception in the catch handler.
+ // ART/Dalvik until ART 15 jumps to 'move-exception' which initializes the local variable with
+ // the pending exception. Thus it is "attached" to the line declaring the exception in the
+ // catch handler. From ART 16 the stepping is the same as for the JVM.
+ boolean art15OrOlder = parameters.getDexRuntimeVersion().isOlderThanOrEqual(Version.V15_0_0);
runDebugTest(
testForD8(parameters.getBackend())
.setMinApi(parameters)
@@ -66,9 +69,9 @@
run(),
checkLine(SOURCE_FILE, 11), // line of the method call throwing the exception
stepOver(),
- checkLine(SOURCE_FILE, 12), // line of the catch declaration
- checkNoLocal("e"),
- stepOver(),
+ applyIf(art15OrOlder, () -> checkLine(SOURCE_FILE, 12)), // line of the catch declaration
+ applyIf(art15OrOlder, () -> checkNoLocal("e")),
+ applyIf(art15OrOlder, this::stepOver),
checkLine(SOURCE_FILE, 13), // first line in the catch handler
checkLocal("e"),
run());
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfacesTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfacesTest.java
index 498db5c..df4566a 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfacesTest.java
@@ -23,14 +23,20 @@
private final List<String> EXPECTED_RESULT_WITHOUT_DESUGARING =
ImmutableList.of(
- WithAnonymousInner.class.getName(), "true", WithLocalInner.class.getName(), "true");
+ WithAnonymousInner.class.getName(),
+ WithAnonymousInner.class.getName(),
+ WithLocalInner.class.getName(),
+ WithLocalInner.class.getName());
private final List<String> EXPECTED_RESULT_WITH_DESUGARING =
ImmutableList.of(
WithAnonymousInner.class.getName() + getCompanionClassNameSuffix(),
- "true",
+ WithAnonymousInner.class.getName() + getCompanionClassNameSuffix(),
WithLocalInner.class.getName() + getCompanionClassNameSuffix(),
- "true");
+ WithLocalInner.class.getName() + getCompanionClassNameSuffix());
+
+ private final List<String> EXPECTED_RESULT_WITH_DESUGARING_R8 =
+ ImmutableList.of("null", "null", "null", "null");
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -78,7 +84,7 @@
.applyIf(
parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
- result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING));
+ result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_R8));
}
@Test
@@ -95,7 +101,7 @@
.applyIf(
parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING),
- result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING));
+ result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_R8));
}
interface WithAnonymousInner {
@@ -143,16 +149,14 @@
public static class TestClass {
public static void main(String[] args) throws Exception {
- System.out.println(new WithAnonymousInner() {}.defaultOuter().call().getName());
- System.out.println(
- new WithAnonymousInner() {}.defaultOuter()
- .call()
- .equals(WithAnonymousInner.staticOuter().call()));
- System.out.println(new WithLocalInner() {}.defaultOuter().call().getName());
- System.out.println(
- new WithLocalInner() {}.defaultOuter()
- .call()
- .equals(WithLocalInner.staticOuter().call()));
+ System.out.println(getName(new WithAnonymousInner() {}.defaultOuter().call()));
+ System.out.println(getName(WithAnonymousInner.staticOuter().call()));
+ System.out.println(getName(new WithLocalInner() {}.defaultOuter().call()));
+ System.out.println(getName(WithLocalInner.staticOuter().call()));
+ }
+
+ static String getName(Class<?> clazz) {
+ return clazz != null ? clazz.getName() : "null";
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildBackportTest.java
index c680498..9d0537d 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildBackportTest.java
@@ -4,14 +4,15 @@
package com.android.tools.r8.desugar.backports;
+import static com.android.tools.r8.utils.AndroidApiLevel.BAKLAVA;
+
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.SingleTestRunResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.desugar.backports.AndroidOsBuildVersionBackportTest.VERSION;
import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
@@ -41,13 +42,24 @@
super(parameters, ANDROID_OS_BUILD_TYPE_NAME, ImmutableList.of(getTestRunner()));
// android.os.Build getMajorSdkVersion and getMinorSdkVersion added on API 36.
- registerTarget(AndroidApiLevel.BAKLAVA, 4);
+ registerTarget(BAKLAVA, 4);
+ }
+
+ public void testD8() throws Exception {
+ testD8(
+ r ->
+ r.applyIf(
+ parameters.getApiLevel().isLessThan(BAKLAVA),
+ SingleTestRunResult::assertSuccess,
+ // No backporting from BAKLAVA, so android.os.Build not found (not in host ART
+ // runtime).
+ rr -> rr.assertFailureWithErrorThatThrows(NoClassDefFoundError.class)));
}
@Override
protected void configure(D8TestBuilder builder) throws Exception {
// Use BAKLAVA library to get API level for getMajorVersion() and getMinorVersion().
- builder.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA));
+ builder.addLibraryFiles(ToolHelper.getAndroidJar(BAKLAVA));
}
@Override
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionBackportTest.java
index 1838d12..3fc6119 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionBackportTest.java
@@ -57,6 +57,11 @@
.writeToZip());
}
+ @Override
+ protected String[] configureD8RunArguments() {
+ return new String[] {Integer.toString(parameters.getApiLevel().getLevel())};
+ }
+
private static byte[] getTransformedBuildVERSIONClassForRuntimeClasspath()
throws IOException, NoSuchFieldException {
return transformer(VERSION.class)
@@ -85,7 +90,8 @@
public static class TestRunner extends MiniAssert {
public static void main(String[] args) throws Exception {
- assertEquals(2100_000, VERSION.SDK_INT_FULL);
+ // No desugaring use the SDK_INT_FULL from the injected android.os.Build$VERSION class.
+ assertEquals(Integer.parseInt(args[0]) < 36 ? 2100_000 : -1, VERSION.SDK_INT_FULL);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionCodesFullBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionCodesFullBackportTest.java
index 9ecf156..4f89067 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionCodesFullBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AndroidOsBuildVersionCodesFullBackportTest.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.desugar.backports;
+import static com.android.tools.r8.utils.AndroidApiLevel.BAKLAVA;
+
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.TestParameters;
@@ -47,7 +49,7 @@
ImmutableList.of(getTestRunner()));
// android.os.Build$VERSION.SDK_INT_FULL is on API 36.
- registerFieldTarget(AndroidApiLevel.BAKLAVA, 36);
+ registerFieldTarget(BAKLAVA, 36);
}
@Override
@@ -59,8 +61,8 @@
@Override
// Add android.os.Build$VERSION_CODES_FULL class to runtime classpath.
protected void configure(D8TestCompileResult result) throws Exception {
- // Only add this for BAKLAVA or higher where this is not backported away.
- if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.BAKLAVA)) {
+ // Only add when this is not backported away.
+ if (parameters.frameworkHasBuildVersionCodesFull()) {
result.addRunClasspathFiles(
testForD8()
.addProgramClassFileData(
@@ -71,6 +73,11 @@
}
}
+ @Override
+ protected String[] configureD8RunArguments() {
+ return new String[] {Integer.toString(parameters.getApiLevel().getLevel())};
+ }
+
private static byte[] getTransformedBuildVERSION_CODES_FULLClassForRuntimeClasspath()
throws IOException {
ClassFileTransformer transformer =
@@ -94,14 +101,9 @@
ClassSubject versionCodesFullClass =
new CodeInspector(parameters.getDefaultAndroidJar())
.clazz("android.os.Build$VERSION_CODES_FULL");
- Assert.assertFalse(versionCodesFullClass.isPresent());
- // Update test when fully testing Android Baklava.
- Assert.assertFalse(parameters.getApiLevel().equals(AndroidApiLevel.BAKLAVA));
- if (parameters.getApiLevel().equals(AndroidApiLevel.V)) {
- versionCodesFullClass =
- new CodeInspector(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
- .clazz("android.os.Build$VERSION_CODES_FULL");
- Assert.assertTrue(versionCodesFullClass.isPresent());
+ Assert.assertEquals(
+ parameters.frameworkHasBuildVersionCodesFull(), versionCodesFullClass.isPresent());
+ if (parameters.frameworkHasBuildVersionCodesFull()) {
// Update test when more version codes full are added.
Assert.assertEquals(36, versionCodesFullClass.allFields().size());
// Copied from BackportedMethodRewriter.
@@ -163,82 +165,97 @@
public static class /*android.os.Build$*/ VERSION_CODES_FULL {
public static /* final */ int BASE = -1;
- public static /* final */ int BASE_1_1 = -1;
- public static /* final */ int CUPCAKE = -1;
- public static /* final */ int DONUT = -1;
- public static /* final */ int ECLAIR = -1;
- public static /* final */ int ECLAIR_0_1 = -1;
- public static /* final */ int ECLAIR_MR1 = -1;
- public static /* final */ int FROYO = -1;
- public static /* final */ int GINGERBREAD = -1;
- public static /* final */ int GINGERBREAD_MR1 = -1;
- public static /* final */ int HONEYCOMB = -1;
- public static /* final */ int HONEYCOMB_MR1 = -1;
- public static /* final */ int HONEYCOMB_MR2 = -1;
- public static /* final */ int ICE_CREAM_SANDWICH = -1;
- public static /* final */ int ICE_CREAM_SANDWICH_MR1 = -1;
- public static /* final */ int JELLY_BEAN = -1;
- public static /* final */ int JELLY_BEAN_MR1 = -1;
- public static /* final */ int JELLY_BEAN_MR2 = -1;
- public static /* final */ int KITKAT = -1;
- public static /* final */ int KITKAT_WATCH = -1;
- public static /* final */ int LOLLIPOP = -1;
- public static /* final */ int LOLLIPOP_MR1 = -1;
- public static /* final */ int M = -1;
- public static /* final */ int N = -1;
- public static /* final */ int N_MR1 = -1;
- public static /* final */ int O = -1;
- public static /* final */ int O_MR1 = -1;
- public static /* final */ int P = -1;
- public static /* final */ int Q = -1;
- public static /* final */ int R = -1;
- public static /* final */ int S = -1;
- public static /* final */ int S_V2 = -1;
- public static /* final */ int TIRAMISU = -1;
- public static /* final */ int UPSIDE_DOWN_CAKE = -1;
- public static /* final */ int VANILLA_ICE_CREAM = -1;
- public static /* final */ int BAKLAVA = -1;
+ public static /* final */ int BASE_1_1 = -2;
+ public static /* final */ int CUPCAKE = -3;
+ public static /* final */ int DONUT = -4;
+ public static /* final */ int ECLAIR = -5;
+ public static /* final */ int ECLAIR_0_1 = -6;
+ public static /* final */ int ECLAIR_MR1 = -7;
+ public static /* final */ int FROYO = -8;
+ public static /* final */ int GINGERBREAD = -9;
+ public static /* final */ int GINGERBREAD_MR1 = -10;
+ public static /* final */ int HONEYCOMB = -11;
+ public static /* final */ int HONEYCOMB_MR1 = -12;
+ public static /* final */ int HONEYCOMB_MR2 = -13;
+ public static /* final */ int ICE_CREAM_SANDWICH = -14;
+ public static /* final */ int ICE_CREAM_SANDWICH_MR1 = -15;
+ public static /* final */ int JELLY_BEAN = -16;
+ public static /* final */ int JELLY_BEAN_MR1 = -17;
+ public static /* final */ int JELLY_BEAN_MR2 = -18;
+ public static /* final */ int KITKAT = -19;
+ public static /* final */ int KITKAT_WATCH = -20;
+ public static /* final */ int LOLLIPOP = -21;
+ public static /* final */ int LOLLIPOP_MR1 = -22;
+ public static /* final */ int M = -23;
+ public static /* final */ int N = -24;
+ public static /* final */ int N_MR1 = -25;
+ public static /* final */ int O = -26;
+ public static /* final */ int O_MR1 = -27;
+ public static /* final */ int P = -28;
+ public static /* final */ int Q = -29;
+ public static /* final */ int R = -30;
+ public static /* final */ int S = -31;
+ public static /* final */ int S_V2 = -32;
+ public static /* final */ int TIRAMISU = -33;
+ public static /* final */ int UPSIDE_DOWN_CAKE = -34;
+ public static /* final */ int VANILLA_ICE_CREAM = -35;
+ public static /* final */ int BAKLAVA = -36;
}
public static class TestRunner extends MiniAssert {
public static void main(String[] args) throws Exception {
- assertEquals(100_000, VERSION_CODES_FULL.BASE);
- assertEquals(200_000, VERSION_CODES_FULL.BASE_1_1);
- assertEquals(300_000, VERSION_CODES_FULL.CUPCAKE);
- assertEquals(400_000, VERSION_CODES_FULL.DONUT);
- assertEquals(500_000, VERSION_CODES_FULL.ECLAIR);
- assertEquals(600_000, VERSION_CODES_FULL.ECLAIR_0_1);
- assertEquals(700_000, VERSION_CODES_FULL.ECLAIR_MR1);
- assertEquals(800_000, VERSION_CODES_FULL.FROYO);
- assertEquals(900_000, VERSION_CODES_FULL.GINGERBREAD);
- assertEquals(1000_000, VERSION_CODES_FULL.GINGERBREAD_MR1);
- assertEquals(1100_000, VERSION_CODES_FULL.HONEYCOMB);
- assertEquals(1200_000, VERSION_CODES_FULL.HONEYCOMB_MR1);
- assertEquals(1300_000, VERSION_CODES_FULL.HONEYCOMB_MR2);
- assertEquals(1400_000, VERSION_CODES_FULL.ICE_CREAM_SANDWICH);
- assertEquals(1500_000, VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1);
- assertEquals(1600_000, VERSION_CODES_FULL.JELLY_BEAN);
- assertEquals(1700_000, VERSION_CODES_FULL.JELLY_BEAN_MR1);
- assertEquals(1800_000, VERSION_CODES_FULL.JELLY_BEAN_MR2);
- assertEquals(1900_000, VERSION_CODES_FULL.KITKAT);
- assertEquals(2000_000, VERSION_CODES_FULL.KITKAT_WATCH);
- assertEquals(2100_000, VERSION_CODES_FULL.LOLLIPOP);
- assertEquals(2200_000, VERSION_CODES_FULL.LOLLIPOP_MR1);
- assertEquals(2300_000, VERSION_CODES_FULL.M);
- assertEquals(2400_000, VERSION_CODES_FULL.N);
- assertEquals(2500_000, VERSION_CODES_FULL.N_MR1);
- assertEquals(2600_000, VERSION_CODES_FULL.O);
- assertEquals(2700_000, VERSION_CODES_FULL.O_MR1);
- assertEquals(2800_000, VERSION_CODES_FULL.P);
- assertEquals(2900_000, VERSION_CODES_FULL.Q);
- assertEquals(3000_000, VERSION_CODES_FULL.R);
- assertEquals(3100_000, VERSION_CODES_FULL.S);
- assertEquals(3200_000, VERSION_CODES_FULL.S_V2);
- assertEquals(3300_000, VERSION_CODES_FULL.TIRAMISU);
- assertEquals(3400_000, VERSION_CODES_FULL.UPSIDE_DOWN_CAKE);
- assertEquals(3500_000, VERSION_CODES_FULL.VANILLA_ICE_CREAM);
- assertEquals(3600_000, VERSION_CODES_FULL.BAKLAVA);
+ int minSdk = Integer.parseInt(args[0]);
+ int sdkWithVersionCodesFull = 36;
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 100_000 : -1, VERSION_CODES_FULL.BASE);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 200_000 : -2, VERSION_CODES_FULL.BASE_1_1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 300_000 : -3, VERSION_CODES_FULL.CUPCAKE);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 400_000 : -4, VERSION_CODES_FULL.DONUT);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 500_000 : -5, VERSION_CODES_FULL.ECLAIR);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 600_000 : -6, VERSION_CODES_FULL.ECLAIR_0_1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 700_000 : -7, VERSION_CODES_FULL.ECLAIR_MR1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 800_000 : -8, VERSION_CODES_FULL.FROYO);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 900_000 : -9, VERSION_CODES_FULL.GINGERBREAD);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1000_000 : -10, VERSION_CODES_FULL.GINGERBREAD_MR1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 1100_000 : -11, VERSION_CODES_FULL.HONEYCOMB);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1200_000 : -12, VERSION_CODES_FULL.HONEYCOMB_MR1);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1300_000 : -13, VERSION_CODES_FULL.HONEYCOMB_MR2);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1400_000 : -14, VERSION_CODES_FULL.ICE_CREAM_SANDWICH);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1500_000 : -15,
+ VERSION_CODES_FULL.ICE_CREAM_SANDWICH_MR1);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1600_000 : -16, VERSION_CODES_FULL.JELLY_BEAN);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1700_000 : -17, VERSION_CODES_FULL.JELLY_BEAN_MR1);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 1800_000 : -18, VERSION_CODES_FULL.JELLY_BEAN_MR2);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 1900_000 : -19, VERSION_CODES_FULL.KITKAT);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 2000_000 : -20, VERSION_CODES_FULL.KITKAT_WATCH);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2100_000 : -21, VERSION_CODES_FULL.LOLLIPOP);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 2200_000 : -22, VERSION_CODES_FULL.LOLLIPOP_MR1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2300_000 : -23, VERSION_CODES_FULL.M);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2400_000 : -24, VERSION_CODES_FULL.N);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2500_000 : -25, VERSION_CODES_FULL.N_MR1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2600_000 : -26, VERSION_CODES_FULL.O);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2700_000 : -27, VERSION_CODES_FULL.O_MR1);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2800_000 : -28, VERSION_CODES_FULL.P);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 2900_000 : -29, VERSION_CODES_FULL.Q);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 3000_000 : -30, VERSION_CODES_FULL.R);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 3100_000 : -31, VERSION_CODES_FULL.S);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 3200_000 : -32, VERSION_CODES_FULL.S_V2);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 3300_000 : -33, VERSION_CODES_FULL.TIRAMISU);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 3400_000 : -34, VERSION_CODES_FULL.UPSIDE_DOWN_CAKE);
+ assertEquals(
+ minSdk < sdkWithVersionCodesFull ? 3500_000 : -35, VERSION_CODES_FULL.VANILLA_ICE_CREAM);
+ assertEquals(minSdk < sdkWithVersionCodesFull ? 3600_000 : -36, VERSION_CODES_FULL.BAKLAVA);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
index 50920ee..1d93acf 100644
--- a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
@@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.enclosingmethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -17,6 +19,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
interface A {
default int def() {
@@ -59,19 +62,14 @@
"42"
};
- private final String[] EXPECTED_CC =
- new String[] {
- "class " + A.class.getTypeName() + "$-CC",
- "public static int " + A.class.getTypeName() + "$-CC.a(" + A.class.getTypeName() + ")",
- "42"
- };
+ private final String[] EXPECTED_CC = new String[] {"null", "null", "42"};
private final String[] EXPECTED_NOUGAT =
new String[] {
"interface " + A.class.getTypeName(), "public int " + A.class.getTypeName() + ".def()", "42"
};
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@@ -140,15 +138,21 @@
private void inspect(CodeInspector inspector) {
ClassSubject cImplSubject = inspector.clazz(A.class.getTypeName() + "$1");
assertThat(cImplSubject, isPresent());
- ClassSubject enclosingClassSubject =
- parameters.canUseDefaultAndStaticInterfaceMethods()
- ? inspector.clazz(A.class.getTypeName())
- : inspector.clazz(A.class.getTypeName()).toCompanionClass();
- assertThat(enclosingClassSubject, isPresent());
- EnclosingMethodAttribute enclosingMethodAttribute =
- cImplSubject.getDexProgramClass().getEnclosingMethodAttribute();
- assertEquals(
- enclosingClassSubject.getDexProgramClass().getType(),
- enclosingMethodAttribute.getEnclosingMethod().getHolderType());
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject enclosingClassSubject = inspector.clazz(A.class.getTypeName());
+ assertThat(enclosingClassSubject, isPresent());
+ EnclosingMethodAttribute enclosingMethodAttribute =
+ cImplSubject.getDexProgramClass().getEnclosingMethodAttribute();
+ assertEquals(
+ enclosingClassSubject.getDexProgramClass().getType(),
+ enclosingMethodAttribute.getEnclosingMethod().getHolderType());
+ } else {
+ ClassSubject enclosingClassSubject =
+ inspector.clazz(A.class.getTypeName()).toCompanionClass();
+ assertThat(enclosingClassSubject, isAbsent());
+ EnclosingMethodAttribute enclosingMethodAttribute =
+ cImplSubject.getDexProgramClass().getEnclosingMethodAttribute();
+ assertNull(enclosingMethodAttribute);
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
index 4c09231..8f54230 100644
--- a/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
+++ b/src/test/java/com/android/tools/r8/graph/InvokeSuperTest.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V15_0_0;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertTrue;
@@ -132,7 +133,7 @@
result.assertFailureWithErrorThatThrows(VerifyError.class);
} else if (version == Version.V5_1_1 || version == Version.V6_0_1) {
result.assertFailure();
- } else if (version == Version.V15_0_0) {
+ } else if (version.isNewerThanOrEqual(V15_0_0)) {
result.assertFailureWithErrorThatThrows(VerifyError.class);
} else {
result.assertSuccessWithOutputThatMatches(containsString(NoSuchMethodError.class.getName()));
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
index 9686456..8d0d237 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreV10Test.java
@@ -5,6 +5,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.core.AnyOf.anyOf;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestBuilder;
import com.android.tools.r8.D8TestCompileResult;
@@ -78,6 +79,11 @@
throws Exception {
return testForD8()
.addProgramFiles(base.resolve(DEPLOY_JAR))
+ .addOptionsModification(
+ options -> {
+ assertTrue(options.getThrowBlockOutlinerOptions().enable);
+ options.getThrowBlockOutlinerOptions().enable = false;
+ })
.setMinApi(parameters)
.apply(configuration)
.compile();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B418719343Test.java b/src/test/java/com/android/tools/r8/ir/optimize/B418719343Test.java
index 00c112b..ab654fa 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/B418719343Test.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B418719343Test.java
@@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V14_0_0;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V15_0_0;
+
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -54,7 +56,7 @@
// TODO(b/418568424): Should succeed with expected output.
.applyIf(
parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V14_0_0),
+ && parameters.getDexRuntimeVersion().isInRangeInclusive(V14_0_0, V15_0_0),
rr -> rr.assertSuccessWithOutputLines("-21090195", "over"),
rr -> rr.assertSuccessWithOutputLines("-21130949", "over"));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index a3de438..3ad5193 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -322,7 +322,7 @@
assertEquals(Collections.emptyList(), synthesizedJavaLambdaClasses);
assertEquals(
- Collections.singleton("java.lang.StringBuilder"),
+ Collections.emptySet(),
collectTypes(clazz.uniqueMethodWithOriginalName("testStatelessLambda")));
assertEquals(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
index 900a30b..0fee177 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/SdkIntMemberValuePropagationTest.java
@@ -77,12 +77,11 @@
@Parameterized.Parameters(name = "{0}, rule: {1}")
public static List<Object[]> data() {
return buildParameters(
- getTestParameters().withAllRuntimesAndApiLevels().build(), Rule.values());
+ getTestParameters().withDexRuntimesAndAllApiLevels().build(), Rule.values());
}
@Test
public void testD8() throws Exception {
- parameters.assumeDexRuntime();
assumeTrue(rule.getRule().equals(""));
testForD8()
.addProgramClassFileData(getTransformedMainClass())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
index 2badee6..87e57c2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerConstArgumentTest.java
@@ -22,6 +22,8 @@
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
import org.junit.Test;
public class ThrowBlockOutlinerConstArgumentTest extends ThrowBlockOutlinerTestBase {
@@ -62,10 +64,12 @@
@Override
public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) {
- // Verify that we have two outlines with one and three users, respectively.
- assertEquals(2, outlines.size());
+ // Verify that we have two throw block outlines with one and three users, respectively.
+ List<ThrowBlockOutline> throwOutlines =
+ outlines.stream().filter(ThrowBlockOutline::isThrowOutline).collect(Collectors.toList());
+ assertEquals(2, throwOutlines.size());
IntSet numberOfUsers = new IntArraySet();
- for (ThrowBlockOutline outline : outlines) {
+ for (ThrowBlockOutline outline : throwOutlines) {
numberOfUsers.add(outline.getNumberOfUsers());
}
assertTrue(numberOfUsers.contains(1));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerFeatureTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerFeatureTest.java
new file mode 100644
index 0000000..b05fe38
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerFeatureTest.java
@@ -0,0 +1,132 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner.exceptions;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.R8TestCompileResultBase;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.Collection;
+import java.util.Iterator;
+import org.junit.Test;
+
+public class ThrowBlockOutlinerFeatureTest extends ThrowBlockOutlinerTestBase {
+
+ @Test
+ public void testR8() throws Exception {
+ assumeRelease();
+ R8TestCompileResultBase<?> compileResult =
+ testForR8(parameters)
+ .addProgramClasses(Main.class)
+ .addFeatureSplit(Feature1.class)
+ .addFeatureSplit(Feature2.class)
+ .addKeepMainRules(Main.class, Feature1.class, Feature2.class)
+ .apply(this::configure)
+ .noInliningOfSynthetics()
+ .compile()
+ .inspect(this::inspectOutput, this::inspectFeature1Output, this::inspectFeature2Output)
+ .addFeatureSplitsToRunClasspathFiles();
+ compileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(IllegalArgumentException.class);
+ compileResult
+ .run(parameters.getRuntime(), Feature1.class)
+ .assertFailureWithErrorThatThrows(IllegalArgumentException.class);
+ compileResult
+ .run(parameters.getRuntime(), Feature2.class)
+ .assertFailureWithErrorThatThrows(RuntimeException.class);
+ }
+
+ @Override
+ public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) {
+ // Verify that we have two outlines after merging.
+ Iterator<ThrowBlockOutline> iterator = outlines.iterator();
+ ThrowBlockOutline outlineFromBase = iterator.next();
+ ThrowBlockOutline outlineFromFeature2;
+ if (outlineFromBase.getChildren().isEmpty()) {
+ outlineFromFeature2 = outlineFromBase;
+ outlineFromBase = iterator.next();
+ } else {
+ outlineFromFeature2 = iterator.next();
+ }
+ assert !iterator.hasNext();
+
+ // Verify that the outline from base has a single child.
+ assertEquals(1, outlineFromBase.getChildren().size());
+ assertEquals(2, outlineFromBase.getNumberOfUsers());
+ assertThat(
+ outlineFromBase.getMaterializedOutlineMethod().getHolder().getTypeName(),
+ containsString(Main.class.getTypeName()));
+
+ // Verify that the outline from feature 2 has no children.
+ assertEquals(0, outlineFromFeature2.getChildren().size());
+ assertEquals(1, outlineFromFeature2.getNumberOfUsers());
+ assertThat(
+ outlineFromFeature2.getMaterializedOutlineMethod().getHolder().getTypeName(),
+ containsString(Feature2.class.getTypeName()));
+ }
+
+ private void inspectOutput(CodeInspector inspector) {
+ assertEquals(2, inspector.allClasses().size());
+ assertThat(inspector.clazz(Main.class), isPresent());
+
+ ClassSubject outlineClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(Main.class, 0));
+ assertThat(outlineClassSubject, isPresent());
+ assertEquals(1, outlineClassSubject.allMethods().size());
+ }
+
+ private void inspectFeature1Output(CodeInspector inspector) {
+ assertEquals(1, inspector.allClasses().size());
+ assertThat(inspector.clazz(Feature1.class), isPresent());
+ }
+
+ private void inspectFeature2Output(CodeInspector inspector) {
+ assertEquals(2, inspector.allClasses().size());
+ assertThat(inspector.clazz(Feature2.class), isPresent());
+
+ ClassSubject outlineClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(Feature2.class, 0));
+ assertThat(outlineClassSubject, isPresent());
+ assertEquals(1, outlineClassSubject.allMethods().size());
+ }
+
+ @Override
+ public boolean shouldOutline(ThrowBlockOutline outline) {
+ return true;
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ static class Feature1 {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ throw new IllegalArgumentException();
+ }
+ }
+ }
+
+ static class Feature2 {
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ throw new RuntimeException();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerBooleanTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerBooleanTypeTest.java
new file mode 100644
index 0000000..be655a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerBooleanTypeTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner.stringbuilders;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutline;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Collection;
+import org.junit.Test;
+
+public class StringBuilderOutlinerBooleanTypeTest extends ThrowBlockOutlinerTestBase {
+
+ @Test
+ public void testD8() throws Exception {
+ runTest(testForD8(parameters));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeRelease();
+ runTest(testForR8(parameters).addKeepAllClassesRule());
+ }
+
+ private void runTest(
+ TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+ throws Exception {
+ testBuilder
+ .addInnerClasses(getClass())
+ .apply(this::configure)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("true");
+ }
+
+ @Override
+ public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) {
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
+ assertTrue(outlines.isEmpty());
+ } else {
+ assertEquals(1, outlines.size());
+ ThrowBlockOutline outline = outlines.iterator().next();
+ assertTrue(outline.getProto().getParameter(0).isIntType());
+ }
+ }
+
+ @Override
+ public boolean shouldOutline(ThrowBlockOutline outline) {
+ return true;
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(toString(true));
+ }
+
+ static String toString(boolean b) {
+ return new StringBuilder().append(b).toString();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerCharTypeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerCharTypeTest.java
new file mode 100644
index 0000000..08c02a8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerCharTypeTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner.stringbuilders;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutline;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerTestBase;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.Collection;
+import org.junit.Test;
+
+public class StringBuilderOutlinerCharTypeTest extends ThrowBlockOutlinerTestBase {
+
+ @Test
+ public void testD8() throws Exception {
+ runTest(testForD8(parameters));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeRelease();
+ runTest(testForR8(parameters).addKeepAllClassesRule());
+ }
+
+ private void runTest(
+ TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+ throws Exception {
+ testBuilder
+ .addInnerClasses(getClass())
+ .apply(this::configure)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("a");
+ }
+
+ @Override
+ public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) {
+ if (parameters.getApiLevel().isLessThan(AndroidApiLevel.L)) {
+ assertTrue(outlines.isEmpty());
+ } else {
+ assertEquals(1, outlines.size());
+ ThrowBlockOutline outline = outlines.iterator().next();
+ assertTrue(outline.getProto().getParameter(0).isIntType());
+ }
+ }
+
+ @Override
+ public boolean shouldOutline(ThrowBlockOutline outline) {
+ return true;
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(toString('a'));
+ }
+
+ static String toString(char c) {
+ return new StringBuilder().append(c).toString();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerTest.java
new file mode 100644
index 0000000..b64c5ba
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/stringbuilders/StringBuilderOutlinerTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize.outliner.stringbuilders;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutline;
+import com.android.tools.r8.ir.optimize.outliner.exceptions.ThrowBlockOutlinerTestBase;
+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.util.Collection;
+import org.junit.Test;
+
+public class StringBuilderOutlinerTest extends ThrowBlockOutlinerTestBase {
+
+ @Test
+ public void testD8() throws Exception {
+ runTest(testForD8(parameters));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeRelease();
+ runTest(testForR8(parameters).addKeepMainRule(Main.class));
+ }
+
+ private void runTest(
+ TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder)
+ throws Exception {
+ testBuilder
+ .addInnerClasses(getClass())
+ .apply(this::configure)
+ .compile()
+ .inspect(this::inspectOutput)
+ .run(parameters.getRuntime(), Main.class, "Hel", "lo", ", world", "!")
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Override
+ public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) {
+ assertEquals(1, outlines.size());
+ ThrowBlockOutline outline = outlines.iterator().next();
+ assertEquals(2, outline.getNumberOfUsers());
+ }
+
+ private void inspectOutput(CodeInspector inspector) {
+ MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod();
+ assertTrue(mainMethod.streamInstructions().noneMatch(InstructionSubject::isNewInstance));
+ }
+
+ @Override
+ public boolean shouldOutline(ThrowBlockOutline outline) {
+ return true;
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ String s1 = new StringBuilder().append(args[0]).append(args[1]).toString();
+ String s2 = new StringBuilder().append(args[2]).append(args[3]).toString();
+ System.out.print(s1);
+ System.out.println(s2);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
index 8132920..b23e434 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -158,6 +158,7 @@
private void test(
CodeInspector codeInspector,
+ boolean isR8,
boolean expectCallPresent,
int expectedGetClassCount,
int expectedConstClassCount) {
@@ -177,7 +178,7 @@
MethodSubject getMainClass = getterClass.uniqueMethodWithOriginalName("getMainClass");
assertThat(getMainClass, isPresent());
// Because of nullable argument, getClass() should remain.
- assertEquals(1, countGetClass(getMainClass));
+ assertEquals(mode == CompilationMode.RELEASE && isR8 ? 0 : 1, countGetClass(getMainClass));
assertEquals(0, countConstClass(getMainClass));
MethodSubject call = getterClass.method("java.lang.Class", "call", ImmutableList.of());
@@ -200,7 +201,7 @@
.setMinApi(parameters)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT)
- .inspect(inspector -> test(inspector, true, 6, 0));
+ .inspect(inspector -> test(inspector, false, true, 6, 0));
}
@Test
@@ -208,19 +209,23 @@
boolean isRelease = mode == CompilationMode.RELEASE;
boolean expectCallPresent = !isRelease;
int expectedGetClassCount = isRelease ? 0 : 5;
- int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 8 : 6) : 1;
- testForR8(parameters.getBackend())
+ int expectedConstClassCount = isRelease ? (parameters.isCfRuntime() ? 9 : 6) : 1;
+ testForR8(parameters)
.setMode(mode)
.addInnerClasses(GetClassTest.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.addKeepMainRule(MAIN)
.addDontObfuscate()
- .setMinApi(parameters)
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(JAVA_OUTPUT)
.inspect(
inspector ->
- test(inspector, expectCallPresent, expectedGetClassCount, expectedConstClassCount));
+ test(
+ inspector,
+ true,
+ expectCallPresent,
+ expectedGetClassCount,
+ expectedConstClassCount));
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 9646cc8..d5965e5 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -6,12 +6,10 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_3_72;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_2_0_20;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_2_1_10;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -60,7 +58,7 @@
}
@Test
- public void testJStyleLambdas() throws Exception {
+ public void testJStyleLambdasNoClassInlining() throws Exception {
// SAM interfaces lambdas are implemented by invoke dynamic in kotlin 1.5 unlike 1.4 where a
// class is generated for each. In CF we leave invokeDynamic but for DEX we desugar the classes
// and merge them.
@@ -87,41 +85,40 @@
"class_inliner_lambda_j_style.MainKt$testStateful3$1");
} else if (testParameters.isDexRuntime()) {
Set<Set<DexType>> mergeGroups = inspector.getMergeGroups();
- assertEquals(1, mergeGroups.size());
- inspector.assertIsCompleteMergeGroup(
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda0",
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2",
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda4",
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda3",
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda5",
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda6");
+ assertEquals(2, mergeGroups.size());
+ inspector
+ .assertIsCompleteMergeGroup(
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda1",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda3",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda4",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda5",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda6",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda7")
+ .assertIsCompleteMergeGroup(
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2",
+ "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticThrowBlockOutline0");
}
inspector.assertNoOtherClassesMerged();
})
.noClassInlining())
.inspect(
inspector -> {
+ if (kotlinc.getCompilerVersion().isLessThan(KOTLINC_2_0_20)) {
+ // Do not inspect output for older Kotlin versions.
+ return;
+ }
if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) {
assertEquals(5, inspector.allClasses().size());
- } else if (!hasKotlinCGeneratedLambdaClasses) {
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda1"),
- isPresent());
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2"),
- isAbsent());
} else {
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
- isAbsent());
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$1"),
- isPresent());
+ assertEquals(7, inspector.allClasses().size());
}
});
+ }
+ @Test
+ public void testJStyleLambdas() throws Exception {
+ boolean hasKotlinCGeneratedLambdaClasses = kotlinParameters.isOlderThan(KOTLINC_1_5_0);
+ String mainClassName = "class_inliner_lambda_j_style.MainKt";
runTest(
"class_inliner_lambda_j_style",
mainClassName,
@@ -134,41 +131,20 @@
.addNoVerticalClassMergingRule("class_inliner_lambda_j_style.SamIface"))
.inspect(
inspector -> {
- if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) {
- assertEquals(5, inspector.allClasses().size());
+ if (kotlinc.getCompilerVersion().isLessThan(KOTLINC_2_0_20)) {
+ // Do not inspect output for older Kotlin versions.
return;
}
- if (!hasKotlinCGeneratedLambdaClasses) {
- // Kotlin 1.6.20 and later do not create intrinsics.stringPlus for two argument
- // string concatination. That allow R8's stringbuilder optimization to reduce the
- // size of strings and therefore inline the synthetic lambda.
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda1"),
- isAbsentIf(kotlinParameters.isNewerThan(KOTLINC_1_6_0)));
+ if (testParameters.isCfRuntime() && !hasKotlinCGeneratedLambdaClasses) {
+ assertEquals(5, inspector.allClasses().size());
} else {
- assertThat(
- inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateless$1"),
- isAbsent());
- }
-
- if (hasKotlinCGeneratedLambdaClasses) {
- assertThat(
- testParameters.isCfRuntime()
- ? inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful2$1")
- : inspector.clazz("class_inliner_lambda_j_style.MainKt$testStateful$2"),
- isPresent());
- } else {
- assertThat(
- inspector.clazz(
- "class_inliner_lambda_j_style.MainKt$$ExternalSyntheticLambda2"),
- isAbsent());
+ assertEquals(7, inspector.allClasses().size());
}
});
}
@Test
- public void testKStyleLambdas() throws Exception {
+ public void testKStyleLambdasNoClassInlining() throws Exception {
String mainClassName = "class_inliner_lambda_k_style.MainKt";
runTest(
"class_inliner_lambda_k_style",
@@ -264,7 +240,11 @@
isPresent());
}
});
+ }
+ @Test
+ public void testKStyleLambdas() throws Exception {
+ String mainClassName = "class_inliner_lambda_k_style.MainKt";
runTest(
"class_inliner_lambda_k_style",
mainClassName,
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
index c9467fa..cdb615d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingCapturesKotlinStyleTest.java
@@ -13,10 +13,12 @@
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -81,7 +83,21 @@
.addProgramFiles(getProgramFiles())
.addKeepMainRule(getMainClassName())
.addHorizontallyMergedClassesInspector(
- HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ inspector -> {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)
+ && kotlinParameters.getLambdaGeneration().isInvokeDynamic()) {
+ inspector
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(
+ getMainClassReference(), 0),
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(
+ getMainClassReference(), 1))
+ .assertNoOtherClassesMerged();
+ } else {
+ inspector.assertNoClassesMerged();
+ }
+ })
.allowAccessModification(allowAccessModification)
.setMinApi(parameters)
.compile()
@@ -138,6 +154,10 @@
return getTestName() + ".MainKt";
}
+ private ClassReference getMainClassReference() {
+ return Reference.classFromTypeName(getMainClassName());
+ }
+
private List<Path> getProgramFiles() {
Path kotlinJarFile =
getCompileMemoizer(getKotlinFilesInResource(getTestName()), getTestName())
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
index da524c4..56178bd 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingKeepAttributesKotlinStyleTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.kotlin.lambda;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
import static com.android.tools.r8.shaking.ProguardKeepAttributes.ENCLOSING_METHOD;
import static com.android.tools.r8.shaking.ProguardKeepAttributes.INNER_CLASSES;
import static com.android.tools.r8.shaking.ProguardKeepAttributes.SIGNATURE;
@@ -189,21 +188,6 @@
ClassReference mainKt = Reference.classFromTypeName(getMainClassName());
List<ClassReference> mergeGroup =
ImmutableList.of(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 9),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 10),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 11),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 12),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 13),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 14),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 19),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 18),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 20),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 21),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 22),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 23));
- List<ClassReference> otherMergeGroup =
- ImmutableList.of(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 0),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 1),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 2),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 3),
@@ -212,24 +196,24 @@
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 6),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 7),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 8),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 9),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 10),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 11),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 12),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 13),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 14),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 15),
SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 16),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 17));
- inspector
- .applyIf(
- kotlinc.getCompilerVersion().isGreaterThanOrEqualTo(KOTLINC_1_9_21)
- && parameters.isDexRuntime()
- && lambdaGeneration.isInvokeDynamic(),
- i ->
- i.assertIsCompleteMergeGroup(
- ImmutableList.<ClassReference>builder()
- .addAll(mergeGroup)
- .addAll(otherMergeGroup)
- .build()),
- i ->
- i.assertIsCompleteMergeGroup(mergeGroup)
- .assertIsCompleteMergeGroup(otherMergeGroup))
- .assertNoOtherClassesMerged();
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 17),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 18),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 19),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 20),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 21),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 22),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 23),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainKt, 24),
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(mainKt, 0));
+ inspector.assertIsCompleteMergeGroup(mergeGroup).assertNoOtherClassesMerged();
}
}
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
index ae02a7f..84575f1 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTrivialJavaStyleTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.kotlin.lambda;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_5_0;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_2_0_20;
import static com.android.tools.r8.utils.PredicateUtils.not;
import static junit.framework.TestCase.assertEquals;
import static org.junit.Assume.assumeFalse;
@@ -18,6 +18,7 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -98,139 +99,84 @@
private void inspect(
HorizontallyMergedClassesInspector inspector, KotlinLambdasInInput lambdasInInput) {
- if (hasKotlinCGeneratedLambdaClasses()
- && kotlinParameters.getCompilerVersion().isLessThan(KOTLINC_1_5_0)) {
- // Don't check exactly how J-style Kotlin lambdas are merged for kotlinc before 1.5.0.
- assertEquals(
- parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods() ? 3 : 10,
- inspector.getMergeGroups().size());
- return;
- }
-
- if (!allowAccessModification && hasKotlinCGeneratedLambdaClasses()) {
- // Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification.
- Set<ClassReference> unmergedLambdas =
- ImmutableSet.of(
- lambdasInInput.getJStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInner1$1"),
- lambdasInInput.getJStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInner1$2"),
- lambdasInInput.getJStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInner1$3"),
- lambdasInInput.getJStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInner1$4"),
- lambdasInInput.getJStyleLambdaReferenceFromTypeName(
- getTestName(), "inner.InnerKt$testInner1$5"));
- inspector
- .assertIsCompleteMergeGroup(
- lambdasInInput.getJStyleLambdas().stream()
- .filter(not(unmergedLambdas::contains))
- .collect(Collectors.toList()))
- .assertClassReferencesNotMerged(unmergedLambdas);
+ if (kotlinParameters.getCompilerVersion().isLessThan(KOTLINC_2_0_20)) {
+ // Don't inspect the output for Kotlin 1.9 and older.
return;
}
if (hasKotlinCGeneratedLambdaClasses()) {
- // All J-style Kotlin lambdas are merged with -allowaccessmodification or because they are
- // generated by R8.
- inspector.assertIsCompleteMergeGroup(lambdasInInput.getJStyleLambdas());
- return;
- }
-
- ClassReference mainClassReference = Reference.classFromTypeName(getTestName() + ".MainKt");
- ClassReference innerClassReference =
- Reference.classFromTypeName(getTestName() + ".inner.InnerKt");
- if (parameters.isCfRuntime()) {
- inspector.assertClassReferencesNotMerged(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 3));
- for (int id = 0; id < 30; id++) {
- inspector.assertClassReferencesNotMerged(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, id));
+ if (allowAccessModification) {
+ // All J-style Kotlin lambdas are merged with -allowaccessmodification or because they are
+ // generated by R8.
+ inspector.assertIsCompleteMergeGroup(lambdasInInput.getJStyleLambdas());
+ } else {
+ // Only a subset of all J-style Kotlin lambdas are merged without -allowaccessmodification.
+ Set<ClassReference> unmergedLambdas =
+ ImmutableSet.of(
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$1"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$2"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$3"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$4"),
+ lambdasInInput.getJStyleLambdaReferenceFromTypeName(
+ getTestName(), "inner.InnerKt$testInner1$5"));
+ inspector
+ .assertIsCompleteMergeGroup(
+ lambdasInInput.getJStyleLambdas().stream()
+ .filter(not(unmergedLambdas::contains))
+ .collect(Collectors.toList()))
+ .assertClassReferencesNotMerged(unmergedLambdas);
}
- } else if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- inspector
- .applyIf(
- kotlinc.getCompilerVersion().isLessThanOrEqualTo(KOTLINC_1_9_21),
- i -> {
- i.assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 1))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 3))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 4),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 5));
- for (int id = 6; id < 30; id++) {
- inspector.assertClassReferencesNotMerged(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, id));
- }
- },
- i -> {
- i.assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 1),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 3),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1));
- for (int id = 4; id < 30; id++) {
- inspector.assertClassReferencesNotMerged(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, id));
- }
- })
- .assertClassReferencesNotMerged(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 3));
} else {
- inspector
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 1),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 3),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 4))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 9),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 10),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 11),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 12),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 21),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 22),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 23),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 24),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 25),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 26),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 27),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 28))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 13),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 14))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 15),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 16))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 17),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 18))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 3),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 19),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 20))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 5),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 6))
- .assertIsCompleteMergeGroup(
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 7),
- SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 8));
+ ClassReference mainClassReference = Reference.classFromTypeName(getTestName() + ".MainKt");
+ ClassReference innerClassReference =
+ Reference.classFromTypeName(getTestName() + ".inner.InnerKt");
+ if (parameters.isCfRuntime()) {
+ inspector.assertClassReferencesNotMerged(
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 3));
+ for (int id = 0; id < 30; id++) {
+ inspector.assertClassReferencesNotMerged(
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, id));
+ }
+ } else if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ inspector
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 3),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 4),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 0))
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 7),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 8),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2))
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 5),
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, 6),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 1))
+ .assertIsCompleteMergeGroup(
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(mainClassReference, 0),
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(mainClassReference, 1),
+ SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(mainClassReference, 2));
+ for (int id = 4; id < 30; id++) {
+ inspector.assertClassReferencesNotMerged(
+ SyntheticItemsTestUtils.syntheticLambdaClass(mainClassReference, id));
+ }
+ inspector.assertClassReferencesNotMerged(
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 2),
+ SyntheticItemsTestUtils.syntheticLambdaClass(innerClassReference, 3));
+ } else {
+ assertEquals(10, inspector.getMergeGroups().size());
+ assertEquals(
+ parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(AndroidApiLevel.L)
+ ? 28
+ : 34,
+ inspector.getSources().size());
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
index 3521a29..8a694dc 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
@@ -3,10 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming.applymapping.desugar;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -14,7 +17,6 @@
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -76,17 +78,81 @@
.addProgramClasses(LibraryInterface.class)
.addKeepClassAndMembersRules(LibraryInterface.class)
.setMinApi(parameters)
- .compile();
- CodeInspector inspector = libraryResult.inspector();
- assertThat(inspector.clazz(LibraryInterface.class), isPresent());
- assertThat(inspector.method(LibraryInterface.class.getMethod("foo")), isPresent());
- if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
- ClassSubject companion =
- inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
- // Check that we included the companion class and method.
- assertThat(companion, isPresent());
- assertEquals(1, companion.allMethods().size());
- }
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject libraryClass = inspector.clazz(LibraryInterface.class);
+ assertThat(libraryClass, isPresent());
+ assertThat(libraryClass.uniqueMethodWithOriginalName("foo"), isPresent());
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ // We don't expect the companion class to be present since -keep does not apply
+ // to synthetics.
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(
+ LibraryInterface.class));
+ assertThat(companion, isAbsent());
+ }
+ });
+
+ testForR8(parameters.getBackend())
+ .addDontShrink()
+ .addProgramClasses(ProgramClass.class)
+ .addClasspathClasses(LibraryInterface.class)
+ .addApplyMapping(libraryResult.getProguardMap())
+ .addKeepMainRule(ProgramClass.class)
+ .setMinApi(parameters)
+ .compile()
+ .addRunClasspathFiles(libraryResult.writeToZip())
+ .run(parameters.getRuntime(), ProgramClass.class)
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ rr -> rr.assertSuccessWithOutput(EXPECTED),
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ rr -> rr.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ rr -> rr.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
+ }
+
+ @Test
+ public void testDesugaredLibraryLinkedWithProgram() throws Throwable {
+ parameters.assumeDexRuntime("Desugaring not required when compiling to CF");
+
+ D8TestCompileResult libraryDesugarResult =
+ testForD8(Backend.CF)
+ .addProgramClasses(LibraryInterface.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
+ assertThat(
+ companion, isAbsentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
+ });
+
+ R8TestCompileResult libraryResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(libraryDesugarResult.writeToZip())
+ .addKeepClassAndMembersRulesWithAllowObfuscation("*")
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject libraryClass = inspector.clazz(LibraryInterface.class);
+ assertThat(libraryClass, isPresent());
+ assertThat(libraryClass.uniqueMethodWithOriginalName("foo"), isPresent());
+ if (!parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(
+ LibraryInterface.class));
+ assertThat(companion, isPresentAndRenamed());
+ assertThat(
+ companion.uniqueMethodWithOriginalName("$default$foo"),
+ isPresentAndRenamed());
+ }
+ });
testForR8(parameters.getBackend())
.addDontShrink()
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
index 45d0a3b..5f6aad0 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
@@ -4,18 +4,20 @@
package com.android.tools.r8.naming.applymapping.desugar;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.D8TestCompileResult;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.naming.applymapping.desugar.DefaultInterfaceMethodTest.LibraryInterface;
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -78,21 +80,84 @@
.addProgramClasses(LibraryInterface.class)
.addKeepClassAndMembersRules(LibraryInterface.class)
.setMinApi(parameters)
- .compile();
- CodeInspector inspector = libraryResult.inspector();
- ClassSubject libraryInterface = inspector.clazz(LibraryInterface.class);
- assertThat(libraryInterface, isPresent());
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isPresent());
- } else {
- // Desugaring must remove the static on the interface.
- assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isAbsent());
- // Check that we included the companion class and method.
- ClassSubject companion =
- inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
- assertThat(companion, isPresent());
- assertEquals(1, companion.allMethods().size());
- }
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject libraryClass = inspector.clazz(LibraryInterface.class);
+ assertThat(libraryClass, isPresent());
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(libraryClass.uniqueMethodWithOriginalName("foo"), isPresent());
+ } else {
+ // We don't expect the companion class to be present since -keep does not apply
+ // to synthetics.
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(
+ LibraryInterface.class));
+ assertThat(companion, isAbsent());
+ assertThat(libraryClass.uniqueMethodWithOriginalName("foo"), isAbsent());
+ }
+ });
+
+ testForR8(parameters.getBackend())
+ .addDontShrink()
+ .addProgramClasses(ProgramClass.class)
+ .addClasspathClasses(LibraryInterface.class)
+ .addApplyMapping(libraryResult.getProguardMap())
+ .addKeepMainRule(ProgramClass.class)
+ .setMinApi(parameters)
+ .compile()
+ .addRunClasspathFiles(libraryResult.writeToZip())
+ .run(parameters.getRuntime(), ProgramClass.class)
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ rr -> rr.assertSuccessWithOutput(EXPECTED),
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ rr -> rr.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ rr -> rr.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
+ }
+
+ @Test
+ public void testDesugaredLibraryLinkedWithProgram() throws Throwable {
+ parameters.assumeDexRuntime("Desugaring not required when compiling to CF");
+
+ D8TestCompileResult libraryDesugarResult =
+ testForD8(Backend.CF)
+ .addProgramClasses(LibraryInterface.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class));
+ assertThat(
+ companion, isAbsentIf(parameters.canUseDefaultAndStaticInterfaceMethods()));
+ });
+
+ R8TestCompileResult libraryResult =
+ testForR8(parameters.getBackend())
+ .addProgramFiles(libraryDesugarResult.writeToZip())
+ .addKeepClassAndMembersRulesWithAllowObfuscation("*")
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject libraryClass = inspector.clazz(LibraryInterface.class);
+ assertThat(libraryClass, isPresentAndRenamed());
+ if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(
+ libraryClass.uniqueMethodWithOriginalName("foo"), isPresentAndRenamed());
+ } else {
+ ClassSubject companion =
+ inspector.clazz(
+ SyntheticItemsTestUtils.syntheticCompanionClass(
+ LibraryInterface.class));
+ assertThat(companion, isPresentAndRenamed());
+ assertThat(
+ companion.uniqueMethodWithOriginalName("foo"), isPresentAndRenamed());
+ }
+ });
testForR8(parameters.getBackend())
.addDontShrink()
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassInliningTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassInliningTest.java
new file mode 100644
index 0000000..e270d8c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassInliningTest.java
@@ -0,0 +1,67 @@
+// Copyright (c) 2025, 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.partial;
+
+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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PartialCompilationLambdaClassInliningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ parameters.assumeCanUseR8Partial();
+ testForR8Partial(parameters)
+ .addR8IncludedClasses(Main.class, I.class)
+ .addR8IncludedClasses(false, NeverInline.class)
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ // The call to run() has been inlined so that only the main() method remains.
+ ClassSubject mainClass = inspector.clazz(Main.class);
+ assertEquals(1, mainClass.allMethods().size());
+ // I should be removed and there should be no lambda class.
+ assertEquals(1, inspector.allClasses().size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ run(() -> System.out.println("Hello, world!"));
+ }
+
+ @NeverInline
+ static void run(I i) {
+ i.m();
+ }
+ }
+
+ interface I {
+
+ void m();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassMergingTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassMergingTest.java
new file mode 100644
index 0000000..b31e356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationLambdaClassMergingTest.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2025, 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.partial;
+
+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.synthesis.SyntheticItemsTestUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PartialCompilationLambdaClassMergingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ parameters.assumeCanUseR8Partial();
+ testForR8Partial(parameters)
+ .addR8IncludedClasses(Main.class, I.class)
+ .addR8IncludedClasses(false, NeverInline.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .compile()
+ .inspect(
+ inspector -> {
+ // The output has three classes: Main, I and a lambda.
+ assertEquals(3, inspector.allClasses().size());
+ // The output has a single synthetic lambda due to class merging.
+ assertEquals(
+ 1,
+ inspector.allClasses().stream()
+ .filter(
+ clazz ->
+ SyntheticItemsTestUtils.isExternalLambda(
+ clazz.getOriginalReference()))
+ .count());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ call(() -> System.out.print("Hello"));
+ call(() -> System.out.println(", world!"));
+ }
+
+ static void call(I i) {
+ i.f();
+ }
+ }
+
+ interface I {
+
+ void f();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticKeepTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticKeepTest.java
new file mode 100644
index 0000000..960cdbb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticKeepTest.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2025, 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.partial;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.errors.UnusedProguardKeepRuleDiagnostic;
+import com.android.tools.r8.partial.PartialCompilationSyntheticKeepTest.Main.NestMember;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PartialCompilationSyntheticKeepTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-keepclassmembers class " + NestMember.class.getTypeName() + " { synthetic *; }")
+ .allowUnusedProguardConfigurationRules()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertInfosMatch(
+ diagnosticType(UnusedProguardKeepRuleDiagnostic.class)))
+ .inspect(inspector -> assertEquals(1, inspector.allClasses().size()))
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isDexRuntime() || parameters.getCfRuntime().isNewerThan(CfVm.JDK9),
+ rr -> rr.assertSuccessWithOutputLines("Hello, world!"));
+ }
+
+ @Test
+ public void testR8Partial() throws Exception {
+ testForR8Partial(parameters)
+ .addProgramClassFileData(getProgramClassFileData())
+ .addR8IncludedClasses(false, Main.class, NestMember.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules(
+ "-keepclassmembers class " + NestMember.class.getTypeName() + " { synthetic *; }")
+ .compile()
+ // TODO(b/394488245): Should be 1.
+ .inspect(inspector -> assertEquals(2, inspector.allClasses().size()))
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ private static List<byte[]> getProgramClassFileData() throws Exception {
+ return ImmutableList.of(
+ transformer(Main.class).setNest(Main.class, NestMember.class).transform(),
+ transformer(NestMember.class)
+ .setNest(Main.class, NestMember.class)
+ .setAccessFlags(
+ NestMember.class.getDeclaredMethod("greet"),
+ flags -> {
+ flags.unsetPublic();
+ flags.setPrivate();
+ })
+ .transform());
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ NestMember.greet();
+ }
+
+ static class NestMember {
+
+ public static void greet() {
+ System.out.println("Hello, world!");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticTreeShakingTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticTreeShakingTest.java
new file mode 100644
index 0000000..ffe845e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationSyntheticTreeShakingTest.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2025, 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.partial;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PartialCompilationSyntheticTreeShakingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ parameters.assumeCanUseR8Partial();
+ testForR8Partial(parameters)
+ .addR8IncludedClasses(Main.class)
+ .addKeepMainRule(Main.class)
+ .compile()
+ .inspect(
+ inspector -> {
+ // The main() method has been optimized into being empty.
+ MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod();
+ assertThat(mainMethod, isPresent());
+ assertTrue(mainMethod.getMethod().getCode().isEmptyVoidMethod());
+ // There should be no lambda class.
+ assertEquals(1, inspector.allClasses().size());
+ });
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ if (alwaysFalse()) {
+ Runnable r = () -> System.out.println("Hello, world!");
+ System.out.println(r);
+ }
+ }
+
+ static boolean alwaysFalse() {
+ return false;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/B341476044Test.java b/src/test/java/com/android/tools/r8/regress/B341476044Test.java
index 89d48ef..f495b56 100644
--- a/src/test/java/com/android/tools/r8/regress/B341476044Test.java
+++ b/src/test/java/com/android/tools/r8/regress/B341476044Test.java
@@ -3,12 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.regress;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V13_0_0;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V15_0_0;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
@@ -75,7 +76,7 @@
.run(parameters.getRuntime(), TestClass.class)
.applyIf(
parameters.isDexRuntime()
- && parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V13_0_0),
+ && parameters.getDexRuntimeVersion().isInRangeInclusive(V13_0_0, V15_0_0),
// TODO(b/341476044): Should be EXPECTED_OUTPUT.
r -> r.assertSuccessWithOutputLines(NOT_EXPECTED_OUTPUT),
r -> r.assertSuccessWithOutputLines(EXPECTED_OUTPUT));
diff --git a/src/test/java/com/android/tools/r8/resolution/ObjectCloneInStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/resolution/ObjectCloneInStaticInterfaceMethodTest.java
index 3f081a4..6b8d4a5 100644
--- a/src/test/java/com/android/tools/r8/resolution/ObjectCloneInStaticInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/resolution/ObjectCloneInStaticInterfaceMethodTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.resolution;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V16_0_0;
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.SingleTestRunResult;
@@ -85,7 +86,12 @@
}
private void checkOutput(SingleTestRunResult<?> r) {
- r.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ if (parameters.isDexRuntimeVersion(V16_0_0)) {
+ // TODO(b/454529390): ART 16 does not enforce protected access to Object.clone.
+ r.assertSuccessWithOutputLines("0");
+ } else {
+ r.assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
}
interface I {
diff --git a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
index 9289f8e..484425e 100644
--- a/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/InvalidTypesTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V16_0_0;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.containsString;
@@ -89,6 +90,7 @@
case V13_0_0:
case V14_0_0:
case V15_0_0:
+ case V16_0_0:
return StringUtils.joinLines(
"Hello!",
"Unexpected outcome of checkcast",
@@ -377,7 +379,10 @@
return allOf(
containsString("java.lang.VerifyError"),
anyOf(
- containsString("register v0 has type Precise Reference: B but expected Reference: A"),
+ containsString(
+ "register v0 has type "
+ + (parameters.getDexRuntimeVersion().isOlderThan(V16_0_0) ? "Precise " : "")
+ + "Reference: B but expected Reference: A"),
containsString("VFY: storing type 'LB;' into field type 'LA;'")));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index ab72799..db98e26 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -321,7 +321,13 @@
result.assertFailureWithErrorThatMatches(
containsString(
parameters.getDexRuntimeVersion().isNewerThan(Version.V4_4_4)
- ? "type Precise Reference: Foo[] but expected Reference: SubInterface[]"
+ ? "type "
+ + (parameters
+ .getDexRuntimeVersion()
+ .isOlderThanOrEqual(Version.V15_0_0)
+ ? "Precise "
+ : "")
+ + "Reference: Foo[] but expected Reference: SubInterface[]"
: "[LFoo; is not instance of [LSubInterface;")))
.assertStderrMatches(not(containsString("ClassNotFoundException")));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
index edc8bce..5771588 100644
--- a/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/defaultmethods/DefaultMethodsTest.java
@@ -22,22 +22,20 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class DefaultMethodsTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public DefaultMethodsTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
private void runTest(
List<String> additionalKeepRules,
ThrowingConsumer<CodeInspector, RuntimeException> inspection)
@@ -66,23 +64,6 @@
assertThat(clazz.method("int", "method", ImmutableList.of()), not(isPresent()));
}
- private void defaultMethodKept(CodeInspector inspector) {
- ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
- assertThat(clazz, isPresent());
- MethodSubject method = clazz.method("int", "method", ImmutableList.of());
- assertThat(method, isPresent());
- ClassSubject companionClass = clazz.toCompanionClass();
- if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
- assertThat(method, not(isAbstract()));
- assertThat(companionClass, not(isPresent()));
- } else {
- assertThat(method, isAbstract());
- assertThat(companionClass, isPresent());
- MethodSubject defaultMethod = method.toMethodOnCompanionClass();
- assertThat(defaultMethod, isPresent());
- }
- }
-
private void defaultMethodKeptWithoutCompanionClass(CodeInspector inspector) {
ClassSubject clazz = inspector.clazz(InterfaceWithDefaultMethods.class);
assertThat(clazz, isPresent());
@@ -117,7 +98,7 @@
"-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
" <methods>;",
"}"),
- this::defaultMethodKept);
+ this::defaultMethodKeptWithoutCompanionClass);
}
@Test
@@ -127,7 +108,7 @@
"-keep interface " + InterfaceWithDefaultMethods.class.getTypeName() + "{",
" public int method();",
"}"),
- this::defaultMethodKept);
+ this::defaultMethodKeptWithoutCompanionClass);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
index 0d9f97c..6d28f87 100644
--- a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.shaking.staticinterfacemethods.defaultmethods;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -31,24 +31,24 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class StaticInterfaceMethodsTest extends TestBase {
- private final TestParameters parameters;
- private final boolean allowObfuscation;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "{0}, allowObfuscation: {1}")
+ @Parameter(1)
+ public boolean allowObfuscation;
+
+ @Parameters(name = "{0}, allowObfuscation: {1}")
public static Collection<Object[]> data() {
return buildParameters(
getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
- public StaticInterfaceMethodsTest(TestParameters parameters, boolean allowObfuscation) {
- this.parameters = parameters;
- this.allowObfuscation = allowObfuscation;
- }
-
private R8TestCompileResult compileTest(
List<String> additionalKeepRules,
ThrowingConsumer<CodeInspector, RuntimeException> inspection)
@@ -113,12 +113,12 @@
MethodSubject method = clazz.method("int", "method", ImmutableList.of());
ClassSubject companionClass = clazz.toCompanionClass();
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
+ assertThat(method, isPresent());
assertThat(method, isStatic());
- assertThat(companionClass, not(isPresent()));
+ assertThat(companionClass, isAbsent());
} else {
- assertThat(method, not(isPresent()));
- assertThat(companionClass, isPresent());
- assertThat(companionClass.uniqueMethodWithOriginalName("method"), isPresent());
+ assertThat(method, isAbsent());
+ assertThat(companionClass, isAbsent());
}
}
@@ -129,14 +129,13 @@
if (parameters.canUseDefaultAndStaticInterfaceMethods()) {
assertThat(clazz, allowObfuscation ? isPresentAndRenamed() : isPresentAndNotRenamed());
assertThat(method, isStatic());
- assertThat(companionClass, not(isPresent()));
+ assertThat(companionClass, isAbsent());
} else {
// When there is only a static method in the interface nothing is left on the interface itself
// after desugaring, only the companion class is left.
- assertThat(clazz, not(isPresent()));
- assertThat(method, not(isPresent()));
+ assertThat(clazz, isAbsent());
// TODO(160142903): The companion class should be present.
- assertThat(companionClass, not(isPresent()));
+ assertThat(companionClass, isAbsent());
// Also check that method exists on companion class.
}
}
@@ -161,7 +160,6 @@
@Test
public void testDefaultMethodKeptWithMethods() throws Exception {
assumeTrue(!allowObfuscation); // No use of allowObfuscation.
-
compileTest(
ImmutableList.of(
"-keep interface " + InterfaceWithStaticMethods.class.getTypeName() + "{",
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java
index 54510e4..3f90d60 100644
--- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDefaultMethodInSubInterfaceTest.java
@@ -208,7 +208,12 @@
.addKeepMainRule(Main.class)
.addRunClasspathFiles(r8CompiledTarget)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ .applyIf(
+ parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(),
+ rr -> rr.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ rr -> rr.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ rr -> rr.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
}
@Test
@@ -290,7 +295,12 @@
.addProgramFiles(sourceJar)
.addRunClasspathFiles(r8CompiledTarget)
.run(parameters.getRuntime(), Main.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ .applyIf(
+ parameters.canUseDefaultAndStaticInterfaceMethods(),
+ rr -> rr.assertSuccessWithOutput(EXPECTED_OUTPUT),
+ parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isDalvik(),
+ rr -> rr.assertFailureWithErrorThatThrows(NoClassDefFoundError.class),
+ rr -> rr.assertFailureWithErrorThatThrows(ClassNotFoundException.class));
}
// Interfaces I and J are in the target set for trace references.
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeInterfaceTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeInterfaceTest.java
index 7a0ac0d..34df157 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeInterfaceTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeInterfaceTest.java
@@ -77,7 +77,7 @@
public void testD8() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
testForD8(parameters)
.addProgramClassFileData(
dumpHost(),
@@ -111,7 +111,7 @@
public void testD8WithClasspathAndMerge() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
Path host =
testForD8(parameters)
@@ -196,7 +196,7 @@
public void testD8WithoutMembersOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
@@ -214,7 +214,7 @@
public void testD8WithoutHostOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSpecialTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSpecialTest.java
index fb7f97c..83f2676 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSpecialTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSpecialTest.java
@@ -163,7 +163,7 @@
parameters.assumeDexRuntime();
assumeTrue(parameters.getApiLevel().getLevel() >= 35);
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
testForD8(parameters)
.addProgramClassesAndInnerClasses(NestHierachy.class)
.setMinApi(AndroidApiLevel.U)
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSuperTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSuperTest.java
index 0adf57e..2e20775 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSuperTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeSuperTest.java
@@ -73,7 +73,7 @@
public void testD8() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
testForD8(parameters)
.addProgramClassFileData(dumpHost(), dumpMember(), dumpSubMember())
.apply(this::configureEmitNestAnnotationsInDex)
@@ -105,7 +105,7 @@
public void testD8WithClasspathAndMerge() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
Path host =
testForD8(parameters)
@@ -190,7 +190,7 @@
public void testD8WithoutMembersOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
@@ -208,7 +208,7 @@
public void testD8WithoutHostOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeVirtualTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeVirtualTest.java
index 9b5bf8b..2c6fae1 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeVirtualTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexRewriteInvokeVirtualTest.java
@@ -73,7 +73,7 @@
public void testD8() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
testForD8(parameters)
.addProgramClassFileData(dumpHost(), dumpMember1(), dumpMember2())
.apply(this::configureEmitNestAnnotationsInDex)
@@ -105,7 +105,7 @@
public void testD8WithClasspathAndMerge() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
Path host =
testForD8(parameters)
@@ -190,7 +190,7 @@
public void testD8WithoutMembersOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
@@ -208,7 +208,7 @@
public void testD8WithoutHostOnClasspath() throws Exception {
parameters.assumeDexRuntime();
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.canUseNestBasedAccesses());
assertFailsCompilation(
() ->
testForD8(parameters)
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingFieldsTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingFieldsTest.java
index d2ed4a2..2742121 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingFieldsTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingFieldsTest.java
@@ -108,7 +108,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
@@ -134,7 +134,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
@@ -155,7 +155,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PUBLIC), dumpMember1(ACC_PUBLIC), dumpMember2(ACC_PUBLIC))
diff --git a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingMethodsTest.java b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingMethodsTest.java
index f368942..7e0d654 100644
--- a/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingMethodsTest.java
+++ b/src/test/java11/com/android/tools/r8/jdk11/nest/dex/NestAttributesInDexShrinkingMethodsTest.java
@@ -107,7 +107,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
@@ -133,7 +133,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PRIVATE), dumpMember1(ACC_PRIVATE), dumpMember2(ACC_PRIVATE))
@@ -154,7 +154,7 @@
parameters.assumeR8TestParameters();
assumeTrue(parameters.isDexRuntime() || isRuntimeWithNestSupport(parameters.asCfRuntime()));
// TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
- assertFalse(parameters.getApiLevel().getLevel() > 35);
+ assertFalse(parameters.isDexRuntime() && isRuntimeWithNestSupport(parameters.asDexRuntime()));
testForR8(parameters)
.addProgramClassFileData(
dumpHost(ACC_PUBLIC), dumpMember1(ACC_PUBLIC), dumpMember2(ACC_PUBLIC))
diff --git a/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java b/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
index 517f276..608686c 100644
--- a/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
+++ b/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.jdk21.autocloseable;
import static com.android.tools.r8.desugar.AutoCloseableAndroidLibraryFileData.getAutoCloseableAndroidClassData;
+import static com.android.tools.r8.utils.AndroidApiLevel.BAKLAVA;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.NeverInline;
@@ -72,7 +73,7 @@
assumeTrue(parameters.isCfRuntime());
testForJvm(parameters)
.addInnerClassesAndStrippedOuter(getClass())
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+ .addLibraryFiles(ToolHelper.getAndroidJar(BAKLAVA))
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@@ -83,7 +84,7 @@
testForD8(parameters.getBackend())
.addInnerClassesAndStrippedOuter(getClass())
.setMinApi(parameters)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+ .addLibraryFiles(ToolHelper.getAndroidJar(BAKLAVA))
.addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
.compile()
.addRunClasspathClassFileData((getAutoCloseableAndroidClassData(parameters)))
@@ -104,8 +105,12 @@
Executor2.class)) {
ClassSubject subj = inspector.clazz(clazz);
Assert.assertTrue(subj.isPresent());
- Assert.assertTrue(subj.allMethods().stream().anyMatch(m -> m.getFinalName().equals("close")));
- Assert.assertTrue(
+ Assert.assertEquals(
+ ImmutableList.of(PrintForkJoinPool.class, Executor2.class).contains(clazz)
+ || !parameters.corelibWithExecutorServiceImplementingAutoClosable(),
+ subj.allMethods().stream().anyMatch(m -> m.getFinalName().equals("close")));
+ Assert.assertEquals(
+ !parameters.corelibWithExecutorServiceImplementingAutoClosable(),
subj.getDexProgramClass()
.getInterfaces()
.contains(inspector.getFactory().autoCloseableType));
@@ -120,7 +125,7 @@
.addInnerClassesAndStrippedOuter(getClass())
.addInliningAnnotations()
.setMinApi(parameters)
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.BAKLAVA))
+ .addLibraryFiles(ToolHelper.getAndroidJar(BAKLAVA))
.addLibraryClassFileData(getAutoCloseableAndroidClassData(parameters))
.compile()
.addRunClasspathClassFileData((getAutoCloseableAndroidClassData(parameters)))
diff --git a/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java b/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java
index 9ecfc4c..ef5b8e7 100644
--- a/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java
+++ b/src/test/java21/com/android/tools/r8/jdk21/autocloseable/AutoCloseableRetargeterExecutorServiceSubtypeTwrTest.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.jdk21.autocloseable.AutoCloseableRetargeterExecutorServiceSubtypeTest.Executor2;
+import com.android.tools.r8.jdk21.autocloseable.AutoCloseableRetargeterExecutorServiceSubtypeTest.PrintForkJoinPool;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DeterminismChecker;
import com.android.tools.r8.utils.StringUtils;
@@ -91,8 +93,13 @@
Executor2.class)) {
ClassSubject subj = inspector.clazz(clazz);
Assert.assertTrue(subj.isPresent());
- Assert.assertTrue(subj.allMethods().stream().anyMatch(m -> m.getFinalName().equals("close")));
- Assert.assertTrue(
+ Assert.assertEquals(
+ ImmutableList.of(PrintForkJoinPool.class, Executor2.class).contains(clazz)
+ || !parameters.corelibWithExecutorServiceImplementingAutoClosable(),
+ subj.allMethods().stream().anyMatch(m -> m.getFinalName().equals("close")));
+ Assert.assertEquals(
+ clazz.toString(),
+ !parameters.corelibWithExecutorServiceImplementingAutoClosable(),
subj.getDexProgramClass()
.getInterfaces()
.contains(inspector.getFactory().autoCloseableType));
diff --git a/src/test/java21/com/android/tools/r8/jdk21/desugaredlibrary/DurationIsPositiveTest.java b/src/test/java21/com/android/tools/r8/jdk21/desugaredlibrary/DurationIsPositiveTest.java
index b5c6800..c91ec95 100644
--- a/src/test/java21/com/android/tools/r8/jdk21/desugaredlibrary/DurationIsPositiveTest.java
+++ b/src/test/java21/com/android/tools/r8/jdk21/desugaredlibrary/DurationIsPositiveTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.jdk21.desugaredlibrary;
+import static com.android.tools.r8.ToolHelper.DexVm.Version.V16_0_0;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8DEBUG;
import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.DEFAULT_SPECIFICATIONS;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
@@ -61,7 +62,8 @@
.setMinApi(parameters)
.run(parameters.getRuntime(), Executor.class)
.applyIf(
- parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O),
+ parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O)
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(V16_0_0),
b -> b.assertSuccessWithOutput(EXPECTED_RESULT),
parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V8_1_0),
b -> b.assertFailureWithErrorThatThrows(NoSuchMethodError.class),
diff --git a/src/test/testbase/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/testbase/java/com/android/tools/r8/R8RunArtTestsTest.java
index 3cfbd85..34b4b48 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -105,7 +105,8 @@
DexVm.Version.V12_0_0,
DexVm.Version.V13_0_0,
DexVm.Version.V14_0_0,
- DexVm.Version.V15_0_0);
+ DexVm.Version.V15_0_0,
+ DexVm.Version.V16_0_0);
private static final TestCondition beforeAndroidN =
TestCondition.match(
@@ -122,8 +123,8 @@
TestCondition.match(TestCondition.runtimesUpTo(DexVm.Version.V7_0_0));
private static final TestCondition fromAndroidS =
TestCondition.match(TestCondition.runtimesFrom(DexVm.Version.V12_0_0));
- private static final TestCondition fromAndroidV =
- TestCondition.match(TestCondition.runtimesFrom(DexVm.Version.V15_0_0));
+ private static final TestCondition androidV =
+ TestCondition.match(TestCondition.runtimes(DexVm.Version.V15_0_0));
// Test that required to set min-api to a specific value.
private static Map<String, AndroidApiLevel> needMinSdkVersion =
@@ -501,6 +502,7 @@
static {
ImmutableMap.Builder<DexVm.Version, List<String>> builder = ImmutableMap.builder();
builder
+ .put(DexVm.Version.V16_0_0, ImmutableList.of("543-env-long-ref", "518-null-array-get"))
.put(DexVm.Version.V15_0_0, ImmutableList.of("543-env-long-ref", "518-null-array-get"))
.put(DexVm.Version.V14_0_0, ImmutableList.of("543-env-long-ref", "518-null-array-get"))
.put(DexVm.Version.V13_0_0, ImmutableList.of("543-env-long-ref", "518-null-array-get"))
@@ -846,7 +848,8 @@
DexVm.Version.V7_0_0,
DexVm.Version.V13_0_0,
DexVm.Version.V14_0_0,
- DexVm.Version.V15_0_0)),
+ DexVm.Version.V15_0_0,
+ DexVm.Version.V16_0_0)),
TestCondition.match(
compilers(
CompilerUnderTest.R8,
@@ -876,7 +879,8 @@
DexVm.Version.V12_0_0,
DexVm.Version.V13_0_0,
DexVm.Version.V14_0_0,
- DexVm.Version.V15_0_0)))
+ DexVm.Version.V15_0_0,
+ DexVm.Version.V16_0_0)))
.put("454-get-vreg", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Fails: regs_jni.cc:42] Check failed: GetVReg(m, 0, kIntVReg, &value)
// The R8/D8 code does not put values in the same registers as the tests expects.
@@ -895,7 +899,8 @@
DexVm.Version.V12_0_0,
DexVm.Version.V13_0_0,
DexVm.Version.V14_0_0,
- DexVm.Version.V15_0_0)))
+ DexVm.Version.V15_0_0,
+ DexVm.Version.V16_0_0)))
.put("457-regs", TestCondition.match(TestCondition.R8DEX_COMPILER))
// Class not found.
.put("529-checker-unresolved", TestCondition.any())
@@ -956,7 +961,7 @@
.put("979-const-method-handle", beforeAndroidP)
.put(
"021-string2",
- fromAndroidV) // Test use com.android.org.bouncycastle.util.Strings.fromUTF8ByteArray
+ androidV) // Test use com.android.org.bouncycastle.util.Strings.fromUTF8ByteArray
// - no longer present.
// Missing class junit.framework.Assert (see JunitAvailabilityInHostArtTest).
// TODO(120884788): Add this again.
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
index 25efdbf..86abea7 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -65,6 +65,7 @@
options -> {
options.testing.enableTestAssertions = true;
options.getThrowBlockOutlinerOptions().enable = true;
+ options.getThrowBlockOutlinerOptions().enableStringBuilderOutlining = true;
};
public static final Consumer<InternalOptions> DEFAULT_D8_OPTIONS = DEFAULT_OPTIONS;
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCondition.java b/src/test/testbase/java/com/android/tools/r8/TestCondition.java
index a4eca96..b7e4851 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCondition.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCondition.java
@@ -28,6 +28,7 @@
ART_V13_0_0,
ART_V14_0_0,
ART_V15_0_0,
+ ART_V16_0_0,
ART_DEFAULT,
ART_MASTER,
JAVA;
@@ -58,6 +59,8 @@
return ART_V14_0_0;
case V15_0_0:
return ART_V15_0_0;
+ case V16_0_0:
+ return ART_V16_0_0;
case DEFAULT:
return ART_DEFAULT;
case MASTER:
diff --git a/src/test/testbase/java/com/android/tools/r8/TestParameters.java b/src/test/testbase/java/com/android/tools/r8/TestParameters.java
index baee34e..7318a40 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestParameters.java
@@ -107,6 +107,8 @@
}
public boolean canUseNestBasedAccesses() {
+ // TODO(b/247047415): Update test when a DEX VM natively supporting nests is added.
+ assertFalse(getApiLevel() != null && getApiLevel().getLevel() > 36);
assert isCfRuntime() || isDexRuntime();
return isCfRuntime() && getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11);
}
@@ -125,6 +127,14 @@
return isDexRuntime() && getApiLevel().isGreaterThan(AndroidApiLevel.U);
}
+ public boolean corelibWithExecutorServiceImplementingAutoClosable() {
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.BAKLAVA);
+ }
+
+ public boolean frameworkHasBuildVersionCodesFull() {
+ return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.BAKLAVA);
+ }
+
public boolean isAccessModificationEnabled(boolean allowAccessModification) {
return allowAccessModification || isAccessModificationEnabledByDefault();
}
diff --git a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
index 268a9e0..f65356c 100644
--- a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
@@ -4,6 +4,8 @@
package com.android.tools.r8;
import static com.android.tools.r8.ToolHelper.TestDataSourceSet.computeLegacyOrGradleSpecifiedLocation;
+import static com.android.tools.r8.utils.DexVersion.V39;
+import static com.android.tools.r8.utils.DexVersion.V41;
import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAVA_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.isDexFile;
@@ -357,6 +359,8 @@
ART_14_0_0_HOST(Version.V14_0_0, Kind.HOST),
ART_15_0_0_TARGET(Version.V15_0_0, Kind.TARGET),
ART_15_0_0_HOST(Version.V15_0_0, Kind.HOST),
+ ART_16_0_0_TARGET(Version.V16_0_0, Kind.TARGET),
+ ART_16_0_0_HOST(Version.V16_0_0, Kind.HOST),
ART_MASTER_TARGET(Version.MASTER, Kind.TARGET),
ART_MASTER_HOST(Version.MASTER, Kind.HOST);
@@ -379,6 +383,7 @@
V13_0_0("13.0.0"),
V14_0_0("14.0.0"),
V15_0_0("15.0.0"),
+ V16_0_0("16.0.0"),
MASTER("master");
/** This should generally be the latest DEX VM fully supported. */
@@ -453,7 +458,7 @@
}
public static Version last() {
- return V15_0_0;
+ return V16_0_0;
}
public static Version master() {
@@ -972,6 +977,7 @@
ImmutableMap.<DexVm, String>builder()
.put(DexVm.ART_DEFAULT, "art")
.put(DexVm.ART_MASTER_HOST, "host/art-master")
+ .put(DexVm.ART_16_0_0_HOST, "host/art-16.0.0")
.put(DexVm.ART_15_0_0_HOST, "host/art-15.0.0-beta2")
.put(DexVm.ART_14_0_0_HOST, "host/art-14.0.0-beta3")
.put(DexVm.ART_13_0_0_HOST, "host/art-13.0.0")
@@ -989,6 +995,7 @@
ImmutableMap.<DexVm, String>builder()
.put(DexVm.ART_DEFAULT, "bin/art")
.put(DexVm.ART_MASTER_HOST, "bin/art")
+ .put(DexVm.ART_16_0_0_HOST, "bin/art")
.put(DexVm.ART_15_0_0_HOST, "bin/art")
.put(DexVm.ART_14_0_0_HOST, "bin/art")
.put(DexVm.ART_13_0_0_HOST, "bin/art")
@@ -1006,6 +1013,7 @@
private static final Map<DexVm, String> ART_BINARY_VERSIONS_X64 =
ImmutableMap.<DexVm, String>builder()
.put(DexVm.ART_DEFAULT, "bin/art")
+ .put(DexVm.ART_16_0_0_HOST, "bin/art")
.put(DexVm.ART_15_0_0_HOST, "bin/art")
.put(DexVm.ART_14_0_0_HOST, "bin/art")
.put(DexVm.ART_13_0_0_HOST, "bin/art")
@@ -1042,6 +1050,7 @@
ImmutableMap.Builder<DexVm, List<String>> builder = ImmutableMap.builder();
builder
.put(DexVm.ART_DEFAULT, ART_7_TO_10_BOOT_LIBS)
+ .put(DexVm.ART_16_0_0_HOST, ART_12_PLUS_BOOT_LIBS)
.put(DexVm.ART_15_0_0_HOST, ART_12_PLUS_BOOT_LIBS)
.put(DexVm.ART_14_0_0_HOST, ART_12_PLUS_BOOT_LIBS)
.put(DexVm.ART_13_0_0_HOST, ART_12_PLUS_BOOT_LIBS)
@@ -1063,6 +1072,7 @@
ImmutableMap.Builder<DexVm, String> builder = ImmutableMap.builder();
builder
.put(DexVm.ART_DEFAULT, "angler")
+ .put(DexVm.ART_16_0_0_HOST, "akita")
.put(DexVm.ART_15_0_0_HOST, "akita")
.put(DexVm.ART_14_0_0_HOST, "redfin")
.put(DexVm.ART_13_0_0_HOST, "redfin")
@@ -1098,6 +1108,7 @@
case V13_0_0:
case V14_0_0:
case V15_0_0:
+ case V16_0_0:
case MASTER:
return base.resolve("host").resolve("art-" + version);
default:
@@ -1134,6 +1145,7 @@
case V13_0_0:
case V14_0_0:
case V15_0_0:
+ case V16_0_0:
case MASTER:
return "x86_64";
default:
@@ -1406,6 +1418,8 @@
switch (dexVm.version) {
case MASTER:
return AndroidApiLevel.MAIN;
+ case V16_0_0:
+ return AndroidApiLevel.BAKLAVA;
case V15_0_0:
return AndroidApiLevel.V;
case V14_0_0:
@@ -1441,6 +1455,8 @@
switch (apiLevel) {
case MAIN:
return DexVm.Version.MASTER;
+ case BAKLAVA:
+ return DexVm.Version.V16_0_0;
case V:
return DexVm.Version.V15_0_0;
case U:
@@ -2470,6 +2486,10 @@
.collect(Collectors.toList())
.equals(SUPPORTED_DEX2OAT_VMS);
DexVersion requiredDexFileVersion = getDexFileVersionForVm(targetVm);
+ // TODO(b/453564724): Get dex2oat supporting V41.
+ if (requiredDexFileVersion.isEqualTo(V41)) {
+ requiredDexFileVersion = V39;
+ }
DexVm vm = null;
for (DexVm supported : SUPPORTED_DEX2OAT_VMS) {
DexVersion supportedDexFileVersion = getDexFileVersionForVm(supported);
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index f2c3ee2..1e26b4b 100644
--- a/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -242,10 +242,15 @@
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.apply(this::configure)
.inspect(this::assertDesugaring)
- .run(parameters.getRuntime(), testClassName)
+ .run(parameters.getRuntime(), testClassName, configureD8RunArguments())
.apply(runResultConsumer);
}
+ protected String[] configureD8RunArguments() {
+ // For subclasses to configure arguments passed to running the test code.
+ return new String[] {};
+ }
+
protected void configure(D8TestBuilder builder) throws Exception {
// For subclasses to further configure the test builder.
}
@@ -264,7 +269,7 @@
.setIncludeClassesChecksum(true)
.compileWithExpectedDiagnostics(this::checkDiagnostics)
.apply(this::configure)
- .run(parameters.getRuntime(), testClassName)
+ .run(parameters.getRuntime(), testClassName, configureD8RunArguments())
.assertSuccess()
.inspect(this::assertDesugaring);
}
diff --git a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 9b5bb95..e70142c 100644
--- a/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/testbase/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -105,6 +105,10 @@
}
public static ClassReference syntheticThrowBlockOutlineClass(Class<?> clazz, int id) {
+ return syntheticThrowBlockOutlineClass(Reference.classFromClass(clazz), id);
+ }
+
+ public static ClassReference syntheticThrowBlockOutlineClass(ClassReference clazz, int id) {
return syntheticClass(clazz, naming.THROW_BLOCK_OUTLINE, id);
}
diff --git a/tools/linux/README.art-versions b/tools/linux/README.art-versions
index ac3139a..12bde86 100644
--- a/tools/linux/README.art-versions
+++ b/tools/linux/README.art-versions
@@ -67,6 +67,34 @@
repo init -b udc-preview1-release
+art-16 (Android Baklava)
+------------------------
+Build branch 25Q2-release. Art at 4a203afc54cfc93b525849fe6dd9aeb9a95f7a06
+
+export BRANCH=25Q2-release
+mkdir ${BRANCH}
+cd ${BRANCH}
+export ANDROID_CHECKOUT=$(pwd)
+repo init -u https://android.googlesource.com/platform/manifest -b ${BRANCH}
+repo sync -cq -j48
+source build/envsetup.sh
+lunch aosp_akita-trunk_staging-userdebug
+m -j
+m -j build-art
+m -j test-art-host
+repo manifest -r -o build_spec.xml
+
+Collected into tools/linux/host/art-16.0.0. The "host" path element is checked
+by the script for running Art.
+
+ cd <r8 checkout>
+ scripts/update-host-art.sh \
+ --android-checkout $ANDROID_CHECKOUT \
+ --art-dir host/art-16.0.0 \
+ --android-product akita
+
+ (cd tools/linux/host; upload_to_google_storage.py -a --bucket r8-deps art-16.0.0)
+
art-15 (Android V)
------------------
diff --git a/tools/linux/host/art-16.0.0.tar.gz.sha1 b/tools/linux/host/art-16.0.0.tar.gz.sha1
new file mode 100644
index 0000000..753d730
--- /dev/null
+++ b/tools/linux/host/art-16.0.0.tar.gz.sha1
@@ -0,0 +1 @@
+28e853aaa247b3dd4435df1a339e2b11e4488105
\ No newline at end of file
diff --git a/tools/test.py b/tools/test.py
index 3ed2c71..5887e9d 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -28,8 +28,8 @@
import thread
ALL_ART_VMS = [
- "default", "15.0.0", "14.0.0", "13.0.0", "12.0.0", "10.0.0", "9.0.0",
- "8.1.0", "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"
+ "default", "16.0.0", "15.0.0", "14.0.0", "13.0.0", "12.0.0", "10.0.0",
+ "9.0.0", "8.1.0", "7.0.0", "6.0.1", "5.1.1", "4.4.4", "4.0.4"
]
# How often do we check for progress on the bots: