From 94fd86f9069c303c3d5ce27778961b68e17df74e Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 28 Feb 2017 12:25:24 +0300 Subject: [PATCH] Implemented delegated properties. --- .../kotlin/backend/konan/KonanLower.kt | 6 +- .../kotlin/backend/konan/KonanPhases.kt | 1 + .../kotlin/backend/konan/llvm/IrToBitcode.kt | 7 +- .../backend/konan/llvm/LlvmDeclarations.kt | 5 +- .../backend/konan/llvm/MetadataGenerator.kt | 2 +- .../backend/konan/lower/BridgesBuilding.kt | 74 +------ .../backend/konan/lower/DelegationLowering.kt | 149 +++++++++++++ runtime/src/main/kotlin/konan/Annotations.kt | 10 + runtime/src/main/kotlin/kotlin/Lazy.kt | 206 ++++++++++++++++++ .../kotlin/kotlin/properties/Delegates.kt | 55 +++++ .../kotlin/kotlin/properties/Interfaces.kt | 49 +++++ .../kotlin/properties/ObservableProperty.kt | 38 ++++ .../kotlin/reflect/KAnnotatedElement.kt | 14 ++ .../main/kotlin/kotlin/reflect/KCallable.kt | 75 +++++++ .../main/kotlin/kotlin/reflect/KProperty.kt | 67 ++++++ 15 files changed, 684 insertions(+), 74 deletions(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt create mode 100644 runtime/src/main/kotlin/kotlin/Lazy.kt create mode 100644 runtime/src/main/kotlin/kotlin/properties/Delegates.kt create mode 100644 runtime/src/main/kotlin/kotlin/properties/Interfaces.kt create mode 100644 runtime/src/main/kotlin/kotlin/properties/ObservableProperty.kt create mode 100644 runtime/src/main/kotlin/kotlin/reflect/KAnnotatedElement.kt create mode 100644 runtime/src/main/kotlin/kotlin/reflect/KCallable.kt create mode 100644 runtime/src/main/kotlin/kotlin/reflect/KProperty.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt index 77cc70ad2d8..b306b6b173b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLower.kt @@ -4,7 +4,6 @@ import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.konan.lower.* import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment internal class KonanLower(val context: Context) { @@ -39,6 +38,10 @@ internal class KonanLower(val context: Context) { phaser.phase(KonanPhase.LOWER_INITIALIZERS) { InitializersLowering(context).runOnFilePostfix(irFile) } + phaser.phase(KonanPhase.LOWER_DELEGATION) { + ClassDelegationLowering(context).runOnFilePostfix(irFile) + PropertyDelegationLowering(context).lower(irFile) + } phaser.phase(KonanPhase.LOWER_TYPE_OPERATORS) { TypeOperatorLowering(context).runOnFilePostfix(irFile) } @@ -58,7 +61,6 @@ internal class KonanLower(val context: Context) { VarargInjectionLowering(context).runOnFilePostfix(irFile) } phaser.phase(KonanPhase.BRIDGES_BUILDING) { - DelegationLowering(context).runOnFilePostfix(irFile) BridgesBuilding(context).runOnFilePostfix(irFile) DirectBridgesCallsLowering(context).runOnFilePostfix(irFile) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt index fc226819486..9cd6542187f 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanPhases.kt @@ -25,6 +25,7 @@ enum class KonanPhase(val description: String, /* ... ... */ LOWER_STRING_CONCAT("String concatenation lowering"), /* ... ... */ LOWER_INITIALIZERS("Initializers lowering"), /* ... ... */ BRIDGES_BUILDING("Bridges building"), + /* ... ... */ LOWER_DELEGATION("Delegation lowering"), /* ... */ BITCODE("LLVM BitCode Generation"), /* ... ... */ RTTI("RTTI Generation"), /* ... ... */ CODEGEN("Code Generation"), diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index e79dda5da2f..820583ee291 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase import org.jetbrains.kotlin.ir.descriptors.IrImplementingDelegateDescriptorImpl +import org.jetbrains.kotlin.ir.descriptors.IrPropertyDelegateDescriptorImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -1226,7 +1227,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid context.log("evaluateGetField : ${ir2string(value)}") if (value.descriptor.dispatchReceiverParameter != null // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. - || value.descriptor is IrImplementingDelegateDescriptorImpl) { + || value.descriptor is IrImplementingDelegateDescriptorImpl + || (value.descriptor is IrPropertyDelegateDescriptorImpl && value.descriptor.containingDeclaration is ClassDescriptor)) { val thisPtr = evaluateExpression(value.receiver!!) return codegen.loadSlot( fieldPtrOfClass(thisPtr, value.descriptor), value.descriptor.isVar()) @@ -1258,7 +1260,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid if (value.descriptor.dispatchReceiverParameter != null // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. - || value.descriptor is IrImplementingDelegateDescriptorImpl) { + || value.descriptor is IrImplementingDelegateDescriptorImpl + || (value.descriptor is IrPropertyDelegateDescriptorImpl && value.descriptor.containingDeclaration is ClassDescriptor)) { val thisPtr = evaluateExpression(value.receiver!!) codegen.storeAnyGlobal(valueToAssign, fieldPtrOfClass(thisPtr, value.descriptor)) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 07353def2d4..206356f61fd 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -125,7 +125,7 @@ private fun ContextUtils.getDeclaredFields(classDescriptor: ClassDescriptor): Li } private fun ContextUtils.createClassBodyType(name: String, fields: List): LLVMTypeRef { - val fieldTypes = fields.map { getLLVMType(it.type) }.toTypedArray() + val fieldTypes = fields.map { getLLVMType(if (it.isDelegated) context.builtIns.nullableAnyType else it.type) }.toTypedArray() val classType = LLVMStructCreateNamed(LLVMGetModuleContext(context.llvmModule), name)!! @@ -300,7 +300,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) : val dispatchReceiverParameter = descriptor.dispatchReceiverParameter if (dispatchReceiverParameter != null // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. - || descriptor is IrImplementingDelegateDescriptorImpl) { + || descriptor is IrImplementingDelegateDescriptorImpl + || (descriptor is IrPropertyDelegateDescriptorImpl && descriptor.containingDeclaration is ClassDescriptor)) { val containingClass = dispatchReceiverParameter?.containingDeclaration // TODO: hack because of IR bug: https://github.com/JetBrains/kotlin/tree/rr/dispatch_receiver_for_delegate_descriptor. ?: descriptor.containingDeclaration diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt index c6a192832e0..ad88bcafe10 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/MetadataGenerator.kt @@ -131,7 +131,7 @@ internal class MetadataGenerator(override val context: Context): ContextUtils { internal fun property(declaration: IrProperty) { if (declaration.backingField == null) return - assert(declaration.backingField!!.descriptor == declaration.descriptor) + assert(declaration.backingField!!.descriptor == declaration.descriptor || declaration.isDelegated) context.ir.propertiesWithBackingFields.add(declaration.descriptor) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt index 2a3f530c3af..5ea815e32a5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BridgesBuilding.kt @@ -13,8 +13,10 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.* -import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.types.KotlinType @@ -66,71 +68,6 @@ internal class DirectBridgesCallsLowering(val context: Context) : BodyLoweringPa } } -private object DECLARATION_ORIGIN_BRIDGE_METHOD : - IrDeclarationOriginImpl("BRIDGE_METHOD") - -internal class DelegationLowering(val context: Context) : ClassLoweringPass { - override fun lower(irClass: IrClass) { - irClass.declarations.transformFlat { - when (it) { - is IrFunction -> { - val transformedFun = transformBridgeToDelegatedMethod(irClass, it) - if (transformedFun == null) null - else listOf(transformedFun) - } - is IrProperty -> { - val getter = transformBridgeToDelegatedMethod(irClass, it.getter) - val setter = transformBridgeToDelegatedMethod(irClass, it.setter) - if (getter != null) it.getter = getter - if (setter != null) it.setter = setter - null - } - else -> null - } - } - } - - // TODO: hack because of broken IR for synthesized delegated members: https://youtrack.jetbrains.com/issue/KT-16486. - private fun transformBridgeToDelegatedMethod(irClass: IrClass, irFunction: IrFunction?): IrFunction? { - if (irFunction == null || irFunction.descriptor.kind != CallableMemberDescriptor.Kind.DELEGATION) return null - - val body = irFunction.body as? IrBlockBody - ?: throw AssertionError("Unexpected method body: ${irFunction.body}") - val statement = body.statements.single() - val delegatedCall = ((statement as? IrReturn)?.value ?: statement) as? IrCall - ?: throw AssertionError("Unexpected method body: $statement") - val propertyGetter = delegatedCall.dispatchReceiver as? IrGetValue - ?: throw AssertionError("Unexpected dispatch receiver: ${delegatedCall.dispatchReceiver}") - val propertyDescriptor = propertyGetter.descriptor as? PropertyDescriptor - ?: throw AssertionError("Unexpected dispatch receiver descriptor: ${propertyGetter.descriptor}") - val delegated = context.specialDescriptorsFactory.getBridgeDescriptor( - OverriddenFunctionDescriptor(irFunction.descriptor, delegatedCall.descriptor as FunctionDescriptor)) - val newFunction = IrFunctionImpl(irFunction.startOffset, irFunction.endOffset, DECLARATION_ORIGIN_BRIDGE_METHOD, delegated) - - val irBlockBody = IrBlockBodyImpl(irFunction.startOffset, irFunction.endOffset) - val returnType = delegatedCall.descriptor.returnType!! - val irCall = IrCallImpl(irFunction.startOffset, irFunction.endOffset, returnType, delegatedCall.descriptor, null) - val receiver = IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, irClass.descriptor.thisAsReceiverParameter) - irCall.dispatchReceiver = IrGetFieldImpl(irFunction.startOffset, irFunction.endOffset, propertyDescriptor, receiver) - irCall.extensionReceiver = delegated.extensionReceiverParameter?.let { extensionReceiver -> - IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, extensionReceiver) - } - irCall.mapValueParameters { overriddenValueParameter -> - val delegatedValueParameter = delegated.valueParameters[overriddenValueParameter.index] - IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, delegatedValueParameter) - } - if (KotlinBuiltIns.isUnit(returnType) || KotlinBuiltIns.isNothing(returnType)) { - irBlockBody.statements.add(irCall) - } else { - val irReturn = IrReturnImpl(irFunction.startOffset, irFunction.endOffset, context.builtIns.nothingType, delegated, irCall) - irBlockBody.statements.add(irReturn) - } - - newFunction.body = irBlockBody - return newFunction - } -} - internal class BridgesBuilding(val context: Context) : ClassLoweringPass { override fun lower(irClass: IrClass) { val functions = mutableSetOf() @@ -160,6 +97,9 @@ internal class BridgesBuilding(val context: Context) : ClassLoweringPass { } } + private object DECLARATION_ORIGIN_BRIDGE_METHOD : + IrDeclarationOriginImpl("BRIDGE_METHOD") + private fun buildBridge(descriptor: OverriddenFunctionDescriptor, irClass: IrClass) { val bridgeDescriptor = context.specialDescriptorsFactory.getBridgeDescriptor(descriptor) val target = descriptor.descriptor.target diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt new file mode 100644 index 00000000000..92ec6d904b5 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -0,0 +1,149 @@ +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.OverriddenFunctionDescriptor +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor + +internal class ClassDelegationLowering(val context: Context) : DeclarationContainerLoweringPass { + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + irDeclarationContainer.declarations.transformFlat { + when (it) { + is IrFunction -> { + val transformedFun = transformBridgeToDelegatedMethod(irDeclarationContainer, it) + if (transformedFun == null) null + else listOf(transformedFun) + } + is IrProperty -> { + val getter = transformBridgeToDelegatedMethod(irDeclarationContainer, it.getter) + val setter = transformBridgeToDelegatedMethod(irDeclarationContainer, it.setter) + if (getter != null) it.getter = getter + if (setter != null) it.setter = setter + null + } + else -> null + } + } + } + + private fun transformBridgeToDelegatedMethod(irDeclarationContainer: IrDeclarationContainer, irFunction: IrFunction?): IrFunction? { + if (irFunction == null) return null + val descriptor = irFunction.descriptor + + if (descriptor.kind != CallableMemberDescriptor.Kind.DELEGATION) return null + + // TODO: hack because of broken IR for synthesized delegated members: https://youtrack.jetbrains.com/issue/KT-16486. + val body = irFunction.body as? IrBlockBody + ?: throw AssertionError("Unexpected method body: ${irFunction.body}") + val statement = body.statements.single() + val delegatedCall = ((statement as? IrReturn)?.value ?: statement) as? IrCall + ?: throw AssertionError("Unexpected method body: $statement") + val propertyGetter = delegatedCall.dispatchReceiver as? IrGetValue + ?: throw AssertionError("Unexpected dispatch receiver: ${delegatedCall.dispatchReceiver}") + val propertyDescriptor = propertyGetter.descriptor as? PropertyDescriptor + ?: throw AssertionError("Unexpected dispatch receiver descriptor: ${propertyGetter.descriptor}") + val delegated = context.specialDescriptorsFactory.getBridgeDescriptor( + OverriddenFunctionDescriptor(descriptor, delegatedCall.descriptor as FunctionDescriptor)) + val newFunction = IrFunctionImpl(irFunction.startOffset, irFunction.endOffset, irFunction.origin, delegated) + + val irBlockBody = IrBlockBodyImpl(irFunction.startOffset, irFunction.endOffset) + val returnType = delegatedCall.descriptor.returnType!! + val irCall = IrCallImpl(irFunction.startOffset, irFunction.endOffset, returnType, delegatedCall.descriptor, null) + val receiver = IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, + (irDeclarationContainer as IrClass).descriptor.thisAsReceiverParameter) + irCall.dispatchReceiver = IrGetFieldImpl(irFunction.startOffset, irFunction.endOffset, propertyDescriptor, receiver) + irCall.extensionReceiver = delegated.extensionReceiverParameter?.let { extensionReceiver -> + IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, extensionReceiver) + } + irCall.mapValueParameters { overriddenValueParameter -> + val delegatedValueParameter = delegated.valueParameters[overriddenValueParameter.index] + IrGetValueImpl(irFunction.startOffset, irFunction.endOffset, delegatedValueParameter) + } + if (KotlinBuiltIns.isUnit(returnType) || KotlinBuiltIns.isNothing(returnType)) { + irBlockBody.statements.add(irCall) + } else { + val irReturn = IrReturnImpl(irFunction.startOffset, irFunction.endOffset, context.builtIns.nothingType, delegated, irCall) + irBlockBody.statements.add(irReturn) + } + + newFunction.body = irBlockBody + return newFunction + } +} + +internal class PropertyDelegationLowering(val context: Context) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitFunction(declaration: IrFunction): IrStatement { + return super.visitFunction(declaration) + } + + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty): IrStatement { + declaration.transformChildrenVoid(this) + val name = declaration.descriptor.name.asString() + val type = declaration.descriptor.type + + val initializer = declaration.delegate.initializer!! + return IrVariableImpl(declaration.startOffset, declaration.endOffset, + declaration.origin, declaration.delegate.descriptor, + IrBlockImpl(initializer.startOffset, initializer.endOffset, initializer.type, null, + listOf( + transformBridgeToDelegate(name, type, declaration.getter), + transformBridgeToDelegate(name, type, declaration.setter), + initializer + ).filterNotNull()) + ) + + } + + override fun visitProperty(declaration: IrProperty): IrStatement { + declaration.transformChildrenVoid(this) + if (declaration.isDelegated) { + val name = declaration.descriptor.name.asString() + val type = declaration.descriptor.returnType!! + declaration.getter = transformBridgeToDelegate(name, type, declaration.getter) + declaration.setter = transformBridgeToDelegate(name, type, declaration.setter) + } + return declaration + } + + private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName.fromSegments(listOf("kotlin", "reflect"))) + private val genericKPropertyImplType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("KPropertyImpl"), + NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + private fun transformBridgeToDelegate(name: String, type: KotlinType, irFunction: IrFunction?): IrFunction? { + irFunction?.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCallableReference(expression: IrCallableReference): IrExpression { + val typeParameterT = genericKPropertyImplType.declaredTypeParameters[0] + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(type))) + val kPropertyImplType = genericKPropertyImplType.substitute(typeSubstitutor) + return IrCallImpl(expression.startOffset, expression.endOffset, + kPropertyImplType.defaultType, kPropertyImplType.unsubstitutedPrimaryConstructor!!, null).apply { + putValueArgument(0, IrConstImpl(expression.startOffset, expression.endOffset, + context.builtIns.stringType, IrConstKind.String, name)) + } + } + }) + return irFunction + } + }) + } +} + diff --git a/runtime/src/main/kotlin/konan/Annotations.kt b/runtime/src/main/kotlin/konan/Annotations.kt index d38c2888f0d..eed960a52b4 100644 --- a/runtime/src/main/kotlin/konan/Annotations.kt +++ b/runtime/src/main/kotlin/konan/Annotations.kt @@ -54,6 +54,16 @@ public annotation class FixmeVariance */ public annotation class FixmeRegex +/** + * Need to be fixed because of reflection. + */ +public annotation class FixmeReflection + +/** + * Need to be fixed because of concurrency. + */ +public annotation class FixmeConcurrency + /** * Need to be fixed. */ diff --git a/runtime/src/main/kotlin/kotlin/Lazy.kt b/runtime/src/main/kotlin/kotlin/Lazy.kt new file mode 100644 index 00000000000..c2c286b3057 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/Lazy.kt @@ -0,0 +1,206 @@ +package kotlin + +import kotlin.reflect.KProperty + +/** + * Represents a value with lazy initialization. + * + * To create an instance of [Lazy] use the [lazy] function. + */ +public interface Lazy { + /** + * Gets the lazily initialized value of the current Lazy instance. + * Once the value was initialized it must not change during the rest of lifetime of this Lazy instance. + */ + public val value: T + /** + * Returns `true` if a value for this Lazy instance has been already initialized, and `false` otherwise. + * Once this function has returned `true` it stays `true` for the rest of lifetime of this Lazy instance. + */ + public fun isInitialized(): Boolean +} + +/** + * Creates a new instance of the [Lazy] that is already initialized with the specified [value]. + */ +public fun lazyOf(value: T): Lazy = InitializedLazyImpl(value) + +/** + * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer] + * and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED]. + * + * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access. + * + * Note that the returned instance uses itself to synchronize on. Do not synchronize from external code on + * the returned instance as it may cause accidental deadlock. Also this behavior can be changed in the future. + */ +@FixmeConcurrency +public fun lazy(initializer: () -> T): Lazy = UnsafeLazyImpl(initializer)//SynchronizedLazyImpl(initializer) + +/** + * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer] + * and thread-safety [mode]. + * + * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access. + * + * Note that when the [LazyThreadSafetyMode.SYNCHRONIZED] mode is specified the returned instance uses itself + * to synchronize on. Do not synchronize from external code on the returned instance as it may cause accidental deadlock. + * Also this behavior can be changed in the future. + */ +@FixmeConcurrency +public fun lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy = + when (mode) { + LazyThreadSafetyMode.SYNCHRONIZED -> TODO()//SynchronizedLazyImpl(initializer) + LazyThreadSafetyMode.PUBLICATION -> TODO()//SafePublicationLazyImpl(initializer) + LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer) + } + +/** + * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer] + * and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED]. + * + * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access. + * + * The returned instance uses the specified [lock] object to synchronize on. + * When the [lock] is not specified the instance uses itself to synchronize on, + * in this case do not synchronize from external code on the returned instance as it may cause accidental deadlock. + * Also this behavior can be changed in the future. + */ +@FixmeConcurrency +public fun lazy(lock: Any?, initializer: () -> T): Lazy = TODO()//SynchronizedLazyImpl(initializer, lock) + +/** + * An extension to delegate a read-only property of type [T] to an instance of [Lazy]. + * + * This extension allows to use instances of Lazy for property delegation: + * `val property: String by lazy { initializer }` + */ +@kotlin.internal.InlineOnly +public inline operator fun Lazy.getValue(thisRef: Any?, property: KProperty<*>): T = value + +/** + * Specifies how a [Lazy] instance synchronizes access among multiple threads. + */ +public enum class LazyThreadSafetyMode { + + /** + * Locks are used to ensure that only a single thread can initialize the [Lazy] instance. + */ + SYNCHRONIZED, + + /** + * Initializer function can be called several times on concurrent access to uninitialized [Lazy] instance value, + * but only first returned value will be used as the value of [Lazy] instance. + */ + PUBLICATION, + + /** + * No locks are used to synchronize the access to the [Lazy] instance value; if the instance is accessed from multiple threads, its behavior is undefined. + * + * This mode should be used only when high performance is crucial and the [Lazy] instance is guaranteed never to be initialized from more than one thread. + */ + NONE, +} + + +private object UNINITIALIZED_VALUE + +//private class SynchronizedLazyImpl(initializer: () -> T, lock: Any? = null) : Lazy, Serializable { +// private var initializer: (() -> T)? = initializer +// @Volatile private var _value: Any? = UNINITIALIZED_VALUE +// // final field is required to enable safe publication of constructed instance +// private val lock = lock ?: this +// +// override val value: T +// get() { +// val _v1 = _value +// if (_v1 !== UNINITIALIZED_VALUE) { +// @Suppress("UNCHECKED_CAST") +// return _v1 as T +// } +// +// return synchronized(lock) { +// val _v2 = _value +// if (_v2 !== UNINITIALIZED_VALUE) { +// @Suppress("UNCHECKED_CAST") (_v2 as T) +// } +// else { +// val typedValue = initializer!!() +// _value = typedValue +// initializer = null +// typedValue +// } +// } +// } +// +// override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE +// +// override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet." +// +// private fun writeReplace(): Any = InitializedLazyImpl(value) +//} + +internal class UnsafeLazyImpl(initializer: () -> T) : Lazy/*, Serializable*/ { + private var initializer: (() -> T)? = initializer + private var _value: Any? = UNINITIALIZED_VALUE + + override val value: T + get() { + if (_value === UNINITIALIZED_VALUE) { + _value = initializer!!() + initializer = null + } + @Suppress("UNCHECKED_CAST") + return _value as T + } + + override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE + + override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet." + + private fun writeReplace(): Any = InitializedLazyImpl(value) +} + +private class InitializedLazyImpl(override val value: T) : Lazy/*, Serializable*/ { + + override fun isInitialized(): Boolean = true + + override fun toString(): String = value.toString() + +} + +//private class SafePublicationLazyImpl(initializer: () -> T) : Lazy, Serializable { +// private var initializer: (() -> T)? = initializer +// @Volatile private var _value: Any? = UNINITIALIZED_VALUE +// // this final field is required to enable safe publication of constructed instance +// private val final: Any = UNINITIALIZED_VALUE +// +// override val value: T +// get() { +// if (_value === UNINITIALIZED_VALUE) { +// val initializerValue = initializer +// // if we see null in initializer here, it means that the value is already set by another thread +// if (initializerValue != null) { +// val newValue = initializerValue() +// if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) { +// initializer = null +// } +// } +// } +// @Suppress("UNCHECKED_CAST") +// return _value as T +// } +// +// override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE +// +// override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet." +// +// private fun writeReplace(): Any = InitializedLazyImpl(value) +// +// companion object { +// private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater( +// SafePublicationLazyImpl::class.java, +// Any::class.java, +// "_value") +// } +//} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/properties/Delegates.kt b/runtime/src/main/kotlin/kotlin/properties/Delegates.kt new file mode 100644 index 00000000000..0353d3b6b18 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/properties/Delegates.kt @@ -0,0 +1,55 @@ +package kotlin.properties + +import kotlin.reflect.KProperty + +/** + * Standard property delegates. + */ +public object Delegates { + /** + * Returns a property delegate for a read/write property with a non-`null` value that is initialized not during + * object construction time but at a later time. Trying to read the property before the initial value has been + * assigned results in an exception. + */ + public fun notNull(): ReadWriteProperty = NotNullVar() + + /** + * Returns a property delegate for a read/write property that calls a specified callback function when changed. + * @param initialValue the initial value of the property. + * @param onChange the callback which is called after the change of the property is made. The value of the property + * has already been changed when this callback is invoked. + */ + public inline fun observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit): + ReadWriteProperty = object : ObservableProperty(initialValue) { + override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue) + } + + /** + * Returns a property delegate for a read/write property that calls a specified callback function when changed, + * allowing the callback to veto the modification. + * @param initialValue the initial value of the property. + * @param onChange the callback which is called before a change to the property value is attempted. + * The value of the property hasn't been changed yet, when this callback is invoked. + * If the callback returns `true` the value of the property is being set to the new value, + * and if the callback returns `false` the new value is discarded and the property remains its old value. + */ + public inline fun vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean): + ReadWriteProperty = object : ObservableProperty(initialValue) { + override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue) + } + +} + + +private class NotNullVar() : ReadWriteProperty { + private var value: T? = null + + public override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") + } + + public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } +} + diff --git a/runtime/src/main/kotlin/kotlin/properties/Interfaces.kt b/runtime/src/main/kotlin/kotlin/properties/Interfaces.kt new file mode 100644 index 00000000000..b8034216340 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/properties/Interfaces.kt @@ -0,0 +1,49 @@ +package kotlin.properties + +import kotlin.reflect.KProperty + +/** + * Base interface that can be used for implementing property delegates of read-only properties. + * + * This is provided only for convenience; you don't have to extend this interface + * as long as your property delegate has methods with the same signatures. + * + * @param R the type of object which owns the delegated property. + * @param T the type of the property value. + */ +public interface ReadOnlyProperty { + /** + * Returns the value of the property for the given object. + * @param thisRef the object for which the value is requested. + * @param property the metadata for the property. + * @return the property value. + */ + public operator fun getValue(thisRef: R, property: KProperty<*>): T +} + +/** + * Base interface that can be used for implementing property delegates of read-write properties. + * + * This is provided only for convenience; you don't have to extend this interface + * as long as your property delegate has methods with the same signatures. + * + * @param R the type of object which owns the delegated property. + * @param T the type of the property value. + */ +public interface ReadWriteProperty { + /** + * Returns the value of the property for the given object. + * @param thisRef the object for which the value is requested. + * @param property the metadata for the property. + * @return the property value. + */ + public operator fun getValue(thisRef: R, property: KProperty<*>): T + + /** + * Sets the value of the property for the given object. + * @param thisRef the object for which the value is requested. + * @param property the metadata for the property. + * @param value the value to set. + */ + public operator fun setValue(thisRef: R, property: KProperty<*>, value: T) +} diff --git a/runtime/src/main/kotlin/kotlin/properties/ObservableProperty.kt b/runtime/src/main/kotlin/kotlin/properties/ObservableProperty.kt new file mode 100644 index 00000000000..cb3eb98026b --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/properties/ObservableProperty.kt @@ -0,0 +1,38 @@ +package kotlin.properties + +import kotlin.reflect.KProperty + +/** + * Implements the core logic of a property delegate for a read/write property that calls callback functions when changed. + * @param initialValue the initial value of the property. + */ +public abstract class ObservableProperty(initialValue: T) : ReadWriteProperty { + private var value = initialValue + + /** + * The callback which is called before a change to the property value is attempted. + * The value of the property hasn't been changed yet, when this callback is invoked. + * If the callback returns `true` the value of the property is being set to the new value, + * and if the callback returns `false` the new value is discarded and the property remains its old value. + */ + protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true + + /** + * The callback which is called after the change of the property is made. The value of the property + * has already been changed when this callback is invoked. + */ + protected open fun afterChange (property: KProperty<*>, oldValue: T, newValue: T): Unit {} + + public override fun getValue(thisRef: Any?, property: KProperty<*>): T { + return value + } + + public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + val oldValue = this.value + if (!beforeChange(property, oldValue, value)) { + return + } + this.value = value + afterChange(property, oldValue, value) + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/reflect/KAnnotatedElement.kt b/runtime/src/main/kotlin/kotlin/reflect/KAnnotatedElement.kt new file mode 100644 index 00000000000..33dab89392f --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KAnnotatedElement.kt @@ -0,0 +1,14 @@ +package kotlin.reflect + +/** + * Represents an annotated element and allows to obtain its annotations. + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/annotations.html) + * for more information. + */ +@FixmeReflection +public interface KAnnotatedElement { +// /** +// * Annotations which are present on this element. +// */ +// public val annotations: List +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/reflect/KCallable.kt b/runtime/src/main/kotlin/kotlin/reflect/KCallable.kt new file mode 100644 index 00000000000..933391f745a --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KCallable.kt @@ -0,0 +1,75 @@ +package kotlin.reflect + +/** + * Represents a callable entity, such as a function or a property. + * + * @param R return type of the callable. + */ +@FixmeReflection +public interface KCallable : KAnnotatedElement { + /** + * The name of this callable as it was declared in the source code. + * If the callable has no name, a special invented name is created. + * Nameless callables include: + * - constructors have the name "", + * - property accessors: the getter for a property named "foo" will have the name "", + * the setter, similarly, will have the name "". + */ + public val name: String + +// /** +// * Parameters required to make a call to this callable. +// * If this callable requires a `this` instance or an extension receiver parameter, +// * they come first in the list in that order. +// */ +// public val parameters: List +// +// /** +// * The type of values returned by this callable. +// */ +// public val returnType: KType +// +// /** +// * The list of type parameters of this callable. +// */ +// @SinceKotlin("1.1") +// public val typeParameters: List +// +// /** +// * Calls this callable with the specified list of arguments and returns the result. +// * Throws an exception if the number of specified arguments is not equal to the size of [parameters], +// * or if their types do not match the types of the parameters. +// */ +// public fun call(vararg args: Any?): R +// +// /** +// * Calls this callable with the specified mapping of parameters to arguments and returns the result. +// * If a parameter is not found in the mapping and is not optional (as per [KParameter.isOptional]), +// * or its type does not match the type of the provided value, an exception is thrown. +// */ +// public fun callBy(args: Map): R +// +// /** +// * Visibility of this callable, or `null` if its visibility cannot be represented in Kotlin. +// */ +// @SinceKotlin("1.1") +// public val visibility: KVisibility? +// +// /** +// * `true` if this callable is `final`. +// */ +// @SinceKotlin("1.1") +// public val isFinal: Boolean +// +// /** +// * `true` if this callable is `open`. +// */ +// @SinceKotlin("1.1") +// public val isOpen: Boolean +// +// /** +// * `true` if this callable is `abstract`. +// */ +// @SinceKotlin("1.1") +// public val isAbstract: Boolean +} diff --git a/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt b/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt new file mode 100644 index 00000000000..e4604877c36 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/reflect/KProperty.kt @@ -0,0 +1,67 @@ +package kotlin.reflect + +/** + * Represents a property, such as a named `val` or `var` declaration. + * Instances of this class are obtainable by the `::` operator. + * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/reflection.html) + * for more information. + * + * @param R the type of the property. + */ +@FixmeReflection +public interface KProperty : KCallable { +// /** +// * `true` if this property is `lateinit`. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isLateinit: Boolean +// +// /** +// * `true` if this property is `const`. +// * See the [Kotlin language documentation](https://kotlinlang.org/docs/reference/properties.html#compile-time-constants) +// * for more information. +// */ +// @SinceKotlin("1.1") +// public val isConst: Boolean +// +// /** The getter of this property, used to obtain the value of the property. */ +// public val getter: Getter +// +// /** +// * Represents a property accessor, which is a `get` or `set` method declared alongside the property. +// * See the [Kotlin language documentation](http://kotlinlang.org/docs/reference/properties.html#getters-and-setters) +// * for more information. +// * +// * @param R the type of the property, which it is an accessor of. +// */ +// public interface Accessor { +// /** The property which this accessor is originated from. */ +// public val property: KProperty +// } +// +// /** +// * Getter of the property is a `get` method declared alongside the property. +// */ +// public interface Getter : Accessor, KFunction +} + +/** + * Represents a property declared as a `var`. + */ +@FixmeReflection +public interface KMutableProperty : KProperty { +// /** The setter of this mutable property, used to change the value of the property. */ +// public val setter: Setter +// +// /** +// * Setter of the property is a `set` method declared alongside the property. +// */ +// public interface Setter : KProperty.Accessor, KFunction +} + +@FixmeReflection +public class KPropertyImpl(override val name: String) : KProperty { + +} \ No newline at end of file