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()
return IrConstructorImpl(
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 {
builder()
buildConstructor()
}
inline fun IrClass.addConstructor(builder: IrFunctionBuilder.() -> Unit = {}): IrConstructor =
inline fun IrClass.addConstructor(builder: IrFunctionBuilder.() -> Unit = {}): IrConstructorImpl =
buildConstructor {
builder()
returnType = defaultType
@@ -188,18 +188,6 @@ private fun IrDeclarationWithVisibility.specialCaseVisibility(kind: OwnerKind?):
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 (isEffectivelyInlineOnly()) {
return Opcodes.ACC_PRIVATE
@@ -286,30 +274,6 @@ private fun IrDeclarationWithVisibility.specialCaseVisibility(kind: OwnerKind?):
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. */
private tailrec fun isInlineOrContainedInInline(declaration: IrDeclaration?): Boolean = when {
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.JvmSymbols
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.descriptors.Visibilities
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.Scope
import org.jetbrains.kotlin.ir.declarations.*
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.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -96,6 +99,9 @@ fun IrType.getArrayElementType(irBuiltIns: IrBuiltIns): IrType =
else
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
class JvmIrBuilder(
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.irArray
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.Visibilities
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.SpecialNames
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.org.objectweb.asm.Type
internal val callableReferencePhase = makeIrFilePhase(
::CallableReferenceLowering,
@@ -383,7 +385,17 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
DescriptorUtils.unwrapFakeOverride(irFunctionReference.symbol.descriptor).original
).owner
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.passTypeArgumentsFrom
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.JvmLoweredDeclarationOrigin
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.Visibilities
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.buildFun
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.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.makeNullable
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.load.java.JavaVisibilities
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrElementTransformerVoidWithContext(), FileLoweringPass {
private val pendingTransformations = mutableListOf<Function0<Unit>>()
@@ -43,6 +47,44 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
override fun lower(irFile: IrFile) {
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)
pendingTransformations.forEach { it() }
}
@@ -108,7 +150,7 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle
classes.lastOrNull { parent is IrClass && it.isSubclassOf(parent) } ?: classes.last()
} else parent
private fun IrConstructor.makeConstructorAccessor(): IrConstructor {
private fun IrConstructor.makeConstructorAccessor(): IrConstructorImpl {
val source = this
return buildConstructor {
@@ -91,15 +91,20 @@ object InlineClassAbi {
}
}
internal val IrType.requiresMangling: Boolean
private val IrType.requiresMangling: Boolean
get() {
val irClass = erasedUpperBound
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
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
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.symbols.IrSimpleFunctionSymbol
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.IrStarProjectionImpl
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
get() = origin != IrDeclarationOrigin.FAKE_OVERRIDE &&
(this is IrSimpleFunction || this is IrConstructor && constructedClass.isInline)
@@ -17,12 +17,15 @@
package org.jetbrains.kotlin.ir.declarations
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
interface IrConstructor : IrFunction, IrSymbolDeclaration<IrConstructorSymbol> {
override val descriptor: ClassConstructorDescriptor
override var visibility: Visibility
val isPrimary: Boolean
}
@@ -31,7 +31,7 @@ abstract class IrFunctionBase(
endOffset: Int,
origin: IrDeclarationOrigin,
override val name: Name,
override val visibility: Visibility,
override var visibility: Visibility,
override val isInline: Boolean,
override val isExternal: Boolean,
returnType: IrType
@@ -23,7 +23,7 @@ abstract class IrLazyFunctionBase(
endOffset: Int,
origin: IrDeclarationOrigin,
override val name: Name,
override val visibility: Visibility,
override var visibility: Visibility,
override val isInline: Boolean,
override val isExternal: Boolean,
stubGenerator: DeclarationStubGenerator,
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR
// IGNORE_BACKEND: JS_IR, JS, NATIVE
// WITH_REFLECT
import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.full.primaryConstructor