| /* |
| * 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.plugins.kotlin |
| |
| import org.junit.Test |
| |
| class FunctionBodySkippingTransformTests : ComposeIrTransformTest() { |
| private fun comparisonPropagation( |
| unchecked: String, |
| checked: String, |
| expectedTransformed: String, |
| dumpTree: Boolean = false |
| ) = verifyComposeIrTransform( |
| """ |
| import androidx.compose.Composable |
| import androidx.compose.ComposableContract |
| |
| $checked |
| """.trimIndent(), |
| expectedTransformed, |
| """ |
| import androidx.compose.Composable |
| |
| $unchecked |
| """.trimIndent(), |
| dumpTree |
| ) |
| |
| @Test |
| fun testIfInLambda(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0) {} |
| @Composable fun Wrap(children: @Composable () -> Unit) { |
| children() |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int = 0, y: Int = 0) { |
| Wrap { |
| if (x > 0) { |
| A(x) |
| } else { |
| A(x) |
| } |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, y: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<Wrap>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| val y = y |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| x = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| y = 0 |
| } |
| Wrap(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (x > 0) { |
| %composer.startReplaceableGroup(<>, "<A(x)>") |
| A(x, 0, %composer, <>, 0b0110 and %dirty, 0b0010) |
| %composer.endReplaceableGroup() |
| } else { |
| %composer.startReplaceableGroup(<>, "<A(x)>") |
| A(x, 0, %composer, <>, 0b0110 and %dirty, 0b0010) |
| %composer.endReplaceableGroup() |
| } |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, y, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testUntrackedLambda(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0) {} |
| @Composable fun Wrap(children: @Composable () -> Unit) { |
| children() |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int = 0, y: Int = 0) { |
| Wrap @ComposableContract(tracked = false) { |
| A(x) |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, y: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<Wrap>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| val y = y |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| x = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| y = 0 |
| } |
| Wrap(composableLambda(%composer, <>, false, "C:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| %composer.startReplaceableGroup(<> xor %key, "<A(x)>") |
| A(x, 0, %composer, <>, 0b0110 and %dirty, 0b0010) |
| %composer.endReplaceableGroup() |
| }, %composer, <>, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, y, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSimpleColumn(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| import androidx.compose.Immutable |
| |
| @Stable |
| interface Modifier { |
| companion object : Modifier { } |
| } |
| |
| @Immutable |
| interface Arrangement { |
| @Immutable |
| interface Vertical : Arrangement |
| |
| object Top : Vertical |
| } |
| |
| enum class LayoutOrientation { |
| Horizontal, |
| Vertical |
| } |
| |
| enum class SizeMode { |
| Wrap, |
| Expand |
| } |
| |
| @Immutable |
| data class Alignment( |
| val verticalBias: Float, |
| val horizontalBias: Float |
| ) { |
| @Immutable |
| data class Horizontal(val bias: Float) |
| |
| companion object { |
| val Start = Alignment.Horizontal(-1f) |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun RowColumnImpl( |
| orientation: LayoutOrientation, |
| modifier: Modifier = Modifier, |
| arrangement: Arrangement.Vertical = Arrangement.Top, |
| crossAxisAlignment: Alignment.Horizontal = Alignment.Start, |
| crossAxisSize: SizeMode = SizeMode.Wrap, |
| children: @Composable() ()->Unit |
| ) { |
| println() |
| } |
| |
| @Composable |
| fun Column( |
| modifier: Modifier = Modifier, |
| verticalArrangement: Arrangement.Vertical = Arrangement.Top, |
| horizontalGravity: Alignment.Horizontal = Alignment.Start, |
| children: @Composable() ()->Unit |
| ) { |
| RowColumnImpl( |
| orientation = LayoutOrientation.Vertical, |
| arrangement = verticalArrangement, |
| crossAxisAlignment = horizontalGravity, |
| crossAxisSize = SizeMode.Wrap, |
| modifier = modifier, |
| children = children |
| ) |
| } |
| """, |
| """ |
| @Composable |
| fun RowColumnImpl(orientation: LayoutOrientation, modifier: Modifier?, arrangement: Vertical?, crossAxisAlignment: Horizontal?, crossAxisSize: SizeMode?, children: Function3<Composer<*>, Int, Int, Unit>, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(RowColumnImpl)P(5,4!1,2,3):Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| val arrangement = arrangement |
| val crossAxisAlignment = crossAxisAlignment |
| val crossAxisSize = crossAxisSize |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(orientation)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(arrangement)) 0b01000000 else 0b00100000 |
| } |
| if (%changed and 0b000110000000 === 0) { |
| %dirty = %dirty or if (%default and 0b1000 === 0 && %composer.changed(crossAxisAlignment)) 0b000100000000 else 0b10000000 |
| } |
| if (%default and 0b00010000 !== 0) { |
| %dirty = %dirty or 0b011000000000 |
| } else if (%changed and 0b011000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(crossAxisSize)) 0b010000000000 else 0b001000000000 |
| } |
| if (%default and 0b00100000 !== 0) { |
| %dirty = %dirty or 0b0001100000000000 |
| } else if (%changed and 0b0001100000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(children)) 0b0001000000000000 else 0b100000000000 |
| } |
| if (%dirty and 0b101010101011 xor 0b101010101010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0010 !== 0) { |
| modifier = Companion |
| } |
| if (%default and 0b0100 !== 0) { |
| arrangement = Top |
| } |
| if (%default and 0b1000 !== 0) { |
| crossAxisAlignment = Companion.Start |
| %dirty = %dirty and 0b000110000000.inv() |
| } |
| if (%default and 0b00010000 !== 0) { |
| crossAxisSize = SizeMode.Wrap |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty and 0b000110000000.inv() |
| } |
| } |
| println() |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| RowColumnImpl(orientation, modifier, arrangement, crossAxisAlignment, crossAxisSize, children, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| @Composable |
| fun Column(modifier: Modifier?, verticalArrangement: Vertical?, horizontalGravity: Horizontal?, children: Function3<Composer<*>, Int, Int, Unit>, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Column)P(2,3,1)<RowCol...>:Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| val verticalArrangement = verticalArrangement |
| val horizontalGravity = horizontalGravity |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(verticalArrangement)) 0b00010000 else 0b1000 |
| } |
| if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%default and 0b0100 === 0 && %composer.changed(horizontalGravity)) 0b01000000 else 0b00100000 |
| } |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty or 0b000110000000 |
| } else if (%changed and 0b000110000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(children)) 0b000100000000 else 0b10000000 |
| } |
| if (%dirty and 0b10101011 xor 0b10101010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| modifier = Companion |
| } |
| if (%default and 0b0010 !== 0) { |
| verticalArrangement = Top |
| } |
| if (%default and 0b0100 !== 0) { |
| horizontalGravity = Companion.Start |
| %dirty = %dirty and 0b01100000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty and 0b01100000.inv() |
| } |
| } |
| val tmp0_orientation = LayoutOrientation.Vertical |
| val tmp1_crossAxisSize = SizeMode.Wrap |
| RowColumnImpl(tmp0_orientation, modifier, verticalArrangement, horizontalGravity, tmp1_crossAxisSize, children, %composer, <>, 0b011000000110 or 0b00011000 and %dirty shl 0b0010 or 0b01100000 and %dirty shl 0b0010 or 0b000110000000 and %dirty shl 0b0010 or 0b0001100000000000 and %dirty shl 0b0100, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Column(modifier, verticalArrangement, horizontalGravity, children, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSimplerBox(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| |
| @Stable |
| interface Modifier { |
| companion object : Modifier { } |
| } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier = Modifier) { |
| println() |
| } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(SimpleBox):Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| modifier = Companion |
| } |
| println() |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| SimpleBox(modifier, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testDefaultSkipping(): Unit = comparisonPropagation( |
| """ |
| fun newInt(): Int = 123 |
| """, |
| """ |
| @Composable |
| fun Example(a: Int = newInt()) { |
| print(a) |
| } |
| """, |
| """ |
| @Composable |
| fun Example(a: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example):Test.kt") |
| val %dirty = %changed |
| val a = a |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%default and 0b0001 === 0 && %composer.changed(a)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| a = newInt() |
| %dirty = %dirty and 0b0110.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty and 0b0110.inv() |
| } |
| } |
| print(a) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(a, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSimpleBoxWithShape(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| |
| @Stable |
| interface Modifier { |
| companion object : Modifier { } |
| } |
| |
| interface Shape { |
| } |
| |
| val RectangleShape = object : Shape { } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier = Modifier, shape: Shape = RectangleShape) { |
| println() |
| } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier?, shape: Shape?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(SimpleBox):Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| val shape = shape |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b1000 |
| } |
| if (%default.inv() and 0b0010 !== 0 || %dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| modifier = Companion |
| } |
| if (%default and 0b0010 !== 0) { |
| shape = RectangleShape |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| println() |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| SimpleBox(modifier, shape, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSimpleBox(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| |
| @Stable |
| interface Modifier { |
| companion object : Modifier { } |
| } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier = Modifier, children: @Composable() () -> Unit = {}) { |
| println() |
| } |
| """, |
| """ |
| @Composable |
| fun SimpleBox(modifier: Modifier?, children: Function3<Composer<*>, Int, Int, Unit>?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(SimpleBox)P(1):Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| val children = children |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(children)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| modifier = Companion |
| } |
| if (%default and 0b0010 !== 0) { |
| children = composableLambdaInstance(<>, true) { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| Unit |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| println() |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| SimpleBox(modifier, children, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambdaWithStableParams(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Immutable |
| |
| @Immutable class Foo |
| @Composable fun A(x: Int) {} |
| @Composable fun B(y: Foo) {} |
| """, |
| """ |
| val foo = @Composable { x: Int, y: Foo -> |
| A(x) |
| B(y) |
| } |
| """, |
| """ |
| val foo: Function5<Int, Foo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b00101011 xor 0b00101010 !== 0 || !%composer.skipping) { |
| A(x, %composer, <>, 0b0110 and %dirty) |
| B(y, %composer, <>, 0b0110 and %dirty shr 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambdaWithUnstableParams(): Unit = comparisonPropagation( |
| """ |
| class Foo |
| @Composable fun A(x: Int) {} |
| @Composable fun B(y: Foo) {} |
| """, |
| """ |
| val foo = @Composable { x: Int, y: Foo -> |
| A(x) |
| B(y) |
| } |
| """, |
| """ |
| val foo: Function5<Int, Foo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, y: Foo, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| A(x, %composer, <>, 0b0110 and %changed) |
| B(y, %composer, <>, 0b0110 and %changed shr 0b0010) |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambdaWithStableParamsAndReturnValue(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable fun SomeThing(children: @Composable() () -> Unit) {} |
| |
| @Composable |
| fun Example() { |
| SomeThing { |
| val id = object {} |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun SomeThing(children: Function3<Composer<*>, Int, Int, Unit>, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(SomeThing):Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(children)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| SomeThing(children, %composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<SomeTh...>:Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| SomeThing(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| val id = object |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(%composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testPrimitiveVarargParams(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable |
| fun B(vararg values: Int) { |
| print(values) |
| } |
| """, |
| """ |
| @Composable |
| fun B(values: IntArray, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B):Test.kt") |
| val %dirty = %changed |
| %composer.startReplaceableGroup(values.size) |
| val tmp0_iterator = values.iterator() |
| while (tmp0_iterator.hasNext()) { |
| val value = tmp0_iterator.next() |
| %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0 |
| } |
| %composer.endReplaceableGroup() |
| if (%dirty and 0b0110 === 0) { |
| %dirty = %dirty or 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| print(values) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| B(*values, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testStableVarargParams(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Immutable |
| @Immutable class Foo |
| """, |
| """ |
| @Composable |
| fun B(vararg values: Foo) { |
| print(values) |
| } |
| """, |
| """ |
| @Composable |
| fun B(values: Array<out Foo>, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B):Test.kt") |
| val %dirty = %changed |
| %composer.startReplaceableGroup(values.size) |
| val tmp0_iterator = values.iterator() |
| while (tmp0_iterator.hasNext()) { |
| val value = tmp0_iterator.next() |
| %dirty = %dirty or if (%composer.changed(value)) 0b0100 else 0 |
| } |
| %composer.endReplaceableGroup() |
| if (%dirty and 0b0110 === 0) { |
| %dirty = %dirty or 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| print(values) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| B(*values, %composer, %key, %changed or 0b0001) |
| } |
| } |
| |
| """ |
| ) |
| |
| @Test |
| fun testUnstableVarargParams(): Unit = comparisonPropagation( |
| """ |
| class Foo |
| """, |
| """ |
| @Composable |
| fun B(vararg values: Foo) { |
| print(values) |
| } |
| """, |
| """ |
| @Composable |
| fun B(values: Array<out Foo>, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B):Test.kt") |
| print(values) |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| B(*values, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testReceiverParamSkippability(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| class Foo { |
| val counter: Int = 0 |
| @Composable fun A() { |
| print("hello world") |
| } |
| @Composable fun B() { |
| print(counter) |
| } |
| } |
| """, |
| """ |
| class Foo { |
| val counter: Int = 0 |
| @Composable |
| fun A(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A):Test.kt") |
| val %dirty = %changed |
| %dirty = %dirty or 0b0110 |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| print("hello world") |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| val tmp0_rcvr = <this> |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| tmp0_rcvr.A(%composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun B(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B):Test.kt") |
| val %dirty = %changed |
| print(counter) |
| val tmp0_rcvr = <this> |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| tmp0_rcvr.B(%composer, %key, %changed or 0b0001) |
| } |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableParameter(): Unit = comparisonPropagation( |
| """ |
| @Composable fun makeInt(): Int = 10 |
| """, |
| """ |
| @Composable |
| fun Example(a: Int = 0, b: Int = makeInt(), c: Int = 0) { |
| |
| } |
| """, |
| """ |
| @Composable |
| fun Example(a: Int, b: Int, c: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<makeIn...>:Test.kt") |
| val %dirty = %changed |
| val a = a |
| val b = b |
| val c = c |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(b)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(c)) 0b01000000 else 0b00100000 |
| } |
| if (%dirty and 0b00101011 xor 0b00101010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| a = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| b = makeInt(%composer, <>, 0) |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| if (%default and 0b0100 !== 0) { |
| c = 0 |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(a, b, c, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableWithAndWithoutDefaultParams(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0) {} |
| """, |
| """ |
| @Composable fun Wrap(y: Int, children: @Composable (x: Int) -> Unit) { |
| children(y) |
| } |
| @Composable |
| fun Test(x: Int = 0, y: Int = 0) { |
| Wrap(10) { |
| A(x) |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun Wrap(y: Int, children: Function4<@[ParameterName(name = 'x')] Int, Composer<*>, Int, Int, Unit>, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Wrap)P(1)<childr...>:Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(children)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| children(y, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Wrap(y, children, %composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun Test(x: Int, y: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<Wrap(1...>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| val y = y |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| x = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| y = 0 |
| } |
| Wrap(10, composableLambda(%composer, <>, true, "C<A(x)>:Test.kt") { it: Int, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(it)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| A(x, 0, %composer, <>, 0b0110 and %dirty, 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b00011110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, y, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableWithReturnValue(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0) {} |
| """, |
| """ |
| @Composable |
| fun Test(x: Int = 0, y: Int = 0): Int { |
| A(x, y) |
| return x + y |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, y: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int): Int { |
| %composer.startReplaceableGroup(<> xor %key, "C(Test)<A(x,>:Test.kt") |
| val x = if (%default and 0b0001 !== 0) 0 else x |
| val y = if (%default and 0b0010 !== 0) 0 else y |
| A(x, y, %composer, <>, 0b0110 and %changed or 0b00011000 and %changed, 0) |
| val tmp0 = x + y |
| %composer.endReplaceableGroup() |
| return tmp0 |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambda(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0) {} |
| """, |
| """ |
| val test = @Composable { x: Int -> |
| A(x) |
| } |
| """, |
| """ |
| val test: Function4<Int, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { x: Int, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| A(x, 0, %composer, <>, 0b0110 and %dirty, 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableFunExprBody(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 } |
| """, |
| """ |
| @Composable fun Test(x: Int) = A() |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int): Int { |
| %composer.startReplaceableGroup(<> xor %key, "C(Test)<A()>:Test.kt") |
| val tmp0 = A(0, 0, %composer, <>, 0, 0b0011) |
| %composer.endReplaceableGroup() |
| return tmp0 |
| } |
| """ |
| ) |
| |
| @Test |
| fun testParamReordering(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 } |
| """, |
| """ |
| @Composable fun Test(x: Int, y: Int) { |
| A(y = y, x = x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, y: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(y>:Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| A(x, y, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, y, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testStableUnstableParams(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int = 0, y: Int = 0): Int { return 10 } |
| class Foo |
| """, |
| """ |
| @Composable fun CanSkip(a: Int = 0, b: Foo = Foo()) { |
| print("Hello World") |
| } |
| @Composable fun CannotSkip(a: Int, b: Foo) { |
| print("Hello World") |
| } |
| @Composable fun NoParams() { |
| print("Hello World") |
| } |
| """, |
| """ |
| @Composable |
| fun CanSkip(a: Int, b: Foo?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(CanSkip):Test.kt") |
| val %dirty = %changed |
| val a = a |
| val b = b |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b1000 |
| } |
| if (%default.inv() and 0b0010 !== 0 || %dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| a = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| b = Foo() |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| print("Hello World") |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| CanSkip(a, b, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| @Composable |
| fun CannotSkip(a: Int, b: Foo, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(CannotSkip):Test.kt") |
| print("Hello World") |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| CannotSkip(a, b, %composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun NoParams(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(NoParams):Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| print("Hello World") |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| NoParams(%composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testNoParams(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A() {} |
| """, |
| """ |
| @Composable |
| fun Test() { |
| A() |
| } |
| """, |
| """ |
| @Composable |
| fun Test(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A()>:Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| A(%composer, <>, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(%composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSingleStableParam(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int) {} |
| """, |
| """ |
| @Composable |
| fun Test(x: Int) { |
| A(x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(x)>: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) { |
| A(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testInlineClassDefaultParameter(): Unit = comparisonPropagation( |
| """ |
| inline class Color(val value: Int) { |
| companion object { |
| val Unset = Color(0) |
| } |
| } |
| """, |
| """ |
| @Composable |
| fun A(text: String) { |
| B(text) |
| } |
| |
| @Composable |
| fun B(text: String, color: Color = Color.Unset) {} |
| """, |
| """ |
| @Composable |
| fun A(text: String, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A)<B(text...>:Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(text)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| B(text, Color(0), %composer, <>, 0b0110 and %dirty, 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| A(text, %composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun B(text: String, color: Color, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B)P(1,0:Color):Test.kt") |
| val %dirty = %changed |
| val color = color |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(text)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(color.value)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0010 !== 0) { |
| color = Companion.Unset |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| B(text, color, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testStaticDetection(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| |
| enum class Foo { |
| Bar, |
| Bam |
| } |
| const val constInt: Int = 123 |
| val normInt = 345 |
| val stableTopLevelProp: Modifier = Modifier |
| @Composable fun C(x: Any?) {} |
| @Stable |
| interface Modifier { |
| companion object : Modifier { } |
| } |
| inline class Dp(val value: Int) |
| @Stable |
| fun stableFun(x: Int): Int = x * x |
| @Stable |
| operator fun Dp.plus(other: Dp): Dp = Dp(this.value + other.value) |
| @Stable |
| val Int.dp: Dp get() = Dp(this) |
| @Composable fun D(content: @Composable() () -> Unit) {} |
| """, |
| """ |
| // all of these should result in 0b0110 |
| @Composable fun A() { |
| val x = 123 |
| D {} |
| C({}) |
| C(stableFun(123)) |
| C(16.dp + 10.dp) |
| C(Dp(16)) |
| C(16.dp) |
| C(normInt) |
| C(Int.MAX_VALUE) |
| C(stableTopLevelProp) |
| C(Modifier) |
| C(Foo.Bar) |
| C(constInt) |
| C(123) |
| C(123 + 345) |
| C(x) |
| C(x * 123) |
| } |
| // all of these should result in 0b0000 |
| @Composable fun B() { |
| C(Math.random()) |
| C(Math.random() / 100f) |
| } |
| """, |
| """ |
| @Composable |
| fun A(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A)<D>,<C({})>,<{}>,<C(stab...>,<C(16.d...>,<C(Dp(1...>,<C(16.d...>,<C(norm...>,<C(Int....>,<C(stab...>,<C(Modi...>,<C(Foo....>,<C(cons...>,<C(123)>,<C(123>,<C(x)>,<C(x>:Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| val x = 123 |
| D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| Unit |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| C(remember({ |
| { |
| } |
| }, %composer, <>, 0), %composer, <>, 0b0110) |
| C(stableFun(123), %composer, <>, 0b0110) |
| C(16.dp + 10.dp, %composer, <>, 0b0110) |
| C(Dp(16), %composer, <>, 0b0110) |
| C(16.dp, %composer, <>, 0b0110) |
| C(normInt, %composer, <>, 0b0110) |
| C(Companion.MAX_VALUE, %composer, <>, 0b0110) |
| C(stableTopLevelProp, %composer, <>, 0b0110) |
| C(Companion, %composer, <>, 0b0110) |
| C(Foo.Bar, %composer, <>, 0b0110) |
| C(constInt, %composer, <>, 0b0110) |
| C(123, %composer, <>, 0b0110) |
| C(123 + 345, %composer, <>, 0b0110) |
| C(x, %composer, <>, 0b0110) |
| C(x * 123, %composer, <>, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| A(%composer, %key, %changed or 0b0001) |
| } |
| } |
| @Composable |
| fun B(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(B)<C(Math...>,<C(Math...>:Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| C(random(), %composer, <>, 0) |
| C(random() / 100.0f, %composer, <>, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| B(%composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testAnnotationChecker(): Unit = comparisonPropagation( |
| """ |
| @Composable fun D(content: @Composable() () -> Unit) {} |
| """, |
| """ |
| @Composable fun Example() { |
| D {} |
| } |
| """, |
| """ |
| @Composable |
| fun Example(%composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<D>:Test.kt") |
| if (%changed !== 0 || !%composer.skipping) { |
| D(composableLambda(%composer, <>, true, "C:Test.kt") { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| if (%changed and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| Unit |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(%composer, %key, %changed or 0b0001) |
| } |
| } |
| |
| """ |
| ) |
| |
| @Test |
| fun testSingleStableParamWithDefault(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int) {} |
| """, |
| """ |
| @Composable |
| fun Test(x: Int = 0) { |
| A(x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(x)>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| x = 0 |
| } |
| A(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSingleStableParamWithComposableDefault(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Int) {} |
| @Composable fun I(): Int { return 10 } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int = I()) { |
| A(x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<I()>,<A(x)>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%default and 0b0001 === 0 && %composer.changed(x)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| x = I(%composer, <>, 0) |
| %dirty = %dirty and 0b0110.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty and 0b0110.inv() |
| } |
| } |
| A(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSingleUnstableParam(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Foo) {} |
| class Foo |
| """, |
| """ |
| @Composable |
| fun Test(x: Foo) { |
| A(x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Foo, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(x)>:Test.kt") |
| A(x, %composer, <>, 0b0110 and %changed) |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testSingleUnstableParamWithDefault(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(x: Foo) {} |
| class Foo |
| """, |
| """ |
| @Composable |
| fun Test(x: Foo = Foo()) { |
| A(x) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(x: Foo?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(x)>:Test.kt") |
| val %dirty = %changed |
| val x = x |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0010 |
| } |
| if (%default.inv() and 0b0001 !== 0 || %dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| x = Foo() |
| %dirty = %dirty and 0b0110.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty and 0b0110.inv() |
| } |
| } |
| A(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(x, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testManyNonOptionalParams(): Unit = comparisonPropagation( |
| """ |
| @Composable fun A(a: Int, b: Boolean, c: Int, d: Foo, e: List<Int>) {} |
| class Foo |
| """, |
| """ |
| @Composable |
| fun Test(a: Int, b: Boolean, c: Int = 0, d: Foo = Foo(), e: List<Int> = emptyList()) { |
| A(a, b, c, d, e) |
| } |
| """, |
| """ |
| @Composable |
| fun Test(a: Int, b: Boolean, c: Int, d: Foo?, e: List<Int>?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Test)<A(a,>:Test.kt") |
| val %dirty = %changed |
| val c = c |
| val d = d |
| val e = e |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(a)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(b)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(c)) 0b01000000 else 0b00100000 |
| } |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty or 0b10000000 |
| } |
| if (%default and 0b00010000 !== 0) { |
| %dirty = %dirty or 0b001000000000 |
| } |
| if (%default.inv() and 0b00011000 !== 0 || %dirty and 0b001010101011 xor 0b001010101010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0100 !== 0) { |
| c = 0 |
| } |
| if (%default and 0b1000 !== 0) { |
| d = Foo() |
| %dirty = %dirty and 0b000110000000.inv() |
| } |
| if (%default and 0b00010000 !== 0) { |
| e = emptyList() |
| %dirty = %dirty and 0b011000000000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty and 0b000110000000.inv() |
| } |
| if (%default and 0b00010000 !== 0) { |
| %dirty = %dirty and 0b011000000000.inv() |
| } |
| } |
| A(a, b, c, d, e, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty or 0b01100000 and %dirty or 0b000110000000 and %dirty or 0b011000000000 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Test(a, b, c, d, e, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testRecursiveCall(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable |
| fun X(x: Int) { |
| X(x + 1) |
| X(x) |
| } |
| """, |
| """ |
| @Composable |
| fun X(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(X)<X(x>,<X(x)>: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(x + 1, %composer, <>, 0) |
| X(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| X(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testDifferentParameters(): Unit = comparisonPropagation( |
| """ |
| @Composable fun B(a: Int, b: Int, c: Int, d: Int) {} |
| val fooGlobal = 10 |
| """, |
| """ |
| @Composable |
| fun A(x: Int) { |
| B( |
| // direct parameter |
| x, |
| // transformation |
| x + 1, |
| // literal |
| 123, |
| // expression with no parameter |
| fooGlobal |
| ) |
| } |
| """, |
| """ |
| @Composable |
| fun A(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A)<B(>: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) { |
| B(x, x + 1, 123, fooGlobal, %composer, <>, 0b000111100000 or 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| A(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testReceiverLambdaCall(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.Stable |
| |
| interface Foo { val x: Int } |
| @Stable |
| interface StableFoo { val x: Int } |
| """, |
| """ |
| val unstableUnused: @Composable Foo.() -> Unit = { |
| } |
| val unstableUsed: @Composable Foo.() -> Unit = { |
| print(x) |
| } |
| val stableUnused: @Composable StableFoo.() -> Unit = { |
| } |
| val stableUsed: @Composable StableFoo.() -> Unit = { |
| print(x) |
| } |
| """, |
| """ |
| val unstableUnused: @[ExtensionFunctionType] Function4<Foo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| %dirty = %dirty or 0b0110 |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| Unit |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| val unstableUsed: @[ExtensionFunctionType] Function4<Foo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| print(x) |
| } |
| val stableUnused: @[ExtensionFunctionType] Function4<StableFoo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| %dirty = %dirty or 0b0110 |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| Unit |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| val stableUsed: @[ExtensionFunctionType] Function4<StableFoo, Composer<*>, Int, Int, Unit> = composableLambdaInstance(<>, true) { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(<this>)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| print(x) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testNestedCalls(): Unit = comparisonPropagation( |
| """ |
| @Composable fun B(a: Int = 0, b: Int = 0, c: Int = 0) {} |
| @Composable fun Provide(children: @Composable (Int) -> Unit) {} |
| """, |
| """ |
| @Composable |
| fun A(x: Int) { |
| Provide { y -> |
| Provide { z -> |
| B(x, y, z) |
| } |
| B(x, y) |
| } |
| B(x) |
| } |
| """, |
| """ |
| @Composable |
| fun A(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A)<Provid...>,<B(x)>: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) { |
| Provide(composableLambda(%composer, <>, true, "C<Provid...>,<B(x,>:Test.kt") { y: Int, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| Provide(composableLambda(%composer, <>, true, "C<B(x,>:Test.kt") { z: Int, %composer: Composer<*>?, %key: Int, %changed: Int -> |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(z)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| B(x, y, z, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty shl 0b0010 or 0b01100000 and %dirty shl 0b0100, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| B(x, y, 0, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty shl 0b0010, 0b0100) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| }, %composer, <>, 0b0110) |
| B(x, 0, 0, %composer, <>, 0b0110 and %dirty, 0b0110) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| A(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testLocalFunction(): Unit = comparisonPropagation( |
| """ |
| @Composable fun B(a: Int, b: Int) {} |
| """, |
| """ |
| @Composable |
| fun A(x: Int) { |
| @Composable fun foo(y: Int) { |
| B(x, y) |
| } |
| foo(x) |
| } |
| """, |
| """ |
| @Composable |
| fun A(x: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(A)<foo(x)>: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) { |
| @Composable |
| fun foo(y: Int, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(foo)<B(x,>:Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(y)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| B(x, y, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty shl 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| foo(y, %composer, %key, %changed or 0b0001) |
| } |
| } |
| foo(x, %composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| A(x, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun test15Parameters(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable |
| fun Example( |
| a00: Int = 0, |
| a01: Int = 0, |
| a02: Int = 0, |
| a03: Int = 0, |
| a04: Int = 0, |
| a05: Int = 0, |
| a06: Int = 0, |
| a07: Int = 0, |
| a08: Int = 0, |
| a09: Int = 0, |
| a10: Int = 0, |
| a11: Int = 0, |
| a12: Int = 0, |
| a13: Int = 0, |
| a14: Int = 0 |
| ) { |
| // in order |
| Example( |
| a00, |
| a01, |
| a02, |
| a03, |
| a04, |
| a05, |
| a06, |
| a07, |
| a08, |
| a09, |
| a10, |
| a11, |
| a12, |
| a13, |
| a14 |
| ) |
| // in opposite order |
| Example( |
| a14, |
| a13, |
| a12, |
| a11, |
| a10, |
| a09, |
| a08, |
| a07, |
| a06, |
| a05, |
| a04, |
| a03, |
| a02, |
| a01, |
| a00 |
| ) |
| } |
| """, |
| """ |
| @Composable |
| fun Example(a00: Int, a01: Int, a02: Int, a03: Int, a04: Int, a05: Int, a06: Int, a07: Int, a08: Int, a09: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<Exampl...>,<Exampl...>:Test.kt") |
| val %dirty = %changed |
| val a00 = a00 |
| val a01 = a01 |
| val a02 = a02 |
| val a03 = a03 |
| val a04 = a04 |
| val a05 = a05 |
| val a06 = a06 |
| val a07 = a07 |
| val a08 = a08 |
| val a09 = a09 |
| val a10 = a10 |
| val a11 = a11 |
| val a12 = a12 |
| val a13 = a13 |
| val a14 = a14 |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(a00)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a01)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a02)) 0b01000000 else 0b00100000 |
| } |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty or 0b000110000000 |
| } else if (%changed and 0b000110000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a03)) 0b000100000000 else 0b10000000 |
| } |
| if (%default and 0b00010000 !== 0) { |
| %dirty = %dirty or 0b011000000000 |
| } else if (%changed and 0b011000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a04)) 0b010000000000 else 0b001000000000 |
| } |
| if (%default and 0b00100000 !== 0) { |
| %dirty = %dirty or 0b0001100000000000 |
| } else if (%changed and 0b0001100000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a05)) 0b0001000000000000 else 0b100000000000 |
| } |
| if (%default and 0b01000000 !== 0) { |
| %dirty = %dirty or 0b0110000000000000 |
| } else if (%changed and 0b0110000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a06)) 0b0100000000000000 else 0b0010000000000000 |
| } |
| if (%default and 0b10000000 !== 0) { |
| %dirty = %dirty or 0b00011000000000000000 |
| } else if (%changed and 0b00011000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a07)) 0b00010000000000000000 else 0b1000000000000000 |
| } |
| if (%default and 0b000100000000 !== 0) { |
| %dirty = %dirty or 0b01100000000000000000 |
| } else if (%changed and 0b01100000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a08)) 0b01000000000000000000 else 0b00100000000000000000 |
| } |
| if (%default and 0b001000000000 !== 0) { |
| %dirty = %dirty or 0b000110000000000000000000 |
| } else if (%changed and 0b000110000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a09)) 0b000100000000000000000000 else 0b10000000000000000000 |
| } |
| if (%default and 0b010000000000 !== 0) { |
| %dirty = %dirty or 0b011000000000000000000000 |
| } else if (%changed and 0b011000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a10)) 0b010000000000000000000000 else 0b001000000000000000000000 |
| } |
| if (%default and 0b100000000000 !== 0) { |
| %dirty = %dirty or 0b0001100000000000000000000000 |
| } else if (%changed and 0b0001100000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a11)) 0b0001000000000000000000000000 else 0b100000000000000000000000 |
| } |
| if (%default and 0b0001000000000000 !== 0) { |
| %dirty = %dirty or 0b0110000000000000000000000000 |
| } else if (%changed and 0b0110000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a12)) 0b0100000000000000000000000000 else 0b0010000000000000000000000000 |
| } |
| if (%default and 0b0010000000000000 !== 0) { |
| %dirty = %dirty or 0b00011000000000000000000000000000 |
| } else if (%changed and 0b00011000000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a13)) 0b00010000000000000000000000000000 else 0b1000000000000000000000000000 |
| } |
| if (%default and 0b0100000000000000 !== 0) { |
| %dirty = %dirty or 0b01100000000000000000000000000000 |
| } else if (%changed and 0b01100000000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a14)) 0b01000000000000000000000000000000 else 0b00100000000000000000000000000000 |
| } |
| if (%dirty and 0b00101010101010101010101010101011 xor 0b00101010101010101010101010101010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| a00 = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| a01 = 0 |
| } |
| if (%default and 0b0100 !== 0) { |
| a02 = 0 |
| } |
| if (%default and 0b1000 !== 0) { |
| a03 = 0 |
| } |
| if (%default and 0b00010000 !== 0) { |
| a04 = 0 |
| } |
| if (%default and 0b00100000 !== 0) { |
| a05 = 0 |
| } |
| if (%default and 0b01000000 !== 0) { |
| a06 = 0 |
| } |
| if (%default and 0b10000000 !== 0) { |
| a07 = 0 |
| } |
| if (%default and 0b000100000000 !== 0) { |
| a08 = 0 |
| } |
| if (%default and 0b001000000000 !== 0) { |
| a09 = 0 |
| } |
| if (%default and 0b010000000000 !== 0) { |
| a10 = 0 |
| } |
| if (%default and 0b100000000000 !== 0) { |
| a11 = 0 |
| } |
| if (%default and 0b0001000000000000 !== 0) { |
| a12 = 0 |
| } |
| if (%default and 0b0010000000000000 !== 0) { |
| a13 = 0 |
| } |
| if (%default and 0b0100000000000000 !== 0) { |
| a14 = 0 |
| } |
| Example(a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty or 0b01100000 and %dirty or 0b000110000000 and %dirty or 0b011000000000 and %dirty or 0b0001100000000000 and %dirty or 0b0110000000000000 and %dirty or 0b00011000000000000000 and %dirty or 0b01100000000000000000 and %dirty or 0b000110000000000000000000 and %dirty or 0b011000000000000000000000 and %dirty or 0b0001100000000000000000000000 and %dirty or 0b0110000000000000000000000000 and %dirty or 0b00011000000000000000000000000000 and %dirty or 0b01100000000000000000000000000000 and %dirty, 0) |
| Example(a14, a13, a12, a11, a10, a09, a08, a07, a06, a05, a04, a03, a02, a01, a00, %composer, <>, 0b0110 and %dirty shr 0b00011100 or 0b00011000 and %dirty shr 0b00011000 or 0b01100000 and %dirty shr 0b00010100 or 0b000110000000 and %dirty shr 0b00010000 or 0b011000000000 and %dirty shr 0b1100 or 0b0001100000000000 and %dirty shr 0b1000 or 0b0110000000000000 and %dirty shr 0b0100 or 0b00011000000000000000 and %dirty or 0b01100000000000000000 and %dirty shl 0b0100 or 0b000110000000000000000000 and %dirty shl 0b1000 or 0b011000000000000000000000 and %dirty shl 0b1100 or 0b0001100000000000000000000000 and %dirty shl 0b00010000 or 0b0110000000000000000000000000 and %dirty shl 0b00010100 or 0b00011000000000000000000000000000 and %dirty shl 0b00011000 or 0b01100000000000000000000000000000 and %dirty shl 0b00011100, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun test16Parameters(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable |
| fun Example( |
| a00: Int = 0, |
| a01: Int = 0, |
| a02: Int = 0, |
| a03: Int = 0, |
| a04: Int = 0, |
| a05: Int = 0, |
| a06: Int = 0, |
| a07: Int = 0, |
| a08: Int = 0, |
| a09: Int = 0, |
| a10: Int = 0, |
| a11: Int = 0, |
| a12: Int = 0, |
| a13: Int = 0, |
| a14: Int = 0, |
| a15: Int = 0 |
| ) { |
| // in order |
| Example( |
| a00, |
| a01, |
| a02, |
| a03, |
| a04, |
| a05, |
| a06, |
| a07, |
| a08, |
| a09, |
| a10, |
| a11, |
| a12, |
| a13, |
| a14, |
| a15 |
| ) |
| // in opposite order |
| Example( |
| a15, |
| a14, |
| a13, |
| a12, |
| a11, |
| a10, |
| a09, |
| a08, |
| a07, |
| a06, |
| a05, |
| a04, |
| a03, |
| a02, |
| a01, |
| a00 |
| ) |
| } |
| """, |
| """ |
| @Composable |
| fun Example(a00: Int, a01: Int, a02: Int, a03: Int, a04: Int, a05: Int, a06: Int, a07: Int, a08: Int, a09: Int, a10: Int, a11: Int, a12: Int, a13: Int, a14: Int, a15: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %changed1: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<Exampl...>,<Exampl...>:Test.kt") |
| val %dirty = %changed |
| val %dirty1 = %changed1 |
| val a00 = a00 |
| val a01 = a01 |
| val a02 = a02 |
| val a03 = a03 |
| val a04 = a04 |
| val a05 = a05 |
| val a06 = a06 |
| val a07 = a07 |
| val a08 = a08 |
| val a09 = a09 |
| val a10 = a10 |
| val a11 = a11 |
| val a12 = a12 |
| val a13 = a13 |
| val a14 = a14 |
| val a15 = a15 |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(a00)) 0b0100 else 0b0010 |
| } |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty or 0b00011000 |
| } else if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a01)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a02)) 0b01000000 else 0b00100000 |
| } |
| if (%default and 0b1000 !== 0) { |
| %dirty = %dirty or 0b000110000000 |
| } else if (%changed and 0b000110000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a03)) 0b000100000000 else 0b10000000 |
| } |
| if (%default and 0b00010000 !== 0) { |
| %dirty = %dirty or 0b011000000000 |
| } else if (%changed and 0b011000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a04)) 0b010000000000 else 0b001000000000 |
| } |
| if (%default and 0b00100000 !== 0) { |
| %dirty = %dirty or 0b0001100000000000 |
| } else if (%changed and 0b0001100000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a05)) 0b0001000000000000 else 0b100000000000 |
| } |
| if (%default and 0b01000000 !== 0) { |
| %dirty = %dirty or 0b0110000000000000 |
| } else if (%changed and 0b0110000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a06)) 0b0100000000000000 else 0b0010000000000000 |
| } |
| if (%default and 0b10000000 !== 0) { |
| %dirty = %dirty or 0b00011000000000000000 |
| } else if (%changed and 0b00011000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a07)) 0b00010000000000000000 else 0b1000000000000000 |
| } |
| if (%default and 0b000100000000 !== 0) { |
| %dirty = %dirty or 0b01100000000000000000 |
| } else if (%changed and 0b01100000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a08)) 0b01000000000000000000 else 0b00100000000000000000 |
| } |
| if (%default and 0b001000000000 !== 0) { |
| %dirty = %dirty or 0b000110000000000000000000 |
| } else if (%changed and 0b000110000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a09)) 0b000100000000000000000000 else 0b10000000000000000000 |
| } |
| if (%default and 0b010000000000 !== 0) { |
| %dirty = %dirty or 0b011000000000000000000000 |
| } else if (%changed and 0b011000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a10)) 0b010000000000000000000000 else 0b001000000000000000000000 |
| } |
| if (%default and 0b100000000000 !== 0) { |
| %dirty = %dirty or 0b0001100000000000000000000000 |
| } else if (%changed and 0b0001100000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a11)) 0b0001000000000000000000000000 else 0b100000000000000000000000 |
| } |
| if (%default and 0b0001000000000000 !== 0) { |
| %dirty = %dirty or 0b0110000000000000000000000000 |
| } else if (%changed and 0b0110000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a12)) 0b0100000000000000000000000000 else 0b0010000000000000000000000000 |
| } |
| if (%default and 0b0010000000000000 !== 0) { |
| %dirty = %dirty or 0b00011000000000000000000000000000 |
| } else if (%changed and 0b00011000000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a13)) 0b00010000000000000000000000000000 else 0b1000000000000000000000000000 |
| } |
| if (%default and 0b0100000000000000 !== 0) { |
| %dirty = %dirty or 0b01100000000000000000000000000000 |
| } else if (%changed and 0b01100000000000000000000000000000 === 0) { |
| %dirty = %dirty or if (%composer.changed(a14)) 0b01000000000000000000000000000000 else 0b00100000000000000000000000000000 |
| } |
| if (%default and 0b1000000000000000 !== 0) { |
| %dirty1 = %dirty1 or 0b0110 |
| } else if (%changed1 and 0b0110 === 0) { |
| %dirty1 = %dirty1 or if (%composer.changed(a15)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b00101010101010101010101010101011 xor 0b00101010101010101010101010101010 !== 0 || %dirty1 and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| if (%default and 0b0001 !== 0) { |
| a00 = 0 |
| } |
| if (%default and 0b0010 !== 0) { |
| a01 = 0 |
| } |
| if (%default and 0b0100 !== 0) { |
| a02 = 0 |
| } |
| if (%default and 0b1000 !== 0) { |
| a03 = 0 |
| } |
| if (%default and 0b00010000 !== 0) { |
| a04 = 0 |
| } |
| if (%default and 0b00100000 !== 0) { |
| a05 = 0 |
| } |
| if (%default and 0b01000000 !== 0) { |
| a06 = 0 |
| } |
| if (%default and 0b10000000 !== 0) { |
| a07 = 0 |
| } |
| if (%default and 0b000100000000 !== 0) { |
| a08 = 0 |
| } |
| if (%default and 0b001000000000 !== 0) { |
| a09 = 0 |
| } |
| if (%default and 0b010000000000 !== 0) { |
| a10 = 0 |
| } |
| if (%default and 0b100000000000 !== 0) { |
| a11 = 0 |
| } |
| if (%default and 0b0001000000000000 !== 0) { |
| a12 = 0 |
| } |
| if (%default and 0b0010000000000000 !== 0) { |
| a13 = 0 |
| } |
| if (%default and 0b0100000000000000 !== 0) { |
| a14 = 0 |
| } |
| if (%default and 0b1000000000000000 !== 0) { |
| a15 = 0 |
| } |
| Example(a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, %composer, <>, 0b0110 and %dirty or 0b00011000 and %dirty or 0b01100000 and %dirty or 0b000110000000 and %dirty or 0b011000000000 and %dirty or 0b0001100000000000 and %dirty or 0b0110000000000000 and %dirty or 0b00011000000000000000 and %dirty or 0b01100000000000000000 and %dirty or 0b000110000000000000000000 and %dirty or 0b011000000000000000000000 and %dirty or 0b0001100000000000000000000000 and %dirty or 0b0110000000000000000000000000 and %dirty or 0b00011000000000000000000000000000 and %dirty or 0b01100000000000000000000000000000 and %dirty, 0b0110 and %dirty1, 0) |
| Example(a15, a14, a13, a12, a11, a10, a09, a08, a07, a06, a05, a04, a03, a02, a01, a00, %composer, <>, 0b0110 and %dirty1 or 0b00011000 and %dirty shr 0b00011010 or 0b01100000 and %dirty shr 0b00010110 or 0b000110000000 and %dirty shr 0b00010010 or 0b011000000000 and %dirty shr 0b1110 or 0b0001100000000000 and %dirty shr 0b1010 or 0b0110000000000000 and %dirty shr 0b0110 or 0b00011000000000000000 and %dirty shr 0b0010 or 0b01100000000000000000 and %dirty shl 0b0010 or 0b000110000000000000000000 and %dirty shl 0b0110 or 0b011000000000000000000000 and %dirty shl 0b1010 or 0b0001100000000000000000000000 and %dirty shl 0b1110 or 0b0110000000000000000000000000 and %dirty shl 0b00010010 or 0b00011000000000000000000000000000 and %dirty shl 0b00010110 or 0b01100000000000000000000000000000 and %dirty shl 0b00011010, 0b0110 and %dirty, 0) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11, a12, a13, a14, a15, %composer, %key, %changed or 0b0001, %changed1, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testGrouplessProperty(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| import androidx.compose.currentComposer |
| import androidx.compose.ExperimentalComposeApi |
| |
| open class Foo { |
| @ComposableContract(readonly = true) |
| @Composable |
| inline val current: Int get() = currentComposer.hashCode() |
| |
| @ComposableContract(readonly = true) |
| @Composable |
| fun getHashCode(): Int = currentComposer.hashCode() |
| } |
| |
| @ComposableContract(readonly = true) |
| @Composable |
| fun getHashCode(): Int = currentComposer.hashCode() |
| """, |
| """ |
| open class Foo { |
| val current: Int |
| get() { |
| val tmp0 = %composer.hashCode() |
| return tmp0 |
| } |
| @ComposableContract(readonly = true) |
| @Composable |
| fun getHashCode(%composer: Composer<*>?, %key: Int, %changed: Int): Int { |
| val tmp0 = %composer.hashCode() |
| return tmp0 |
| } |
| } |
| @ComposableContract(readonly = true) |
| @Composable |
| fun getHashCode(%composer: Composer<*>?, %key: Int, %changed: Int): Int { |
| val tmp0 = %composer.hashCode() |
| return tmp0 |
| } |
| """ |
| ) |
| |
| @Test |
| fun testStaticAndNonStaticDefaultValueSkipping(): Unit = comparisonPropagation( |
| """ |
| import androidx.compose.ambientOf |
| |
| val AmbientColor = ambientOf { 123 } |
| @Composable fun A(a: Int) {} |
| """, |
| """ |
| @Composable |
| fun Example( |
| wontChange: Int = 123, |
| mightChange: Int = AmbientColor.current |
| ) { |
| A(wontChange) |
| A(mightChange) |
| } |
| """, |
| """ |
| @Composable |
| fun Example(wontChange: Int, mightChange: Int, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)P(1)<curren...>,<A(wont...>,<A(migh...>:Test.kt") |
| val %dirty = %changed |
| val wontChange = wontChange |
| val mightChange = mightChange |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(wontChange)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(mightChange)) 0b00010000 else 0b1000 |
| } |
| if (%dirty and 0b1011 xor 0b1010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| wontChange = 123 |
| } |
| if (%default and 0b0010 !== 0) { |
| mightChange = AmbientColor.current |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| A(wontChange, %composer, <>, 0b0110 and %dirty) |
| A(mightChange, %composer, <>, 0b0110 and %dirty shr 0b0010) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(wontChange, mightChange, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambdaInvoke(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| @Composable fun Example(content: @Composable() () -> Unit) { |
| content.invoke() |
| } |
| """, |
| """ |
| @Composable |
| fun Example(content: Function3<Composer<*>, Int, Int, Unit>, %composer: Composer<*>?, %key: Int, %changed: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Example)<invoke...>:Test.kt") |
| val %dirty = %changed |
| if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(content)) 0b0100 else 0b0010 |
| } |
| if (%dirty and 0b0011 xor 0b0010 !== 0 || !%composer.skipping) { |
| content(%composer, <>, 0b0110 and %dirty) |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Example(content, %composer, %key, %changed or 0b0001) |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testComposableLambdasWithReturnGetGroups(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| fun A(factory: @Composable () -> Int): Unit {} |
| fun B() = A { 123 } |
| """, |
| """ |
| fun A(factory: Function3<Composer<*>, Int, Int, Int>) { } |
| fun B() { |
| return A { %composer: Composer<*>?, %key: Int, %changed: Int -> |
| %composer.startReplaceableGroup(<> xor %key) |
| val tmp0 = 123 |
| %composer.endReplaceableGroup() |
| tmp0 |
| } |
| } |
| """ |
| ) |
| |
| @Test |
| fun testDefaultsIssue(): Unit = comparisonPropagation( |
| """ |
| """, |
| """ |
| import androidx.ui.core.Modifier |
| import androidx.compose.ui.unit.Dp |
| import androidx.compose.emptyContent |
| |
| @Composable |
| fun Box2( |
| modifier: Modifier = Modifier, |
| paddingStart: Dp = Dp.Unspecified, |
| children: @Composable () -> Unit = emptyContent() |
| ) { |
| |
| } |
| """, |
| """ |
| @Composable |
| fun Box2(modifier: Modifier?, paddingStart: Dp, children: Function3<Composer<*>, Int, Int, Unit>?, %composer: Composer<*>?, %key: Int, %changed: Int, %default: Int) { |
| %composer.startRestartGroup(<> xor %key, "C(Box2)P(1,2:c#ui.unit.Dp):Test.kt") |
| val %dirty = %changed |
| val modifier = modifier |
| val paddingStart = paddingStart |
| val children = children |
| if (%default and 0b0001 !== 0) { |
| %dirty = %dirty or 0b0110 |
| } else if (%changed and 0b0110 === 0) { |
| %dirty = %dirty or if (%composer.changed(modifier)) 0b0100 else 0b0010 |
| } |
| if (%changed and 0b00011000 === 0) { |
| %dirty = %dirty or if (%default and 0b0010 === 0 && %composer.changed(paddingStart.value)) 0b00010000 else 0b1000 |
| } |
| if (%default and 0b0100 !== 0) { |
| %dirty = %dirty or 0b01100000 |
| } else if (%changed and 0b01100000 === 0) { |
| %dirty = %dirty or if (%composer.changed(children)) 0b01000000 else 0b00100000 |
| } |
| if (%dirty and 0b00101011 xor 0b00101010 !== 0 || !%composer.skipping) { |
| if (%changed and 0b0001 === 0 || %composer.defaultsInvalid) { |
| %composer.startDefaults() |
| if (%default and 0b0001 !== 0) { |
| modifier = Companion |
| } |
| if (%default and 0b0010 !== 0) { |
| paddingStart = Companion.Unspecified |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| if (%default and 0b0100 !== 0) { |
| children = emptyContent() |
| } |
| %composer.endDefaults() |
| } else { |
| %composer.skipCurrentGroup() |
| if (%default and 0b0010 !== 0) { |
| %dirty = %dirty and 0b00011000.inv() |
| } |
| } |
| } else { |
| %composer.skipToGroupEnd() |
| } |
| %composer.endRestartGroup()?.updateScope { %composer: Composer<*>?, %key: Int, %force: Int -> |
| Box2(modifier, paddingStart, children, %composer, %key, %changed or 0b0001, %default) |
| } |
| } |
| """ |
| ) |
| } |