Avoid synthesizing reference to Build.SDK_INT in desugaring when not needed
This adds a new closeExecutorService helper that is specialized to API level >= 24. When the min API is >= 24, the new helper can be used to avoid that closeExecutorService synthesizes a referenced to Build.SDK_INT, which may lead to missing class errors.
Bug: b/415072833
Change-Id: I3aca9b10b04794cf40eaad4847a6b7af57c7f5ef
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 6399485..d7aff3c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -1875,7 +1875,9 @@
addProvider(
new StatifyingMethodGenerator(
method,
- BackportedMethods::ExecutorServiceMethods_closeExecutorService,
+ appView.options().getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ ? BackportedMethods::ExecutorServiceMethods_closeExecutorServiceNPlus
+ : BackportedMethods::ExecutorServiceMethods_closeExecutorService,
"closeExecutorService",
type));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index 02eda10..6c3f005 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -2613,6 +2613,197 @@
ImmutableList.of());
}
+ public static CfCode ExecutorServiceMethods_closeExecutorServiceNPlus(
+ DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ CfLabel label6 = new CfLabel();
+ CfLabel label7 = new CfLabel();
+ CfLabel label8 = new CfLabel();
+ CfLabel label9 = new CfLabel();
+ CfLabel label10 = new CfLabel();
+ CfLabel label11 = new CfLabel();
+ CfLabel label12 = new CfLabel();
+ CfLabel label13 = new CfLabel();
+ CfLabel label14 = new CfLabel();
+ CfLabel label15 = new CfLabel();
+ CfLabel label16 = new CfLabel();
+ CfLabel label17 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 4,
+ 4,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/util/concurrent/ForkJoinPool;"),
+ factory.createProto(factory.createType("Ljava/util/concurrent/ForkJoinPool;")),
+ factory.createString("commonPool")),
+ false),
+ new CfIfCmp(IfType.NE, ValueType.OBJECT, label2),
+ label1,
+ new CfReturnVoid(),
+ label2,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;"))
+ })),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/concurrent/ExecutorService;"),
+ factory.createProto(factory.booleanType),
+ factory.createString("isTerminated")),
+ true),
+ new CfStore(ValueType.INT, 1),
+ label3,
+ new CfLoad(ValueType.INT, 1),
+ new CfIf(IfType.NE, ValueType.INT, label16),
+ label4,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/concurrent/ExecutorService;"),
+ factory.createProto(factory.voidType),
+ factory.createString("shutdown")),
+ true),
+ label5,
+ new CfConstNumber(0, ValueType.INT),
+ new CfStore(ValueType.INT, 2),
+ label6,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;")),
+ FrameType.intType(),
+ FrameType.intType()
+ })),
+ new CfLoad(ValueType.INT, 1),
+ new CfIf(IfType.NE, ValueType.INT, label14),
+ label7,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfConstNumber(1, ValueType.LONG),
+ new CfStaticFieldRead(
+ factory.createField(
+ factory.createType("Ljava/util/concurrent/TimeUnit;"),
+ factory.createType("Ljava/util/concurrent/TimeUnit;"),
+ factory.createString("DAYS"))),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/concurrent/ExecutorService;"),
+ factory.createProto(
+ factory.booleanType,
+ factory.longType,
+ factory.createType("Ljava/util/concurrent/TimeUnit;")),
+ factory.createString("awaitTermination")),
+ true),
+ new CfStore(ValueType.INT, 1),
+ label8,
+ new CfGoto(label6),
+ label9,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;")),
+ FrameType.intType(),
+ FrameType.intType()
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/lang/InterruptedException;"))))),
+ new CfStore(ValueType.OBJECT, 3),
+ label10,
+ new CfLoad(ValueType.INT, 2),
+ new CfIf(IfType.NE, ValueType.INT, label13),
+ label11,
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfInvoke(
+ 185,
+ factory.createMethod(
+ factory.createType("Ljava/util/concurrent/ExecutorService;"),
+ factory.createProto(factory.createType("Ljava/util/List;")),
+ factory.createString("shutdownNow")),
+ true),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label12,
+ new CfConstNumber(1, ValueType.INT),
+ new CfStore(ValueType.INT, 2),
+ label13,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;")),
+ FrameType.intType(),
+ FrameType.intType()
+ })),
+ new CfGoto(label6),
+ label14,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;")),
+ FrameType.intType(),
+ FrameType.intType()
+ })),
+ new CfLoad(ValueType.INT, 2),
+ new CfIf(IfType.EQ, ValueType.INT, label16),
+ label15,
+ new CfInvoke(
+ 184,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Thread;"),
+ factory.createProto(factory.createType("Ljava/lang/Thread;")),
+ factory.createString("currentThread")),
+ false),
+ new CfInvoke(
+ 182,
+ factory.createMethod(
+ factory.createType("Ljava/lang/Thread;"),
+ factory.createProto(factory.voidType),
+ factory.createString("interrupt")),
+ false),
+ label16,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initializedNonNullReference(
+ factory.createType("Ljava/util/concurrent/ExecutorService;")),
+ FrameType.intType()
+ })),
+ new CfReturnVoid(),
+ label17),
+ ImmutableList.of(
+ new CfTryCatch(
+ label7,
+ label8,
+ ImmutableList.of(factory.createType("Ljava/lang/InterruptedException;")),
+ ImmutableList.of(label9))),
+ ImmutableList.of());
+ }
+
public static CfCode FloatMethods_isFinite(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
index c0382b0..9beb64b 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/ExecutorServiceMethods.java
@@ -35,4 +35,33 @@
}
}
}
+
+ /**
+ * Similar to {@link #closeExecutorService(ExecutorService)}, but assumes SDK_INT is > 23. This
+ * avoids synthesizing a reference to android.os.Build.SDK_INT when compiling libcore. See also
+ * b/415072833.
+ */
+ public static void closeExecutorServiceNPlus(ExecutorService executorService) {
+ if (executorService == ForkJoinPool.commonPool()) {
+ return;
+ }
+ boolean terminated = executorService.isTerminated();
+ if (!terminated) {
+ executorService.shutdown();
+ boolean interrupted = false;
+ while (!terminated) {
+ try {
+ terminated = executorService.awaitTermination(1L, TimeUnit.DAYS);
+ } catch (InterruptedException e) {
+ if (!interrupted) {
+ executorService.shutdownNow();
+ interrupted = true;
+ }
+ }
+ }
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
}