Skip to content

Commit

Permalink
Generate binary compat stubs for nullable value classes
Browse files Browse the repository at this point in the history
aosp/3017515 changed default parameter value class types to be nullable if inline class wraps a non-primitive value. Kotlin encodes nullability into the method mangle, however, and this breaks binary compatibility.

This change generates a wrapper function that simply redirects the call. This function will only be called if the module doesn't expect new mangle for the function and will be ignored normally.

Fixes: 335384193 ( https://issuetracker.google.com/issues/335384193 )
Test: compiler tests + verified that repro project successfully runs with recompiled artifact.
Change-Id: I894651168d0521cdc639d4f4c415b28c4e5d348f ( https://android-review.googlesource.com/q/I894651168d0521cdc639d4f4c415b28c4e5d348f )

Moved from: androidx/androidx@60cef20
  • Loading branch information
ShikaSD authored and Space Cloud committed May 2, 2024
1 parent a3e7d78 commit 8a6f64a
Show file tree
Hide file tree
Showing 7 changed files with 709 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package androidx.compose.compiler.plugins.kotlin

import kotlin.test.assertFalse
import kotlin.test.assertTrue
import org.junit.Assume.assumeFalse
import org.junit.Test

Expand Down Expand Up @@ -796,11 +797,18 @@ class ComposeBytecodeCodegenTest(useFir: Boolean) : AbstractCodegenTest(useFir)
""",
validate = {
// select Example function body
val match = Regex("public final static Example[\\s\\S]*?LOCALVARIABLE").find(it)!!
val func = Regex("public final static Example[\\s\\S]*?LOCALVARIABLE")
.findAll(it)
.single()
assertFalse(message = "Function body should not contain a not-null check.") {
match.value.contains("Intrinsics.checkNotNullParameter")
func.value.contains("Intrinsics.checkNotNullParameter")
}
val stub = Regex("public final static synthetic Example[\\s\\S]*?LOCALVARIABLE")
.findAll(it)
.single()
assertTrue(message = "Function stub should contain a not-null check.") {
stub.value.contains("Intrinsics.checkNotNullParameter")
}
},
dumpClasses = true
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -583,12 +583,27 @@ class ComposerParamTransformTests(useFir: Boolean) : AbstractIrTransformTest(use
@JvmInline
value class Data(val string: String)
@JvmInline
value class NullableData(val string: String?)
@JvmInline
value class IntData(val value: Int)
""",
source = """
import androidx.compose.runtime.*
@Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable fun ExampleNullable(data: Data? = Data(""), intData: IntData = IntData(0)) {}
@Composable fun ExampleNullableData(data: NullableData = NullableData(null), intData: IntData = IntData(0)) {}
@Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
abstract class Test {
@Composable private fun PrivateExample(data: Data = Data("")) {}
@Composable fun PublicExample(data: Data = Data("")) {}
@Composable internal fun InternalExample(data: Data = Data("")) {}
@Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
@Composable protected fun ProtectedExample(data: Data = Data("")) {}
}
"""
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
import androidx.compose.runtime.*

@Composable fun Example(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable fun ExampleNullable(data: Data? = Data(""), intData: IntData = IntData(0)) {}
@Composable fun ExampleNullableData(data: NullableData = NullableData(null), intData: IntData = IntData(0)) {}
@Composable private fun PrivateExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable internal fun InternalExample(data: Data = Data(""), intData: IntData = IntData(0)) {}
@Composable @PublishedApi internal fun PublishedExample(data: Data = Data(""), intData: IntData = IntData(0)) {}

abstract class Test {
@Composable private fun PrivateExample(data: Data = Data("")) {}
@Composable fun PublicExample(data: Data = Data("")) {}
@Composable internal fun InternalExample(data: Data = Data("")) {}
@Composable @PublishedApi internal fun PublishedExample(data: Data = Data("")) {}
@Composable protected fun ProtectedExample(data: Data = Data("")) {}
}

//
// Transformed IR
Expand Down Expand Up @@ -34,3 +47,266 @@ fun Example(data: Data?, intData: IntData, %composer: Composer?, %changed: Int,
Example(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
fun ExampleNullable(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(ExampleNullable)P(0:Data,1:IntData):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (%default and 0b0010 != 0) {
intData = IntData(0)
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
ExampleNullable(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
fun ExampleNullableData(data: NullableData?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(ExampleNullableData)P(0:NullableData,1:IntData):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = NullableData(null)
}
if (%default and 0b0010 != 0) {
intData = IntData(0)
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
ExampleNullableData(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
private fun PrivateExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(PrivateExample)P(0:Data,1:IntData):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (%default and 0b0010 != 0) {
intData = IntData(0)
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
PrivateExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
internal fun InternalExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(InternalExample)P(0:Data,1:IntData):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (%default and 0b0010 != 0) {
intData = IntData(0)
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
InternalExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
@PublishedApi
internal fun PublishedExample(data: Data?, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(PublishedExample)P(0:Data,1:IntData):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (%default and 0b0010 != 0) {
intData = IntData(0)
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
PublishedExample(data, intData, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@StabilityInferred(parameters = 1)
abstract class Test {
@Composable
private fun PrivateExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(PrivateExample)P(0:Data):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
val tmp0_rcvr = <this>
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
tmp0_rcvr.PrivateExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
fun PublicExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(PublicExample)P(0:Data):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
val tmp0_rcvr = <this>
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
tmp0_rcvr.PublicExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
internal fun InternalExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(InternalExample)P(0:Data):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
val tmp0_rcvr = <this>
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
tmp0_rcvr.InternalExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
@PublishedApi
internal fun PublishedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(PublishedExample)P(0:Data):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
val tmp0_rcvr = <this>
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
tmp0_rcvr.PublishedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
@Composable
protected fun ProtectedExample(data: Data?, %composer: Composer?, %changed: Int, %default: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(ProtectedExample)P(0:Data):Test.kt")
if (%changed and 0b0001 != 0 || !%composer.skipping) {
if (%default and 0b0001 != 0) {
data = Data("")
}
if (isTraceInProgress()) {
traceEventStart(<>, %changed, -1, <>)
}
if (isTraceInProgress()) {
traceEventEnd()
}
} else {
%composer.skipToGroupEnd()
}
val tmp0_rcvr = <this>
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
tmp0_rcvr.ProtectedExample(data, %composer, updateChangedFlags(%changed or 0b0001), %default)
}
}
static val %stable: Int = 0
@Composable
@JvmSynthetic
fun PublicExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
return PublicExample(data, %composer, %changed, %default)
}
@Composable
@PublishedApi
@JvmSynthetic
internal fun PublishedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
return PublishedExample(data, %composer, %changed, %default)
}
@Composable
@JvmSynthetic
protected fun ProtectedExample(data: Data, %composer: Composer?, %changed: Int, %default: Int) {
return ProtectedExample(data, %composer, %changed, %default)
}
}
@Composable
@JvmSynthetic
fun Example(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
return Example(data, intData, %composer, %changed, %default)
}
@Composable
@PublishedApi
@JvmSynthetic
internal fun PublishedExample(data: Data, intData: IntData, %composer: Composer?, %changed: Int, %default: Int) {
return PublishedExample(data, intData, %composer, %changed, %default)
}
Loading

0 comments on commit 8a6f64a

Please sign in to comment.