androidx.compose.compiler.plugins.kotlin.inference.LazyScheme.kt Maven / Gradle / Ivy
/*
* Copyright 2021 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.inference
/**
* A LazyScheme is a scheme that is lazily and progressively inferred by [ApplierInferencer] and is
* used to store the current inference state for a function type. [Scheme] is used to control the
* shape and initial values of the lazy scheme. Bound appliers are not changed but open types are
* inferred or bound together, if possible, by [ApplierInferencer].
*/
class LazyScheme(
scheme: Scheme,
context: MutableList = mutableListOf(),
val bindings: Bindings = Bindings(),
) {
val target: Binding = scheme.target.toBinding(bindings, context)
val anyParameters = scheme.anyParameters
val parameters = scheme.parameters.map { LazyScheme(it, context, bindings) }
val result = scheme.result?.let { LazyScheme(it, context, bindings) }
val closed: Boolean get() = target.token != null &&
(result == null || result.closed) && parameters.all { it.closed }
/**
* Create a [Scheme] from the current state of this.
*/
fun toScheme(): Scheme {
val context: MutableMap = mutableMapOf()
var uniqueNumber = 0
fun mapValues(scheme: LazyScheme) {
val target = scheme.target
if (target.token == null) {
val value = target.value
val index = context[value]
if (index == -1) {
context[value] = uniqueNumber++
} else if (index == null) {
context[value] = -1
}
}
scheme.parameters.forEach { mapValues(it) }
scheme.result?.let { mapValues(it) }
}
fun itemOf(binding: Binding) = binding.token?.let { Token(it) }
?: context[binding.value]?.let { Open(it) } ?: Open(-1)
fun schemeOf(lazyScheme: LazyScheme): Scheme = Scheme(
itemOf(lazyScheme.target),
lazyScheme.parameters.map { schemeOf(it) },
lazyScheme.result?.let { schemeOf(it) },
lazyScheme.anyParameters
)
mapValues(this)
return schemeOf(this)
}
/**
* Create a call binding for use when validating a call to the function this lazy scheme is for.
*/
fun toCallBindings(): CallBindings = CallBindings(
target,
parameters.map { it.toCallBindings() },
result = result?.toCallBindings(),
anyParameters
)
/**
* Call [callback] whenever the lazy changes.
*/
fun onChange(callback: () -> Unit): () -> Unit {
var previousScheme = toScheme()
return bindings.onChange {
val newScheme = toScheme()
if (newScheme != previousScheme) {
callback()
previousScheme = newScheme
}
}
}
override fun toString(): String =
"[$targetStr$parametersStr$resultStr]"
private val targetStr get() =
target.token ?: target.value.index.toString()
private val parametersStr get() =
if (parameters.isNotEmpty()) ", ${parameters.joinToString(", ")}"
else ""
private val resultStr get() = result?.let { ":$it" } ?: ""
companion object {
fun open() = Open(-1).let { target ->
LazyScheme(
Scheme(
target = target,
result = Scheme(target)
)
)
}
}
}