From 406fbfdba86495adc4944b4de96ad64ff8f0848c Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Thu, 13 Oct 2016 11:54:57 +0300 Subject: [PATCH] Synthetic accessors support --- .../kotlin/backend/jvm/DeclarationOrigins.kt | 1 + .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 1 + .../jvm/lower/ClassLowerWithContext.kt | 63 ++++++ .../jvm/lower/SyntheticAccessorLowering.kt | 201 ++++++++++++++++++ .../backend/jvm/lower/descriptorUtils.kt | 59 +++++ 5 files changed, 325 insertions(+) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ClassLowerWithContext.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/descriptorUtils.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 750a10dc5f9..01e165d4586 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -26,6 +26,7 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object FIELD_FOR_ENUM_ENTRY : IrDeclarationOriginImpl("FIELD_FOR_ENUM_ENTRY") object FIELD_FOR_ENUM_VALUES : IrDeclarationOriginImpl("FIELD_FOR_ENUM_VALUES") object FIELD_FOR_OBJECT_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_OBJECT_INSTANCE") + object SYNTHETIC_ACCESSOR : IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR") } interface JvmLoweredStatementOrigin : IrStatementOrigin { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 6e62e2afee9..903caebd855 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -40,6 +40,7 @@ class JvmLower(val context: JvmBackendContext) { ObjectClassLowering(context).runOnFilePostfix(irFile) InitializersLowering(context).runOnFilePostfix(irFile) SingletonReferencesLowering(context).runOnFilePostfix(irFile) + SyntheticAccessorLowering(context.state).lower(irFile) } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ClassLowerWithContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ClassLowerWithContext.kt new file mode 100644 index 00000000000..7947453fbbd --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ClassLowerWithContext.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.jvm.FileLoweringPass +import org.jetbrains.kotlin.backend.jvm.descriptors.FileClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer + +class IrClassContext(val irClass: IrClass, val parent: IrClassContext?) + +abstract class ClassLowerWithContext : FileLoweringPass, IrElementTransformer { + + private val companion2Context = mapOf() + + private val irClass2Context = hashMapOf() + + override fun lower(irFile: IrFile) { + val packageIr = irFile.declarations.filter { it.descriptor is FileClassDescriptor }.singleOrNull() + if (packageIr != null) { + visitClass(packageIr as IrClass, null) + irFile.declarations.filterNot { it == packageIr }.forEach { it.accept(this, irClass2Context[packageIr]!!) } + } else { + irFile.accept(this, null) + } + } + + override fun visitClass(declaration: IrClass, data: IrClassContext?): IrStatement { + val context = IrClassContext(declaration, data) + irClass2Context.put(declaration, context) + lowerBefore(declaration, context) + val clazz = super.visitClass(declaration, context) + lower(declaration, context) + return clazz + } + + open fun lowerBefore(irClass: IrClass, data: IrClassContext) {} + + abstract fun lower(irCLass: IrClass, data: IrClassContext) + + fun findCompanionFor(irClass: IrClass, descriptor: ClassDescriptor): IrClassContext { + val companion = companion2Context[descriptor] ?: throw RuntimeException("Can't find companion object for $irClass by $descriptor") + assert(irClass == companion.parent?.irClass) {"Wrong companion object: $irClass != ${companion.parent?.irClass}"} + return companion + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt new file mode 100644 index 00000000000..8cae9af1737 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.jvm.FileLoweringPass +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.backend.jvm.descriptors.FileClassDescriptor +import org.jetbrains.kotlin.backend.jvm.intrinsics.receiverAndArgs +import org.jetbrains.kotlin.codegen.AccessorForCallableDescriptor +import org.jetbrains.kotlin.codegen.AccessorForPropertyDescriptor +import org.jetbrains.kotlin.codegen.JvmCodegenUtil +import org.jetbrains.kotlin.codegen.OwnerKind +import org.jetbrains.kotlin.codegen.context.ClassContext +import org.jetbrains.kotlin.codegen.context.CodegenContext +import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression +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.IrElementTransformer +import org.jetbrains.kotlin.name.Name +import kotlin.properties.Delegates + +interface StubContext { + val irClassContext: IrClassContext +} + +class StubCodegenContext( + contextDescriptor: ClassDescriptor, + parentContext: CodegenContext<*>?, + override val irClassContext: IrClassContext +) :StubContext, CodegenContext( + if (contextDescriptor is FileClassDescriptor) contextDescriptor.containingDeclaration else contextDescriptor, + OwnerKind.IMPLEMENTATION, parentContext, null, + if (contextDescriptor is FileClassDescriptor) null else contextDescriptor, + null +) + +class ClassStubContext( + contextDescriptor: ClassDescriptor, + parentContext: CodegenContext<*>?, + override val irClassContext: IrClassContext, + typeMapper: KotlinTypeMapper +) : StubContext, ClassContext( typeMapper, contextDescriptor, OwnerKind.IMPLEMENTATION, parentContext, null) + +class ContextAnnotator(val state: GenerationState) : ClassLowerWithContext() { + + val context2Codegen = hashMapOf>() + val class2Codegen = hashMapOf>() + + private val IrClassContext.codegenContext: CodegenContext<*> + get() = context2Codegen[this]!! + + private val ClassDescriptor.codegenContext: CodegenContext<*> + get() = class2Codegen[this]!! + + + override fun lowerBefore(irClass: IrClass, data: IrClassContext) { + val descriptor = irClass.descriptor + val newContext: CodegenContext<*> = if (descriptor is FileClassDescriptor || descriptor !is ClassDescriptor) { + StubCodegenContext(descriptor, data.parent?.codegenContext, data) + } + else { + ClassStubContext(descriptor, data.parent?.codegenContext, data, state.typeMapper) + } + newContext.apply { + context2Codegen.put(data, this) + class2Codegen.put(descriptor, this) + } + } + + override fun lower(irCLass: IrClass, data: IrClassContext) { + + } +} + +class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass, IrElementTransformer { + + private val IrClassContext.codegenContext: CodegenContext<*> + get() = contextAnnotator.context2Codegen[this]!! + + var contextAnnotator by Delegates.notNull() + + private val ClassDescriptor.codegenContext: CodegenContext<*> + get() = contextAnnotator.class2Codegen[this]!! + + override fun lower(irFile: IrFile) { + contextAnnotator = ContextAnnotator(state) + contextAnnotator.lower(irFile) + irFile.transform(this, null) + } + + override fun visitClass(declaration: IrClass, data: IrClassContext?): IrStatement { + val classContext = (declaration.descriptor.codegenContext as StubContext).irClassContext + return super.visitClass(declaration, classContext).apply { + lower(this as IrClass, classContext) + } + } + + fun lower(irCLass: IrClass, data: IrClassContext) { + val codegenContext = data.codegenContext + val accessors = codegenContext.accessors + val allAccessors = + ( + accessors.filterIsInstance() + + accessors.filterIsInstance().flatMap { + listOf(if (it.isWithSyntheticGetterAccessor) it.getter else null, if (it.isWithSyntheticSetterAccessor) it.setter else null).filterNotNull() + } + ).filterIsInstance>() + allAccessors.forEach { accessor -> + val accessorOwner = (accessor as FunctionDescriptor).containingDeclaration as ClassOrPackageFragmentDescriptor + val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) + val accessorDescriptor = accessor.toStatic(accessorOwner, Name.identifier(state.typeMapper.mapAsmMethod(accessor).name)) + val syntheticFunction = IrFunctionImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR, + accessorDescriptor, body + ) + val calleeDescriptor = accessor.calleeDescriptor + val returnExpr = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, calleeDescriptor.returnType!!, calleeDescriptor, emptyMap()) + copyAllArgsToValueParams(returnExpr, accessorDescriptor) + body.statements.add(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, accessor, returnExpr)) + data.irClass.declarations.add(syntheticFunction) + } + } + + + override fun visitMemberAccess(expression: IrMemberAccessExpression, data: IrClassContext?): IrElement { + val superResult = super.visitMemberAccess(expression, data) + val descriptor = expression.descriptor + if (descriptor is FunctionDescriptor) { + val directAccessor = data!!.codegenContext.accessibleDescriptor(JvmCodegenUtil.getDirectMember(descriptor), (expression as? IrCall)?.superQualifier) + val accessor = actualAccessor(descriptor, directAccessor) + + if (accessor is AccessorForCallableDescriptor<*> && descriptor !is AccessorForCallableDescriptor<*>) { + val accessorOwner = accessor.containingDeclaration as ClassOrPackageFragmentDescriptor + val staticAccessor = descriptor.toStatic(accessorOwner, Name.identifier(state.typeMapper.mapAsmMethod(accessor as FunctionDescriptor).name)) //TODO change call + val call = IrCallImpl(expression.startOffset, expression.endOffset, expression.descriptor.returnType!!, staticAccessor, emptyMap(), + expression.origin/*TODO super*/) + //copyAllArgsToValueParams(call, expression) + expression.receiverAndArgs().forEachIndexed { i, irExpression -> + call.putValueArgument(i, irExpression) + } + return call + } + } + return superResult + } + + private fun actualAccessor(descriptor: FunctionDescriptor, calculatedAccessor: CallableMemberDescriptor): CallableMemberDescriptor { + if (calculatedAccessor is AccessorForPropertyDescriptor) { + val isGetter = descriptor is PropertyGetterDescriptor + val propertyAccessor = if (isGetter) calculatedAccessor.getter!! else calculatedAccessor.setter!! + if (isGetter && calculatedAccessor.isWithSyntheticGetterAccessor || !isGetter && calculatedAccessor.isWithSyntheticSetterAccessor) { + return propertyAccessor + } + return descriptor + + } + return calculatedAccessor + } + + private fun copyAllArgsToValueParams(call: IrCallImpl, fromDescriptor: CallableMemberDescriptor) { + var offset = 0 + val newDescriptor = call.descriptor + newDescriptor.dispatchReceiverParameter?.let { + call.dispatchReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, fromDescriptor.valueParameters[offset++]) + } + + newDescriptor.extensionReceiverParameter?.let { + call.extensionReceiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, fromDescriptor.valueParameters[offset++]) + } + + call.descriptor.valueParameters.forEachIndexed { i, valueParameterDescriptor -> + call.putValueArgument(i, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, fromDescriptor.valueParameters[i + offset])) + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/descriptorUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/descriptorUtils.kt new file mode 100644 index 00000000000..63423b8acd7 --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/descriptorUtils.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl +import org.jetbrains.kotlin.name.Name + +fun FunctionDescriptor.toStatic( + newOwner: ClassOrPackageFragmentDescriptor, + name: Name = this.name, + dispatchReceiverClass: ClassDescriptor? = this.containingDeclaration as? ClassDescriptor +): FunctionDescriptor { + val newFunction = SimpleFunctionDescriptorImpl.create( + newOwner, AnnotationsImpl(emptyList()), + name, //Name.identifier(typeMapper.mapAsmMethod(this).name), + CallableMemberDescriptor.Kind.DECLARATION, this.source + ) + + var offset = 0 + val dispatchReceiver = this.dispatchReceiverParameter?.let { + offset++ + ValueParameterDescriptorImpl.createWithDestructuringDeclarations( + newFunction, null, 0, AnnotationsImpl(emptyList()), Name.identifier("this"), + dispatchReceiverClass!!.defaultType, false, false, false, false, null, dispatchReceiverClass.source, null) + } + + val extensionReceiver = extensionReceiverParameter?.let { + offset++ + ValueParameterDescriptorImpl.createWithDestructuringDeclarations( + newFunction, null, 1, AnnotationsImpl(emptyList()), Name.identifier("receiver"), + it.value.type, false, false, false, false, null, it.source, null) + } + + val valueParameters = listOf(dispatchReceiver, extensionReceiver).filterNotNull() + + valueParameters.map { it.copy(newFunction, it.name, it.index + offset) } + + newFunction.initialize( + null, null, emptyList()/*TODO: type parameters*/, + valueParameters, returnType, Modality.FINAL, Visibilities.PUBLIC + ) + return newFunction +} \ No newline at end of file