Determine more precise conditions when to enable serialization intrinsic

Disable it if we do not have required `noCompiledSerializer` function in
runtime. Leave it enabled in tests.

Rollback some changes for old backend as it is unsupported now.
This commit is contained in:
Leonid Startsev
2022-09-05 19:40:51 +02:00
committed by Space
parent 637e8c7d8a
commit 033538161e
14 changed files with 128 additions and 160 deletions
@@ -8,17 +8,10 @@ package org.jetbrains.kotlin.codegen.extensions
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
interface ExpressionCodegenExtension {
companion object : ProjectExtensionDescriptor<ExpressionCodegenExtension>(
@@ -42,7 +42,7 @@ class PsiInlineCodegen(
) : InlineCodegen<ExpressionCodegen>(
codegen, state, signature, typeParameterMappings, sourceCompiler,
ReifiedTypeInliner(
typeParameterMappings, PsiInlineIntrinsicsSupport(state, reportErrorsOn, codegen.typeSystem), codegen.typeSystem,
typeParameterMappings, PsiInlineIntrinsicsSupport(state, reportErrorsOn), codegen.typeSystem,
state.languageVersionSettings, state.unifiedNullChecks
),
), CallGenerator {
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
@@ -20,22 +19,16 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.TYPEOF_SUSPEND_TYPE
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeSystemCommonBackendContext
import org.jetbrains.kotlin.types.model.TypeParameterMarker
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Type.INT_TYPE
import org.jetbrains.org.objectweb.asm.Type.VOID_TYPE
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
class PsiInlineIntrinsicsSupport(
override val state: GenerationState,
private val reportErrorsOn: KtElement,
private val typeSystem: TypeSystemCommonBackendContext
) : ReifiedTypeInliner.IntrinsicsSupport<KotlinType> {
private val pluginExtensions = ExpressionCodegenExtension.getInstances(state.project)
override fun putClassInstance(v: InstructionAdapter, type: KotlinType) {
DescriptorAsmUtil.putJavaLangClassInstance(v, state.typeMapper.mapType(type), type, state.typeMapper)
}
@@ -66,7 +66,7 @@ class ReifiedTypeInliner<KT : KotlinTypeMarker>(
fun reportSuspendTypeUnsupported()
fun reportNonReifiedTypeParameterWithRecursiveBoundUnsupported(typeParameterName: Name)
fun rewritePluginDefinedOperationMarker(v: InstructionAdapter, next: AbstractInsnNode, instructions: InsnList, type: KT): Boolean =
fun rewritePluginDefinedOperationMarker(v: InstructionAdapter, stubConstNull: AbstractInsnNode, instructions: InsnList, type: KT): Boolean =
false
}
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.common.extensions
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.extensions.ProjectExtensionDescriptor
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
@@ -17,7 +18,7 @@ interface IrGenerationExtension : IrDeserializer.IrLinkerExtension {
fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext)
fun getPlatformIntrinsicExtension(): IrIntrinsicExtension? = null
fun getPlatformIntrinsicExtension(backendContext: BackendContext): IrIntrinsicExtension? = null
}
/**
@@ -44,7 +44,7 @@ class IrInlineIntrinsicsSupport(
get() = classCodegen.context.state
private val pluginExtensions = IrGenerationExtension.getInstances(classCodegen.context.state.project)
.mapNotNull { it.getPlatformIntrinsicExtension() as? JvmIrIntrinsicExtension }
.mapNotNull { it.getPlatformIntrinsicExtension(classCodegen.context) as? JvmIrIntrinsicExtension }
override fun putClassInstance(v: InstructionAdapter, type: IrType) {
ExpressionCodegen.generateClassInstance(v, type, classCodegen.typeMapper, wrapPrimitives = false)
@@ -127,7 +127,7 @@ class IrInlineIntrinsicsSupport(
.report(JvmBackendErrors.TYPEOF_NON_REIFIED_TYPE_PARAMETER_WITH_RECURSIVE_BOUND, typeParameterName.asString())
}
override fun rewritePluginDefinedOperationMarker(v: InstructionAdapter, next: AbstractInsnNode, instructions: InsnList, type: IrType): Boolean {
return pluginExtensions.any { it.rewritePluginDefinedOperationMarker(v, next, instructions, type, classCodegen.context) }
override fun rewritePluginDefinedOperationMarker(v: InstructionAdapter, stubConstNull: AbstractInsnNode, instructions: InsnList, type: IrType): Boolean {
return pluginExtensions.any { it.rewritePluginDefinedOperationMarker(v, stubConstNull, instructions, type) }
}
}
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.common.extensions.IrIntrinsicExtension
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethod
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
@@ -20,12 +19,13 @@ interface JvmIrIntrinsicExtension : IrIntrinsicExtension {
/**
* Should return `true` if marker was processed.
* If this method returns `false`, a regular `TYPE_OF` intrinsic would be inserted.
*
* This is plugin's responsibility to remove any calls to MagicApiIntrinsics.voidMagicApiCall and its arguments.
*/
fun rewritePluginDefinedOperationMarker(
v: InstructionAdapter,
next: AbstractInsnNode,
stubConstNull: AbstractInsnNode,
instructions: InsnList,
type: IrType,
jvmBackendContext: JvmBackendContext
type: IrType
): Boolean
}
@@ -288,7 +288,7 @@ open class JvmIrCodegenFactory(
context.localDeclarationsLoweringData = mutableMapOf()
}
val generationExtensions = IrGenerationExtension.getInstances(state.project)
.mapNotNull { it.getPlatformIntrinsicExtension() as? JvmIrIntrinsicExtension }
.mapNotNull { it.getPlatformIntrinsicExtension(context) as? JvmIrIntrinsicExtension }
val intrinsics by lazy { IrIntrinsicMethods(irModuleFragment.irBuiltins, context.ir.symbols) }
context.getIntrinsic = { symbol: IrFunctionSymbol ->
intrinsics.getIntrinsic(symbol) ?: generationExtensions.firstNotNullOfOrNull { it.getIntrinsic(symbol) }
@@ -19,9 +19,9 @@ import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.pluginIn
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
@@ -56,7 +56,7 @@ import org.jetbrains.org.objectweb.asm.tree.InsnList
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContext) : SerializationBaseContext {
class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContext) : SerializationBaseContext, JvmIrIntrinsicExtension {
sealed class IntrinsicType(val methodDescriptor: String) {
object Simple : IntrinsicType(stubCallDescriptor)
@@ -69,35 +69,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
}
}
companion object {
fun intrinsicForMethod(method: IrFunction): IntrinsicMethod? {
if (method.fqNameWhenAvailable?.asString() != "kotlinx.serialization.SerializersKt.serializer"
|| method.dispatchReceiverParameter != null
|| method.typeParameters.size != 1
|| method.valueParameters.isNotEmpty()
) return null
val receiver = method.extensionReceiverParameter
return if (receiver == null)
ReifiedSerializerMethod(withModule = false)
else if (receiver.type.classFqName?.asString() == "kotlinx.serialization.modules.SerializersModule")
ReifiedSerializerMethod(withModule = true)
else null
}
val serializersModuleType: Type = Type.getObjectType("kotlinx/serialization/modules/SerializersModule")
val kTypeType: Type = AsmTypes.K_TYPE
val stubCallDescriptorWithModule = "(${serializersModuleType.descriptor}${kTypeType.descriptor})${kSerializerType.descriptor}"
val stubCallDescriptor = "(${kTypeType.descriptor})${kSerializerType.descriptor}"
const val serializersKtInternalName = "kotlinx/serialization/SerializersKt"
const val callMethodName = "serializer"
const val noCompiledSerializerMethodName = "noCompiledSerializer"
const val magicMarkerStringPrefix = "kotlinx.serialization.serializer."
}
class ReifiedSerializerMethod(private val withModule: Boolean) : IntrinsicMethod() {
inner class ReifiedSerializerMethod(private val withModule: Boolean) : IntrinsicMethod() {
override fun invoke(
expression: IrFunctionAccessExpression,
codegen: ExpressionCodegen,
@@ -115,7 +87,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
codegen.markLineNumber(expression)
IntrinsicType.Simple
}
SerializationJvmIrIntrinsicSupport(codegen.context).generateSerializerForType(
generateSerializerForType(
argument,
mv,
intrinsicType
@@ -128,6 +100,35 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
}
}
companion object {
val serializersModuleType: Type = Type.getObjectType("kotlinx/serialization/modules/SerializersModule")
val kTypeType: Type = AsmTypes.K_TYPE
val stubCallDescriptorWithModule = "(${serializersModuleType.descriptor}${kTypeType.descriptor})${kSerializerType.descriptor}"
val stubCallDescriptor = "(${kTypeType.descriptor})${kSerializerType.descriptor}"
const val serializersKtInternalName = "kotlinx/serialization/SerializersKt"
const val callMethodName = "serializer"
const val noCompiledSerializerMethodName = "noCompiledSerializer"
const val magicMarkerStringPrefix = "kotlinx.serialization.serializer."
}
override fun getIntrinsic(symbol: IrFunctionSymbol): IntrinsicMethod? {
val method = symbol.owner
if (method.fqNameWhenAvailable?.asString() != "kotlinx.serialization.SerializersKt.serializer"
|| method.dispatchReceiverParameter != null
|| method.typeParameters.size != 1
|| method.valueParameters.isNotEmpty()
) return null
val receiver = method.extensionReceiverParameter
return if (receiver == null)
ReifiedSerializerMethod(withModule = false)
else if (receiver.type.classFqName?.asString() == "kotlinx.serialization.modules.SerializersModule")
ReifiedSerializerMethod(withModule = true)
else null
}
private val emptyGenerator: BaseIrGenerator? = null
private val module = jvmBackendContext.state.module
@@ -143,14 +144,16 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
return module.findClassAcrossModuleDependencies(classId)?.let { jvmBackendContext.referenceClass(it) }
}
private val currentVersion = VersionReader.getVersionsForCurrentModuleFromTrace(module, jvmBackendContext.state.bindingTrace)
?.implementationVersion
private val currentVersion by lazy {
VersionReader.getVersionsForCurrentModuleFromTrace(module, jvmBackendContext.state.bindingTrace)
?.implementationVersion
}
override val runtimeHasEnumSerializerFactoryFunctions: Boolean
get() = currentVersion != null && currentVersion > ApiVersion.parse("1.4.0")!!
get() = currentVersion != null && currentVersion!! > ApiVersion.parse("1.4.0")!!
private val hasNewContextSerializerSignature: Boolean
get() = currentVersion != null && currentVersion >= ApiVersion.parse("1.2.0")!!
get() = currentVersion != null && currentVersion!! >= ApiVersion.parse("1.2.0")!!
private fun findTypeSerializerOrContext(argType: IrType): IrClassSymbol? =
emptyGenerator.findTypeSerializerOrContextUnchecked(this, argType)
@@ -181,7 +184,7 @@ class SerializationJvmIrIntrinsicSupport(val jvmBackendContext: JvmBackendContex
* We need to remove instructions from 1 to 5
* Instructions 0, -1 -2 and -3 would be removed by inliner.
*/
fun rewritePluginDefinedReifiedOperationMarker(
override fun rewritePluginDefinedOperationMarker(
v: InstructionAdapter,
stubConstNull: AbstractInsnNode,
instructions: InsnList,
@@ -8,10 +8,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.ClassContext
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapperBase
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
@@ -35,7 +32,6 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
@@ -251,89 +247,56 @@ internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(
}
}
internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
expressionCodegen: ExpressionCodegen, codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?,
genericIndex: Int? = null,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
): Boolean {
return stackValueSerializerInstance(
expressionCodegen,
codegen.typeMapper,
module,
kType,
maybeSerializer,
iv,
genericIndex,
false,
genericSerializerFieldGetter
)
}
// returns false is cannot not use serializer
// use iv == null to check only (do not emit serializer onto stack)
internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
expressionCodegen: ExpressionCodegen?, typeMapper: KotlinTypeMapper, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?,
genericIndex: Int? = null,
insertExceptionOnNoSerializer: Boolean = false,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null,
internal fun AbstractSerialGenerator.stackValueSerializerInstance(expressionCodegen: ExpressionCodegen, classCodegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?,
genericIndex: Int? = null,
genericSerializerFieldGetter: (InstructionAdapter.(Int, KotlinType) -> Unit)? = null
): Boolean {
if (maybeSerializer == null && genericIndex != null) {
// get field from serializer object
iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex, kType) }
return true
}
val serializer = maybeSerializer ?: run {
if (insertExceptionOnNoSerializer) iv?.apply {
aconst(kType.getJetTypeFqName(false))
invokestatic(
"kotlinx/serialization/SerializersKt",
"noCompiledSerializer",
"(Ljava/lang/String;)Lkotlinx/serialization/KSerializer;",
false
)
}
return false
}
val serializer = maybeSerializer ?: return false
if (serializer.kind == ClassKind.OBJECT) {
// singleton serializer -- just get it
if (iv != null)
StackValue.singleton(serializer, typeMapper).put(kSerializerType, iv)
StackValue.singleton(serializer, classCodegen.typeMapper).put(kSerializerType, iv)
return true
}
// serializer is not singleton object and shall be instantiated
val argSerializers = kType.arguments.map { projection ->
// check if any type argument is not serializable
// bail out from stackValueSerializerInstance if any type argument is not serializable
val argType = projection.type
val argSerializer =
if (argType.isTypeParameter()) null else findTypeSerializerOrContextUnchecked(module, argType)
val argSerializer = if (argType.isTypeParameter()) null else {
findTypeSerializerOrContext(module, argType, sourceElement = classCodegen.descriptor.findPsi())
?: return false
}
// check if it can be properly serialized with its args recursively
if (!stackValueSerializerInstance(
expressionCodegen,
typeMapper,
classCodegen,
module,
argType,
argSerializer,
null,
argType.genericIndex,
insertExceptionOnNoSerializer = false,
genericSerializerFieldGetter
)
) {
// bail out only if we do not need to insert exception
if (!insertExceptionOnNoSerializer) return false
}
)
return false
Pair(argType, argSerializer)
}
// new serializer if needed
iv?.apply {
val serializerType = typeMapper.mapClass(serializer)
val serializerType = classCodegen.typeMapper.mapClass(serializer)
val classDescriptor = kType.toClassDescriptor!!
if (serializer.classId == enumSerializerId && !classDescriptor.useGeneratedEnumSerializer) {
// runtime contains enum serializer factory functions
val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
val enumJavaType = typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val serialName = classDescriptor.serialName()
if (classDescriptor.isEnumWithSerialInfoAnnotation()) {
@@ -360,7 +323,7 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
} else {
fillArray(annotationType, annotations) { _, annotation ->
val (annotationClass, args, consParams) = annotation
expressionCodegen?.generateSyntheticAnnotationOnStack(annotationClass, args, consParams) ?: nop()
expressionCodegen.generateSyntheticAnnotationOnStack(annotationClass, args, consParams)
}
}
}
@@ -398,15 +361,14 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
assert(
stackValueSerializerInstance(
expressionCodegen,
typeMapper,
classCodegen,
module,
argType,
argSerializer,
this,
argType.genericIndex,
insertExceptionOnNoSerializer,
genericSerializerFieldGetter
) || insertExceptionOnNoSerializer
)
)
// wrap into nullable serializer if argType is nullable
if (argType.isMarkedNullable) wrapStackValueIntoNullableSerializer()
@@ -419,16 +381,16 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
// support legacy serializer instantiation by constructor for old runtimes
aconst(serialName)
signature.append("Ljava/lang/String;")
val enumJavaType = typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val enumJavaType = classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT)
val javaEnumArray = Type.getType("[Ljava/lang/Enum;")
invokestatic(enumJavaType.internalName, "values", "()[${enumJavaType.descriptor}", false)
invokestatic(enumJavaType.internalName, "values","()[${enumJavaType.descriptor}", false)
checkcast(javaEnumArray)
signature.append(javaEnumArray.descriptor)
}
contextSerializerId, polymorphicSerializerId -> {
// a special way to instantiate enum -- need a enum KClass reference
// GENERIC_ARGUMENT forces boxing in order to obtain KClass
aconst(typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
if (serializer.classId == contextSerializerId && serializer.constructors.any { it.valueParameters.size == 3 }) {
@@ -448,7 +410,7 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
}
referenceArraySerializerId -> {
// a special way to instantiate reference array serializer -- need an element KClass reference
aconst(typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT))
aconst(classCodegen.typeMapper.mapType(kType.arguments[0].type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
// Reference array serializer still needs serializer for its argument type
@@ -457,13 +419,13 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
sealedSerializerId -> {
aconst(serialName)
signature.append("Ljava/lang/String;")
aconst(typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
aconst(classCodegen.typeMapper.mapType(kType, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
signature.append(AsmTypes.K_CLASS_TYPE.descriptor)
val (subClasses, subSerializers) = allSealedSerializableSubclassesFor(kType.toClassDescriptor!!, module)
// KClasses vararg
fillArray(AsmTypes.K_CLASS_TYPE, subClasses) { _, type ->
aconst(typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
aconst(classCodegen.typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
AsmUtil.wrapJavaClassIntoKClass(this)
}
signature.append(AsmTypes.K_CLASS_ARRAY_TYPE.descriptor)
@@ -473,7 +435,7 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
assert(
stackValueSerializerInstance(
expressionCodegen,
typeMapper,
classCodegen,
module,
argType,
argSerializer,
@@ -484,12 +446,11 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
assert(
stackValueSerializerInstance(
expressionCodegen,
typeMapper,
classCodegen,
module,
(genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound,
module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer),
this,
insertExceptionOnNoSerializer = insertExceptionOnNoSerializer
this
)
)
}
@@ -501,7 +462,7 @@ internal fun AbstractSerialGenerator?.stackValueSerializerInstance(
objectSerializerId -> {
aconst(serialName)
signature.append("Ljava/lang/String;")
StackValue.singleton(kType.toClassDescriptor!!, typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
StackValue.singleton(kType.toClassDescriptor!!, classCodegen.typeMapper).put(Type.getType("Ljava/lang/Object;"), iv)
signature.append("Ljava/lang/Object;")
}
// all serializers get arguments with serializers of their generic types
@@ -5,6 +5,7 @@
package org.jetbrains.kotlinx.serialization.compiler.extensions
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.CompilationException
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
@@ -12,15 +13,12 @@ import org.jetbrains.kotlin.backend.common.extensions.IrIntrinsicExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.codegen.JvmIrIntrinsicExtension
import org.jetbrains.kotlin.backend.jvm.intrinsics.IntrinsicMethod
import org.jetbrains.kotlin.backend.jvm.ir.fileParent
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
@@ -33,9 +31,6 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializationJvmI
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnList
import java.util.concurrent.ConcurrentHashMap
/**
@@ -128,14 +123,20 @@ private class SerializerClassPreLowering(
}
}
enum class SerializationIntrinsicsState {
NORMAL, // depends on whether we have noCompiledSerializer function in runtime
DISABLED, // disabled if corresponding CLI flag passed
FORCE_ENABLED // used for test purposes ONLY
}
open class SerializationLoweringExtension @JvmOverloads constructor(
private val metadataPlugin: SerializationDescriptorSerializerPlugin? = null
) : IrGenerationExtension {
private var disableIntrinsics = false
private var intrinsicsState = SerializationIntrinsicsState.NORMAL
constructor(metadataPlugin: SerializationDescriptorSerializerPlugin, disableIntrinsics: Boolean) : this(metadataPlugin) {
this.disableIntrinsics = disableIntrinsics
constructor(metadataPlugin: SerializationDescriptorSerializerPlugin, intrinsicsState: SerializationIntrinsicsState) : this(metadataPlugin) {
this.intrinsicsState = intrinsicsState
}
override fun generate(
@@ -148,21 +149,27 @@ open class SerializationLoweringExtension @JvmOverloads constructor(
moduleFragment.files.forEach(pass2::runOnFileInOrder)
}
override fun getPlatformIntrinsicExtension(): IrIntrinsicExtension? {
return if (disableIntrinsics) null else object : JvmIrIntrinsicExtension {
override fun getIntrinsic(symbol: IrFunctionSymbol): IntrinsicMethod? =
SerializationJvmIrIntrinsicSupport.intrinsicForMethod(symbol.owner)
override fun getPlatformIntrinsicExtension(backendContext: BackendContext): IrIntrinsicExtension? {
val ctx = backendContext as? JvmBackendContext ?: return null
if (!canEnableIntrinsics(ctx)) return null
return SerializationJvmIrIntrinsicSupport(ctx)
}
override fun rewritePluginDefinedOperationMarker(
v: InstructionAdapter,
next: AbstractInsnNode,
instructions: InsnList,
type: IrType,
jvmBackendContext: JvmBackendContext
): Boolean {
return SerializationJvmIrIntrinsicSupport(jvmBackendContext).rewritePluginDefinedReifiedOperationMarker(
v, next, instructions, type
)
private fun canEnableIntrinsics(ctx: JvmBackendContext): Boolean {
return when (intrinsicsState) {
SerializationIntrinsicsState.FORCE_ENABLED -> true
SerializationIntrinsicsState.DISABLED -> false
SerializationIntrinsicsState.NORMAL -> {
val module = ctx.state.module
if (module.findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.packageFqName,
SerialEntityNames.KSERIALIZER_NAME
)
) == null
) return false
module.getPackage(SerializationPackages.packageFqName).memberScope.getFunctionNames()
.any { it.asString() == SerializationJvmIrIntrinsicSupport.noCompiledSerializerMethodName }
}
}
}
@@ -53,13 +53,14 @@ class SerializationComponentRegistrar : CompilerPluginRegistrar() {
Companion.registerExtensions(this, loadDisableIntrinsic(configuration))
}
private fun loadDisableIntrinsic(configuration: CompilerConfiguration) = configuration.get(DISABLE_INTRINSIC) ?: false
private fun loadDisableIntrinsic(configuration: CompilerConfiguration) =
if (configuration.get(DISABLE_INTRINSIC) == true) SerializationIntrinsicsState.DISABLED else SerializationIntrinsicsState.NORMAL
override val supportsK2: Boolean
get() = false
companion object {
fun registerExtensions(extensionStorage: ExtensionStorage, disableIntrinsics: Boolean = false) = with(extensionStorage) {
fun registerExtensions(extensionStorage: ExtensionStorage, intrinsicsState: SerializationIntrinsicsState = SerializationIntrinsicsState.NORMAL) = with(extensionStorage) {
// This method is never called in the IDE, therefore this extension is not available there.
// Since IDE does not perform any serialization of descriptors, metadata written to the 'serializationDescriptorSerializer'
// is never deleted, effectively causing memory leaks.
@@ -72,7 +73,7 @@ class SerializationComponentRegistrar : CompilerPluginRegistrar() {
ExpressionCodegenExtension.registerExtension(SerializationCodegenExtension(serializationDescriptorSerializer))
JsSyntheticTranslateExtension.registerExtension(SerializationJsExtension(serializationDescriptorSerializer))
IrGenerationExtension.registerExtension(SerializationLoweringExtension(serializationDescriptorSerializer, disableIntrinsics))
IrGenerationExtension.registerExtension(SerializationLoweringExtension(serializationDescriptorSerializer, intrinsicsState))
StorageComponentContainerContributor.registerExtension(SerializationPluginComponentContainerContributor())
}
@@ -6,12 +6,15 @@
package org.jetbrains.kotlinx.serialization.compiler.diagnostic
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.util.slicedMap.Slices
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
import java.io.File
object VersionReader {
@@ -30,7 +33,12 @@ object VersionReader {
}
fun getVersionsForCurrentModule(module: ModuleDescriptor): RuntimeVersions? {
val markerClass = module.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
val markerClass = module.findClassAcrossModuleDependencies(
ClassId(
SerializationPackages.packageFqName,
Name.identifier(SerialEntityNames.KSERIALIZER_CLASS)
)
) ?: return null
return CommonVersionReader.computeRuntimeVersions(markerClass.source)
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationComponentRegistrar
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIntrinsicsState
import org.jetbrains.kotlinx.serialization.compiler.fir.FirSerializationExtensionRegistrar
import java.io.File
@@ -34,7 +35,7 @@ class SerializationEnvironmentConfigurator(
module: TestModule,
configuration: CompilerConfiguration
) {
SerializationComponentRegistrar.registerExtensions(this)
SerializationComponentRegistrar.registerExtensions(this, SerializationIntrinsicsState.FORCE_ENABLED)
FirExtensionRegistrarAdapter.registerExtension(FirSerializationExtensionRegistrar())
}
}