[Wasm] Add property reference stdlib part

This commit is contained in:
Svyatoslav Kuzmich
2021-09-27 17:08:55 +03:00
parent 5c05ff48ff
commit dd45714137
3 changed files with 503 additions and 0 deletions
@@ -0,0 +1,313 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.wasm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.createArrayOfExpression
import org.jetbrains.kotlin.backend.common.ir.getKFunctionType
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlock
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.backend.wasm.lower.WasmPropertyReferenceLowering.KTypeGeneratorInterface
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrLocalDelegatedPropertyReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
internal class WasmPropertyReferenceLowering(val context: WasmBackendContext) : FileLoweringPass {
private var tempIndex = 0
val symbols = context.wasmSymbols
fun createKTypeGenerator(): KTypeGeneratorInterface {
return KTypeGeneratorInterface { this.irCall(symbols.kTypeStub) }
}
private fun getKPropertyImplConstructor(
receiverTypes: List<IrType>,
returnType: IrType,
isLocal: Boolean,
isMutable: Boolean
): Pair<IrConstructorSymbol, List<IrType>> {
val classSymbol =
if (isLocal) {
assert(receiverTypes.isEmpty()) { "Local delegated property cannot have explicit receiver" }
when {
isMutable -> symbols.kLocalDelegatedMutablePropertyImpl
else -> symbols.kLocalDelegatedPropertyImpl
}
} else {
when (receiverTypes.size) {
0 -> when {
isMutable -> symbols.kMutableProperty0Impl
else -> symbols.kProperty0Impl
}
1 -> when {
isMutable -> symbols.kMutableProperty1Impl
else -> symbols.kProperty1Impl
}
2 -> when {
isMutable -> symbols.kMutableProperty2Impl
else -> symbols.kProperty2Impl
}
else -> error("More than 2 receivers is not allowed")
}
}
val arguments = (receiverTypes + listOf(returnType))
return classSymbol.constructors.single() to arguments
}
fun interface KTypeGeneratorInterface {
fun IrBuilderWithScope.irKType(type: IrType): IrExpression
}
override fun lower(irFile: IrFile) {
// Somehow there is no reasonable common ancestor for IrProperty and IrLocalDelegatedProperty,
// so index by IrDeclaration.
val kProperties = mutableMapOf<IrDeclaration, Pair<IrExpression, Int>>()
val arrayClass = context.irBuiltIns.arrayClass.owner
val arrayItemGetter = arrayClass.functions.single { it.name == Name.identifier("get") }
val anyType = context.irBuiltIns.anyType
val kPropertyImplType = symbols.kProperty1Impl.typeWith(anyType, anyType)
val kPropertiesFieldType: IrType = arrayClass.typeWith(kPropertyImplType)
val kPropertiesField =
context.irFactory.createField(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION,
IrFieldSymbolImpl(),
Name.identifier("\$KPROPERTIES"),
kPropertiesFieldType,
DescriptorVisibilities.PRIVATE,
isFinal = true,
isExternal = false,
isStatic = true,
).apply {
parent = irFile
}
irFile.transformChildrenVoid(object : IrElementTransformerVoidWithContext() {
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
expression.transformChildrenVoid(this)
val kTypeGenerator = createKTypeGenerator()
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
irBuilder.run {
val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null }
return when (receiversCount) {
0 -> { // Cache KProperties with no arguments.
val field = kProperties.getOrPut(expression.symbol.owner) {
createKProperty(expression, kTypeGenerator, this) to kProperties.size
}
irCall(arrayItemGetter).apply {
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
}
1 -> createKProperty(expression, kTypeGenerator, this) // Has receiver.
else -> error("Callable reference to properties with two receivers is not allowed: ${expression.symbol.owner.name}")
}
}
}
override fun visitLocalDelegatedPropertyReference(expression: IrLocalDelegatedPropertyReference): IrExpression {
expression.transformChildrenVoid(this)
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
irBuilder.run {
val receiversCount = listOf(expression.dispatchReceiver, expression.extensionReceiver).count { it != null }
if (receiversCount == 2)
error("Callable reference to properties with two receivers is not allowed: ${expression}")
else { // Cache KProperties with no arguments.
// TODO: what about `receiversCount == 1` case?
val field = kProperties.getOrPut(expression.symbol.owner) {
createLocalKProperty(
expression.symbol.owner.name.asString(),
expression.getter.owner.returnType,
createKTypeGenerator(),
this
) to kProperties.size
}
return irCall(arrayItemGetter).apply {
dispatchReceiver = irGetField(null, kPropertiesField)
putValueArgument(0, irInt(field.second))
}
}
}
}
})
if (kProperties.isNotEmpty()) {
val initializers = kProperties.values.sortedBy { it.second }.map { it.first }
// TODO: replace with static initialization.
kPropertiesField.initializer = context.irFactory.createExpressionBody(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET,
context.createArrayOfExpression(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, kPropertyImplType, initializers)
)
irFile.declarations.add(0, kPropertiesField)
}
}
private fun createKProperty(
expression: IrPropertyReference,
kTypeGenerator: KTypeGeneratorInterface,
irBuilder: IrBuilderWithScope
): IrExpression {
val startOffset = expression.startOffset
val endOffset = expression.endOffset
return irBuilder.irBlock(expression) {
val receiverTypes = mutableListOf<IrType>()
val dispatchReceiver = expression.dispatchReceiver?.let {
irTemporary(value = it, nameHint = "\$dispatchReceiver${tempIndex++}")
}
val extensionReceiver = expression.extensionReceiver?.let {
irTemporary(value = it, nameHint = "\$extensionReceiver${tempIndex++}")
}
val returnType = expression.getter?.owner?.returnType ?: expression.field!!.owner.type
val getterCallableReference = expression.getter?.owner?.let { getter ->
getter.extensionReceiverParameter.let {
if (it != null && expression.extensionReceiver == null)
receiverTypes.add(it.type)
}
getter.dispatchReceiverParameter.let {
if (it != null && expression.dispatchReceiver == null)
receiverTypes.add(it.type)
}
val getterKFunctionType = this@WasmPropertyReferenceLowering.context.irBuiltIns.getKFunctionType(
returnType,
receiverTypes
)
IrFunctionReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = getterKFunctionType,
symbol = expression.getter!!,
typeArgumentsCount = getter.typeParameters.size,
valueArgumentsCount = getter.valueParameters.size,
reflectionTarget = expression.getter!!
).apply {
this.dispatchReceiver = dispatchReceiver?.let { irGet(it) }
this.extensionReceiver = extensionReceiver?.let { irGet(it) }
for (index in 0 until expression.typeArgumentsCount)
putTypeArgument(index, expression.getTypeArgument(index))
}
}
val setterCallableReference = expression.setter?.owner?.let { setter ->
if (!isKMutablePropertyType(expression.type)) null
else {
val setterKFunctionType = this@WasmPropertyReferenceLowering.context.irBuiltIns.getKFunctionType(
context.irBuiltIns.unitType,
receiverTypes + returnType
)
IrFunctionReferenceImpl(
startOffset = startOffset,
endOffset = endOffset,
type = setterKFunctionType,
symbol = expression.setter!!,
typeArgumentsCount = setter.typeParameters.size,
valueArgumentsCount = setter.valueParameters.size,
reflectionTarget = expression.setter!!
).apply {
this.dispatchReceiver = dispatchReceiver?.let { irGet(it) }
this.extensionReceiver = extensionReceiver?.let { irGet(it) }
for (index in 0 until expression.typeArgumentsCount)
putTypeArgument(index, expression.getTypeArgument(index))
}
}
}
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor(
receiverTypes = receiverTypes,
returnType = returnType,
isLocal = false,
isMutable = setterCallableReference != null
)
val initializerType = symbol.owner.returnType.classifierOrFail.typeWith(constructorTypeArguments)
val initializer = irCall(symbol, initializerType, constructorTypeArguments).apply {
putValueArgument(0, irString(expression.symbol.owner.name.asString()))
putValueArgument(1, with(kTypeGenerator) { irKType(returnType) })
if (getterCallableReference != null)
putValueArgument(2, getterCallableReference)
if (setterCallableReference != null)
putValueArgument(3, setterCallableReference)
}
+initializer
}
}
private fun createLocalKProperty(
propertyName: String,
propertyType: IrType,
kTypeGenerator: KTypeGeneratorInterface,
irBuilder: IrBuilderWithScope
): IrExpression {
irBuilder.run {
val (symbol, constructorTypeArguments) = getKPropertyImplConstructor(
receiverTypes = emptyList(),
returnType = propertyType,
isLocal = true,
isMutable = false
)
val initializerType = symbol.owner.returnType.classifierOrFail.typeWith(constructorTypeArguments)
val initializer = irCall(symbol, initializerType, constructorTypeArguments).apply {
putValueArgument(0, irString(propertyName))
putValueArgument(1, with(kTypeGenerator) { irKType(propertyType) })
}
return initializer
}
}
private fun isKMutablePropertyType(type: IrType): Boolean {
if (type !is IrSimpleType) return false
val expectedClass = when (type.arguments.size) {
0 -> return false
1 -> symbols.kMutableProperty0
2 -> symbols.kMutableProperty1
3 -> symbols.kMutableProperty2
else -> error("More than 2 receivers is not allowed")
}
return type.classifier == expectedClass
}
private object DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION : IrDeclarationOriginImpl("KPROPERTIES_FOR_DELEGATION")
}
@@ -0,0 +1,172 @@
/*
* Copyright 2010-20211 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("IMPLEMENTING_FUNCTION_INTERFACE")
package kotlin.wasm.internal
import kotlin.UnsupportedOperationException
import kotlin.reflect.*
internal open class KProperty0Impl<out R>(override val name: String, override val returnType: KType, val getter: () -> R) : KProperty0<R> {
override fun get(): R {
return getter()
}
override fun invoke(): R {
return getter()
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KProperty0Impl<*>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter
}
override fun hashCode(): Int {
return name.hashCode() * 31 + getter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal open class KProperty1Impl<T, out R>(override val name: String, override val returnType: KType, val getter: (T) -> R) : KProperty1<T, R> {
override fun get(receiver: T): R {
return getter(receiver)
}
override fun invoke(p1: T): R {
return getter(p1)
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KProperty1Impl<*, *>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter
}
override fun hashCode(): Int {
return name.hashCode() * 31 + getter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal open class KProperty2Impl<T1, T2, out R>(override val name: String, override val returnType: KType, val getter: (T1, T2) -> R) :
KProperty2<T1, T2, R> {
override fun get(receiver1: T1, receiver2: T2): R {
return getter(receiver1, receiver2)
}
override fun invoke(p1: T1, p2: T2): R {
return getter(p1, p2)
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KProperty2Impl<*, *, *>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter
}
override fun hashCode(): Int {
return name.hashCode() * 31 + getter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal class KMutableProperty0Impl<R>(name: String, returnType: KType, getter: () -> R, val setter: (R) -> Unit) :
KProperty0Impl<R>(name, returnType, getter), KMutableProperty0<R> {
override fun set(value: R): Unit {
setter(value)
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KMutableProperty0Impl<*>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter && setter == otherKProperty.setter
}
override fun hashCode(): Int {
return (name.hashCode() * 31 + getter.hashCode()) * 31 + setter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal class KMutableProperty1Impl<T, R>(name: String, returnType: KType, getter: (T) -> R, val setter: (T, R) -> Unit) :
KProperty1Impl<T, R>(name, returnType, getter), KMutableProperty1<T, R> {
override fun set(receiver: T, value: R): Unit {
setter(receiver, value)
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KMutableProperty1Impl<*, *>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter && setter == otherKProperty.setter
}
override fun hashCode(): Int {
return (name.hashCode() * 31 + getter.hashCode()) * 31 + setter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal class KMutableProperty2Impl<T1, T2, R>(name: String, returnType: KType, getter: (T1, T2) -> R, val setter: (T1, T2, R) -> Unit) :
KProperty2Impl<T1, T2, R>(name, returnType, getter), KMutableProperty2<T1, T2, R> {
override fun set(receiver1: T1, receiver2: T2, value: R): Unit {
setter(receiver1, receiver2, value)
}
override fun equals(other: Any?): Boolean {
val otherKProperty = other as? KMutableProperty2Impl<*, *, *>
if (otherKProperty == null) return false
return name == otherKProperty.name && getter == otherKProperty.getter && setter == otherKProperty.setter
}
override fun hashCode(): Int {
return (name.hashCode() * 31 + getter.hashCode()) * 31 + setter.hashCode()
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal open class KLocalDelegatedPropertyImpl<out R>(override val name: String, override val returnType: KType) : KProperty0<R> {
override fun get(): R {
throw UnsupportedOperationException("Not supported for local property reference.")
}
override fun invoke(): R {
throw UnsupportedOperationException("Not supported for local property reference.")
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
internal class KLocalDelegatedMutablePropertyImpl<R>(name: String, returnType: KType) : KLocalDelegatedPropertyImpl<R>(name, returnType),
KMutableProperty0<R> {
override fun set(value: R): Unit {
throw UnsupportedOperationException("Not supported for local property reference.")
}
override fun toString(): String {
return "property $name (Kotlin reflection is not available)"
}
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.wasm.internal
import kotlin.reflect.KClassifier
import kotlin.reflect.KType
import kotlin.reflect.KTypeProjection
internal fun kTypeStub(): KType = object : KType {
override val classifier: KClassifier?
get() = null
override val arguments: List<KTypeProjection>
get() = emptyList()
override val isMarkedNullable: Boolean
get() = false
}