Support serializable inline classes in IR plugin

as well as standard unsigned types.
This commit is contained in:
Leonid Startsev
2020-12-04 20:10:59 +03:00
parent 631a72d14a
commit 272273baed
11 changed files with 245 additions and 94 deletions
@@ -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)
}
@@ -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"
@@ -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)
@@ -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<IrClass>()
private val annotationToImpl = mutableMapOf<IrClass, IrClass>()
@@ -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("<unsafe-coerce>")
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)
}
}
@@ -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<IrFunctionSymbol, List<IrExpression>>
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<IrExpression>) = if (innerSerial == null) {
val f =
kOutputClass.referenceMethod("${CallingConventions.encode}${sti.elementMethodPrefix}${CallingConventions.elementPostfix}")
val args: MutableList<IrExpression> = 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<IrExpression> = 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<IrExpression>) = 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: <number type> for primitives
private fun IrBuilderWithScope.defaultValueAndType(prop: SerializableProperty): Pair<IrExpression, IrType> {
val kType = prop.descriptor.returnType!!
@@ -403,33 +419,23 @@ open class SerializerIrGenerator(
val decoderCalls: List<Pair<Int, IrExpression>> =
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<IrExpression>(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)
}
}
@@ -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)
@@ -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))
}
}
@@ -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"
@@ -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)
}
}
}
@@ -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 <T> 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<Unit>(clazz, body) }
fun <T> getIfEnabledOn(clazz: ClassDescriptor, body: () -> T): T? {
return if (isEnabledIn(clazz.module)) body() else null
}
fun runIfEnabledOn(clazz: ClassDescriptor, body: () -> Unit) { getIfEnabledOn<Unit>(clazz, body) }
fun runIfEnabledIn(moduleDescriptor: ModuleDescriptor, block: () -> Unit) { if (isEnabledIn(moduleDescriptor)) block() }