Add test for creating uninitialized instances of android.graphics.SurfaceTexture
R=christofferqa@google.com
Bug: b/441137561
Change-Id: I8a682b8691a8bb084d5d62f9067c122609e394ab
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningInSynchronizedMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningInSynchronizedMethodTest.java
new file mode 100644
index 0000000..3d28108
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningInSynchronizedMethodTest.java
@@ -0,0 +1,154 @@
+// 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.apimodel;
+
+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 com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+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 ApiModelExcludeClinitOutliningInSynchronizedMethodTest extends TestBase {
+
+ private static final String SURFACE_TEXTURE_TYPE_NAME = "android.graphics.SurfaceTexture";
+ private static final String SURFACE_TEXTURE_DESCRIPTOR =
+ DescriptorUtils.javaTypeToDescriptor(SURFACE_TEXTURE_TYPE_NAME);
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // Run tests with API level 21 to get <clinit> outlining (disabled below API 21).
+ return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.L).build();
+ }
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("SurfaceTexture <clinit>", "DONE");
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters)
+ .addProgramClassFileData(getTestClass())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ 2,
+ inspector
+ .clazz(TestClass.class)
+ .uniqueMethodWithOriginalName("constructorArgumentInSynchronizedMethod")
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .map(InstructionSubject::getMethod)
+ .filter(
+ dexMethod ->
+ SyntheticItemsTestUtils.isExternalApiOutlineClass(
+ dexMethod.getHolderType().asClassReference()))
+ .count()))
+ .addRunClasspathClassFileData(getSurfaceTexture())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters)
+ .addProgramClassFileData(getTestClass())
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ MethodSubject constructorArgumentInSynchronizedMethod =
+ inspector
+ .clazz(TestClass.class)
+ .uniqueMethodWithOriginalName("constructorArgumentInSynchronizedMethod");
+ assertEquals(
+ 1,
+ constructorArgumentInSynchronizedMethod
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .map(InstructionSubject::getMethod)
+ .filter(
+ dexMethod ->
+ dexMethod
+ .getHolderType()
+ .getTypeName()
+ .equals(
+ inspector.getObfuscatedTypeName(
+ SyntheticItemsTestUtils.syntheticApiOutlineClass(
+ TestClass.class, 0)
+ .getTypeName())))
+ .count());
+ // As android.graphics.SurfaceTexture was introduced in API level 11 the <clinit>
+ // outline is inlined.
+ assertEquals(
+ 1,
+ constructorArgumentInSynchronizedMethod
+ .streamInstructions()
+ .filter(InstructionSubject::isNewInstance)
+ .filter(
+ i ->
+ i.asDexInstruction()
+ .getInstruction()
+ .asNewInstance()
+ .getType()
+ .getTypeName()
+ .equals("android.graphics.SurfaceTexture"))
+ .count());
+ })
+ .addRunClasspathClassFileData(getSurfaceTexture())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private static byte[] getTestClass() throws IOException {
+ return transformer(TestClass.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(SurfaceTextureStub.class), SURFACE_TEXTURE_DESCRIPTOR)
+ .transform();
+ }
+
+ private static byte[] getSurfaceTexture() throws IOException {
+ return transformer(SurfaceTextureStub.class)
+ .setClassDescriptor(SURFACE_TEXTURE_DESCRIPTOR)
+ .transform();
+ }
+
+ // Transformed to android.graphics.SurfaceTexture
+ public static class SurfaceTextureStub {
+ static {
+ System.out.println("SurfaceTexture <clinit>");
+ }
+
+ public SurfaceTextureStub(boolean arg) {}
+ }
+
+ static class TestClass {
+ @NeverInline
+ private static synchronized void constructorArgumentInSynchronizedMethod() {
+ new SurfaceTextureStub(true);
+ }
+
+ public static void main(String[] args) {
+ constructorArgumentInSynchronizedMethod();
+ System.out.println("DONE");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningWhereArgumentsCouldCauseSideEffectsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningWhereArgumentsCouldCauseSideEffectsTest.java
new file mode 100644
index 0000000..1a76b6d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelExcludeClinitOutliningWhereArgumentsCouldCauseSideEffectsTest.java
@@ -0,0 +1,161 @@
+// 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.apimodel;
+
+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 com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.IOException;
+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 ApiModelExcludeClinitOutliningWhereArgumentsCouldCauseSideEffectsTest
+ extends TestBase {
+
+ private static final String SURFACE_TEXTURE_TYPE_NAME = "android.graphics.SurfaceTexture";
+ private static final String SURFACE_TEXTURE_DESCRIPTOR =
+ DescriptorUtils.javaTypeToDescriptor(SURFACE_TEXTURE_TYPE_NAME);
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ // Run tests with API level 21 to get <clinit> outlining (disabled below API 21).
+ return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.L).build();
+ }
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("SurfaceTexture <clinit>", "getArgument", "DONE");
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters)
+ .addProgramClassFileData(getTestClass())
+ .compile()
+ .inspect(
+ inspector ->
+ assertEquals(
+ 2,
+ inspector
+ .clazz(TestClass.class)
+ .uniqueMethodWithOriginalName("constructorArgumentCouldCauseSideEffects")
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .map(InstructionSubject::getMethod)
+ .filter(
+ dexMethod ->
+ SyntheticItemsTestUtils.isExternalApiOutlineClass(
+ dexMethod.getHolderType().asClassReference()))
+ .count()))
+ .addRunClasspathClassFileData(getSurfaceTexture())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters)
+ .addProgramClassFileData(getTestClass())
+ .addKeepMainRule(TestClass.class)
+ .enableInliningAnnotations()
+ .compile()
+ .inspect(
+ inspector -> {
+ MethodSubject constructorArgumentCouldCauseSideEffects =
+ inspector
+ .clazz(TestClass.class)
+ .uniqueMethodWithOriginalName("constructorArgumentCouldCauseSideEffects");
+ assertEquals(
+ 1,
+ constructorArgumentCouldCauseSideEffects
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeStatic)
+ .map(InstructionSubject::getMethod)
+ .filter(
+ dexMethod ->
+ dexMethod
+ .getHolderType()
+ .getTypeName()
+ .equals(
+ inspector.getObfuscatedTypeName(
+ SyntheticItemsTestUtils.syntheticApiOutlineClass(
+ TestClass.class, 0)
+ .getTypeName())))
+ .count());
+ // As android.graphics.SurfaceTexture was introduced in API level 11 the <clinit>
+ // outline is inlined.
+ assertEquals(
+ 1,
+ constructorArgumentCouldCauseSideEffects
+ .streamInstructions()
+ .filter(InstructionSubject::isNewInstance)
+ .filter(
+ i ->
+ i.asDexInstruction()
+ .getInstruction()
+ .asNewInstance()
+ .getType()
+ .getTypeName()
+ .equals("android.graphics.SurfaceTexture"))
+ .count());
+ })
+ .addRunClasspathClassFileData(getSurfaceTexture())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private static byte[] getTestClass() throws IOException {
+ return transformer(TestClass.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(SurfaceTextureStub.class), SURFACE_TEXTURE_DESCRIPTOR)
+ .transform();
+ }
+
+ private static byte[] getSurfaceTexture() throws IOException {
+ return transformer(SurfaceTextureStub.class)
+ .setClassDescriptor(SURFACE_TEXTURE_DESCRIPTOR)
+ .transform();
+ }
+
+ // Transformed to android.graphics.SurfaceTexture
+ public static class SurfaceTextureStub {
+ static {
+ System.out.println("SurfaceTexture <clinit>");
+ }
+
+ public SurfaceTextureStub(boolean arg) {}
+ }
+
+ static class TestClass {
+ @NeverInline
+ private static boolean getArgument() {
+ System.out.println("getArgument");
+ return System.currentTimeMillis() > 0;
+ }
+
+ @NeverInline
+ private static void constructorArgumentCouldCauseSideEffects() {
+ new SurfaceTextureStub(getArgument());
+ }
+
+ public static void main(String[] args) {
+ constructorArgumentCouldCauseSideEffects();
+ System.out.println("DONE");
+ }
+ }
+}
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 753027f..7ce29d2 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
@@ -338,6 +338,10 @@
return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, naming.OUTLINE);
}
+ public static boolean isExternalApiOutlineClass(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, naming.API_MODEL_OUTLINE);
+ }
+
public static boolean isInitializerTypeArgument(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, null, naming.INIT_TYPE_ARGUMENT);
}