Support synthetic accessors for constructors

This commit is contained in:
Mikhael Bogdanov
2017-11-22 17:08:40 +01:00
parent 5cbfdf6024
commit 224adfabc5
2 changed files with 75 additions and 26 deletions
@@ -50,7 +50,7 @@ class JvmLower(val context: JvmBackendContext) {
ObjectClassLowering(context).runOnFilePostfix(irFile) ObjectClassLowering(context).runOnFilePostfix(irFile)
InitializersLowering(context).runOnFilePostfix(irFile) InitializersLowering(context).runOnFilePostfix(irFile)
SingletonReferencesLowering(context).runOnFilePostfix(irFile) SingletonReferencesLowering(context).runOnFilePostfix(irFile)
SyntheticAccessorLowering(context.state).lower(irFile) SyntheticAccessorLowering(context).lower(irFile)
BridgeLowering(context.state).runOnFilePostfix(irFile) BridgeLowering(context.state).runOnFilePostfix(irFile)
TailrecLowering(context).runOnFilePostfix(irFile) TailrecLowering(context).runOnFilePostfix(irFile)
@@ -17,18 +17,19 @@
package org.jetbrains.kotlin.backend.jvm.lower package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.intrinsics.receiverAndArgs import org.jetbrains.kotlin.backend.jvm.intrinsics.receiverAndArgs
import org.jetbrains.kotlin.codegen.AccessorForCallableDescriptor import org.jetbrains.kotlin.codegen.*
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.ClassContext
import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.descriptors.FileClassDescriptor import org.jetbrains.kotlin.codegen.descriptors.FileClassDescriptor
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -36,13 +37,15 @@ import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.impl.createFunctionSymbol
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.types.KotlinType
interface StubContext { interface StubContext {
val irClassContext: IrClassContext val irClassContext: IrClassContext
@@ -97,7 +100,11 @@ class ContextAnnotator(val state: GenerationState) : ClassLowerWithContext() {
} }
} }
class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass, IrElementTransformer<IrClassContext?> { class SyntheticAccessorLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformer<IrClassContext?> {
private val state = context.state
var pendingTransformations = mutableListOf<Function0<Unit>>()
private val IrClassContext.codegenContext: CodegenContext<*> private val IrClassContext.codegenContext: CodegenContext<*>
get() = contextAnnotator.context2Codegen[this]!! get() = contextAnnotator.context2Codegen[this]!!
@@ -111,12 +118,14 @@ class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass,
contextAnnotator = ContextAnnotator(state) contextAnnotator = ContextAnnotator(state)
contextAnnotator.lower(irFile) contextAnnotator.lower(irFile)
irFile.transform(this, null) irFile.transform(this, null)
pendingTransformations.forEach { it() }
} }
override fun visitClass(declaration: IrClass, data: IrClassContext?): IrStatement { override fun visitClass(declaration: IrClass, data: IrClassContext?): IrStatement {
val classContext = (declaration.descriptor.codegenContext as StubContext).irClassContext val classContext = (declaration.descriptor.codegenContext as StubContext).irClassContext
return super.visitClass(declaration, classContext).apply { return super.visitClass(declaration, classContext).apply {
lower(classContext) pendingTransformations.add { lower(classContext) }
} }
} }
@@ -127,24 +136,24 @@ class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass,
( (
accessors.filterIsInstance<FunctionDescriptor>() + accessors.filterIsInstance<FunctionDescriptor>() +
accessors.filterIsInstance<AccessorForPropertyDescriptor>().flatMap { accessors.filterIsInstance<AccessorForPropertyDescriptor>().flatMap {
listOf(if (it.isWithSyntheticGetterAccessor) it.getter else null, if (it.isWithSyntheticSetterAccessor) it.setter else null).filterNotNull() listOfNotNull(if (it.isWithSyntheticGetterAccessor) it.getter else null, if (it.isWithSyntheticSetterAccessor) it.setter else null)
} }
).filterIsInstance<AccessorForCallableDescriptor<*>>() ).filterIsInstance<AccessorForCallableDescriptor<*>>()
val irClassToAddAccessor = data.irClass
val irClassToAddAccessor = data.irClass
allAccessors.forEach { accessor -> allAccessors.forEach { accessor ->
addAccessorToClass(accessor, irClassToAddAccessor, state.typeMapper) addAccessorToClass(accessor, irClassToAddAccessor, context)
} }
} }
override fun visitMemberAccess(expression: IrMemberAccessExpression, data: IrClassContext?): IrElement { override fun visitMemberAccess(expression: IrMemberAccessExpression, data: IrClassContext?): IrElement {
val superResult = super.visitMemberAccess(expression, data) val superResult = super.visitMemberAccess(expression, data)
return createSyntheticAccessorCallForFunction(superResult, expression, data?.codegenContext, state.typeMapper) return createSyntheticAccessorCallForFunction(superResult, expression, data?.codegenContext, context)
} }
companion object { companion object {
fun createSyntheticAccessorCallForFunction(superResult: IrElement, expression: IrMemberAccessExpression, codegenContext: CodegenContext<*>?, typeMapper: KotlinTypeMapper): IrElement { fun createSyntheticAccessorCallForFunction(superResult: IrElement, expression: IrMemberAccessExpression, codegenContext: CodegenContext<*>?, context: JvmBackendContext): IrElement {
val descriptor = expression.descriptor val descriptor = expression.descriptor
if (descriptor is FunctionDescriptor) { if (descriptor is FunctionDescriptor) {
@@ -152,31 +161,58 @@ class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass,
val accessor = Companion.actualAccessor(descriptor, directAccessor) val accessor = Companion.actualAccessor(descriptor, directAccessor)
if (accessor is AccessorForCallableDescriptor<*> && descriptor !is AccessorForCallableDescriptor<*>) { if (accessor is AccessorForCallableDescriptor<*> && descriptor !is AccessorForCallableDescriptor<*>) {
val isConstructor = descriptor is ConstructorDescriptor
val accessorOwner = accessor.containingDeclaration as ClassOrPackageFragmentDescriptor val accessorOwner = accessor.containingDeclaration as ClassOrPackageFragmentDescriptor
val staticAccessor = descriptor.toStatic(accessorOwner, Name.identifier(typeMapper.mapAsmMethod(accessor as FunctionDescriptor).name)) //TODO change call val accessorForIr =
val call = IrCallImpl(expression.startOffset, expression.endOffset, staticAccessor, emptyMap(), expression.origin/*TODO super*/) accessorToIrAccessor(isConstructor, accessor, context, descriptor, accessorOwner) //TODO change call
val call =
if (isConstructor && expression is IrDelegatingConstructorCall)
IrDelegatingConstructorCallImpl(expression.startOffset, expression.endOffset, accessorForIr as ClassConstructorDescriptor)
else IrCallImpl(expression.startOffset, expression.endOffset, accessorForIr, emptyMap(), expression.origin/*TODO super*/)
//copyAllArgsToValueParams(call, expression) //copyAllArgsToValueParams(call, expression)
expression.receiverAndArgs().forEachIndexed { i, irExpression -> val receiverAndArgs = expression.receiverAndArgs()
receiverAndArgs.forEachIndexed { i, irExpression ->
call.putValueArgument(i, irExpression) call.putValueArgument(i, irExpression)
} }
if (isConstructor) {
call.putValueArgument(receiverAndArgs.size, IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.ir.symbols.defaultConstructorMarker.descriptor.defaultType))
}
return call return call
} }
} }
return superResult return superResult
} }
fun addAccessorToClass(accessor: AccessorForCallableDescriptor<*>, irClassToAddAccessor: IrClass, typeMapper: KotlinTypeMapper) { private fun accessorToIrAccessor(isConstructor: Boolean, accessor: CallableMemberDescriptor, context: JvmBackendContext, descriptor: FunctionDescriptor, accessorOwner: ClassOrPackageFragmentDescriptor): FunctionDescriptor {
return if (isConstructor)
(accessor as AccessorForConstructorDescriptor).constructorDescriptorWithMarker(
context.ir.symbols.defaultConstructorMarker.descriptor.defaultType
)
else descriptor.toStatic(accessorOwner, Name.identifier(context.state.typeMapper.mapAsmMethod(accessor as FunctionDescriptor).name))
}
fun addAccessorToClass(accessor: AccessorForCallableDescriptor<*>, irClassToAddAccessor: IrClass, context: JvmBackendContext) {
val accessorOwner = (accessor as FunctionDescriptor).containingDeclaration as ClassOrPackageFragmentDescriptor val accessorOwner = (accessor as FunctionDescriptor).containingDeclaration as ClassOrPackageFragmentDescriptor
val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET) val body = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
val accessorDescriptor = accessor.toStatic(accessorOwner, Name.identifier(typeMapper.mapAsmMethod(accessor).name)) val isConstructor = accessor.calleeDescriptor is ConstructorDescriptor
val accessorForIr = accessorToIrAccessor(isConstructor, accessor, context, accessor.calleeDescriptor as FunctionDescriptor, accessorOwner)
val syntheticFunction = IrFunctionImpl( val syntheticFunction = IrFunctionImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR, UNDEFINED_OFFSET, UNDEFINED_OFFSET, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR,
accessorDescriptor, body accessorForIr, body
) )
val calleeDescriptor = accessor.calleeDescriptor as FunctionDescriptor val calleeDescriptor = accessor.calleeDescriptor as FunctionDescriptor
val returnExpr = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, calleeDescriptor) val delegationCall =
Companion.copyAllArgsToValueParams(returnExpr, accessorDescriptor) if (!isConstructor)
body.statements.add(IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, accessor, returnExpr)) IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, calleeDescriptor)
else IrDelegatingConstructorCallImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
createFunctionSymbol(accessor.calleeDescriptor) as IrConstructorSymbol,
accessor.calleeDescriptor as ClassConstructorDescriptor
)
copyAllArgsToValueParams(delegationCall, accessorForIr)
body.statements.add(if (isConstructor) delegationCall else IrReturnImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, accessor, delegationCall))
irClassToAddAccessor.declarations.add(syntheticFunction) irClassToAddAccessor.declarations.add(syntheticFunction)
} }
@@ -193,7 +229,7 @@ class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass,
return calculatedAccessor return calculatedAccessor
} }
private fun copyAllArgsToValueParams(call: IrCallImpl, fromDescriptor: CallableMemberDescriptor) { private fun copyAllArgsToValueParams(call: IrMemberAccessExpression, fromDescriptor: CallableMemberDescriptor) {
var offset = 0 var offset = 0
val newDescriptor = call.descriptor val newDescriptor = call.descriptor
newDescriptor.dispatchReceiverParameter?.let { newDescriptor.dispatchReceiverParameter?.let {
@@ -208,5 +244,18 @@ class SyntheticAccessorLowering(val state: GenerationState) : FileLoweringPass,
call.putValueArgument(i, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, fromDescriptor.valueParameters[i + offset])) call.putValueArgument(i, IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, fromDescriptor.valueParameters[i + offset]))
} }
} }
private fun AccessorForConstructorDescriptor.constructorDescriptorWithMarker(marker: KotlinType) =
ClassConstructorDescriptorImpl.createSynthesized(containingDeclaration, annotations, false, source).also {
it.initialize(
DescriptorUtils.getReceiverParameterType(extensionReceiverParameter),
dispatchReceiverParameter,
emptyList()/*TODO*/,
calleeDescriptor.valueParameters.map { it.copy(this, it.name, it.index) } + ValueParameterDescriptorImpl.createWithDestructuringDeclarations(it, null, calleeDescriptor.valueParameters.size, Annotations.EMPTY, Name.identifier("marker"), marker, false, false, false, null, SourceElement.NO_SOURCE, null),
calleeDescriptor.returnType,
Modality.FINAL,
Visibilities.LOCAL
)
}
} }
} }