JVM IR: Hide constructors with inline class parameters

This commit is contained in:
Steven Schäfer
2019-09-23 11:28:30 +02:00
committed by Alexander Udalov
parent bdd5705a30
commit 9389d88232
12 changed files with 79 additions and 54 deletions
@@ -127,7 +127,7 @@ fun IrFunctionBuilder.buildFun(originalDescriptor: FunctionDescriptor? = null):
} }
} }
fun IrFunctionBuilder.buildConstructor(): IrConstructor { fun IrFunctionBuilder.buildConstructor(): IrConstructorImpl {
val wrappedDescriptor = WrappedClassConstructorDescriptor() val wrappedDescriptor = WrappedClassConstructorDescriptor()
return IrConstructorImpl( return IrConstructorImpl(
startOffset, endOffset, origin, startOffset, endOffset, origin,
@@ -179,13 +179,13 @@ fun IrDeclarationContainer.addFunction(
} }
} }
inline fun buildConstructor(builder: IrFunctionBuilder.() -> Unit): IrConstructor = inline fun buildConstructor(builder: IrFunctionBuilder.() -> Unit): IrConstructorImpl =
IrFunctionBuilder().run { IrFunctionBuilder().run {
builder() builder()
buildConstructor() buildConstructor()
} }
inline fun IrClass.addConstructor(builder: IrFunctionBuilder.() -> Unit = {}): IrConstructor = inline fun IrClass.addConstructor(builder: IrFunctionBuilder.() -> Unit = {}): IrConstructorImpl =
buildConstructor { buildConstructor {
builder() builder()
returnType = defaultType returnType = defaultType
@@ -188,18 +188,6 @@ private fun IrDeclarationWithVisibility.specialCaseVisibility(kind: OwnerKind?):
return Opcodes.ACC_PRIVATE return Opcodes.ACC_PRIVATE
} }
// if (kind !== OwnerKind.ERASED_INLINE_CLASS &&
// memberDescriptor is ConstructorDescriptor &&
// memberDescriptor !is AccessorForConstructorDescriptor &&
// shouldHideConstructorDueToInlineClassTypeValueParameters(memberDescriptor)
// ) {
if (kind !== OwnerKind.ERASED_INLINE_CLASS &&
this is IrConstructor &&
shouldHideDueToInlineClassTypeValueParameters()
) {
return Opcodes.ACC_PRIVATE
}
// if (memberDescriptor.isEffectivelyInlineOnly()) { // if (memberDescriptor.isEffectivelyInlineOnly()) {
if (isEffectivelyInlineOnly()) { if (isEffectivelyInlineOnly()) {
return Opcodes.ACC_PRIVATE return Opcodes.ACC_PRIVATE
@@ -286,30 +274,6 @@ private fun IrDeclarationWithVisibility.specialCaseVisibility(kind: OwnerKind?):
return null return null
} }
/* From inlineClassManglingRules.kt */
fun IrConstructor.shouldHideDueToInlineClassTypeValueParameters() =
!Visibilities.isPrivate(visibility) &&
!parentAsClass.isInline &&
parentAsClass.modality !== Modality.SEALED &&
valueParameters.any { it.type.requiresFunctionNameMangling() }
fun IrType.requiresFunctionNameMangling(): Boolean =
isInlineClassThatRequiresMangling() || isTypeParameterWithUpperBoundThatRequiresMangling()
fun IrType.isInlineClassThatRequiresMangling() =
safeAs<IrSimpleType>()?.classifier?.owner?.safeAs<IrClass>()?.let {
it.isInline && !it.isDontMangleClass()
} ?: false
fun IrClass.isDontMangleClass() =
fqNameWhenAvailable != DescriptorUtils.RESULT_FQ_NAME
fun IrType.isTypeParameterWithUpperBoundThatRequiresMangling() =
safeAs<IrSimpleType>()?.classifier?.owner.safeAs<IrTypeParameter>()?.let { param ->
param.superTypes.any { it.requiresFunctionNameMangling() }
} ?: false
/* Borrowed from InlineUtil. */ /* Borrowed from InlineUtil. */
private tailrec fun isInlineOrContainedInInline(declaration: IrDeclaration?): Boolean = when { private tailrec fun isInlineOrContainedInInline(declaration: IrDeclaration?): Boolean = when {
declaration === null -> false declaration === null -> false
@@ -9,12 +9,15 @@ import org.jetbrains.kotlin.backend.common.lower.IrLoweringContext
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmSymbols import org.jetbrains.kotlin.backend.jvm.JvmSymbols
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.hasMangledParameters
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.Scope import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -96,6 +99,9 @@ fun IrType.getArrayElementType(irBuiltIns: IrBuiltIns): IrType =
else else
irBuiltIns.primitiveArrayElementTypes.getValue(this.classOrNull!!) irBuiltIns.primitiveArrayElementTypes.getValue(this.classOrNull!!)
val IrConstructor.shouldBeHidden: Boolean
get() = !Visibilities.isPrivate(visibility) && !constructedClass.isInline && hasMangledParameters
// An IR builder with a reference to the JvmBackendContext // An IR builder with a reference to the JvmBackendContext
class JvmIrBuilder( class JvmIrBuilder(
val backendContext: JvmBackendContext, val backendContext: JvmBackendContext,
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.backend.jvm.codegen.isInlineIrExpression
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.irArray import org.jetbrains.kotlin.backend.jvm.ir.irArray
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
import org.jetbrains.kotlin.backend.jvm.ir.shouldBeHidden
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -40,6 +41,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.org.objectweb.asm.Type
internal val callableReferencePhase = makeIrFilePhase( internal val callableReferencePhase = makeIrFilePhase(
::CallableReferenceLowering, ::CallableReferenceLowering,
@@ -383,7 +385,17 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
DescriptorUtils.unwrapFakeOverride(irFunctionReference.symbol.descriptor).original DescriptorUtils.unwrapFakeOverride(irFunctionReference.symbol.descriptor).original
).owner ).owner
val method = codegenContext.methodSignatureMapper.mapAsmMethod(declaration) val method = codegenContext.methodSignatureMapper.mapAsmMethod(declaration)
irExprBody(irString(method.name + method.descriptor)) // HACK: When referencing a constructor taking inline class parameters, we will actually
// end up calling a public bridge method with an additional argument.
// TODO: Don't compute signatures in CallableReferenceLowering
val descriptor = if (declaration is IrConstructor && declaration.shouldBeHidden) {
val marker =
codegenContext.typeMapper.mapType(codegenContext.ir.symbols.defaultConstructorMarker.owner.defaultType)
Type.getMethodDescriptor(method.returnType, *method.argumentTypes, marker)
} else {
method.descriptor
}
irExprBody(irString(method.name + descriptor))
} }
} }
@@ -11,10 +11,10 @@ import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.copyValueParametersToStatic import org.jetbrains.kotlin.backend.common.ir.copyValueParametersToStatic
import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom
import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext 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.backend.jvm.ir.shouldBeHidden
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.declarations.* 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.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
@@ -32,10 +33,13 @@ import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isSubtypeOfClass import org.jetbrains.kotlin.ir.types.isSubtypeOfClass
import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass { internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
private val pendingTransformations = mutableListOf<Function0<Unit>>() private val pendingTransformations = mutableListOf<Function0<Unit>>()
@@ -43,6 +47,44 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
inlineLambdaToCallSite.putAll(InlineReferenceLocator.scan(context, irFile).lambdaToCallSite) inlineLambdaToCallSite.putAll(InlineReferenceLocator.scan(context, irFile).lambdaToCallSite)
// Unconditionally add bridges for hidden constructors
irFile.acceptChildrenVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
private fun handleConstructor(declaration: IrConstructor) {
if (!declaration.shouldBeHidden)
return
declaration.visibility = Visibilities.PRIVATE
functionMap.computeIfAbsent(declaration.symbol) {
declaration.makeConstructorAccessor().also { accessor ->
// There's a special case in the JVM backend for serializing the metadata of hidden
// constructors - we serialize the descriptor of the original constructor, but the
// signature of the bridge. We implement this special case in the JVM IR backend by
// attaching the metadata directly to the bridge. We also have to move all annotations
// to the bridge method. Parameter annotations are already moved by the copyTo method.
accessor.metadata = declaration.metadata
declaration.safeAs<IrConstructorImpl>()?.metadata = null
accessor.annotations += declaration.annotations
declaration.annotations.clear()
declaration.valueParameters.forEach { it.annotations.clear() }
}.symbol
}
}
override fun visitConstructor(declaration: IrConstructor) {
handleConstructor(declaration)
super.visitConstructor(declaration)
}
override fun visitConstructorCall(expression: IrConstructorCall) {
handleConstructor(expression.symbol.owner)
super.visitConstructorCall(expression)
}
})
irFile.transformChildrenVoid(this) irFile.transformChildrenVoid(this)
pendingTransformations.forEach { it() } pendingTransformations.forEach { it() }
} }
@@ -108,7 +150,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
classes.lastOrNull { parent is IrClass && it.isSubclassOf(parent) } ?: classes.last() classes.lastOrNull { parent is IrClass && it.isSubclassOf(parent) } ?: classes.last()
} else parent } else parent
private fun IrConstructor.makeConstructorAccessor(): IrConstructor { private fun IrConstructor.makeConstructorAccessor(): IrConstructorImpl {
val source = this val source = this
return buildConstructor { return buildConstructor {
@@ -91,15 +91,20 @@ object InlineClassAbi {
} }
} }
internal val IrType.requiresMangling: Boolean private val IrType.requiresMangling: Boolean
get() { get() {
val irClass = erasedUpperBound val irClass = erasedUpperBound
return irClass.isInline && irClass.fqNameWhenAvailable != DescriptorUtils.RESULT_FQ_NAME return irClass.isInline && irClass.fqNameWhenAvailable != DescriptorUtils.RESULT_FQ_NAME
} }
internal val IrFunction.fullValueParameterList: List<IrValueParameter> private val IrFunction.fullValueParameterList: List<IrValueParameter>
get() = listOfNotNull(extensionReceiverParameter) + valueParameters get() = listOfNotNull(extensionReceiverParameter) + valueParameters
internal val IrFunction.hasMangledParameters: Boolean
get() = dispatchReceiverParameter?.type?.getClass()?.isInline == true ||
fullValueParameterList.any { it.type.requiresMangling } ||
(this is IrConstructor && constructedClass.isInline)
internal val IrClass.inlineClassFieldName: Name internal val IrClass.inlineClassFieldName: Name
get() = primaryConstructor!!.valueParameters.single().name get() = primaryConstructor!!.valueParameters.single().name
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.constructedClass
@@ -57,11 +56,6 @@ class MemoizedInlineClassReplacements {
} }
} }
private val IrFunction.hasMangledParameters: Boolean
get() = dispatchReceiverParameter?.type?.getClass()?.isInline == true ||
fullValueParameterList.any { it.type.requiresMangling } ||
(this is IrConstructor && constructedClass.isInline)
private val IrFunction.hasStaticReplacement: Boolean private val IrFunction.hasStaticReplacement: Boolean
get() = origin != IrDeclarationOrigin.FAKE_OVERRIDE && get() = origin != IrDeclarationOrigin.FAKE_OVERRIDE &&
(this is IrSimpleFunction || this is IrConstructor && constructedClass.isInline) (this is IrSimpleFunction || this is IrConstructor && constructedClass.isInline)
@@ -17,12 +17,15 @@
package org.jetbrains.kotlin.ir.declarations package org.jetbrains.kotlin.ir.declarations
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
interface IrConstructor : IrFunction, IrSymbolDeclaration<IrConstructorSymbol> { interface IrConstructor : IrFunction, IrSymbolDeclaration<IrConstructorSymbol> {
override val descriptor: ClassConstructorDescriptor override val descriptor: ClassConstructorDescriptor
override var visibility: Visibility
val isPrimary: Boolean val isPrimary: Boolean
} }
@@ -31,7 +31,7 @@ abstract class IrFunctionBase(
endOffset: Int, endOffset: Int,
origin: IrDeclarationOrigin, origin: IrDeclarationOrigin,
override val name: Name, override val name: Name,
override val visibility: Visibility, override var visibility: Visibility,
override val isInline: Boolean, override val isInline: Boolean,
override val isExternal: Boolean, override val isExternal: Boolean,
returnType: IrType returnType: IrType
@@ -23,7 +23,7 @@ abstract class IrLazyFunctionBase(
endOffset: Int, endOffset: Int,
origin: IrDeclarationOrigin, origin: IrDeclarationOrigin,
override val name: Name, override val name: Name,
override val visibility: Visibility, override var visibility: Visibility,
override val isInline: Boolean, override val isInline: Boolean,
override val isExternal: Boolean, override val isExternal: Boolean,
stubGenerator: DeclarationStubGenerator, stubGenerator: DeclarationStubGenerator,
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR // IGNORE_BACKEND: JS_IR, JS, NATIVE
// WITH_REFLECT // WITH_REFLECT
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_REFLECT // WITH_REFLECT
import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.primaryConstructor