https://kotlinlang.org logo
#compose
Title
# compose
j

James Richardson

12/10/2019, 11:40 AM
hi - i'm trying to use LiveData and Compose with a pretty simple use case. However, I am getting the following stack trace, as soon as my @Composable calls a method that takes a parameter... It's a bit confusing. I don't know what's going on under the hood, so can't really add much more detail... This works
Copy code
Column {
                        devices.map {
                            Text(text = it.name)
                        }
                    }
This fails:
Copy code
Column {
                        devices.map(::device)
                    }
[...]
@Composable
private fun device(device: UpnpDevice) {
    Text(text = device.name)
}
the stacktrace is:
Copy code
2-10 11:39:52.827 3603-3603/? D/Error: ERR: stack=java.lang.IllegalStateException: Expected a group start
        at androidx.compose.SlotTableKt.getAsGroupStart(SlotTable.kt:641)
        at androidx.compose.SlotTableKt.access$getAsGroupStart$p(SlotTable.kt:1)
        at androidx.compose.SlotEditor.recordStartGroup$compose_runtime_release(SlotTable.kt:150)
        at androidx.compose.SlotReader.startGroup(SlotTable.kt:277)
        at androidx.compose.SlotReader.startGroup(SlotTable.kt:273)
        at androidx.compose.ComposerKt.start(Composer.kt:1791)
        at androidx.compose.ComposerKt.access$start(Composer.kt:1)
        at androidx.compose.Composer.start(Composer.kt:776)
        at androidx.compose.Composer.startGroup(Composer.kt:397)
        at androidx.compose.ObserveKt.Observe(Observe.kt:37)
        at com.example.filecopier.FileCopierAppKt.device(FileCopierApp.kt)
        at com.example.filecopier.FileCopierAppKt.access$device$0(FileCopierApp.kt)
        at com.example.filecopier.FileCopierAppKt$fileCopierApp$1$2$invoke$1$2$invoke$1$1$invoke$1.invoke(FileCopierApp.kt:32)
        at com.example.filecopier.FileCopierAppKt$fileCopierApp$1$2$invoke$1$2$invoke$1$1$invoke$1.invoke(FileCopierApp.kt)
        at androidx.compose.ObserveKt.Observe(Observe.kt:39)
        at com.example.filecopier.FileCopierAppKt$fileCopierApp$1$2$invoke$1$2$invoke$1$1.invoke(FileCopierApp.kt)
        at com.example.filecopier.FileCopierAppKt$fileCopierApp$1$2$invoke$1$2$invoke$1$1.invoke(FileCopierApp.kt)
        at androidx.ui.layout.FlexKt$Column$1$1$invoke$1.invoke(Flex.kt:362)
        at androidx.ui.layout.FlexKt$Column$1$1$invoke$1.invoke(Flex.kt)
        at androidx.compose.ObserveKt.Observe(Observe.kt:39)
        at androidx.ui.layout.FlexKt$Column$1$1.invoke(Flex.kt)
        at androidx.ui.layout.FlexKt$Column$1$1.invoke(Flex.kt)
I'm using the
+observe
snippet previously posted... any ideas much appreciated! I should mention that the code works fine in the preview, so I'm guessing its something to do with the
observe
a

Adam Powell

12/10/2019, 2:49 PM
what type is
devices
that you're calling
.map
on?
also, does it work to call
devices.map { device(it) }
rather than use the method reference syntax?
j

James Richardson

12/10/2019, 3:01 PM
devices : List<UpnpDevice> UpnpDevice is just a simple data class ... but i think you are on to something with the other thing! - yes it does work... it fails with method reference, but calling the method explicitly works fine. I simply didn't think to call it that way...
thanks! - although I cannot say that i understand why this works. I did try to attach the source code for the various compose bits, so I could see what was happening, but failed.
a

Adam Powell

12/10/2019, 3:05 PM
if you have a few spare minutes and could paste your code, the stack trace, what worked, and what didn't into a ticket at b.android.com I'd appreciate it; I suspect that the code being generated isn't being inlined in the same way for the method reference and you're hitting a code path where it thinks you're calling a composable function from a non-composable function
j

James Richardson

12/10/2019, 3:41 PM
i just took a look at the kotlin bytecode in the IDE, and the two look identical to my eye. (using view kotlin bytecode, jvm8 target) - in both cases it looks like:
Copy code
INVOKESTATIC com/example/filecopier/FileCopierAppKt.device (Lcom/example/filecopier/UpnpDevice;)V
with that method being defined as:
Copy code
// access flags 0x19
  public final static device(Lcom/example/filecopier/UpnpDevice;)V
  @Landroidx/compose/Composable;() // invisible
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
ok, this works when i don't make the code +observe()...
Actually I'm not sure about that last statementaboutit working without +observe. Will update tomorrow
c

Chuck Jazdzewski [G]

12/10/2019, 7:16 PM
I added comments to the bug. In short, the call
devices.map(::device)
should be marked as invalid because the lambda produced by
::device
does correctly capture the semantics necessary for composition. We will be shortly tightening up the rules here so detecting this error should be part of that.