diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt index e08b73f2115..2c32d3c88ab 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt @@ -29,10 +29,7 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isTypeParameter import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound -import org.jetbrains.kotlinx.serialization.compiler.backend.common.AbstractSerialGenerator -import org.jetbrains.kotlinx.serialization.compiler.backend.common.allSealedSerializableSubclassesFor -import org.jetbrains.kotlinx.serialization.compiler.backend.common.findTypeSerializerOrContext -import org.jetbrains.kotlinx.serialization.compiler.backend.common.serialName +import org.jetbrains.kotlinx.serialization.compiler.backend.common.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.* import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -567,118 +564,145 @@ interface IrBuilderExtension { } if (serializerClassOriginal.kind == ClassKind.OBJECT) { return irGetObject(serializerClassOriginal) - } else { - fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? { - val expr = serializerInstance( - enclosingGenerator, - serializer, - module, - type, - type.genericIndex, - genericGetter - ) ?: return null - return wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass) - } - var serializerClass = serializerClassOriginal - var args: List - var typeArgs: List - val thisIrType = kType.toIrType() - when (serializerClassOriginal.classId) { - contextSerializerId, polymorphicSerializerId -> { - args = listOf(classReference(kType)) - typeArgs = listOf(thisIrType) - } - objectSerializerId -> { - args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!)) - typeArgs = listOf(thisIrType) - } - sealedSerializerId -> { - args = mutableListOf().apply { - add(irString(kType.serialName())) - add(classReference(kType)) - val (subclasses, subSerializers) = enclosingGenerator.allSealedSerializableSubclassesFor( - kType.toClassDescriptor!!, - module - ) - val projectedOutCurrentKClass = kClassTypeFor(TypeProjectionImpl(Variance.OUT_VARIANCE, kType)) - add( - createArrayOfExpression( - projectedOutCurrentKClass.toIrType(), - subclasses.map { classReference(it) } - ) - ) - add( - createArrayOfExpression( - wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE), - subSerializers.mapIndexed { i, serializer -> - val type = subclasses[i] - val expr = serializerInstance( - enclosingGenerator, - serializer, - module, - type, - type.genericIndex - ) { _, genericType -> - serializerInstance( - enclosingGenerator, - module.getClassFromSerializationPackage( - SpecialBuiltins.polymorphicSerializer - ), - module, - (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound - )!! - }!! - wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass) - } - ) - ) - } - typeArgs = listOf(thisIrType) - } - enumSerializerId -> { - serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer) - args = kType.toClassDescriptor!!.let { enumDesc -> - listOf( - irString(enumDesc.serialName()), - irCall(findEnumValuesMethod(enumDesc)) - ) - } - typeArgs = listOf(thisIrType) - } - else -> { - args = kType.arguments.map { - val argSer = enclosingGenerator.findTypeSerializerOrContext( - module, - it.type, - sourceElement = serializerClassOriginal.findPsi() - ) - instantiate(argSer, it.type) ?: return null - } - typeArgs = kType.arguments.map { it.type.toIrType() } - } - - } - if (serializerClassOriginal.classId == referenceArraySerializerId) { - args = listOf(classReference(kType.arguments[0].type)) + args - typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs - } - - - val serializable = getSerializableClassDescriptorBySerializer(serializerClass) - val ctor = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { - requireNotNull( - findSerializerConstructorForTypeArgumentsSerializers(serializerClass) - ) { "Generated serializer does not have constructor with required number of arguments" } - } else { - compilerContext.referenceConstructors(serializerClass.fqNameSafe).single { it.owner.isPrimary } - } - // Return type should be correctly substituted - assert(ctor.isBound) - val ctorDecl = ctor.owner - val typeParameters = ctorDecl.parentAsClass.typeParameters - val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs) - return irInvoke(null, ctor, typeArguments = typeArgs, valueArguments = args, returnTypeHint = substitutedReturnType) } + fun instantiate(serializer: ClassDescriptor?, type: KotlinType): IrExpression? { + val expr = serializerInstance( + enclosingGenerator, + serializer, + module, + type, + type.genericIndex, + genericGetter + ) ?: return null + return wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass) + } + + var serializerClass = serializerClassOriginal + var args: List + var typeArgs: List + val thisIrType = kType.toIrType() + val hasNewCtxSerCtor = + serializerClassOriginal.classId == contextSerializerId && compilerContext.referenceConstructors(serializerClass.fqNameSafe) + .any { it.owner.valueParameters.size == 3 } + when (serializerClassOriginal.classId) { + contextSerializerId, polymorphicSerializerId -> { + args = listOf(classReference(kType)) + typeArgs = listOf(thisIrType) + + if (hasNewCtxSerCtor) { + // new signature of context serializer + args = args + mutableListOf().apply { + val fallbackDefaultSerializer = findTypeSerializer(module, kType) + add(instantiate(fallbackDefaultSerializer, kType) ?: irNull()) + add( + createArrayOfExpression( + wrapIrTypeIntoKSerializerIrType( + module, + thisIrType, + variance = Variance.OUT_VARIANCE + ), + kType.arguments.map { + val argSer = enclosingGenerator.findTypeSerializerOrContext( + module, + it.type, + sourceElement = serializerClassOriginal.findPsi() + ) + instantiate(argSer, it.type)!! + }) + ) + } + } + } + objectSerializerId -> { + args = listOf(irString(kType.serialName()), irGetObject(kType.toClassDescriptor!!)) + typeArgs = listOf(thisIrType) + } + sealedSerializerId -> { + args = mutableListOf().apply { + add(irString(kType.serialName())) + add(classReference(kType)) + val (subclasses, subSerializers) = enclosingGenerator.allSealedSerializableSubclassesFor( + kType.toClassDescriptor!!, + module + ) + val projectedOutCurrentKClass = kClassTypeFor(TypeProjectionImpl(Variance.OUT_VARIANCE, kType)) + add( + createArrayOfExpression( + projectedOutCurrentKClass.toIrType(), + subclasses.map { classReference(it) } + ) + ) + add( + createArrayOfExpression( + wrapIrTypeIntoKSerializerIrType(module, thisIrType, variance = Variance.OUT_VARIANCE), + subSerializers.mapIndexed { i, serializer -> + val type = subclasses[i] + val expr = serializerInstance( + enclosingGenerator, + serializer, + module, + type, + type.genericIndex + ) { _, genericType -> + serializerInstance( + enclosingGenerator, + module.getClassFromSerializationPackage( + SpecialBuiltins.polymorphicSerializer + ), + module, + (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound + )!! + }!! + wrapWithNullableSerializerIfNeeded(module, type, expr, nullableSerClass) + } + ) + ) + } + typeArgs = listOf(thisIrType) + } + enumSerializerId -> { + serializerClass = module.getClassFromInternalSerializationPackage(SpecialBuiltins.enumSerializer) + args = kType.toClassDescriptor!!.let { enumDesc -> + listOf( + irString(enumDesc.serialName()), + irCall(findEnumValuesMethod(enumDesc)) + ) + } + typeArgs = listOf(thisIrType) + } + else -> { + args = kType.arguments.map { + val argSer = enclosingGenerator.findTypeSerializerOrContext( + module, + it.type, + sourceElement = serializerClassOriginal.findPsi() + ) + instantiate(argSer, it.type) ?: return null + } + typeArgs = kType.arguments.map { it.type.toIrType() } + } + + } + if (serializerClassOriginal.classId == referenceArraySerializerId) { + args = listOf(classReference(kType.arguments[0].type)) + args + typeArgs = listOf(typeArgs[0].makeNotNull()) + typeArgs + } + + + val serializable = getSerializableClassDescriptorBySerializer(serializerClass) + val ctor = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { + requireNotNull( + findSerializerConstructorForTypeArgumentsSerializers(serializerClass) + ) { "Generated serializer does not have constructor with required number of arguments" } + } else { + compilerContext.referenceConstructors(serializerClass.fqNameSafe).single { it.owner.isPrimary } + } + // Return type should be correctly substituted + assert(ctor.isBound) + val ctorDecl = ctor.owner + val typeParameters = ctorDecl.parentAsClass.typeParameters + val substitutedReturnType = ctorDecl.returnType.substitute(typeParameters, typeArgs) + return irInvoke(null, ctor, typeArguments = typeArgs, valueArguments = args, returnTypeHint = substitutedReturnType) } private fun findSerializerConstructorForTypeArgumentsSerializers(serializer: ClassDescriptor): IrConstructorSymbol? { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt index e8c7222d949..85e73430768 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 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. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlinx.serialization.compiler.backend.js @@ -151,78 +140,92 @@ internal fun AbstractSerialGenerator.serializerInstance( } if (serializerClass.kind == ClassKind.OBJECT) { return context.serializerObjectGetter(serializerClass) - } else { - fun instantiate(serializer: ClassDescriptor?, type: KotlinType): JsExpression? { - val expr = serializerInstance(context, serializer, module, type, type.genericIndex, genericGetter) ?: return null - return if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr - } - var args = when { - serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf( - ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!) - ) - serializerClass.classId == enumSerializerId -> listOf( - JsStringLiteral(kType.serialName()), - // EnumClass.values() invocation - JsInvocation( - context.getInnerNameForDescriptor( - DescriptorUtils.getFunctionByName( - kType.toClassDescriptor!!.staticScope, - DescriptorUtils.ENUM_VALUES - ) - ).makeRef() - ) - ) - serializerClass.classId == objectSerializerId -> listOf( - JsStringLiteral(kType.serialName()), - context.serializerObjectGetter(kType.toClassDescriptor!!) - ) - serializerClass.classId == sealedSerializerId -> mutableListOf().apply { - add(JsStringLiteral(kType.serialName())) - add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)) - val (subclasses, subSerializers) = allSealedSerializableSubclassesFor( - kType.toClassDescriptor!!, - module - ) - add(JsArrayLiteral(subclasses.map { - ExpressionVisitor.getObjectKClass( - context, - it.toClassDescriptor!! - ) - })) - add(JsArrayLiteral(subSerializers.mapIndexed { i, serializer -> - val type = subclasses[i] - val expr = serializerInstance(context, serializer, module, type, type.genericIndex) { _, genericType -> - serializerInstance( - context, - module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer), - module, - (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound - )!! - }!! - if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr - })) - } - else -> kType.arguments.map { - val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) - instantiate(argSer, it.type) ?: return null - } - } - if (serializerClass.classId == referenceArraySerializerId) - args = listOf(ExpressionVisitor.getObjectKClass(context, kType.arguments[0].type.toClassDescriptor!!)) + args - val serializable = getSerializableClassDescriptorBySerializer(serializerClass) - val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { - val desc = requireNotNull( - findSerializerConstructorForTypeArgumentsSerializers(serializerClass) - ) { "Generated serializer does not have constructor with required number of arguments" } - if (!desc.isPrimary) - JsInvocation(context.getInnerReference(desc), args) - else - JsNew(context.getInnerReference(desc), args) - } else { - JsNew(context.translateQualifiedReference(serializerClass), args) - } - return ref } + val hasNewCtxSerCtor = + serializerClass.classId == contextSerializerId && serializerClass.constructors.any { it.valueParameters.size == 3 } + + fun instantiate(serializer: ClassDescriptor?, type: KotlinType): JsExpression? { + val expr = serializerInstance(context, serializer, module, type, type.genericIndex, genericGetter) ?: return null + return if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr + } + + var args = when { + hasNewCtxSerCtor -> { + mutableListOf().apply { + add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)) + val fallbackDefaultSerializer = findTypeSerializer(module, kType) + add(instantiate(fallbackDefaultSerializer, kType) ?: JsNullLiteral()) + add(JsArrayLiteral(kType.arguments.map { + val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) + instantiate(argSer, it.type)!! + })) + } + } + serializerClass.classId == contextSerializerId || serializerClass.classId == polymorphicSerializerId -> listOf( + ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!) + ) + serializerClass.classId == enumSerializerId -> listOf( + JsStringLiteral(kType.serialName()), + // EnumClass.values() invocation + JsInvocation( + context.getInnerNameForDescriptor( + DescriptorUtils.getFunctionByName( + kType.toClassDescriptor!!.staticScope, + DescriptorUtils.ENUM_VALUES + ) + ).makeRef() + ) + ) + serializerClass.classId == objectSerializerId -> listOf( + JsStringLiteral(kType.serialName()), + context.serializerObjectGetter(kType.toClassDescriptor!!) + ) + serializerClass.classId == sealedSerializerId -> mutableListOf().apply { + add(JsStringLiteral(kType.serialName())) + add(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)) + val (subclasses, subSerializers) = allSealedSerializableSubclassesFor( + kType.toClassDescriptor!!, + module + ) + add(JsArrayLiteral(subclasses.map { + ExpressionVisitor.getObjectKClass( + context, + it.toClassDescriptor!! + ) + })) + add(JsArrayLiteral(subSerializers.mapIndexed { i, serializer -> + val type = subclasses[i] + val expr = serializerInstance(context, serializer, module, type, type.genericIndex) { _, genericType -> + serializerInstance( + context, + module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer), + module, + (genericType.constructor.declarationDescriptor as TypeParameterDescriptor).representativeUpperBound + )!! + }!! + if (type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr + })) + } + else -> kType.arguments.map { + val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) + instantiate(argSer, it.type) ?: return null + } + } + if (serializerClass.classId == referenceArraySerializerId) + args = listOf(ExpressionVisitor.getObjectKClass(context, kType.arguments[0].type.toClassDescriptor!!)) + args + val serializable = getSerializableClassDescriptorBySerializer(serializerClass) + val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { + val desc = requireNotNull( + findSerializerConstructorForTypeArgumentsSerializers(serializerClass) + ) { "Generated serializer does not have constructor with required number of arguments" } + if (!desc.isPrimary) + JsInvocation(context.getInnerReference(desc), args) + else + JsNew(context.getInnerReference(desc), args) + } else { + JsNew(context.translateQualifiedReference(serializerClass), args) + } + return ref } fun TranslationContext.buildInitializersRemapping( diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index 54883ad4083..966cd95aaeb 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 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. + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm @@ -302,6 +291,20 @@ internal fun AbstractSerialGenerator.stackValueSerializerInstance(codegen: Class aconst(codegen.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 }) { + // append new additional arguments + val fallbackDefaultSerializer = findTypeSerializer(module, kType) + if (fallbackDefaultSerializer != null) { + instantiate(kType to fallbackDefaultSerializer, writeSignature = false) + } else { + aconst(null) + } + signature.append(kSerializerType.descriptor) + fillArray(kSerializerType, argSerializers) { _, serializer -> + instantiate(serializer, writeSignature = false) + } + signature.append(kSerializerArrayType.descriptor) + } } referenceArraySerializerId -> { // a special way to instantiate reference array serializer -- need an element KClass reference