[go: nahoru, domu]

blob: 1518d3aa440497c72ae07deab52f71e8e677b070 [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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
// No composable calls, so no group generated except for at function boundary
if (x > 0) {
NA()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfWithCallsInBranch(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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 implicit
// "else" blocks
if (x > 0) {
A()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A()>")
A(%composer, <>, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfElseWithCallsInBranch(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfWithCallInCondition(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<B()>:Test.kt")
if (B(%composer, <>, 0)) {
NA()
} else {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testIfElseWithCallsInConditions(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (%composer.startReplaceableGroup(<>, "<B(a)>")
val tmp0_group = B(a, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp0_group) {
NA()
} else if (%composer.startReplaceableGroup(<>, "<B(b)>")
val tmp1_group = B(b, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp1_group) {
NA()
} else {
NA()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithSubjectAndNoCalls(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
// nothing needed except for the function boundary group
when (x) {
0 -> 8
1 -> 10
else -> x
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
val tmp0_subject = x
when {
tmp0_subject == 0 -> {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endReplaceableGroup()
}
tmp0_subject == 0b0001 -> {
%composer.startReplaceableGroup(<>, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
else -> {
%composer.startReplaceableGroup(<>, "<A(c)>")
A(c, %composer, <>, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithSubjectAndCallsWithResult(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
val y = val tmp0_subject = x
when {
tmp0_subject == 0 -> {
%composer.startReplaceableGroup(<>, "<R(a)>")
val tmp0_group = R(a, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp0_group
}
tmp0_subject == 0b0001 -> {
%composer.startReplaceableGroup(<>, "<R(b)>")
val tmp1_group = R(b, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp1_group
}
else -> {
%composer.startReplaceableGroup(<>, "<R(c)>")
val tmp2_group = R(c, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp2_group
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCalls(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
when {
x < 0 -> {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endReplaceableGroup()
}
x > 30 -> {
%composer.startReplaceableGroup(<>, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
else -> {
%composer.startReplaceableGroup(<>, "<A(c)>")
A(c, %composer, <>, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInSomeResults(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
when {
x < 0 -> {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endReplaceableGroup()
}
x > 30 -> {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
NA()
}
else -> {
%composer.startReplaceableGroup(<>, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInConditions(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
when {
%composer.startReplaceableGroup(<>, "<R(a)>")
val tmp0_group = x == R(a, %composer, 0b01110111010101111000001000101110, 0)
%composer.endReplaceableGroup()
tmp0_group -> {
NA()
}
%composer.startReplaceableGroup(<>, "<R(b)>")
val tmp1_group = x > R(b, %composer, <>, 0)
%composer.endReplaceableGroup()
tmp1_group -> {
NA()
}
else -> {
NA()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhenWithCallsInConditionsAndCallAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>)
when {
%composer.startReplaceableGroup(<>, "<R(a)>")
val tmp0_group = x == R(a, %composer, 0b01110111010101111000000111010110, 0)
%composer.endReplaceableGroup()
tmp0_group -> {
NA()
}
%composer.startReplaceableGroup(<>, "<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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int?) {
// the composable call is made conditionally, which means it is like an if, but one
// with static groups, so no wrapping group needed.
x?.A()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int?, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
val tmp0_safe_receiver = x
when {
tmp0_safe_receiver == null -> {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
null
}
else -> {
%composer.startReplaceableGroup(<>, "<A()>")
tmp0_safe_receiver.A(%composer, <>, 0b0110 and %changed)
%composer.endReplaceableGroup()
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testElvis(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int?) {
// the composable call is made conditionally, which means it is like an if, but one
// with static groups, so no wrapping group needed.
val y = x ?: R()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int?, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
val y = val tmp0_elvis_lhs = x
when {
tmp0_elvis_lhs == null -> {
%composer.startReplaceableGroup(<>, "<R()>")
val tmp0_group = R(%composer, <>, 0)
%composer.endReplaceableGroup()
tmp0_group
}
else -> {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
tmp0_elvis_lhs
}
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testForLoopWithCallsInBody(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: List<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: List<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: MutableList<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: MutableList<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @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")
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<B()>:Test.kt")
while (B(%composer, <>, 0)) {
print("hello world")
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "*<B()>")
while (B(%composer, <>, 0)) {
print("hello world")
}
%composer.endReplaceableGroup()
A(%composer, <>, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndBody(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<B()>,<A()>:Test.kt")
while (B(%composer, <>, 0)) {
A(%composer, <>, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileLoopWithCallsInConditionAndBodyAndCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A(b)>:Test.kt")
%composer.startReplaceableGroup(<>, "*<B()>,<A(a)>")
while (B(%composer, <>, 0)) {
A(a, %composer, <>, 0)
}
%composer.endReplaceableGroup()
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnWithCallsBeforeButNotAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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")
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A()>")
A(%composer, <>, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
print("hello")
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnWithCallsAfterButNotBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
// we can just close out the open groups at the return.
if (x > 0) {
return
}
A()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
if (x > 0) {
%composer.endReplaceableGroup()
return
}
A(%composer, <>, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testEarlyReturnValue(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int): Int {
if (x > 0) {
A()
return 1
}
return 2
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A()>")
A(%composer, <>, 0)
val tmp1_return = 1
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return tmp1_return
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
val tmp0 = 2
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testEarlyReturnValueWithCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int): Int {
if (x > 0) {
return 1
}
A()
return 2
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(%composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>,<R()>:Test.kt")
A(%composer, <>, 0)
val tmp0 = R(%composer, <>, 0)
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testEarlyReturnCallValue(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int): Int {
if (x > 0) {
return R()
}
return R()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<R()>:Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<R()>")
val tmp1_return = R(%composer, <>, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return tmp1_return
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
val tmp0 = R(%composer, <>, 0)
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testReturnFromLoop(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(<>, "<P(j)>")
P(j, %composer, <>, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
} else {
%composer.startReplaceableGroup(<>, "<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<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "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(<>, "<P(j)>")
P(j, %composer, <>, 0)
%composer.endReplaceableGroup()
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Example(items, %composer, %key, %changed or 0b0001)
}
return
} else {
%composer.startReplaceableGroup(<>, "<P(k)>")
P(k, %composer, <>, 0)
%composer.endReplaceableGroup()
}
P(l, %composer, <>, 0)
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Example(items, %composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testBreakWithCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
break
}
P(i)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
break
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
continue
}
P(i)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<P(i)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
if (i == 0) {
continue
}
P(i, %composer, <>, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testContinueWithCallsBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
continue
}
print(i)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<P(i)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
P(i, %composer, <>, 0)
if (i == 0) {
continue
}
print(i)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testContinueWithCallsBeforeAndAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(items: Iterator<Int>) {
while (items.hasNext()) {
val i = items.next()
P(i)
if (i == 0) {
continue
}
P(i)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(items: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<P(i)>,<P(i)>:Test.kt")
while (items.hasNext()) {
val i = items.next()
P(i, %composer, <>, 0)
if (i == 0) {
continue
}
P(i, %composer, <>, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testLoopWithReturn(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
while (a.hasNext()) {
val x = a.next()
if (x > 100) {
return
}
A()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<A()>:Test.kt")
a@while (a.hasNext()) {
val x = a.next()
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<A()>:Test.kt")
a@while (a.hasNext()) {
val x = a.next()
if (x == 0) {
break
}
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>) {
a@while (a.hasNext()) {
b@while (b.hasNext()) {
A()
}
A()
}
A()
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(a: Iterator<Int>, b: Iterator<Int>, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "*<A()>")
a@while (a.hasNext()) {
%composer.startReplaceableGroup(<>, "*<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(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
while (x > 0) {
A()
}
A()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A()>")
%composer.startReplaceableGroup(<>, "*<A()>")
while (x > 0) {
A(%composer, <>, 0)
}
%composer.endReplaceableGroup()
A(%composer, <>, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileInsideIfAndCallBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
A()
while (x > 0) {
A()
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A()>,*<A()>")
A(%composer, <>, 0)
while (x > 0) {
A(%composer, <>, 0)
}
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileInsideIf(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
while (x > 0) {
A()
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "*<A()>")
while (x > 0) {
A(%composer, <>, 0)
}
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKey(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A()
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x, "<A()>")
A(%composer, <>, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithTwoKeys(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A(a)
}
key(x+1) {
A(b)
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endMovableGroup()
%composer.startMovableGroup(<>, x + 1, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while (x > 0) {
key(x) {
A(a)
}
A(b)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<A(b)>:Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, x, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endMovableGroup()
A(b, %composer, <>, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while (x > 0) {
A(a)
key(x) {
A(b)
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<A(a)>:Test.kt")
while (x > 0) {
A(a, %composer, <>, 0)
%composer.startMovableGroup(<>, x, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testWhileWithKeyAndCallBeforeAndAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while (x > 0) {
A(a)
key(x) {
A(b)
}
A(c)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<A(a)>,<A(c)>:Test.kt")
while (x > 0) {
A(a, %composer, <>, 0)
%composer.startMovableGroup(<>, x, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endMovableGroup()
A(c, %composer, <>, 0)
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevel(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
key(x) {
A()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
%composer.startMovableGroup(<>, x, "<A()>")
A(%composer, <>, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevelAndCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
key(x) {
A(a)
}
A(b)
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A(b)>:Test.kt")
%composer.startMovableGroup(<>, x, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endMovableGroup()
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAtRootLevelAndCallsBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
A(a)
key(x) {
A(b)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<A(a)>:Test.kt")
A(a, %composer, <>, 0)
%composer.startMovableGroup(<>, x, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIf(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
key(x) {
A()
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>)
%composer.startMovableGroup(<>, x, "<A()>")
A(%composer, <>, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIfAndCallsAfter(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
key(x) {
A(a)
}
A(b)
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A(b)>")
%composer.startMovableGroup(<>, x, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endMovableGroup()
A(b, %composer, <>, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyInIfAndCallsBefore(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
if (x > 0) {
A(a)
key(x) {
A(b)
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
if (x > 0) {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.startMovableGroup(<>, x, "<A(b)>")
A(b, %composer, <>, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyWithLotsOfValues(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(a: Int, b: Int, c: Int, d: Int) {
key(a, b, c, d) {
A()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(a: Int, b: Int, c: Int, d: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
%composer.startMovableGroup(<>, %composer.joinKey(%composer.joinKey(%composer.joinKey(a, b), c), d), "<A()>")
A(%composer, <>, 0)
%composer.endMovableGroup()
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyWithComposableValue(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
while(x > 0) {
key(R()) {
A()
}
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)*<R()>:Test.kt")
while (x > 0) {
%composer.startMovableGroup(<>, R(%composer, <>, 0), "<A()>")
A(%composer, <>, 0)
%composer.endMovableGroup()
}
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testKeyAsAValue(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int) {
val y = key(x) { R() }
P(y)
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Example)<P(y)>:Test.kt")
val y =
%composer.startMovableGroup(<>, x, "<R()>")
val tmp0 = R(%composer, <>, 0)
%composer.endMovableGroup()
tmp0
P(y, %composer, <>, 0)
%composer.endReplaceableGroup()
}
"""
)
@Test
fun testDynamicWrappingGroupWithReturnValue(): Unit = controlFlow(
"""
@ComposableContract(restartable = false) @Composable
fun Example(x: Int): Int {
return if (x > 0) {
if (B()) 1
else if (B()) 2
else 3
} else 4
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Example(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "C(Example):Test.kt")
val tmp0 = if (x > 0) {
%composer.startReplaceableGroup(<>)
val tmp4_group =
val tmp3_group = if (%composer.startReplaceableGroup(<>, "<B()>")
val tmp1_group = B(%composer, <>, 0)
%composer.endReplaceableGroup()
tmp1_group) 1 else if (%composer.startReplaceableGroup(<>, "<B()>")
val tmp2_group = B(%composer, <>, 0)
%composer.endReplaceableGroup()
tmp2_group) 2 else 3
tmp3_group
%composer.endReplaceableGroup()
tmp4_group
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
4
}
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testTheThing(): Unit = controlFlow(
"""
@ComposableContract(restartable = false)
@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()
}
@ComposableContract(restartable = false)
@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()
}
@ComposableContract(restartable = false)
@Composable
fun NoCalls() {
// this has no composable calls in it, so shouldn't cause any groups to get created
run {
println("hello world")
}
A()
}
@ComposableContract(restartable = false)
@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()
}
}
""",
"""
@ComposableContract(restartable = false)
@Composable
fun Simple(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(Simple)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "<A()>")
run {
A(%composer, <>, 0)
}
%composer.endReplaceableGroup()
A(%composer, <>, 0)
%composer.endReplaceableGroup()
}
@ComposableContract(restartable = false)
@Composable
fun WithReturn(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(WithReturn)<A()>:Test.kt")
%composer.startReplaceableGroup(<>, "<A()>")
run {
A(%composer, <>, 0)
%composer.endReplaceableGroup()
%composer.endReplaceableGroup()
return
}
%composer.endReplaceableGroup()
A(%composer, <>, 0)
%composer.endReplaceableGroup()
}
@ComposableContract(restartable = false)
@Composable
fun NoCalls(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "C(NoCalls)<A()>:Test.kt")
run {
println("hello world")
}
A(%composer, <>, 0)
%composer.endReplaceableGroup()
}
@ComposableContract(restartable = false)
@Composable
fun NoCallsAfter(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startReplaceableGroup(<> xor %key, "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<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Example)<A(c)>:Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
}
if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
val tmp0_safe_receiver = x
when {
tmp0_safe_receiver == null -> {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
null
}
else -> {
%composer.startReplaceableGroup(<>, "<A(b)>")
tmp0_safe_receiver.let { it: Int ->
if (it > 0) {
%composer.startReplaceableGroup(<>, "<A(a)>")
A(a, %composer, <>, 0)
%composer.endReplaceableGroup()
} else {
%composer.startReplaceableGroup(<>)
%composer.endReplaceableGroup()
}
A(b, %composer, <>, 0)
}
%composer.endReplaceableGroup()
}
}
A(c, %composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Example(x, %composer, %key, %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<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Example)<A()>:Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010
}
if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
x?.let { it: Int ->
if (it > 0) {
NA()
}
NA()
}
A(%composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Example(x, %composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testApplyOnComposableCallResult(): Unit = controlFlow(
"""
import androidx.compose.runtime.state
import androidx.compose.runtime.State
@Composable
fun <T> provided(value: T): State<T> = state { value }.apply {
this.value = value
}
""",
"""
@Composable
fun <T> provided(value: T, %composer: Composer<*>?, %key: Int, %changed: Int): State<T> {
%composer.startReplaceableGroup(<> xor %key, "C(provided):Test.kt")
val tmp0 = state(null, {
val tmp0_return = value
tmp0_return
}, %composer, <>, 0, 0b0001).apply {
value = value
}
%composer.endReplaceableGroup()
return tmp0
}
"""
)
@Test
fun testReturnInlinedExpressionWithCall(): Unit = controlFlow(
"""
import androidx.compose.runtime.state
import androidx.compose.runtime.State
@Composable
fun Test(x: Int): Int {
return x.let {
A()
123
}
}
""",
"""
@Composable
fun Test(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int {
%composer.startReplaceableGroup(<> xor %key, "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<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test)<W>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
W(composableLambda(%composer, <>, true, "C<A()>:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int ->
if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
A(%composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
}, %composer, <>, 0b0110)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test(%composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testCallingAnInlineWrapperComposable(): Unit = controlFlow(
"""
@Composable
fun Test() {
IW {
A()
}
}
""",
"""
@Composable
fun Test(%composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test)<IW>:Test.kt")
if (%changed !== 0 || !%composer.skipping) {
IW({ %composer: Composer<*>?, %key: Int, %changed: Int ->
%composer.startReplaceableGroup(<>, "C<A()>:Test.kt")
if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
A(%composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endReplaceableGroup()
}, %composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test(%composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testComposableWithInlineClass(): Unit = controlFlow(
"""
@Composable
fun Test(value: InlineClass) {
A()
}
""",
"""
@Composable
fun Test(value: InlineClass, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test)P(0:InlineClass)<A()>:Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(value.value)) 0b0100 else 0b0010
}
if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
A(%composer, <>, 0)
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test(value, %composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testParameterOrderInformation(): Unit = controlFlow(
"""
@Composable fun Test01(p0: Int, p1: Int, p2: Int, p3: Int) { }
@Composable fun Test02(p0: Int, p1: Int, p3: Int, p2: Int) { }
@Composable fun Test03(p0: Int, p2: Int, p1: Int, p3: Int) { }
@Composable fun Test04(p0: Int, p2: Int, p3: Int, p1: Int) { }
@Composable fun Test05(p0: Int, p3: Int, p1: Int, p2: Int) { }
@Composable fun Test06(p0: Int, p3: Int, p2: Int, p1: Int) { }
@Composable fun Test07(p1: Int, p0: Int, p2: Int, p3: Int) { }
@Composable fun Test08(p1: Int, p0: Int, p3: Int, p2: Int) { }
@Composable fun Test09(p1: Int, p2: Int, p0: Int, p3: Int) { }
@Composable fun Test00(p1: Int, p2: Int, p3: Int, p0: Int) { }
@Composable fun Test11(p1: Int, p3: Int, p0: Int, p2: Int) { }
@Composable fun Test12(p1: Int, p3: Int, p2: Int, p0: Int) { }
@Composable fun Test13(p2: Int, p0: Int, p1: Int, p3: Int) { }
@Composable fun Test14(p2: Int, p0: Int, p3: Int, p1: Int) { }
@Composable fun Test15(p2: Int, p1: Int, p0: Int, p3: Int) { }
@Composable fun Test16(p2: Int, p1: Int, p3: Int, p0: Int) { }
@Composable fun Test17(p2: Int, p3: Int, p0: Int, p1: Int) { }
@Composable fun Test18(p2: Int, p3: Int, p1: Int, p0: Int) { }
@Composable fun Test19(p3: Int, p0: Int, p1: Int, p2: Int) { }
@Composable fun Test20(p3: Int, p0: Int, p2: Int, p1: Int) { }
@Composable fun Test21(p3: Int, p1: Int, p0: Int, p2: Int) { }
@Composable fun Test22(p3: Int, p1: Int, p2: Int, p0: Int) { }
@Composable fun Test23(p3: Int, p2: Int, p0: Int, p1: Int) { }
@Composable fun Test24(p3: Int, p2: Int, p1: Int, p0: Int) { }
""",
"""
@Composable
fun Test01(p0: Int, p1: Int, p2: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test01):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test01(p0, p1, p2, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test02(p0: Int, p1: Int, p3: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test02)P(!2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test02(p0, p1, p3, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test03(p0: Int, p2: Int, p1: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test03)P(!1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test03(p0, p2, p1, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test04(p0: Int, p2: Int, p3: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test04)P(!1,2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test04(p0, p2, p3, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test05(p0: Int, p3: Int, p1: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test05)P(!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test05(p0, p3, p1, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test06(p0: Int, p3: Int, p2: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test06)P(!1,3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test06(p0, p3, p2, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test07(p1: Int, p0: Int, p2: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test07)P(1):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test07(p1, p0, p2, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test08(p1: Int, p0: Int, p3: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test08)P(1!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test08(p1, p0, p3, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test09(p1: Int, p2: Int, p0: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test09)P(1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test09(p1, p2, p0, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test00(p1: Int, p2: Int, p3: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test00)P(1,2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test00(p1, p2, p3, p0, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test11(p1: Int, p3: Int, p0: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test11)P(1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test11(p1, p3, p0, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test12(p1: Int, p3: Int, p2: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test12)P(1,3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test12(p1, p3, p2, p0, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test13(p2: Int, p0: Int, p1: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test13)P(2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test13(p2, p0, p1, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test14(p2: Int, p0: Int, p3: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test14)P(2!1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test14(p2, p0, p3, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test15(p2: Int, p1: Int, p0: Int, p3: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test15)P(2,1):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test15(p2, p1, p0, p3, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test16(p2: Int, p1: Int, p3: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test16)P(2,1,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test16(p2, p1, p3, p0, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test17(p2: Int, p3: Int, p0: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test17)P(2,3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test17(p2, p3, p0, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test18(p2: Int, p3: Int, p1: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test18)P(2,3,1):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test18(p2, p3, p1, p0, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test19(p3: Int, p0: Int, p1: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test19)P(3):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test19(p3, p0, p1, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test20(p3: Int, p0: Int, p2: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test20)P(3!1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test20(p3, p0, p2, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test21(p3: Int, p1: Int, p0: Int, p2: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test21)P(3,1):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test21(p3, p1, p0, p2, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test22(p3: Int, p1: Int, p2: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test22)P(3,1,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test22(p3, p1, p2, p0, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test23(p3: Int, p2: Int, p0: Int, p1: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test23)P(3,2):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test23(p3, p2, p0, p1, %composer, %key, %changed or 0b0001)
}
}
@Composable
fun Test24(p3: Int, p2: Int, p1: Int, p0: Int, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test24)P(3,2,1):Test.kt")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(p3)) 0b0100 else 0b0010
}
if (%changed and 0b00011000 === 0) {
%dirty = %dirty or if (%composer.changed(p2)) 0b00010000 else 0b1000
}
if (%changed and 0b01100000 === 0) {
%dirty = %dirty or if (%composer.changed(p1)) 0b01000000 else 0b00100000
}
if (%changed and 0b000110000000 === 0) {
%dirty = %dirty or if (%composer.changed(p0)) 0b000100000000 else 0b10000000
}
if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test24(p3, p2, p1, p0, %composer, %key, %changed or 0b0001)
}
}
"""
)
@Test
fun testSourceInformationWithPackageName(): Unit = verifyComposeIrTransform(
source = """
package androidx.compose.runtime.tests
import androidx.compose.runtime.Composable
@Composable
fun Test(value: LocalInlineClass) {
}
""",
extra = """
package androidx.compose.runtime.tests
inline class LocalInlineClass(val value: Int)
""",
expectedTransformed = """
@Composable
fun Test(value: LocalInlineClass, %composer: Composer<*>?, %key: Int, %changed: Int) {
%composer.startRestartGroup(<> xor %key, "C(Test)P(0:c#runtime.tests.LocalInlineClass):Test.kt#992ot2")
val %dirty = %changed
if (%changed and 0b0110 === 0) {
%dirty = %dirty or if (%composer.changed(value.value)) 0b0100 else 0b0010
}
if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) {
} else {
%composer.skipToGroupEnd()
}
%composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int ->
Test(value, %composer, %key, %changed or 0b0001)
}
}
"""
)
}