| /* |
| * 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. |
| */ |
| |
| @file:OptIn(InternalComposeApi::class) |
| package androidx.compose.internal |
| |
| import androidx.compose.ComposeCompilerApi |
| import androidx.compose.Composer |
| import androidx.compose.FrameManager |
| import androidx.compose.InternalComposeApi |
| import androidx.compose.SlotTable |
| import androidx.compose.Stable |
| import kotlin.jvm.functions.FunctionN |
| |
| private const val SLOTS_PER_INT = 15 |
| |
| @Stable |
| @ComposeCompilerApi |
| class ComposableLambdaN<R>( |
| val key: Int, |
| private val tracked: Boolean, |
| private val sourceInformation: String?, |
| override val arity: Int |
| ) : FunctionN<R> { |
| private var _block: Any? = null |
| |
| fun update(block: Any) { |
| if (block != this._block) { |
| if (tracked) { |
| FrameManager.recordWrite(this) |
| } |
| this._block = block as FunctionN<*> |
| } |
| } |
| |
| private fun realParamCount(params: Int): Int { |
| var realParams = params |
| realParams-- // composer parameter |
| realParams-- // key parameter |
| realParams-- // changed parameter |
| var changedParams = 1 |
| while (changedParams * SLOTS_PER_INT < realParams) { |
| realParams-- |
| changedParams++ |
| } |
| return realParams |
| } |
| |
| override fun invoke(vararg args: Any?): R { |
| val realParams = realParamCount(args.size) |
| val c = args[realParams] as Composer<*> |
| val allArgsButLast = args.slice(0 until args.size - 1).toTypedArray() |
| val lastChanged = args[args.size - 1] as Int |
| c.startRestartGroup(key, sourceInformation) |
| val dirty = lastChanged or if (c.changed(this)) |
| differentBits(realParams) |
| else |
| sameBits(realParams) |
| if (tracked) { |
| FrameManager.recordRead(this) |
| } |
| @Suppress("UNCHECKED_CAST") |
| val result = (_block as FunctionN<*>)(*allArgsButLast, dirty) as R |
| c.endRestartGroup()?.updateScope { nc, nk, _ -> |
| val params = args.slice(0 until realParams).toTypedArray() |
| @Suppress("UNUSED_VARIABLE") |
| val key = args[realParams + 1] as Int |
| val changed = args[realParams + 2] as Int |
| val changedN = args.slice(realParams + 3 until args.size).toTypedArray() |
| this( |
| *params, |
| nc, |
| nk, |
| changed or 0b1, |
| *changedN |
| ) |
| } |
| return result |
| } |
| } |
| |
| @Suppress("unused") |
| @ComposeCompilerApi |
| fun composableLambdaN( |
| composer: Composer<*>, |
| key: Int, |
| tracked: Boolean, |
| sourceInformation: String?, |
| arity: Int, |
| block: Any |
| ): ComposableLambdaN<*> { |
| composer.startReplaceableGroup(key) |
| val slot = composer.nextSlot() |
| val result = if (slot === SlotTable.EMPTY) { |
| val value = ComposableLambdaN<Any>(key, tracked, sourceInformation, arity) |
| composer.updateValue(value) |
| value |
| } else { |
| @Suppress("UNCHECKED_CAST") |
| slot as ComposableLambdaN<Any> |
| } |
| result.update(block) |
| composer.endReplaceableGroup() |
| return result |
| } |
| |
| @Suppress("unused") |
| @ComposeCompilerApi |
| fun composableLambdaNInstance( |
| key: Int, |
| tracked: Boolean, |
| arity: Int, |
| block: Any |
| ): ComposableLambdaN<*> = ComposableLambdaN<Any>( |
| key, |
| tracked, |
| null, |
| arity |
| ).apply { update(block) } |