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/ir/IrUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt index 41efb6bd32d..e3e76608d93 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/IrUtils.kt @@ -1,10 +1,21 @@ package org.jetbrains.kotlin.backend.konan.ir +import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.* import org.jetbrains.kotlin.ir.util.DumpIrTreeVisitor import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +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 import java.io.StringWriter @@ -76,3 +87,50 @@ fun ir2stringWhole(ir: IrElement?): String { return strWriter.toString() } +internal fun ClassDescriptor.createSimpleDelegatingConstructor(superConstructorDescriptor: ClassConstructorDescriptor) + : Pair { + val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( + this, + Annotations.EMPTY, + superConstructorDescriptor.isPrimary, + SourceElement.NO_SOURCE) + val valueParameters = superConstructorDescriptor.valueParameters.map { + it.copy(constructorDescriptor, it.name, it.index) + } + constructorDescriptor.initialize(valueParameters, superConstructorDescriptor.visibility) + constructorDescriptor.returnType = superConstructorDescriptor.returnType + + val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + listOf( + IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, superConstructorDescriptor).apply { + valueParameters.forEachIndexed { idx, parameter -> + putValueArgument(idx, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter)) + } + }, + IrInstanceInitializerCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, this) + ) + ) + val constructor = IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body) + + return Pair(constructorDescriptor, constructor) +} + +internal fun Context.createArrayOfExpression(arrayElementType: KotlinType, + arrayElements: List): IrExpression { + val kotlinPackage = irModule!!.descriptor.getPackage(FqName("kotlin")) + val genericArrayOfFun = kotlinPackage.memberScope.getContributedFunctions(Name.identifier("arrayOf"), NoLookupLocation.FROM_BACKEND).first() + val typeParameter0 = genericArrayOfFun.typeParameters[0] + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameter0.typeConstructor to TypeProjectionImpl(arrayElementType))) + val substitutedArrayOfFun = genericArrayOfFun.substitute(typeSubstitutor)!! + + val typeArguments = mapOf(typeParameter0 to arrayElementType) + + val valueParameter0 = substitutedArrayOfFun.valueParameters[0] + val arg0VarargType = valueParameter0.type + val arg0VarargElementType = valueParameter0.varargElementType!! + val arg0 = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arg0VarargType, arg0VarargElementType, arrayElements) + + return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedArrayOfFun, typeArguments).apply { + putValueArgument(0, arg0) + } +} \ No newline at end of file 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..10b02e99038 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DelegationLowering.kt @@ -0,0 +1,200 @@ +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.jvm.descriptors.initialize +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.descriptors.OverriddenFunctionDescriptor +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.createArrayOfExpression +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl +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.* + +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 { + private val kotlinReflectPackage = context.irModule!!.descriptor.getPackage(FqName.fromSegments(listOf("kotlin", "reflect"))) + private val genericKPropertyImplType = kotlinReflectPackage.memberScope.getContributedClassifier(Name.identifier("KPropertyImpl"), + NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin")) + private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor + + fun getKPropertyImplConstructorDescriptorWithProjection(type: KotlinType): ClassConstructorDescriptor { + val typeParameterT = genericKPropertyImplType.declaredTypeParameters[0] + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(type))) + return genericKPropertyImplType.unsubstitutedPrimaryConstructor!!.substitute(typeSubstitutor)!! + } + + fun ClassDescriptor.replace(vararg type: KotlinType): SimpleType { + return this.defaultType.replace(type.map(::TypeProjectionImpl)) + } + + override fun lower(irFile: IrFile) { + val kProperties = mutableListOf() + + val getter = genericArrayType.unsubstitutedMemberScope.getContributedFunctions(Name.identifier("get"), NoLookupLocation.FROM_BACKEND).single() + val typeParameterT = genericArrayType.declaredTypeParameters[0] + val kPropertyImplType = genericKPropertyImplType.replace(context.builtIns.anyType) + val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameterT.typeConstructor to TypeProjectionImpl(kPropertyImplType))) + val substitutedGetter = getter.substitute(typeSubstitutor)!! + + val kPropertiesField = createKPropertiesFieldDescriptor(irFile.packageFragmentDescriptor, genericArrayType.replace(kPropertyImplType)) + + 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 fun transformBridgeToDelegate(name: String, type: KotlinType, irFunction: IrFunction?): IrFunction? { + irFunction?.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCallableReference(expression: IrCallableReference): IrExpression { + val fieldInitializer = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + getKPropertyImplConstructorDescriptorWithProjection(type)).apply { + putValueArgument(0, IrConstImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + context.builtIns.stringType, IrConstKind.String, name)) + } + + val index = kProperties.size + kProperties.add(fieldInitializer) + + return IrCallImpl(expression.startOffset, expression.endOffset, substitutedGetter).apply { + dispatchReceiver = IrGetFieldImpl(expression.startOffset, expression.endOffset, kPropertiesField) + putValueArgument(0, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, index)) + } + } + }) + return irFunction + } + + }) + + if (kProperties.size > 0) { + irFile.declarations.add(0, IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION, + kPropertiesField, + IrExpressionBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, + context.createArrayOfExpression(kPropertyImplType, kProperties)))) + } + } + + private object DECLARATION_ORIGIN_KPROPERTIES_FOR_DELEGATION : + IrDeclarationOriginImpl("KPROPERTIES_FOR_DELEGATION") + + private fun createKPropertiesFieldDescriptor(containingDeclaration: DeclarationDescriptor, fieldType: SimpleType): PropertyDescriptorImpl { + return PropertyDescriptorImpl.create(containingDeclaration, Annotations.EMPTY, Modality.FINAL, Visibilities.PRIVATE, + false, "KPROPERTIES".synthesizedName, CallableMemberDescriptor.Kind.SYNTHESIZED, SourceElement.NO_SOURCE, + false, false, false, false, false, false).initialize(fieldType) + } +} + diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index d64b9dd1dfd..e96b03e1445 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -10,6 +10,8 @@ import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanPlatform import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.createArrayOfExpression +import org.jetbrains.kotlin.backend.konan.ir.createSimpleDelegatingConstructor import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl @@ -171,7 +173,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { descriptor.constructors.forEach { val loweredEnumConstructor = loweredEnumConstructors[it]!! - val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(defaultClassDescriptor, loweredEnumConstructor) + val (constructorDescriptor, constructor) = defaultClassDescriptor.createSimpleDelegatingConstructor(loweredEnumConstructor) constructors.add(constructorDescriptor) defaultClass.declarations.add(constructor) defaultEnumEntryConstructors.put(loweredEnumConstructor, constructorDescriptor) @@ -233,7 +235,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val memberScope = MemberScope.Empty val constructorOfAny = irClass.descriptor.module.builtIns.any.constructors.first() - val (constructorDescriptor, constructor) = createSimpleDelegatingConstructor(implObjectDescriptor, constructorOfAny) + val (constructorDescriptor, constructor) = implObjectDescriptor.createSimpleDelegatingConstructor(constructorOfAny) implObjectDescriptor.initialize(memberScope, setOf(constructorDescriptor), constructorDescriptor) @@ -260,34 +262,6 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { return newDescriptor } - private fun createSimpleDelegatingConstructor(classDescriptor: ClassDescriptor, - superConstructorDescriptor: ClassConstructorDescriptor) - : Pair { - val constructorDescriptor = ClassConstructorDescriptorImpl.createSynthesized( - classDescriptor, - Annotations.EMPTY, - superConstructorDescriptor.isPrimary, - SourceElement.NO_SOURCE) - val valueParameters = superConstructorDescriptor.valueParameters.map { - it.copy(constructorDescriptor, it.name, it.index) - } - constructorDescriptor.initialize(valueParameters, superConstructorDescriptor.visibility) - constructorDescriptor.returnType = superConstructorDescriptor.returnType - - val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, - listOf( - IrDelegatingConstructorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, superConstructorDescriptor).apply { - valueParameters.forEachIndexed { idx, parameter -> - putValueArgument(idx, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, parameter)) - } - }, - IrInstanceInitializerCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, classDescriptor) - ) - ) - val constructor = IrConstructorImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, constructorDescriptor, body) - return Pair(constructorDescriptor, constructor) - } - private fun createSyntheticValuesFieldDeclaration(implObjectDescriptor: ClassDescriptor, enumEntries: List): IrFieldImpl { val valuesArrayType = context.builtIns.getArrayType(Variance.INVARIANT, irClass.descriptor.defaultType) @@ -304,7 +278,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { val loweredEnumEntry = LoweredEnumEntry(implObjectDescriptor, valuesFieldDescriptor, substitutedValueOf, entriesMap) loweredEnumEntries.put(irClass.descriptor, loweredEnumEntry) - val irValuesInitializer = createArrayOfExpression(irClass.descriptor.defaultType, enumEntries.map { it.initializerExpression }) + val irValuesInitializer = context.createArrayOfExpression(irClass.descriptor.defaultType, enumEntries.map { it.initializerExpression }) val irField = IrFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, valuesFieldDescriptor, @@ -321,31 +295,12 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { private val kotlinPackage = context.irModule!!.descriptor.getPackage(FqName("kotlin")) - private val genericArrayOfFun = kotlinPackage.memberScope.getContributedFunctions(Name.identifier("arrayOf"), NoLookupLocation.FROM_BACKEND).first() - private val genericValueOfFun = context.builtIns.getKonanInternalFunctions("valueOfForEnum").single() private val genericValuesFun = context.builtIns.getKonanInternalFunctions("valuesForEnum").single() private val genericArrayType = kotlinPackage.memberScope.getContributedClassifier(Name.identifier("Array"), NoLookupLocation.FROM_BACKEND) as ClassDescriptor - private fun createArrayOfExpression(arrayElementType: KotlinType, arrayElements: List): IrExpression { - val typeParameter0 = genericArrayOfFun.typeParameters[0] - val typeSubstitutor = TypeSubstitutor.create(mapOf(typeParameter0.typeConstructor to TypeProjectionImpl(arrayElementType))) - val substitutedArrayOfFun = genericArrayOfFun.substitute(typeSubstitutor)!! - - val typeArguments = mapOf(typeParameter0 to arrayElementType) - - val valueParameter0 = substitutedArrayOfFun.valueParameters[0] - val arg0VarargType = valueParameter0.type - val arg0VarargElementType = valueParameter0.varargElementType!! - val arg0 = IrVarargImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, arg0VarargType, arg0VarargElementType, arrayElements) - - return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, substitutedArrayOfFun, typeArguments).apply { - putValueArgument(0, arg0) - } - } - private fun createSyntheticValuesMethodDeclaration(companionObjectDescriptor: ClassDescriptor): IrFunction { val typeParameterT = genericValuesFun.typeParameters[0] val enumClassType = irClass.descriptor.defaultType diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 81d806407ee..3133961b1f2 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -593,6 +593,47 @@ task classDelegation_withBridge(type: RunKonanTest) { source = "codegen/classDelegation/withBridge.kt" } +task delegatedProperty_simpleVal(type: RunKonanTest) { + goldValue = "x\n42\n" + source = "codegen/delegatedProperty/simpleVal.kt" +} + +task delegatedProperty_simpleVar(type: RunKonanTest) { + goldValue = "get x\n42\nset x\nget x\n117\n" + source = "codegen/delegatedProperty/simpleVar.kt" +} + +task delegatedProperty_packageLevel(type: RunKonanTest) { + goldValue = "x\n42\n" + source = "codegen/delegatedProperty/packageLevel.kt" +} + +task delegatedProperty_local(type: RunKonanTest) { + goldValue = "x\n42\n" + source = "codegen/delegatedProperty/local.kt" +} + +task delegatedProperty_delegatedOverride(type: LinkKonanTest) { + goldValue = "156\nx\n117\n42\n" + source = "codegen/delegatedProperty/delegatedOverride_main.kt" + lib = "codegen/delegatedProperty/delegatedOverride_lib.kt" +} + +task delegatedProperty_lazy(type: RunKonanTest) { + goldValue = "computed!\nHello\nHello\n" + source = "codegen/delegatedProperty/lazy.kt" +} + +task delegatedProperty_observable(type: RunKonanTest) { + goldValue = " -> first\nfirst -> second\n" + source = "codegen/delegatedProperty/observable.kt" +} + +task delegatedProperty_map(type: RunKonanTest) { + goldValue = "John Doe\n25\n" + source = "codegen/delegatedProperty/map.kt" +} + task array0(type: RunKonanTest) { goldValue = "5\n6\n7\n8\n9\n10\n11\n12\n13\n" source = "runtime/collections/array0.kt" diff --git a/backend.native/tests/codegen/delegatedProperty/delegatedOverride_lib.kt b/backend.native/tests/codegen/delegatedProperty/delegatedOverride_lib.kt new file mode 100644 index 00000000000..0aceea0ffdd --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/delegatedOverride_lib.kt @@ -0,0 +1,23 @@ +package a + +import kotlin.reflect.KProperty + +open class A { + open val x = 42 +} + +class Delegate { + val f = 117 + operator fun getValue(receiver: Any?, p: KProperty<*>): Int { + println(p.name) + return f + } +} + +open class B: A() { + override val x: Int by Delegate() + + fun bar() { + println(super.x) + } +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/delegatedOverride_main.kt b/backend.native/tests/codegen/delegatedProperty/delegatedOverride_main.kt new file mode 100644 index 00000000000..ba4fadc4e25 --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/delegatedOverride_main.kt @@ -0,0 +1,17 @@ +import a.* + +open class C: B() { + override val x: Int = 156 + + fun foo() { + println(x) + + println(super.x) + bar() + } +} + +fun main(args: Array) { + val c = C() + c.foo() +} diff --git a/backend.native/tests/codegen/delegatedProperty/lazy.kt b/backend.native/tests/codegen/delegatedProperty/lazy.kt new file mode 100644 index 00000000000..6fe68e92fce --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/lazy.kt @@ -0,0 +1,9 @@ +val lazyValue: String by lazy { + println("computed!") + "Hello" +} + +fun main(args: Array) { + println(lazyValue) + println(lazyValue) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/local.kt b/backend.native/tests/codegen/delegatedProperty/local.kt new file mode 100644 index 00000000000..f80c389e7ce --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/local.kt @@ -0,0 +1,18 @@ +import kotlin.reflect.KProperty + +fun foo(): Int { + class Delegate { + operator fun getValue(receiver: Any?, p: KProperty<*>): Int { + println(p.name) + return 42 + } + } + + val x: Int by Delegate() + + return x +} + +fun main(args: Array) { + println(foo()) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/map.kt b/backend.native/tests/codegen/delegatedProperty/map.kt new file mode 100644 index 00000000000..6bd335ab010 --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/map.kt @@ -0,0 +1,13 @@ +class User(val map: Map) { + val name: String by map + val age: Int by map +} + +fun main(args: Array) { + val user = User(mapOf( + "name" to "John Doe", + "age" to 25 + )) + println(user.name) // Prints "John Doe" + println(user.age) // Prints 25 +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/observable.kt b/backend.native/tests/codegen/delegatedProperty/observable.kt new file mode 100644 index 00000000000..c209bc5da2f --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/observable.kt @@ -0,0 +1,14 @@ +import kotlin.properties.Delegates + +class User { + var name: String by Delegates.observable("") { + prop, old, new -> + println("$old -> $new") + } +} + +fun main(args: Array) { + val user = User() + user.name = "first" + user.name = "second" +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/packageLevel.kt b/backend.native/tests/codegen/delegatedProperty/packageLevel.kt new file mode 100644 index 00000000000..78002766016 --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/packageLevel.kt @@ -0,0 +1,14 @@ +import kotlin.reflect.KProperty + +class Delegate { + operator fun getValue(receiver: Any?, p: KProperty<*>): Int { + println(p.name) + return 42 + } +} + +val x: Int by Delegate() + +fun main(args: Array) { + println(x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/simpleVal.kt b/backend.native/tests/codegen/delegatedProperty/simpleVal.kt new file mode 100644 index 00000000000..efa491997fe --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/simpleVal.kt @@ -0,0 +1,16 @@ +import kotlin.reflect.KProperty + +class Delegate { + operator fun getValue(receiver: Any?, p: KProperty<*>): Int { + println(p.name) + return 42 + } +} + +class C { + val x: Int by Delegate() +} + +fun main(args: Array) { + println(C().x) +} \ No newline at end of file diff --git a/backend.native/tests/codegen/delegatedProperty/simpleVar.kt b/backend.native/tests/codegen/delegatedProperty/simpleVar.kt new file mode 100644 index 00000000000..4e934e1b7a4 --- /dev/null +++ b/backend.native/tests/codegen/delegatedProperty/simpleVar.kt @@ -0,0 +1,26 @@ +import kotlin.reflect.KProperty + +class Delegate { + var f: Int = 42 + + operator fun getValue(receiver: Any?, p: KProperty<*>): Int { + println("get ${p.name}") + return f + } + + operator fun setValue(receiver: Any?, p: KProperty<*>, value: Int) { + println("set ${p.name}") + f = value + } +} + +class C { + var x: Int by Delegate() +} + +fun main(args: Array) { + val c = C() + println(c.x) + c.x = 117 + println(c.x) +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy index 55767258bf4..339dd5fd07a 100644 --- a/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy +++ b/buildSrc/src/main/groovy/org/jetbrains/kotlin/KonanTest.groovy @@ -186,7 +186,7 @@ class RunInteropKonanTest extends KonanTest { } @ParallelizableTask -class LinkKonanTest extends KonanTest { +class LinkKonanTestNoStdlib extends KonanTest { protected String lib void compileTest(List filesToCompile, String exe) { @@ -198,6 +198,19 @@ class LinkKonanTest extends KonanTest { } } +@ParallelizableTask +class LinkKonanTest extends KonanTest { + protected String lib + + void compileTest(List filesToCompile, String exe) { + def libDir = project.file(lib).absolutePath + def libBc = "${libDir}.bc" + + runCompiler(lib, libBc, ['-nolink']) + runCompiler(filesToCompile, exe, ['-library', libBc]) + } +} + @ParallelizableTask class RunExternalTestGroup extends RunKonanTest { 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/collections/MapAccessors.kt b/runtime/src/main/kotlin/kotlin/collections/MapAccessors.kt new file mode 100644 index 00000000000..5e2847903a3 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/MapAccessors.kt @@ -0,0 +1,39 @@ +package kotlin.collections + +import kotlin.reflect.KProperty +import kotlin.internal.Exact + +/** + * Returns the value of the property for the given object from this read-only map. + * @param thisRef the object for which the value is requested (not used). + * @param property the metadata for the property, used to get the name of property and lookup the value corresponding to this name in the map. + * @return the property value. + * + * @throws NoSuchElementException when the map doesn't contain value for the property name and doesn't provide an implicit default (see [withDefault]). + */ +@kotlin.internal.InlineOnly +public inline operator fun Map.getValue(thisRef: Any?, property: KProperty<*>): V1 + = @Suppress("UNCHECKED_CAST", "NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") (getOrImplicitDefault(property.name) as V1) + +/** + * Returns the value of the property for the given object from this mutable map. + * @param thisRef the object for which the value is requested (not used). + * @param property the metadata for the property, used to get the name of property and lookup the value corresponding to this name in the map. + * @return the property value. + * + * @throws NoSuchElementException when the map doesn't contain value for the property name and doesn't provide an implicit default (see [withDefault]). + */ +@kotlin.internal.InlineOnly +public inline operator fun MutableMap.getValue(thisRef: Any?, property: KProperty<*>): V + = @Suppress("UNCHECKED_CAST", "NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") (getOrImplicitDefault(property.name) as V) + +/** + * Stores the value of the property for the given object in this mutable map. + * @param thisRef the object for which the value is requested (not used). + * @param property the metadata for the property, used to get the name of property and store the value associated with that name in the map. + * @param value the value to set. + */ +@kotlin.internal.InlineOnly +public inline operator fun MutableMap.setValue(thisRef: Any?, property: KProperty<*>, value: V) { + this.put(property.name, value) +} diff --git a/runtime/src/main/kotlin/kotlin/collections/MapWithDefault.kt b/runtime/src/main/kotlin/kotlin/collections/MapWithDefault.kt new file mode 100644 index 00000000000..6d26218751d --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/MapWithDefault.kt @@ -0,0 +1,94 @@ +package kotlin.collections + +/** + * Returns the value for the given key, or the implicit default value for this map. + * By default no implicit value is provided for maps and a [NoSuchElementException] is thrown. + * To create a map with implicit default value use [withDefault] method. + * + * @throws NoSuchElementException when the map doesn't contain a value for the specified key and no implicit default was provided for that map. + */ +@PublishedApi +internal fun Map.getOrImplicitDefault(key: K): V { + if (this is MapWithDefault) + return this.getOrImplicitDefault(key) + + return getOrElseNullable(key, { throw NoSuchElementException("Key $key is missing in the map.") }) +} + +/** + * Returns a wrapper of this read-only map, having the implicit default value provided with the specified function [defaultValue]. + * This implicit default value is used when properties are delegated to the returned map, + * and that map doesn't contain a value for the key specified. + * + * When this map already has an implicit default value provided with a former call to [withDefault], it is being replaced by this call. + */ +public fun Map.withDefault(defaultValue: (key: K) -> V): Map = + when (this) { + is MapWithDefault -> this.map.withDefault(defaultValue) + else -> MapWithDefaultImpl(this, defaultValue) + } + +/** + * Returns a wrapper of this mutable map, having the implicit default value provided with the specified function [defaultValue]. + * This implicit default value is used when properties are delegated to the returned map, + * and that map doesn't contain a value for the key specified. + * + * When this map already has an implicit default value provided with a former call to [withDefault], it is being replaced by this call. + */ +public fun MutableMap.withDefault(defaultValue: (key: K) -> V): MutableMap = + when (this) { + is MutableMapWithDefault -> this.map.withDefault(defaultValue) + else -> MutableMapWithDefaultImpl(this, defaultValue) + } + + + + + +private interface MapWithDefault: Map { + public val map: Map + public fun getOrImplicitDefault(key: K): V +} + +private interface MutableMapWithDefault: MutableMap, MapWithDefault { + public override val map: MutableMap +} + + +private class MapWithDefaultImpl(public override val map: Map, private val default: (key: K) -> V) : MapWithDefault { + override fun equals(other: Any?): Boolean = map.equals(other) + override fun hashCode(): Int = map.hashCode() + override fun toString(): String = map.toString() + override val size: Int get() = map.size + override fun isEmpty(): Boolean = map.isEmpty() + override fun containsKey(key: K): Boolean = map.containsKey(key) + override fun containsValue(value: @UnsafeVariance V): Boolean = map.containsValue(value) + override fun get(key: K): V? = map.get(key) + override val keys: Set get() = map.keys + override val values: Collection get() = map.values + override val entries: Set> get() = map.entries + + override fun getOrImplicitDefault(key: K): V = map.getOrElseNullable(key, { default(key) }) +} + +private class MutableMapWithDefaultImpl(public override val map: MutableMap, private val default: (key: K) -> V): MutableMapWithDefault { + override fun equals(other: Any?): Boolean = map.equals(other) + override fun hashCode(): Int = map.hashCode() + override fun toString(): String = map.toString() + override val size: Int get() = map.size + override fun isEmpty(): Boolean = map.isEmpty() + override fun containsKey(key: K): Boolean = map.containsKey(key) + override fun containsValue(value: @UnsafeVariance V): Boolean = map.containsValue(value) + override fun get(key: K): V? = map.get(key) + override val keys: MutableSet get() = map.keys + override val values: MutableCollection get() = map.values + override val entries: MutableSet> get() = map.entries + + override fun put(key: K, value: V): V? = map.put(key, value) + override fun remove(key: K): V? = map.remove(key) + override fun putAll(from: Map) = map.putAll(from) + override fun clear() = map.clear() + + override fun getOrImplicitDefault(key: K): V = map.getOrElseNullable(key, { default(key) }) +} + 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