blob: 6a38826f7e05a6bcf55007b52d86efb00829bef5 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.compiler.plugins.kotlin
import org.junit.Test
class ControlFlowTransformTests : AbstractControlFlowTransformTests() {
@Test
fun testIfNonComposable(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// No composable calls, so no group generated except for at function boundary
if (x > 0) {
NA()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
if (x > 0) {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testAND(): Unit = controlFlow(
"""
@NonRestartableComposable
@Composable
fun Example() {
B() && B()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
val tmp0_group = B(%composer, 0) && B(%composer, 0)
tmp0_group
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testOR(): Unit = controlFlow(
"""
@NonRestartableComposable
@Composable
fun Example() {
B() || B()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<B()>,<B()>:Test.kt")
val tmp0_group = B(%composer, 0) || B(%composer, 0)
tmp0_group
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfWithCallsInBranch(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// Only one composable call in the result blocks, so we can just generate
// a single group around the whole expression.
if (x > 0) {
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfElseWithCallsInBranch(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// Composable calls in the result blocks, so we can determine static number of
// groups executed. This means we put a group around the "then" and the
// "else" blocks
if (x > 0) {
A(a)
} else {
A(b)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfWithCallInCondition(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// Since the first condition of an if/else is unconditionally executed, it does not
// necessitate a group of any kind, so we just end up with the function boundary
// group
if (B()) {
NA()
} else {
NA()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<B()>:Test.kt")
if (B(%composer, 0)) {
NA()
} else {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testInlineReturnLabel(): Unit = controlFlow(
"""
@Composable
@NonRestartableComposable
fun CustomTextBroken(condition: Boolean) {
FakeBox {
if (condition) {
return@FakeBox
}
A()
}
}
@Composable
inline fun FakeBox(content: @Composable () -> Unit) {
content()
}
""",
"""
@Composable
@NonRestartableComposable
fun CustomTextBroken(condition: Boolean, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(CustomTextBroken)<FakeBo...>:Test.kt")
FakeBox({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C<A()>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
if (condition) {
}
A(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
%composer.endReplaceableGroup()
}
@Composable
@ComposableInferredTarget(scheme = "[0[0]]")
fun FakeBox(content: Function2<Composer, Int, Unit>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(FakeBox)<conten...>:Test.kt")
content(%composer, 0b1110 and %changed)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfElseWithCallsInConditions(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// Since the condition in the else-if is conditionally executed, it means we have
// dynamic execution and we can't statically guarantee the number of groups. As a
// result, we generate a group around the if statement in addition to a group around
// each of the conditions with composable calls in them. Note that no group is
// needed around the else condition
if (B(a)) {
NA()
} else if (B(b)) {
NA()
} else {
NA()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
if (%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<B(a)>")
val tmp0_group = B(a, %composer, 0)
%composer.endReplaceableGroup()
tmp0_group) {
NA()
} else if (%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<B(b)>")
val tmp1_group = B(b, %composer, 0)
%composer.endReplaceableGroup()
tmp1_group) {
NA()
} else {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithSubjectAndNoCalls(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// nothing needed except for the function boundary group
when (x) {
0 -> 8
1 -> 10
else -> x
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
val tmp0_subject = x
when {
tmp0_subject == 0 -> {
8
}
tmp0_subject == 0b0001 -> {
10
}
else -> {
x
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithSubjectAndCalls(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// calls only in the result block, which means we can statically guarantee the
// number of groups, so no group around the when is needed, just groups around the
// result blocks.
when (x) {
0 -> A(a)
1 -> A(b)
else -> A(c)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
val tmp0_subject = x
when {
tmp0_subject == 0 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endReplaceableGroup()
}
tmp0_subject == 0b0001 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
else -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(c)>")
A(c, %composer, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithSubjectAndCallsWithResult(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// no need for a group around the when expression overall, but since the result
// of the expression is now being used, we need to generate temporary variables to
// capture the result but still do the execution of the expression inside of groups.
val y = when (x) {
0 -> R(a)
1 -> R(b)
else -> R(c)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
val y = val tmp0_subject = x
when {
tmp0_subject == 0 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(a)>")
val tmp0_group = R(a, %composer, 0)
%composer.endReplaceableGroup()
tmp0_group
}
tmp0_subject == 0b0001 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(b)>")
val tmp1_group = R(b, %composer, 0)
%composer.endReplaceableGroup()
tmp1_group
}
else -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(c)>")
val tmp2_group = R(c, %composer, 0)
%composer.endReplaceableGroup()
tmp2_group
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCalls(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// result blocks have composable calls, so we generate groups round them. It's a
// statically guaranteed number of groups at execution, so no wrapping group is
// needed.
when {
x < 0 -> A(a)
x > 30 -> A(b)
else -> A(c)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
when {
x < 0 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endReplaceableGroup()
}
x > 30 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
else -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(c)>")
A(c, %composer, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInSomeResults(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// result blocks have composable calls, so we generate groups round them. It's a
// statically guaranteed number of groups at execution, so no wrapping group is
// needed.
when {
x < 0 -> A(a)
x > 30 -> NA()
else -> A(b)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
when {
x < 0 -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endReplaceableGroup()
}
x > 30 -> {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
NA()
}
else -> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInConditions(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// composable calls are in the condition blocks of the when statement. Since these
// are conditionally executed, we can't statically know the number of groups during
// execution. as a result, we must wrap the when clause with a group. Since there
// are no other composable calls, the function body group will suffice.
when {
x == R(a) -> NA()
x > R(b) -> NA()
else -> NA()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
when {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(a)>")
val tmp0_group = x == R(a, %composer, 0)
%composer.endReplaceableGroup()
tmp0_group -> {
NA()
}
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(b)>")
val tmp1_group = x > R(b, %composer, 0)
%composer.endReplaceableGroup()
tmp1_group -> {
NA()
}
else -> {
NA()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInConditionsAndCallAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// composable calls are in the condition blocks of the when statement. Since these
// are conditionally executed, we can't statically know the number of groups during
// execution. as a result, we must wrap the when clause with a group.
when {
x == R(a) -> NA()
x > R(b) -> NA()
else -> NA()
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "")
when {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(a)>")
val tmp0_group = x == R(a, %composer, 0)
%composer.endReplaceableGroup()
tmp0_group -> {
NA()
}
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R(b)>")
val tmp1_group = x > R(b, %composer, 0)
%composer.endReplaceableGroup()
tmp1_group -> {
NA()
}
else -> {
NA()
}
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testSafeCall(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int?) {
// The composable call is made conditionally, which means it is like an if with
// only one result having a composable call, so we just generate a single group
// around the whole expression.
x?.A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int?, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
x?.A(%composer, 0b1110 and %changed)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testElvis(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int?) {
// The composable call is made conditionally, which means it is like an if, but with
// only one result having a composable call, so we just generate a single group
// around the whole expression.
val y = x ?: R()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int?, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<R()>:Test.kt")
val y = val tmp0_elvis_lhs = x
val tmp0_group = when {
tmp0_elvis_lhs == null -> {
R(%composer, 0)
}
else -> {
tmp0_elvis_lhs
}
}
tmp0_group
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testForLoopWithCallsInBody(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: List<Int>) {
// The composable call is made a conditional number of times, so we need to wrap
// the loop with a dynamic wrapping group. Since there are no other calls, the
// function body group will suffice.
for (i in items) {
P(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
val tmp0_iterator = items.iterator()
while (tmp0_iterator.hasNext()) {
val i = tmp0_iterator.next()
P(i, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testForLoopWithCallsInBodyAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: List<Int>) {
// The composable call is made a conditional number of times, so we need to wrap
// the loop with a dynamic wrapping group.
for (i in items) {
P(i)
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: List<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<P(i)>")
val tmp0_iterator = items.iterator()
while (tmp0_iterator.hasNext()) {
val i = tmp0_iterator.next()
P(i, %composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testForLoopWithCallsInSubject(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example() {
// The for loop's subject expression is only executed once, so we don't need any
// additional groups
for (i in L()) {
print(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<L()>:Test.kt")
val tmp0_iterator = L(%composer, 0).iterator()
while (tmp0_iterator.hasNext()) {
val i = tmp0_iterator.next()
print(i)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInBody(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: MutableList<Int>) {
// since we have a composable call which is called a conditional number of times,
// we need to generate groups around the loop's block as well as a group around the
// overall statement. Since there are no calls after the while loop, the function
// body group will suffice.
while (items.isNotEmpty()) {
val item = items.removeAt(items.size - 1)
P(item)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(item...>:Test.kt")
while (items.isNotEmpty()) {
val item = items.removeAt(items.size - 1)
P(item, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInBodyAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: MutableList<Int>) {
// since we have a composable call which is called a conditional number of times,
// we need to generate groups around the loop's block as well as a group around the
// overall statement.
while (items.isNotEmpty()) {
val item = items.removeAt(items.size - 1)
P(item)
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: MutableList<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<P(item...>")
while (items.isNotEmpty()) {
val item = items.removeAt(items.size - 1)
P(item, %composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInCondition(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example() {
// A while loop's condition block gets executed a conditional number of times, so
// so we must generate a group around the while expression overall. The function
// body group will suffice.
while (B()) {
print("hello world")
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<B()>:Test.kt")
while (B(%composer, 0)) {
print("hello world")
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example() {
// A while loop's condition block gets executed a conditional number of times, so
// so we must generate a group around the while expression overall.
while (B()) {
print("hello world")
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<B()>")
while (B(%composer, 0)) {
print("hello world")
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndBody(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example() {
// Both the condition and the body of the loop get groups because they have
// composable calls in them. We must generate a group around the while statement
// overall, but the function body group will suffice.
while (B()) {
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<B()>,<A()>:Test.kt")
while (B(%composer, 0)) {
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndBodyAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example() {
// Both the condition and the body of the loop get groups because they have
// composable calls in them. We must generate a group around the while statement
// overall.
while (B()) {
A(a)
}
A(b)
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<B()>,<A(a)>")
while (B(%composer, 0)) {
A(a, %composer, 0)
}
%composer.endReplaceableGroup()
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnWithCallsBeforeButNotAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// in the early return path, we need only close out the opened groups
if (x > 0) {
A()
return
}
print("hello")
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
A(%composer, 0)
%composer.endReplaceableGroup()
return
}
print("hello")
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnWithCallsAfterButNotBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
// we can just close out the open groups at the return.
if (x > 0) {
return
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
%composer.endReplaceableGroup()
return
}
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int): Int {
if (x > 0) {
A()
return 1
}
return 2
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
A(%composer, 0)
val tmp1_return = 1
%composer.endReplaceableGroup()
return tmp1_return
}
val tmp0 = 2
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testEarlyReturnValueWithCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int): Int {
if (x > 0) {
return 1
}
A()
return 2
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
val tmp1_return = 1
%composer.endReplaceableGroup()
return tmp1_return
}
A(%composer, 0)
val tmp0 = 2
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testReturnCallValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(): Int {
// since the return expression is a composable call, we need to generate a
// temporary variable and then return it after ending the open groups.
A()
return R()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(%composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>,<R()>:Test.kt")
A(%composer, 0)
val tmp0 = R(%composer, 0)
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testEarlyReturnCallValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int): Int {
if (x > 0) {
return R()
}
return R()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<R()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<R()>")
if (x > 0) {
val tmp1_return = R(%composer, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return tmp1_return
}
%composer.endReplaceableGroup()
val tmp0 = R(%composer, 0)
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testReturnFromLoop(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
val j = i
val k = i
val l = i
P(i)
if (i == 0) {
P(j)
return
} else {
P(k)
}
P(l)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>,<P(l)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
val j = i
val k = i
val l = i
P(i, %composer, 0)
if (i == 0) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(j)>")
P(j, %composer, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
} else {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(k)>")
P(k, %composer, 0)
%composer.endReplaceableGroup()
}
P(l, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testOrderingOfPushedEndCallsWithEarlyReturns(): Unit = controlFlow(
"""
@Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
val j = i
val k = i
val l = i
P(i)
if (i == 0) {
P(j)
return
} else {
P(k)
}
P(l)
}
}
""",
"""
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>,<P(l)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
val j = i
val k = i
val l = i
P(i, %composer, 0)
if (i == 0) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(j)>")
P(j, %composer, 0)
%composer.endReplaceableGroup()
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Example(items, %composer, %changed or 0b0001)
}
return
} else {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(k)>")
P(k, %composer, 0)
%composer.endReplaceableGroup()
}
P(l, %composer, 0)
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Example(items, %composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testBreakWithCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
break
}
P(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
break
}
P(i, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testBreakWithCallsBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
break
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
P(i, %composer, 0)
if (i == 0) {
break
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testBreakWithCallsBeforeAndAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
// a group around while is needed here, but the function body group will suffice
while (items.hasNext()) {
val i = items.next()
val j = i
P(i)
if (i == 0) {
break
}
P(j)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<P(i)>,<P(j)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
val j = i
P(i, %composer, 0)
if (i == 0) {
break
}
P(j, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testBreakWithCallsBeforeAndAfterAndCallAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
// a group around while is needed here
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
break
}
P(i)
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<P(i)>,<P(i)>")
while (items.hasNext()) {
val i = items.next()
P(i, %composer, 0)
if (i == 0) {
break
}
P(i, %composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testContinueWithCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
continue
}
P(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
while (items.hasNext()) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(i)>")
val i = items.next()
if (i == 0) {
%composer.endReplaceableGroup()
continue
}
P(i, %composer, 0)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testContinueWithCallsBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
continue
}
print(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
while (items.hasNext()) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(i)>")
val i = items.next()
P(i, %composer, 0)
if (i == 0) {
%composer.endReplaceableGroup()
continue
}
print(i)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testContinueWithCallsBeforeAndAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
continue
}
P(i)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(items: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
while (items.hasNext()) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<P(i)>,<P(i)>")
val i = items.next()
P(i, %composer, 0)
if (i == 0) {
%composer.endReplaceableGroup()
continue
}
P(i, %composer, 0)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testLoopWithReturn(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
while (a.hasNext()) {
val x = a.next()
if (x > 100) {
return
}
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
while (a.hasNext()) {
val x = a.next()
if (x > 100) {
%composer.endReplaceableGroup()
return
}
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testLoopWithBreak(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
a@while (a.hasNext()) {
val x = a.next()
b@while (b.hasNext()) {
val y = b.next()
if (y == x) {
break@a
}
A()
}
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
a@while (a.hasNext()) {
val x = a.next()
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
b@while (b.hasNext()) {
val y = b.next()
if (y == x) {
%composer.endReplaceableGroup()
break@a
}
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testNestedLoopsAndBreak(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
a@while (a.hasNext()) {
val x = a.next()
if (x == 0) {
break
}
b@while (b.hasNext()) {
val y = b.next()
if (y == 0) {
break
}
if (y == x) {
break@a
}
if (y > 100) {
return
}
A()
}
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
a@while (a.hasNext()) {
val x = a.next()
if (x == 0) {
break
}
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
b@while (b.hasNext()) {
val y = b.next()
if (y == 0) {
break
}
if (y == x) {
%composer.endReplaceableGroup()
break@a
}
if (y > 100) {
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
}
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testNestedLoops(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
a@while (a.hasNext()) {
b@while (b.hasNext()) {
A()
}
A()
}
A()
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
a@while (a.hasNext()) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
b@while (b.hasNext()) {
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileInsideIfAndCallAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
while (x > 0) {
A()
}
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
while (x > 0) {
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileInsideIfAndCallBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
A()
while (x > 0) {
A()
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A()>,*<A()>:Test.kt")
if (x > 0) {
A(%composer, 0)
while (x > 0) {
A(%composer, 0)
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileInsideIf(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
while (x > 0) {
A()
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A()>:Test.kt")
if (x > 0) {
while (x > 0) {
A(%composer, 0)
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKey(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A()
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A()>")
A(%composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithTwoKeys(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A(a)
}
key(x+1) {
A(b)
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endMovableGroup()
%composer.startMovableGroup(<>, x + 1)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A(a)
}
A(b)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A(b)>:Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endMovableGroup()
A(b, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while (x > 0) {
A(a)
key(x) {
A(b)
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A(a)>:Test.kt")
while (x > 0) {
A(a, %composer, 0)
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallBeforeAndAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while (x > 0) {
A(a)
key(x) {
A(b)
}
A(c)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<A(a)>,<A(c)>:Test.kt")
while (x > 0) {
A(a, %composer, 0)
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endMovableGroup()
A(c, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevel(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
key(x) {
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A()>")
A(%composer, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevelAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
key(x) {
A(a)
}
A(b)
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endMovableGroup()
A(b, %composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevelAndCallsBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
A(a)
key(x) {
A(b)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
A(a, %composer, 0)
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIf(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
key(x) {
A()
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
if (x > 0) {
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A()>")
A(%composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIfAndCallsAfter(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
key(x) {
A(a)
}
A(b)
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A(b)>:Test.kt")
if (x > 0) {
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(a)>")
A(a, %composer, 0)
%composer.endMovableGroup()
A(b, %composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIfAndCallsBefore(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
if (x > 0) {
A(a)
key(x) {
A(b)
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<A(a)>:Test.kt")
if (x > 0) {
A(a, %composer, 0)
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<A(b)>")
A(b, %composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyWithLotsOfValues(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(a: Int, b: Int, c: Int, d: Int) {
key(a, b, c, d) {
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(a: Int, b: Int, c: Int, d: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
%composer.startMovableGroup(<>, %composer.joinKey(%composer.joinKey(%composer.joinKey(a, b), c), d))
sourceInformation(%composer, "<A()>")
A(%composer, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyWithComposableValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
while(x > 0) {
key(R()) {
A()
}
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)*<R()>:Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, R(%composer, 0))
sourceInformation(%composer, "<A()>")
A(%composer, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAsAValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int) {
val y = key(x) { R() }
P(y)
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example)<P(y)>:Test.kt")
val y =
%composer.startMovableGroup(<>, x)
sourceInformation(%composer, "<R()>")
val tmp0 = R(%composer, 0)
%composer.endMovableGroup()
tmp0
P(y, %composer, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testDynamicWrappingGroupWithReturnValue(): Unit = controlFlow(
"""
@NonRestartableComposable @Composable
fun Example(x: Int): Int {
return if (x > 0) {
if (B()) 1
else if (B()) 2
else 3
} else 4
}
""",
"""
@NonRestartableComposable
@Composable
fun Example(x: Int, %composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Example):Test.kt")
val tmp0 =
val tmp4_group = if (x > 0) {
val tmp3_group = if (%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<B()>")
val tmp1_group = B(%composer, 0)
%composer.endReplaceableGroup()
tmp1_group) 1 else if (%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<B()>")
val tmp2_group = B(%composer, 0)
%composer.endReplaceableGroup()
tmp2_group) 2 else 3
tmp3_group
} else {
4
}
tmp4_group
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testTheThing(): Unit = controlFlow(
"""
@NonRestartableComposable
@Composable
fun Simple() {
// this has a composable call in it, and since we don't know the number of times the
// lambda will get called, we place a group around the whole call
run {
A()
}
A()
}
@NonRestartableComposable
@Composable
fun WithReturn() {
// this has an early return in it, so it needs to end all of the groups present.
run {
A()
return@WithReturn
}
A()
}
@NonRestartableComposable
@Composable
fun NoCalls() {
// this has no composable calls in it, so shouldn't cause any groups to get created
run {
println("hello world")
}
A()
}
@NonRestartableComposable
@Composable
fun NoCallsAfter() {
// this has a composable call in the lambda, but not after it, which means the
// group should be able to be coalesced into the group of the function
run {
A()
}
}
""",
"""
@NonRestartableComposable
@Composable
fun Simple(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Simple)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
run {
A(%composer, 0)
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
@NonRestartableComposable
@Composable
fun WithReturn(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(WithReturn)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A()>")
run {
A(%composer, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
}
%composer.endReplaceableGroup()
A(%composer, 0)
%composer.endReplaceableGroup()
}
@NonRestartableComposable
@Composable
fun NoCalls(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(NoCalls)<A()>:Test.kt")
run {
println("hello world")
}
A(%composer, 0)
%composer.endReplaceableGroup()
}
@NonRestartableComposable
@Composable
fun NoCallsAfter(%composer: Composer?, %changed: Int) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(NoCallsAfter)*<A()>:Test.kt")
run {
A(%composer, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testLetWithComposableCalls(): Unit = controlFlow(
"""
@Composable
fun Example(x: Int?) {
x?.let {
if (it > 0) {
A(a)
}
A(b)
}
A(c)
}
""",
"""
@Composable
fun Example(x: Int?, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Example)<A(c)>:Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
}
if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
val tmp0_safe_receiver = x
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<A(b)>")
val tmp0_group = when {
tmp0_safe_receiver == null -> {
null
}
else -> {
tmp0_safe_receiver.let { it: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<A(a)>")
if (it > 0) {
A(a, %composer, 0)
}
%composer.endReplaceableGroup()
A(b, %composer, 0)
}
}
}
%composer.endReplaceableGroup()
tmp0_group
A(c, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Example(x, %composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testLetWithoutComposableCalls(): Unit = controlFlow(
"""
@Composable
fun Example(x: Int?) {
x?.let {
if (it > 0) {
NA()
}
NA()
}
A()
}
""",
"""
@Composable
fun Example(x: Int?, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Example)<A()>:Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
}
if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
x?.let { it: Int ->
if (it > 0) {
NA()
}
NA()
}
A(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Example(x, %composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testApplyOnComposableCallResult(): Unit = controlFlow(
"""
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.State
@Composable
fun <T> provided(value: T): State<T> = remember { mutableStateOf(value) }.apply {
this.value = value
}
""",
"""
@Composable
fun <T> provided(value: T, %composer: Composer?, %changed: Int): State<T> {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(provided)*<rememb...>:Test.kt")
val tmp0 = remember({
mutableStateOf(
value = value
)
}, %composer, 0).apply {
%this%apply.value = value
}
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testReturnInlinedExpressionWithCall(): Unit = controlFlow(
"""
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.State
@Composable
fun Test(x: Int): Int {
return x.let {
A()
123
}
}
""",
"""
@Composable
fun Test(x: Int, %composer: Composer?, %changed: Int): Int {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C(Test)*<A()>:Test.kt")
val tmp0 =
val tmp1_group = x.let { it: Int ->
A(%composer, 0)
val tmp0_return = 123
tmp0_return
}
tmp1_group
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testCallingAWrapperComposable(): Unit = controlFlow(
"""
@Composable
fun Test() {
W {
A()
}
}
""",
"""
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<W>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
W(ComposableSingletons%TestKt.lambda-1, %composer, 0b0110)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
internal object ComposableSingletons%TestKt {
val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
sourceInformation(%composer, "C<A()>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
A(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
}
}
"""
)
@Test
fun testCallingAnInlineWrapperComposable(): Unit = controlFlow(
"""
@Composable
fun Test() {
IW {
A()
}
}
""",
"""
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<IW>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
IW({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C<A()>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
A(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testRepeatedCallsToEffects(): Unit = verifyComposeIrTransform(
"""
import androidx.compose.runtime.Composable
@Composable
fun Test() {
Wrap {
repeat(number) {
effects[it] = effect { 0 }
}
outside = effect { "0" }
}
}
""",
"""
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<Wrap>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
Wrap(ComposableSingletons%TestKt.lambda-1, %composer, 0b0110)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
internal object ComposableSingletons%TestKt {
val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
sourceInformation(%composer, "C<effect>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<effect>")
repeat(number) { it: Int ->
effects[it] = effect({
0
}, %composer, 0b0110)
}
%composer.endReplaceableGroup()
outside = effect({
"0"
}, %composer, 0b0110)
} else {
%composer.skipToGroupEnd()
}
}
}
""",
"""
import androidx.compose.runtime.Composable
var effects = mutableListOf<Any>()
var outside: Any = ""
var number = 1
@Composable fun Wrap(block: @Composable () -> Unit) = block()
@Composable fun <T> effect(block: () -> T): T = block()
"""
)
@Test
fun testComposableWithInlineClass(): Unit = controlFlow(
"""
@Composable
fun Test(value: InlineClass) {
used(value)
A()
}
""",
"""
@Composable
fun Test(value: InlineClass, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)P(0:InlineClass)<A()>:Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(<unsafe-coerce>(value))) 0b0100 else 0b0010
}
if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
used(value)
A(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(value, %composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testParameterOrderInformation(): Unit = controlFlow(
"""
@Composable fun Test01(p0: Int, p1: Int, p2: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test02(p0: Int, p1: Int, p3: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test03(p0: Int, p2: Int, p1: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test04(p0: Int, p2: Int, p3: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test05(p0: Int, p3: Int, p1: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test06(p0: Int, p3: Int, p2: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test07(p1: Int, p0: Int, p2: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test08(p1: Int, p0: Int, p3: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test09(p1: Int, p2: Int, p0: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test00(p1: Int, p2: Int, p3: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test11(p1: Int, p3: Int, p0: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test12(p1: Int, p3: Int, p2: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test13(p2: Int, p0: Int, p1: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test14(p2: Int, p0: Int, p3: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test15(p2: Int, p1: Int, p0: Int, p3: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test16(p2: Int, p1: Int, p3: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test17(p2: Int, p3: Int, p0: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test18(p2: Int, p3: Int, p1: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test19(p3: Int, p0: Int, p1: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test20(p3: Int, p0: Int, p2: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test21(p3: Int, p1: Int, p0: Int, p2: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test22(p3: Int, p1: Int, p2: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test23(p3: Int, p2: Int, p0: Int, p1: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
@Composable fun Test24(p3: Int, p2: Int, p1: Int, p0: Int) {
used(p0)
used(p1)
used(p2)
used(p3)
}
""",
"""
@Composable
fun Test01(p0: Int, p1: Int, p2: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test01):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test01(p0, p1, p2, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test02(p0: Int, p1: Int, p3: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test02)P(!2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test02(p0, p1, p3, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test03(p0: Int, p2: Int, p1: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test03)P(!1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test03(p0, p2, p1, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test04(p0: Int, p2: Int, p3: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test04)P(!1,2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test04(p0, p2, p3, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test05(p0: Int, p3: Int, p1: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test05)P(!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test05(p0, p3, p1, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test06(p0: Int, p3: Int, p2: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test06)P(!1,3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test06(p0, p3, p2, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test07(p1: Int, p0: Int, p2: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test07)P(1):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test07(p1, p0, p2, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test08(p1: Int, p0: Int, p3: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test08)P(1!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test08(p1, p0, p3, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test09(p1: Int, p2: Int, p0: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test09)P(1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test09(p1, p2, p0, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test00(p1: Int, p2: Int, p3: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test00)P(1,2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test00(p1, p2, p3, p0, %composer, %changed or 0b0001)
}
}
@Composable
fun Test11(p1: Int, p3: Int, p0: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test11)P(1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test11(p1, p3, p0, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test12(p1: Int, p3: Int, p2: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test12)P(1,3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test12(p1, p3, p2, p0, %composer, %changed or 0b0001)
}
}
@Composable
fun Test13(p2: Int, p0: Int, p1: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test13)P(2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test13(p2, p0, p1, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test14(p2: Int, p0: Int, p3: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test14)P(2!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test14(p2, p0, p3, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test15(p2: Int, p1: Int, p0: Int, p3: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test15)P(2,1):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test15(p2, p1, p0, p3, %composer, %changed or 0b0001)
}
}
@Composable
fun Test16(p2: Int, p1: Int, p3: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test16)P(2,1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test16(p2, p1, p3, p0, %composer, %changed or 0b0001)
}
}
@Composable
fun Test17(p2: Int, p3: Int, p0: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test17)P(2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test17(p2, p3, p0, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test18(p2: Int, p3: Int, p1: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test18)P(2,3,1):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test18(p2, p3, p1, p0, %composer, %changed or 0b0001)
}
}
@Composable
fun Test19(p3: Int, p0: Int, p1: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test19)P(3):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test19(p3, p0, p1, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test20(p3: Int, p0: Int, p2: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test20)P(3!1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test20(p3, p0, p2, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test21(p3: Int, p1: Int, p0: Int, p2: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test21)P(3,1):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test21(p3, p1, p0, p2, %composer, %changed or 0b0001)
}
}
@Composable
fun Test22(p3: Int, p1: Int, p2: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test22)P(3,1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test22(p3, p1, p2, p0, %composer, %changed or 0b0001)
}
}
@Composable
fun Test23(p3: Int, p2: Int, p0: Int, p1: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test23)P(3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test23(p3, p2, p0, p1, %composer, %changed or 0b0001)
}
}
@Composable
fun Test24(p3: Int, p2: Int, p1: Int, p0: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test24)P(3,2,1):Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00100000 else 0b00010000
}
if (%changed and 0b001110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%changed and 0b0001110000000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b100000000000 else 0b010000000000
}
if (%dirty and 0b0001011011011011 !== 0b010010010010 || !%composer.skipping) {
used(p0)
used(p1)
used(p2)
used(p3)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test24(p3, p2, p1, p0, %composer, %changed or 0b0001)
}
}
"""
)
@Test
fun testSourceInformationWithPackageName(): Unit = verifyComposeIrTransform(
source = """
package androidx.compose.runtime.tests
import androidx.compose.runtime.Composable
@Composable
fun Test(value: LocalInlineClass) {
used(value)
}
""",
extra = """
package androidx.compose.runtime.tests
inline class LocalInlineClass(val value: Int)
fun used(x: Any?) {}
""",
expectedTransformed = """
@Composable
fun Test(value: LocalInlineClass, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)P(0:c#runtime.tests.LocalInlineClass):Test.kt#992ot2")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(<unsafe-coerce>(value))) 0b0100 else 0b0010
}
if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
used(value)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(value, %composer, %changed or 0b0001)
}
}
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testSourceOffsetOrderForParameterExpressions(): Unit = verifyComposeIrTransform(
source = """
import androidx.compose.runtime.Composable
@Composable
fun Test() {
A(b(), c(), d())
B()
}
""",
extra = """
import androidx.compose.runtime.Composable
@Composable fun A(a: Int, b: Int, c: Int) { }
@Composable fun B() { }
@Composable fun b(): Int = 1
@Composable fun c(): Int = 1
@Composable fun d(): Int = 1
""",
expectedTransformed = """
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<b()>,<c()>,<d()>,<A(b(),>,<B()>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
A(b(%composer, 0), c(%composer, 0), d(%composer, 0), %composer, 0)
B(%composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testSourceLocationOfCapturingComposableLambdas(): Unit = verifyComposeIrTransform(
source = """
import androidx.compose.runtime.Composable
class SomeClass {
var a = "Test"
fun onCreate() {
setContent {
B(a)
B(a)
}
}
}
fun Test() {
var a = "Test"
setContent {
B(a)
B(a)
}
}
""",
extra = """
import androidx.compose.runtime.Composable
fun setContent(block: @Composable () -> Unit) { }
@Composable fun B(value: String) { }
""",
expectedTransformed = """
@StabilityInferred(parameters = 0)
class SomeClass {
var a: String = "Test"
fun onCreate() {
setContent(composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
sourceInformation(%composer, "C<B(a)>,<B(a)>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
B(a, %composer, 0)
B(a, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
}
)
}
static val %stable: Int = 8
}
fun Test() {
var a = "Test"
setContent(composableLambdaInstance(<>, true) { %composer: Composer?, %changed: Int ->
sourceInformation(%composer, "C<B(a)>,<B(a)>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
B(a, %composer, 0)
B(a, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
}
)
}
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testSourceLineInformationForNormalInline(): Unit = verifyComposeIrTransform(
source = """
import androidx.compose.runtime.Composable
@Composable
fun Test() {
W {
IW {
T(2)
repeat(3) {
T(3)
}
T(4)
}
}
}
""",
extra = """
import androidx.compose.runtime.Composable
@Composable fun W(block: @Composable () -> Unit) = block()
@Composable inline fun IW(block: @Composable () -> Unit) = block()
@Composable fun T(value: Int) { }
""",
expectedTransformed = """
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<W>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
W(ComposableSingletons%TestKt.lambda-1, %composer, 0b0110)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
internal object ComposableSingletons%TestKt {
val lambda-1: Function2<Composer, Int, Unit> = composableLambdaInstance(<>, false) { %composer: Composer?, %changed: Int ->
sourceInformation(%composer, "C<IW>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
IW({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C<T(2)>,<T(4)>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
T(2, %composer, 0b0110)
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<T(3)>")
repeat(3) { it: Int ->
T(3, %composer, 0b0110)
}
%composer.endReplaceableGroup()
T(4, %composer, 0b0110)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
}
}
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testInlineReadOnlySourceLocations() = verifyComposeIrTransform(
"""
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
val current
@Composable
@ReadOnlyComposable
get() = 0
@Composable
@ReadOnlyComposable
fun calculateSometing(): Int {
return 0;
}
@Composable
fun Test() {
val c = current
val cl = calculateSometing()
Layout {
Text("${'$'}c ${'$'}cl")
}
}
""",
"""
val current: Int
@Composable @ReadOnlyComposable @JvmName(name = "getCurrent")
get() {
sourceInformationMarkerStart(%composer, <>, "C:Test.kt")
val tmp0 = 0
sourceInformationMarkerEnd(%composer)
return tmp0
}
@Composable
@ReadOnlyComposable
fun calculateSometing(%composer: Composer?, %changed: Int): Int {
sourceInformationMarkerStart(%composer, <>, "C(calculateSometing):Test.kt")
val tmp0 = 0
sourceInformationMarkerEnd(%composer)
return tmp0
}
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<curren...>,<calcul...>,<Layout>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
val c = current
val cl = calculateSometing(%composer, 0)
Layout({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C<Text("...>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
Text("%c %cl", %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
""",
"""
import androidx.compose.runtime.Composable
@Composable
inline fun Layout(content: @Composable () -> Unit) { content() }
@Composable
fun Text(text: String) { }
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testReadOnlyInlineValSourceLocations() = verifyComposeIrTransform(
"""
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
class CurrentHolder {
inline val current: Int
@ReadOnlyComposable
@Composable
get() = 0
}
class HolderHolder {
private val _currentHolder = CurrentHolder()
val current: Int
@ReadOnlyComposable
@Composable
get() = _currentHolder.current
}
val holderHolder = HolderHolder()
@Composable
@ReadOnlyComposable
fun calculateSometing(): Int {
return 0;
}
@Composable
fun Test() {
val c = holderHolder.current
val cl = calculateSometing()
Layout {
Text("${'$'}c ${'$'}cl")
}
}
""",
"""
@StabilityInferred(parameters = 0)
class CurrentHolder {
val current: Int
@ReadOnlyComposable @Composable @JvmName(name = "getCurrent")
get() {
sourceInformationMarkerStart(%composer, <>, "C:Test.kt")
val tmp0 = 0
sourceInformationMarkerEnd(%composer)
return tmp0
}
static val %stable: Int = 0
}
@StabilityInferred(parameters = 0)
class HolderHolder {
val _currentHolder: CurrentHolder = CurrentHolder()
val current: Int
@ReadOnlyComposable @Composable @JvmName(name = "getCurrent")
get() {
sourceInformationMarkerStart(%composer, <>, "C<curren...>:Test.kt")
val tmp0 = _currentHolder.current
sourceInformationMarkerEnd(%composer)
return tmp0
}
static val %stable: Int = 0
}
val holderHolder: HolderHolder = HolderHolder()
@Composable
@ReadOnlyComposable
fun calculateSometing(%composer: Composer?, %changed: Int): Int {
sourceInformationMarkerStart(%composer, <>, "C(calculateSometing):Test.kt")
val tmp0 = 0
sourceInformationMarkerEnd(%composer)
return tmp0
}
@Composable
fun Test(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)<curren...>,<calcul...>,<Layout>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
val c = holderHolder.current
val cl = calculateSometing(%composer, 0)
Layout({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C<Text("...>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
Text("%c %cl", %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(%composer, %changed or 0b0001)
}
}
""",
"""
import androidx.compose.runtime.Composable
@Composable
inline fun Layout(content: @Composable () -> Unit) { content() }
@Composable
fun Text(text: String) { }
""",
truncateTracingInfoMode = TruncateTracingInfoMode.KEEP_INFO_STRING
)
@Test
fun testReadOnlyComposableWithEarlyReturn() = controlFlow(
source = """
import androidx.compose.runtime.ReadOnlyComposable
@ReadOnlyComposable
@Composable
fun getSomeValue(a: Int): Int {
if (a < 100) return 0
return 1
}
""",
"""
@ReadOnlyComposable
@Composable
fun getSomeValue(a: Int, %composer: Composer?, %changed: Int): Int {
sourceInformationMarkerStart(%composer, <>, "C(getSomeValue):Test.kt")
if (a < 100) {
val tmp1_return = 0
sourceInformationMarkerEnd(%composer)
return tmp1_return
}
val tmp0 = 1
sourceInformationMarkerEnd(%composer)
return tmp0
}
"""
)
@Test
fun testMultipleNestedInlines() = verifyComposeIrTransform(
source = """
import androidx.compose.runtime.Composable
@Composable
fun AttemptedToRealizeGroupTwice() {
Wrapper {
repeat(1) {
repeat(1) {
Leaf(0)
}
Leaf(0)
}
}
}
""",
expectedTransformed = """
@Composable
fun AttemptedToRealizeGroupTwice(%composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(AttemptedToRealizeGroupTwice)<Wrappe...>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
Wrapper({ %composer: Composer?, %changed: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "C*<Leaf(0...>:Test.kt")
if (%changed and 0b1011 !== 0b0010 || !%composer.skipping) {
repeat(1) { it: Int ->
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "*<Leaf(0...>")
repeat(1) { it: Int ->
Leaf(0, %composer, 0b0110, 0)
}
%composer.endReplaceableGroup()
Leaf(0, %composer, 0b0110, 0)
}
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
AttemptedToRealizeGroupTwice(%composer, %changed or 0b0001)
}
}
""",
extra = """
import androidx.compose.runtime.Composable
@Composable
inline fun Wrapper(content: @Composable () -> Unit) { }
@Composable
fun Leaf(default: Int = 0) {}
"""
)
@Test // Regression test for 205590513
fun testGroupAroundExtensionFunctions() = verifyComposeIrTransform(
source = """
import androidx.compose.runtime.*
@Composable
fun Test(start: Int, end: Int) {
val a = remember { A() }
for (i in start until end) {
val b = a.get(bKey)
if (i == 2) {
a.get(cKey)
}
}
}
""",
extra = """
import androidx.compose.runtime.*
class A
class B
class C
val bKey: () -> B = { B() }
val cKey: () -> C = { C() }
@Composable
fun <T> A.get(block: () -> T) = block()
""",
expectedTransformed = """
@Composable
fun Test(start: Int, end: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(Test)P(1)<rememb...>,*<get(bK...>:Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(start)) 0b0100 else 0b0010
}
if (%changed and 0b01110000 === 0) {
%dirty = %dirty or if (%composer.changed(end)) 0b00100000 else 0b00010000
}
if (%dirty and 0b01011011 !== 0b00010010 || !%composer.skipping) {
val a = remember({
A()
}, %composer, 0)
val tmp0_iterator = start until end.iterator()
while (tmp0_iterator.hasNext()) {
val i = tmp0_iterator.next()
val b = a.get(bKey, %composer, 0b00110110)
%composer.startReplaceableGroup(<>)
sourceInformation(%composer, "<get(cK...>")
if (i == 0b0010) {
a.get(cKey, %composer, 0b00110110)
}
%composer.endReplaceableGroup()
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
Test(start, end, %composer, %changed or 0b0001)
}
}
"""
)
// There are a number of "inline constructors" in the Kotlin standard library for Array types.
// These are special cases, since normal constructors cannot be inlined.
@Test
fun testInlineArrayConstructor() = verifyComposeIrTransform(
"""
import androidx.compose.runtime.*
@Composable
fun ArrayConstructorTest(n: Int) {
Array(n) { remember { it } }
ByteArray(n) { remember { it.toByte() } }
CharArray(n) { remember { it.toChar() } }
ShortArray(n) { remember { it.toShort() } }
IntArray(n) { remember { it } }
LongArray(n) { remember { it.toLong() } }
FloatArray(n) { remember { it.toFloat() } }
DoubleArray(n) { remember { it.toDouble() } }
BooleanArray(n) { remember { false } }
}
""",
"""
@Composable
fun ArrayConstructorTest(n: Int, %composer: Composer?, %changed: Int) {
%composer = %composer.startRestartGroup(<>)
sourceInformation(%composer, "C(ArrayConstructorTest)<rememb...>,<rememb...>,<rememb...>,<rememb...>,<rememb...>,<rememb...>,<rememb...>,<rememb...>,<rememb...>:Test.kt")
val %dirty = %changed
if (%changed and 0b1110 === 0) {
%dirty = %dirty or if (%composer.changed(n)) 0b0100 else 0b0010
}
if (%dirty and 0b1011 !== 0b0010 || !%composer.skipping) {
Array(n) { it: Int ->
val tmp0_return = remember({
it
}, %composer, 0)
tmp0_return
}
ByteArray(n) { it: Int ->
val tmp0_return = remember({
it.toByte()
}, %composer, 0)
tmp0_return
}
CharArray(n) { it: Int ->
val tmp0_return = remember({
it.toChar()
}, %composer, 0)
tmp0_return
}
ShortArray(n) { it: Int ->
val tmp0_return = remember({
it.toShort()
}, %composer, 0)
tmp0_return
}
IntArray(n) { it: Int ->
val tmp0_return = remember({
it
}, %composer, 0)
tmp0_return
}
LongArray(n) { it: Int ->
val tmp0_return = remember({
it.toLong()
}, %composer, 0)
tmp0_return
}
FloatArray(n) { it: Int ->
val tmp0_return = remember({
it.toFloat()
}, %composer, 0)
tmp0_return
}
DoubleArray(n) { it: Int ->
val tmp0_return = remember({
it.toDouble()
}, %composer, 0)
tmp0_return
}
BooleanArray(n) { it: Int ->
val tmp0_return = remember({
false
}, %composer, 0)
tmp0_return
}
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer?, %force: Int ->
ArrayConstructorTest(n, %composer, %changed or 0b0001)
}
}
"""
)
}