diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt index 36537011385..9ee3b134375 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCodegen.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.common @@ -41,9 +30,12 @@ abstract class SerializableCodegen( generateSyntheticMethods() } + private inline fun ClassDescriptor.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> FunctionDescriptor?) = + !isInline && (isAbstractSerializableClass() || isSealedSerializableClass() || functionPresenceChecker() != null) + private fun generateSyntheticInternalConstructor() { val serializerDescriptor = serializableDescriptor.classSerializer ?: return - if (serializableDescriptor.isAbstractSerializableClass() || serializableDescriptor.isSealedSerializableClass() || SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) != null) { + if (serializableDescriptor.shouldHaveSpecificSyntheticMethods { SerializerCodegen.getSyntheticLoadMember(serializerDescriptor) }) { val constrDesc = serializableDescriptor.secondaryConstructors.find(ClassConstructorDescriptor::isSerializationCtor) ?: return generateInternalConstructor(constrDesc) } @@ -51,7 +43,7 @@ abstract class SerializableCodegen( private fun generateSyntheticMethods() { val serializerDescriptor = serializableDescriptor.classSerializer ?: return - if (serializableDescriptor.isAbstractSerializableClass() || serializableDescriptor.isSealedSerializableClass() || SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) != null) { + if (serializableDescriptor.shouldHaveSpecificSyntheticMethods { SerializerCodegen.getSyntheticSaveMember(serializerDescriptor) }) { val func = KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(serializableDescriptor) generateWriteSelfMethod(func) } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 48948665f6c..80eaf2f4afc 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -168,6 +168,10 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType "F", "kotlin.Float" -> "FloatSerializer" "D", "kotlin.Double" -> "DoubleSerializer" "C", "kotlin.Char" -> "CharSerializer" + "kotlin.UInt" -> "UIntSerializer" + "kotlin.ULong" -> "ULongSerializer" + "kotlin.UByte" -> "UByteSerializer" + "kotlin.UShort" -> "UShortSerializer" "kotlin.String" -> "StringSerializer" "kotlin.Pair" -> "PairSerializer" "kotlin.Triple" -> "TripleSerializer" 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 aa5f80cfd1e..2acab1b6317 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 @@ -99,7 +99,7 @@ interface IrBuilderExtension { typeHint: IrType? = null ): IrMemberAccessExpression<*> { assert(callee.isBound) { "Symbol $callee expected to be bound" } - val returnType = typeHint ?: callee.run { owner.returnType } + val returnType = typeHint ?: callee.owner.returnType val call = irCall(callee, type = returnType) call.dispatchReceiver = dispatchReceiver args.forEachIndexed(call::putValueArgument) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt index 391d2a7f95f..e9de46f84f7 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerialInfoImplJvmIrGenerator.kt @@ -31,7 +31,7 @@ class SerialInfoImplJvmIrGenerator( override val compilerContext: SerializationPluginContext get() = context - private val jvmNameClass = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner + private val jvmNameClass get() = context.referenceClass(DescriptorUtils.JVM_NAME)!!.owner private val implGenerated = mutableSetOf() private val annotationToImpl = mutableMapOf() diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt new file mode 100644 index 00000000000..9e3785d6f5e --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerForInlineClassGenerator.kt @@ -0,0 +1,142 @@ +/* + * 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.ir + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.defaultType +import org.jetbrains.kotlin.ir.types.typeOrNull +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.platform.jvm.isJvm +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +private fun SerializationPluginContext.coerceInlineClasses(argument: IrExpression, from: IrType, to: IrType): IrExpression { + if (this.platform?.isJvm() != true) return argument.apply { type = to } + val unsafeCoerce = irFactory.buildFun { + name = Name.special("") + origin = IrDeclarationOrigin.IR_BUILTINS_STUB + }.apply { + parent = IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(moduleDescriptor, FqName("kotlin.jvm.internal")) + val src = addTypeParameter("T", irBuiltIns.anyNType) + val dst = addTypeParameter("R", irBuiltIns.anyNType) + addValueParameter("v", src.defaultType) + returnType = dst.defaultType + }.symbol + return IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, to, unsafeCoerce).apply { + putTypeArgument(0, from) + putTypeArgument(1, to) + putValueArgument(0, argument) + } +} + +class SerializerForInlineClassGenerator( + irClass: IrClass, + compilerContext: SerializationPluginContext, + bindingContext: BindingContext, + serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, +) : SerializerIrGenerator(irClass, compilerContext, bindingContext, null, serialInfoJvmGenerator) { + override fun generateSave(function: FunctionDescriptor) = irClass.contributeFunction(function) { saveFunc -> + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, saveFunc.dispatchReceiverParameter!!.symbol) + + val encoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.ENCODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol + val encodeInline = encoderClass.referenceMethod(CallingConventions.encodeInline) + val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) + + // val inlineEncoder = encoder.encodeInline() + val encodeInlineCall: IrExpression = irInvoke(irGet(saveFunc.valueParameters[0]), encodeInline, serialDescGetter) + val inlineEncoder = irTemporary(encodeInlineCall, nameHint = "inlineEncoder") + + val property = serializableProperties.first() + val value = getFromBox(irGet(saveFunc.valueParameters[1]), property) + + // inlineEncoder.encodeInt/String/SerializableValue + val elementCall = formEncodeDecodePropertyCall(irGet(inlineEncoder), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> + val f = + encoderClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}SerializableValue") + f to listOf( + innerSerial, + value + ) + }, { + val f = + encoderClass.referenceMethod("${CallingConventions.encode}${it.elementMethodPrefix}") + val args = if (it.elementMethodPrefix != "Unit") listOf(value) else emptyList() + f to args + }) + + val actualEncodeCall = irIfNull(compilerContext.irBuiltIns.unitType, irGet(inlineEncoder), irNull(), elementCall) + +actualEncodeCall + } + + override fun generateLoad(function: FunctionDescriptor) = irClass.contributeFunction(function) { loadFunc -> + fun irThis(): IrExpression = + IrGetValueImpl(startOffset, endOffset, loadFunc.dispatchReceiverParameter!!.symbol) + + val decoderClass = serializerDescriptor.getClassFromSerializationPackage(SerialEntityNames.DECODER_CLASS) + val descriptorGetterSymbol = irAnySerialDescProperty?.owner?.getter!!.symbol + val decodeInline = decoderClass.referenceMethod(CallingConventions.decodeInline) + val serialDescGetter = irGet(descriptorGetterSymbol.owner.returnType, irThis(), descriptorGetterSymbol) + + // val inlineDecoder = decoder.decodeInline() + val inlineDecoder: IrExpression = irInvoke(irGet(loadFunc.valueParameters[0]), decodeInline, serialDescGetter) + + val property = serializableProperties.first() + val inlinedType = property.type.toIrType() + val actualCall = formEncodeDecodePropertyCall(inlineDecoder, loadFunc.dispatchReceiverParameter!!, property, { innerSerial, sti -> + decoderClass.referenceMethod( "${CallingConventions.decode}${sti.elementMethodPrefix}SerializableValue") to listOf(innerSerial) + }, { + decoderClass.referenceMethod("${CallingConventions.decode}${it.elementMethodPrefix}") to listOf() + }, returnTypeHint = inlinedType) + val value = coerceToBox(actualCall, inlinedType, loadFunc.returnType) + +irReturn(value) + } + + override val serialDescImplClass: ClassDescriptor = serializerDescriptor + .getClassFromInternalSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_FOR_INLINE) + + override fun IrBlockBodyBuilder.instantiateNewDescriptor( + serialDescImplClass: ClassDescriptor, + correctThis: IrExpression + ): IrExpression { + val ctor = compilerContext.referenceConstructors(serialDescImplClass.fqNameSafe).single { it.owner.isPrimary } + return irInvoke( + null, ctor, + irString(serialName), + correctThis + ) + } + + + // Compiler will elide these in corresponding inline class lowerings (when serialize/deserialize functions will be split in two) + + fun IrBlockBodyBuilder.coerceToBox(expression: IrExpression, propertyType: IrType, inlineClassBoxType: IrType): IrExpression { + return irInvoke(null, serializableIrClass.constructors.single { it.isPrimary }.symbol, (inlineClassBoxType as IrSimpleType).arguments.map { it.typeOrNull }, listOf(expression)) + } + + fun IrBlockBodyBuilder.getFromBox(expression: IrExpression, serializableProperty: SerializableProperty): IrExpression { + return getProperty(expression, serializableProperty.irProp) + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt index fc2efe10b0d..8f47bfc6ef5 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerIrGenerator.kt @@ -25,8 +25,11 @@ import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerialTypeInfo import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.getSerialTypeInfo +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerForEnumsCodegen import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext import org.jetbrains.kotlinx.serialization.compiler.resolve.* @@ -39,6 +42,8 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UN object SERIALIZABLE_PLUGIN_ORIGIN : IrDeclarationOriginImpl("SERIALIZER", true) +internal typealias FunctionWithArgs = Pair> + open class SerializerIrGenerator( val irClass: IrClass, final override val compilerContext: SerializationPluginContext, @@ -286,22 +291,7 @@ open class SerializerIrGenerator( // internal serialization via virtual calls? for ((index, property) in serializableProperties.filter { !it.transient }.withIndex()) { // output.writeXxxElementValue(classDesc, index, value) - val sti = getSerialTypeInfo(property) - val innerSerial = serializerInstance( - this@SerializerIrGenerator, - saveFunc.dispatchReceiverParameter!!, - sti.serializer, - property.module, - property.type, - genericIndex = property.genericIndex - ) - val (writeFunc, args: List) = if (innerSerial == null) { - val f = - kOutputClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}") - val args: MutableList = mutableListOf(irGet(localSerialDesc), irInt(index)) - if (sti.elementMethodPrefix != "Unit") args.add(property.irGet()) - f to args - } else { + val elementCall = formEncodeDecodePropertyCall(irGet(localOutput), saveFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> val f = kOutputClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}") f to listOf( @@ -310,9 +300,13 @@ open class SerializerIrGenerator( innerSerial, property.irGet() ) - } - val typeArgs = if (writeFunc.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() - val elementCall = irInvoke(irGet(localOutput), writeFunc, typeArguments = typeArgs, valueArguments = args) + }, { + val f = + kOutputClass.referenceMethod("${CallingConventions.encode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}") + val args: MutableList = mutableListOf(irGet(localSerialDesc), irInt(index)) + if (it.elementMethodPrefix != "Unit") args.add(property.irGet()) + f to args + }) // check for call to .shouldEncodeElementDefault if (!property.optional || index < ignoreIndexTo) { @@ -337,6 +331,28 @@ open class SerializerIrGenerator( +irInvoke(irGet(localOutput), wEndFunc, irGet(localSerialDesc)) } + protected inline fun IrBlockBodyBuilder.formEncodeDecodePropertyCall( + encoder: IrExpression, + dispatchReceiver: IrValueParameter, + property: SerializableProperty, + whenHaveSerializer: (serializer: IrExpression, sti: SerialTypeInfo) -> FunctionWithArgs, + whenDoNot: (sti: SerialTypeInfo) -> FunctionWithArgs, + returnTypeHint: IrType? = null + ): IrExpression { + val sti = getSerialTypeInfo(property) + val innerSerial = serializerInstance( + this@SerializerIrGenerator, + dispatchReceiver, + sti.serializer, + property.module, + property.type, + genericIndex = property.genericIndex + ) + val (functionToCall, args: List) = if (innerSerial != null) whenHaveSerializer(innerSerial, sti) else whenDoNot(sti) + val typeArgs = if (functionToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() + return irInvoke(encoder, functionToCall, typeArguments = typeArgs, valueArguments = args, returnTypeHint = returnTypeHint) + } + // returns null: Any? for boxed types and 0: for primitives private fun IrBuilderWithScope.defaultValueAndType(prop: SerializableProperty): Pair { val kType = prop.descriptor.returnType!! @@ -403,33 +419,23 @@ open class SerializerIrGenerator( val decoderCalls: List> = serializableProperties.mapIndexed { index, property -> val body = irBlock { - val sti = getSerialTypeInfo(property) - val innerSerial = serializerInstance( - this@SerializerIrGenerator, - loadFunc.dispatchReceiverParameter!!, - sti.serializer, - property.module, - property.type, - genericIndex = property.genericIndex - ) - val isSerializable = innerSerial != null - val decodeFuncToCall = - (if (isSerializable) "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}" - else "${CallingConventions.decode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}") - .let { - inputClass.referenceMethod(it) { it.valueParameters.size == if (isSerializable) 4 else 2 } - } - val typeArgs = - if (decodeFuncToCall.descriptor.typeParameters.isNotEmpty()) listOf(property.type.toIrType()) else listOf() - val args = mutableListOf(localSerialDesc.get(), irInt(index)) - if (isSerializable) { - args.add(innerSerial!!) - args.add(localProps[index].get()) - } + val decodeFuncToCall = formEncodeDecodePropertyCall(localInput.get(), loadFunc.dispatchReceiverParameter!!, property, {innerSerial, sti -> + inputClass.referenceMethod( + "${CallingConventions.decode}${sti.elementMethodPrefix}Serializable${CallingConventions.elementPostfix}", {it.valueParameters.size == 4} + ) to listOf( + localSerialDesc.get(), irInt(index), innerSerial, localProps[index].get() + ) + }, { + inputClass.referenceMethod( + "${CallingConventions.decode}${it.elementMethodPrefix}${CallingConventions.elementPostfix}", {it.valueParameters.size == 2} + ) to listOf( + localSerialDesc.get(), irInt(index) + ) + }, returnTypeHint = property.type.toIrType()) // local$i = localInput.decode...(...) +irSet( localProps[index].symbol, - irInvoke(localInput.get(), decodeFuncToCall, typeArgs, args, returnTypeHint = property.type.toIrType()) + decodeFuncToCall ) // bitMask[i] |= 1 << x val bitPos = 1 shl (index % 32) @@ -507,11 +513,12 @@ open class SerializerIrGenerator( serialInfoJvmGenerator: SerialInfoImplJvmIrGenerator, ) { val serializableDesc = getSerializableClassDescriptorBySerializer(irClass.symbol.descriptor) ?: return - if (serializableDesc.isSerializableEnum()) { - SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator).generate() - } else { - SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator).generate() + val generator = when { + serializableDesc.isSerializableEnum() -> SerializerForEnumsGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) + serializableDesc.isInline -> SerializerForInlineClassGenerator(irClass, context, bindingContext, serialInfoJvmGenerator) + else -> SerializerIrGenerator(irClass, context, bindingContext, metadataPlugin, serialInfoJvmGenerator) } + generator.generate() irClass.patchDeclarationParents(irClass.parent) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index 378d047e290..6c45e5a74fd 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -123,10 +123,10 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { return false } - if (descriptor.isInlineClass()) { - trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED) - return false - } +// if (descriptor.isInlineClass()) { +// trace.reportOnSerializableAnnotation(descriptor, SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED) +// return false +// } if (!descriptor.hasSerializableAnnotationWithoutArgs) return false if (descriptor.serializableAnnotationIsUseless) { @@ -258,9 +258,9 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker { ) { if (type.genericIndex != null) return // type arguments always have serializer stored in class' field val element = ktType?.typeElement - if (type.isUnsupportedInlineType()) { - trace.report(SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(element ?: fallbackElement)) - } +// if (type.isUnsupportedInlineType()) { +// trace.report(SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(element ?: fallbackElement)) +// } val serializer = findTypeSerializerOrContextUnchecked(module, type) if (serializer != null) { checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt index 3e56c7254d1..4a7c308eb7a 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationResolveExtension.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.extensions @@ -26,6 +15,7 @@ import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.platform import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension +import org.jetbrains.kotlin.resolve.isInlineClass import org.jetbrains.kotlin.resolve.lazy.LazyClassContext import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider import org.jetbrains.kotlin.types.KotlinType @@ -87,6 +77,7 @@ open class SerializationResolveExtension @JvmOverloads constructor(val metadataP if (thisDescriptor.isInternalSerializable) { // do not add synthetic deserialization constructor if .deserialize method is customized if (thisDescriptor.hasCompanionObjectAsSerializer && SerializerCodegen.getSyntheticLoadMember(thisDescriptor.companionObjectDescriptor!!) == null) return + if (thisDescriptor.isInline) return result.add(KSerializerDescriptorResolver.createLoadConstructorDescriptor(thisDescriptor, bindingContext, metadataPlugin)) } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index 2d99e69c211..67c02566f98 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -69,6 +69,7 @@ object SerialEntityNames { const val SERIAL_DESCRIPTOR_CLASS = "SerialDescriptor" const val SERIAL_DESCRIPTOR_CLASS_IMPL = "PluginGeneratedSerialDescriptor" const val SERIAL_DESCRIPTOR_FOR_ENUM = "EnumDescriptor" + const val SERIAL_DESCRIPTOR_FOR_INLINE = "InlineClassDescriptor" const val PLUGIN_EXCEPTIONS_FILE = "PluginExceptions" @@ -114,6 +115,8 @@ object CallingConventions { const val encode = "encode" const val encodeEnum = "encodeEnum" const val decodeEnum = "decodeEnum" + const val encodeInline = "encodeInline" + const val decodeInline = "decodeInline" const val decodeElementIndex = "decodeElementIndex" const val decodeSequentially = "decodeSequentially" const val elementPostfix = "Element" diff --git a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDECodegenExtensions.kt b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDECodegenExtensions.kt index 8467edf5a4d..45f91aa6dc9 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDECodegenExtensions.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationIDECodegenExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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. */ @@ -8,10 +8,12 @@ package org.jetbrains.kotlinx.serialization.compiler.extensions import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.psi.KtPureClassOrObject +import org.jetbrains.kotlinx.serialization.idea.runIfEnabledIn import org.jetbrains.kotlinx.serialization.idea.runIfEnabledOn class SerializationIDECodegenExtension : SerializationCodegenExtension() { @@ -31,7 +33,10 @@ class SerializationIDEJsExtension : SerializationJsExtension() { } class SerializationIDEIrExtension : SerializationLoweringExtension() { + @OptIn(ObsoleteDescriptorBasedAPI::class) override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { - /* No-op – don't enable IR extensions in IDE */ + runIfEnabledIn(pluginContext.moduleDescriptor) { + super.generate(moduleFragment, pluginContext) + } } } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/SerializationSwitchUtils.kt b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/SerializationSwitchUtils.kt index 38dc51c1613..26acdeaa31f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/SerializationSwitchUtils.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-ide/src/org/jetbrains/kotlinx/serialization/idea/SerializationSwitchUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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. */ @@ -7,16 +7,23 @@ package org.jetbrains.kotlinx.serialization.idea import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.idea.core.unwrapModuleSourceInfo import org.jetbrains.kotlin.idea.facet.KotlinFacet import org.jetbrains.kotlin.resolve.descriptorUtil.module -fun getIfEnabledOn(clazz: ClassDescriptor, body: () -> T): T? { - val module = clazz.module.getCapability(ModuleInfo.Capability)?.unwrapModuleSourceInfo()?.module ?: return null - val facet = KotlinFacet.get(module) ?: return null - val pluginClasspath = facet.configuration.settings.compilerArguments?.pluginClasspaths ?: return null - if (pluginClasspath.none(KotlinSerializationImportHandler::isPluginJarPath)) return null - return body() +private fun isEnabledIn(moduleDescriptor: ModuleDescriptor): Boolean { + val module = moduleDescriptor.getCapability(ModuleInfo.Capability)?.unwrapModuleSourceInfo()?.module ?: return false + val facet = KotlinFacet.get(module) ?: return false + val pluginClasspath = facet.configuration.settings.compilerArguments?.pluginClasspaths ?: return false + if (pluginClasspath.none(KotlinSerializationImportHandler::isPluginJarPath)) return false + return true } -fun runIfEnabledOn(clazz: ClassDescriptor, body: () -> Unit) { getIfEnabledOn(clazz, body) } \ No newline at end of file +fun getIfEnabledOn(clazz: ClassDescriptor, body: () -> T): T? { + return if (isEnabledIn(clazz.module)) body() else null +} + +fun runIfEnabledOn(clazz: ClassDescriptor, body: () -> Unit) { getIfEnabledOn(clazz, body) } + +fun runIfEnabledIn(moduleDescriptor: ModuleDescriptor, block: () -> Unit) { if (isEnabledIn(moduleDescriptor)) block() } \ No newline at end of file