From 703d7153346ebf669c7eb0b7e2d2c767ba81beaa Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Wed, 25 Oct 2017 16:09:23 +0300 Subject: [PATCH] JPS plugin Breaking changes in compiler plugin: * Remove java.Serializable supertype * Move serializer impl from companion to special inner class * Add generic serializer fields in $serializer and serializer getter in companion kotlin.Pair serializer Generating getter also for non-generic serializers Generic serializers in JS --- .../build.gradle.kts | 1 + .../src/META-INF/plugin.xml | 7 +- ...s.build.KotlinJpsCompilerArgumentsProvider | 17 +++ .../common/SerializableCompanionCodegen.kt | 37 +++++ .../backend/common/SerializerCodegen.kt | 7 +- .../compiler/backend/common/TypeUtil.kt | 7 +- .../js/SerializableCompanionJsTranslator.kt | 56 ++++++++ .../backend/js/SerializerJsTranslator.kt | 46 +++++-- .../compiler/backend/jvm/JVMCodegenUtil.kt | 43 ++++-- .../backend/jvm/SerializableCodegenImpl.kt | 22 +-- .../jvm/SerializableCompanionCodegenImpl.kt | 45 +++++++ .../backend/jvm/SerializerCodegenImpl.kt | 32 ++++- .../SerializationCodegenExtension.kt | 2 + .../extensions/SerializationJsExtension.kt | 2 + .../SerializationResolveExtension.kt | 13 +- .../compiler/resolve/KSerializationUtil.kt | 22 ++- .../resolve/KSerializerDescriptorResolver.kt | 127 ++++++++++++++++-- .../compiler/resolve/SerializableProperty.kt | 1 + .../jps/KotlinSerializationJpsPlugin.kt | 45 +++++++ 19 files changed, 474 insertions(+), 58 deletions(-) create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt create mode 100644 plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/jps/KotlinSerializationJpsPlugin.kt diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts index d40a79db9b0..45aab860288 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/build.gradle.kts @@ -5,6 +5,7 @@ apply { plugin("kotlin") } dependencies { compileOnly(ideaSdkCoreDeps("intellij-core")) + compileOnly(project(":jps-plugin")) compileOnly(project(":compiler:plugin-api")) compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:backend")) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml index 47067b63923..c577b332d8d 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/plugin.xml @@ -1,7 +1,7 @@ Kotlin-serialization org.jetbrains.kotlinx.serialization - 0.1-SNAPSHOT + 0.2 org.jetbrains.kotlin @@ -11,4 +11,9 @@ + + + + + \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider new file mode 100644 index 00000000000..c44d1917ec1 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/META-INF/services/org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider @@ -0,0 +1,17 @@ +# +# 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. +# + +org.jetbrains.kotlinx.serialization.jps.KotlinSerializationJpsPlugin \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt new file mode 100644 index 00000000000..b69efb52a8b --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializableCompanionCodegen.kt @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.common + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.psi.KtPureClassOrObject +import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion + +abstract class SerializableCompanionCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) { + protected val companionDescriptor: ClassDescriptor = declaration.findClassDescriptor(bindingContext) + protected val serializableDescriptor: ClassDescriptor = getSerializableClassDescriptorByCompanion(companionDescriptor)!! + + fun generate() { + val fser = KSerializerDescriptorResolver.createSerializerGetterDescriptor(companionDescriptor, serializableDescriptor) + generateSerializerGetter(fser) + } + + protected abstract fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt index e217f52e317..3522211710f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/SerializerCodegen.kt @@ -25,8 +25,8 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) { @@ -43,12 +43,17 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex val load = generateLoadIfNeeded() if (save || load || prop) generateSerialDesc() + if (serializableDescriptor.declaredTypeParameters.isNotEmpty()) { + generateGenericFieldsAndConstructor(createTypedSerializerConstructorDescriptor(serializerDescriptor, serializableDescriptor)) + } } protected val serialDescPropertyDescriptor = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD, serializerDescriptor::checkSerializableClassPropertyResult) protected abstract fun generateSerialDesc() + protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) + protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor) protected abstract fun generateSave(function: FunctionDescriptor) 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 b5414c51ca9..767bf5c2e72 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 @@ -28,9 +28,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments -import org.jetbrains.kotlin.types.typeUtil.isBoolean -import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType +import org.jetbrains.kotlin.types.typeUtil.* import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId @@ -47,6 +45,7 @@ open class SerialTypeInfo( fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { val T = property.type return when { + T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null) T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(property, T.nameIfStandardType.toString().capitalize()) KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String") @@ -71,6 +70,7 @@ fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module) + else if (kType.isTypeParameter()) return null else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type ?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer ?: findEnumTypeSerializer(module, kType) @@ -89,6 +89,7 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType "D", "kotlin.Double" -> "DoubleSerializer" "C", "kotlin.Char" -> "CharSerializer" "kotlin.String" -> "StringSerializer" + "kotlin.Pair" -> "PairSerializer" "kotlin.collections.Collection", "kotlin.collections.List", "kotlin.collections.ArrayList" -> "ArrayListSerializer" "kotlin.collections.Set", "kotlin.collections.LinkedHashSet" -> "LinkedHashSetSerializer" "kotlin.collections.HashSet" -> "HashSetSerializer" diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt new file mode 100644 index 00000000000..aac2cd1337f --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializableCompanionJsTranslator.kt @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.js + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.js.backend.ast.JsInvocation +import org.jetbrains.kotlin.js.backend.ast.JsNameRef +import org.jetbrains.kotlin.js.backend.ast.JsReturn +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.compiler.backend.common.SerializableCompanionCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +class SerializableCompanionJsTranslator(declaration: KtPureClassOrObject, + val translator: DeclarationBodyVisitor, + val context: TranslationContext): SerializableCompanionCodegen(declaration, context.bindingContext()) { + override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { + val f = context.buildFunction(methodDescriptor) {jsFun, context -> + val serializer = serializableDescriptor.classSerializer?.toClassDescriptor!! + val stmt = if (serializableDescriptor.declaredTypeParameters.isEmpty()) { + context.getQualifiedReference(serializer) + } else { + val args = jsFun.parameters.map { JsNameRef(it.name) } + val ref = context.getInnerNameForDescriptor( + KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializer, serializableDescriptor)) + JsInvocation(ref.makeRef(), args) + } + +JsReturn(stmt) + } + translator.addFunction(methodDescriptor, f, null) + } + + companion object { + fun translate(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) { + val serializableClass = getSerializableClassDescriptorByCompanion(descriptor) ?: return + if (serializableClass.isInternalSerializable) + SerializableCompanionJsTranslator(declaration, translator, context).generate() + } + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index 5bb53d9c74b..4e715929db2 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -19,12 +19,14 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.js import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.resolve.descriptorUtil.classId @@ -39,6 +41,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerialize import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix class SerializerJsTranslator(declaration: KtPureClassOrObject, val translator: DeclarationBodyVisitor, @@ -94,6 +97,22 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, translator.addProperty(propDesc, getterExpr, null) } + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { + val f = context.buildFunction(typedConstructorDescriptor) { jsFun, context -> + val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef() + + +JsVars(JsVars.JsVar(thiz.name, JsNew(getQualifiedClassReferenceName(serializerDescriptor)))) + jsFun.parameters.forEachIndexed { i, parameter -> + val thisFRef = JsNameRef(context.scope().declareName("$typeArgPrefix$i"), thiz) + +JsAstUtils.assignment(thisFRef, JsNameRef(parameter.name)).makeStmt() + } + +JsReturn(thiz) + } + + f.name = context.getInnerNameForDescriptor(typedConstructorDescriptor); + context.addDeclarationStatement(f.makeStmt()) + } + private fun ClassDescriptor.getFuncDesc(funcName: String) = unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) } @@ -120,7 +139,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, if (property.transient) continue // output.writeXxxElementValue(classDesc, index, value) val sti = getSerialTypeInfo(property) - val innerSerial = if (sti.serializer == null) null else serializerInstance(sti.serializer, property.module, property.type) + val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex) if (innerSerial == null) { val writeFunc = kOutputClass.getFuncDesc("write${sti.elementMethodPrefix}ElementValue").single() @@ -152,9 +171,13 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, return context.getQualifiedReference(classDescriptor) } - private fun serializerInstance(serializerClass: ClassDescriptor, module: ModuleDescriptor, kType: KotlinType): JsExpression? { - val nullableSerClass = getQualifiedClassReferenceName( - serializerClass.getClassFromInternalSerializationPackage("NullableSerializer")) + private fun serializerInstance(serializerClass: ClassDescriptor?, module: ModuleDescriptor, kType: KotlinType, genericIndex: Int? = null): JsExpression? { + val nullableSerClass = getQualifiedClassReferenceName(requireNotNull( + module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, Name.identifier("NullableSerializer"))))) + if (serializerClass == null) { + if (genericIndex == null) return null + return JsNameRef(context.scope().declareName("$typeArgPrefix$genericIndex"), JsThisRef()) + } if (serializerClass.kind == ClassKind.OBJECT) { return getQualifiedClassReferenceName(serializerClass) } @@ -162,13 +185,20 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId) listOf(createGetKClassExpression(kType.toClassDescriptor!!)) else kType.arguments.map { - val argSer = findTypeSerializer(module, it.type) ?: return null - val expr = serializerInstance(argSer, module, it.type) ?: return null + val argSer = findTypeSerializer(module, it.type) + val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr } if (serializerClass.classId == referenceArraySerializerId) args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args - return JsNew(getQualifiedClassReferenceName(serializerClass), args) + val serializable = getSerializableClassDescriptorBySerializer(serializerClass) + val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { + val desc = KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor(serializerClass, serializableDescriptor) + JsInvocation(context.getInnerNameForDescriptor(desc).makeRef(), args) + } else { + JsNew(getQualifiedClassReferenceName(serializerClass), args) + } + return ref } } @@ -229,7 +259,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject, case(JsIntLiteral(i)) { // input.readXxxElementValue val sti = getSerialTypeInfo(property) - val innerSerial = if (sti.serializer == null) null else serializerInstance(sti.serializer, property.module, property.type) + val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex) val call = if (innerSerial == null) { val unknownSer = (sti.elementMethodPrefix.isEmpty()) val readFunc = 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 2e50508b13a..30775e8f4a4 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 @@ -32,6 +32,7 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.findEnumTypeS import org.jetbrains.kotlinx.serialization.compiler.backend.common.findPolymorphicSerializer import org.jetbrains.kotlinx.serialization.compiler.backend.common.requiresPolymorphism import org.jetbrains.kotlinx.serialization.compiler.resolve.* +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -71,10 +72,12 @@ internal fun InstructionAdapter.genExceptionThrow(exceptionClass: String, messag athrow() } -fun InstructionAdapter.genKOutputMethodCall(property: SerializableProperty, classCodegen: ImplementationBodyCodegen, expressionCodegen: ExpressionCodegen, propertyOwnerType: Type, ownerVar: Int) { +fun InstructionAdapter.genKOutputMethodCall(property: SerializableProperty, classCodegen: ImplementationBodyCodegen, expressionCodegen: ExpressionCodegen, + propertyOwnerType: Type, ownerVar: Int, fromClassStartVar: Int? = null) { val propertyType = classCodegen.typeMapper.mapType(property.type) val sti = getSerialTypeInfo(property, propertyType) - val useSerializer = stackValueSerializerInstance(classCodegen, sti) + val useSerializer = if (fromClassStartVar == null) stackValueSerializerInstanceFromSerializer(classCodegen, sti) + else stackValueSerializerInstanceFromClass(classCodegen, sti, fromClassStartVar) if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar) invokevirtual(kOutputType.internalName, "write" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue", @@ -114,16 +117,32 @@ internal val polymorphicSerializerId = ClassId(packageFqName, Name.identifier("P internal val referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier("ReferenceArraySerializer")) internal val contextSerializerId = ClassId(packageFqName, Name.identifier("ContextSerializer")) -// returns false is property should not use serializer -internal fun InstructionAdapter.stackValueSerializerInstance(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo): Boolean { + +internal fun InstructionAdapter.stackValueSerializerInstanceFromClass(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo, varIndexStart: Int): Boolean { val serializer = sti.serializer ?: return false - return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this) + return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this, sti.property.genericIndex) { idx -> + load(varIndexStart + idx, kSerializerType) + } +} + +internal fun InstructionAdapter.stackValueSerializerInstanceFromSerializer(codegen: ClassBodyCodegen, sti: JVMSerialTypeInfo): Boolean { + val serializer = sti.serializer ?: return false + return stackValueSerializerInstance(codegen, sti.property.module, sti.property.type, serializer, this, sti.property.genericIndex) { idx -> + load(0, kSerializerType) + getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$idx", kSerializerType.descriptor) + } } // returns false is cannot not use serializer // use iv == null to check only (do not emit serializer onto stack) -internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, serializer: ClassDescriptor, - iv: InstructionAdapter?): Boolean { +internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?, + iv: InstructionAdapter?, genericIndex: Int? = null, genericSerializerFieldGetter: (InstructionAdapter.(Int) -> Unit)? = null): Boolean { + if (genericIndex != null) { + // get field from serializer object + iv?.run { genericSerializerFieldGetter?.invoke(this, genericIndex) } + return true + } + val serializer = requireNotNull(maybeSerializer) if (serializer.kind == ClassKind.OBJECT) { // singleton serializer -- just get it if (iv != null) @@ -133,11 +152,12 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod // serializer is not singleton object and shall be instantiated val argSerializers = kType.arguments.map { projection -> // bail out from stackValueSerializerInstance if any type argument is not serializable - val argSerializer = findTypeSerializer(module, projection.type, codegen.typeMapper.mapType(projection.type)) ?: return false + val argType = projection.type + val argSerializer = findTypeSerializer(module, argType, codegen.typeMapper.mapType(argType)) ?: return false // check if it can be properly serialized with its args recursively - if (!stackValueSerializerInstance(codegen, module, projection.type, argSerializer, null)) + if (!stackValueSerializerInstance(codegen, module, argType, argSerializer, null, argType.genericIndex, genericSerializerFieldGetter)) return false - Pair(projection.type, argSerializer) + Pair(argType, argSerializer) } // new serializer if needed iv?.apply { @@ -163,7 +183,7 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod } // all serializers get arguments with serializers of their generic types argSerializers.forEach { (argType, argSerializer) -> - assert(stackValueSerializerInstance(codegen, module, argType, argSerializer, this)) + assert(stackValueSerializerInstance(codegen, module, argType, argSerializer, this, argType.genericIndex, genericSerializerFieldGetter)) // wrap into nullable serializer if argType is nullable if (argType.isMarkedNullable) { invokestatic("kotlinx/serialization/internal/BuiltinSerializersKt", "makeNullable", @@ -261,5 +281,6 @@ internal val org.jetbrains.org.objectweb.asm.Type.standardSerializer: String? ge "Ljava/util/Map;", "Ljava/util/LinkedHashMap;" -> "LinkedHashMapSerializer" "Ljava/util/HashMap;" -> "HashMapSerializer" "Ljava/util/Map\$Entry;" -> "MapEntrySerializer" + "Lkotlin/Pair;" -> "PairSerializer" else -> null } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt index d863e95562f..feba3a0ecbf 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCodegenImpl.kt @@ -26,14 +26,13 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties -import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty -import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable +import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -73,22 +72,29 @@ class SerializableCodegenImpl( val thisI = 0 val outputI = 1 val serialDescI = 2 -// val offsetI = 3 + val offsetI = 3 val superClass = serializableDescriptor.getSuperClassOrAny() val myPropsStart: Int if (superClass.isInternalSerializable) { myPropsStart = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties.size + val superTypeArguments = serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments //super.writeSelf(output, serialDesc) load(thisI, thisAsmType) load(outputI, kOutputType) load(serialDescI, descType) - invokespecial(classCodegen.typeMapper.mapType(superClass).internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false) + superTypeArguments.forEach { + val genericIdx = serializableDescriptor.defaultType.arguments.indexOf(it).let { if (it == -1) null else it } + val serial = findTypeSerializer(serializableDescriptor.module, it.type, classCodegen.typeMapper.mapType(it.type)) + stackValueSerializerInstance(classCodegen, serializableDescriptor.module, it.type, serial, this, genericIdx) { + load(offsetI + it, kSerializerType) + } + } + val superSignature = classCodegen.typeMapper.mapSignatureSkipGeneric(KSerializerDescriptorResolver.createWriteSelfFunctionDescriptor(superClass)) + invokespecial(classCodegen.typeMapper.mapType(superClass).internalName, superSignature.asmMethod.name, superSignature.asmMethod.descriptor, false) } else { myPropsStart = 0 - // offset = 0 -// iconst(0) } for (i in myPropsStart until properties.serializableProperties.size) { @@ -97,7 +103,7 @@ class SerializableCodegenImpl( load(outputI, kOutputType) load(serialDescI, descType) iconst(i) - genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI) + genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI, offsetI) } areturn(Type.VOID_TYPE) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt new file mode 100644 index 00000000000..5677a36199e --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializableCompanionCodegenImpl.kt @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package org.jetbrains.kotlinx.serialization.compiler.backend.jvm + +import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCompanionCodegen +import org.jetbrains.kotlinx.serialization.compiler.resolve.* + +class SerializableCompanionCodegenImpl(private val codegen: ImplementationBodyCodegen) : + SerializableCompanionCodegen(codegen.myClass, codegen.bindingContext) { + + companion object { + fun generateSerializableExtensions(codegen: ImplementationBodyCodegen) { + val serializableClass = getSerializableClassDescriptorByCompanion(codegen.descriptor) ?: return + if (serializableClass.isInternalSerializable) + SerializableCompanionCodegenImpl(codegen).generate() + } + } + + override fun generateSerializerGetter(methodDescriptor: FunctionDescriptor) { + val serial = serializableDescriptor.classSerializer?.toClassDescriptor ?: return + codegen.generateMethod(methodDescriptor) {sig, expr -> + stackValueSerializerInstance(codegen, serializableDescriptor.module, serializableDescriptor.defaultType, serial, this, null) { + load(it+1, kSerializerType) + } + areturn(kSerializerType) + } + } +} \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt index 7d3700fb48e..d66a2dc29d8 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializerCodegenImpl.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.resolve.jvm.AsmTypes @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen import org.jetbrains.kotlinx.serialization.compiler.backend.common.annotationVarsAndDesc import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable import org.jetbrains.org.objectweb.asm.Label @@ -50,6 +52,25 @@ class SerializerCodegenImpl( } } + override fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor) { + serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ -> + codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_SYNTHETIC, + "$typeArgPrefix$i", kSerializerType.descriptor, null, null ) + } + + codegen.generateMethod(typedConstructorDescriptor) { sig, exprCodegen -> + load(0, serializerAsmType) + invokespecial("java/lang/Object", "", "()V", false) + serializableDescriptor.declaredTypeParameters.forEachIndexed { i, _ -> + load(0, serializerAsmType) + load(i+1, kSerializerType) + putfield(serializerAsmType.internalName, "$typeArgPrefix$i", kSerializerType.descriptor) + } + areturn(Type.VOID_TYPE) + } + + } + override fun generateSerialDesc() { codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC, serialDescField, descType.descriptor, null, null) @@ -126,12 +147,19 @@ class SerializerCodegenImpl( ")" + kOutputType.descriptor, false) store(outputVar, kOutputType) if (serializableDescriptor.isInternalSerializable) { + val sig = StringBuilder("(${kOutputType.descriptor}${descType.descriptor}") // call obj.write$Self(output, classDesc) load(objVar, objType) load(outputVar, kOutputType) load(descVar, descType) + serializableDescriptor.declaredTypeParameters.forEachIndexed {i, _ -> + load(0, kSerializerType) + getfield(codegen.typeMapper.mapClass(codegen.descriptor).internalName, "$typeArgPrefix$i", kSerializerType.descriptor) + sig.append(kSerializerType.descriptor) + } + sig.append(")V") invokevirtual(objType.internalName, KSerializerDescriptorResolver.WRITE_SELF_NAME.asString(), - "(${kOutputType.descriptor}${descType.descriptor})V", false) + sig.toString(), false) } else { // loop for all properties @@ -235,7 +263,7 @@ class SerializerCodegenImpl( iconst(labelNum) val sti = getSerialTypeInfo(property, propertyType) - val useSerializer = stackValueSerializerInstance(codegen, sti) + val useSerializer = stackValueSerializerInstanceFromSerializer(codegen, sti) val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty()) if (unknownSer) { aconst(codegen.typeMapper.mapType(property.type)) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationCodegenExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationCodegenExtension.kt index e5d71867d1c..37074b6654c 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationCodegenExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationCodegenExtension.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerialInfoCodegenImpl import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCodegenImpl +import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCompanionCodegenImpl import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl class SerializationCodegenExtension : ExpressionCodegenExtension { @@ -27,5 +28,6 @@ class SerializationCodegenExtension : ExpressionCodegenExtension { SerialInfoCodegenImpl.generateSerialInfoImplBody(codegen) SerializableCodegenImpl.generateSerializableExtensions(codegen) SerializerCodegenImpl.generateSerializerExtensions(codegen) + SerializableCompanionCodegenImpl.generateSerializableExtensions(codegen) } } \ No newline at end of file diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt index 1847bd7062b..4d447a27a22 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/extensions/SerializationJsExtension.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension import org.jetbrains.kotlin.psi.KtPureClassOrObject +import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializableCompanionJsTranslator import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializableJsTranslator import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializerJsTranslator @@ -28,5 +29,6 @@ class SerializationJsExtension: JsSyntheticTranslateExtension { override fun generateClassSyntheticParts(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) { SerializerJsTranslator.translate(declaration, descriptor, translator, context) SerializableJsTranslator.translate(declaration, descriptor, translator, context) + SerializableCompanionJsTranslator.translate(declaration, descriptor, translator, context) } } \ No newline at end of file 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 99dae84ab05..3f5997f8ba2 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 @@ -31,16 +31,17 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.serialInfoFqName import java.util.* class SerializationResolveExtension : SyntheticResolveExtension { - override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List { - if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName)) - return listOf(KSerializerDescriptorResolver.IMPL_NAME) - else - return listOf() + override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List = when { + thisDescriptor.annotations.hasAnnotation(serialInfoFqName) -> listOf(KSerializerDescriptorResolver.IMPL_NAME) + thisDescriptor.isInternalSerializable -> listOf(KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME) + else -> listOf() } override fun generateSyntheticClasses(thisDescriptor: ClassDescriptor, name: Name, ctx: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider, result: MutableSet) { if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == KSerializerDescriptorResolver.IMPL_NAME) result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx)) + else if (thisDescriptor.isInternalSerializable && name == KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME) + result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx)) return } @@ -50,12 +51,12 @@ class SerializationResolveExtension : SyntheticResolveExtension { override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList) { KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes) - KSerializerDescriptorResolver.addSerializableSupertypes(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes) } override fun generateSyntheticMethods(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: List, result: MutableCollection) { KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result) + KSerializerDescriptorResolver.generateCompanionObjectMethods(thisDescriptor, fromSupertypes, name, result) } override fun generateSyntheticProperties(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: ArrayList, result: MutableSet) { diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt index bf59da60a15..ef6a154965f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializationUtil.kt @@ -17,17 +17,16 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf +import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME internal val packageFqName = FqName("kotlinx.serialization") internal val internalPackageFqName = FqName("kotlinx.serialization.internal") @@ -123,7 +122,9 @@ internal val ClassDescriptor?.classSerializer: KotlinType? // serializer annotation on class? annotations.serializableWith?.let { return it } // default serializable? - if (isInternalSerializable) return companionObjectDescriptor?.defaultType + if (isInternalSerializable) return this.unsubstitutedMemberScope + .getDescriptorsFiltered(nameFilter = {it == SERIALIZER_CLASS_NAME}) + .filterIsInstance().singleOrNull()?.defaultType return null } @@ -131,11 +132,20 @@ internal val ClassDescriptor?.classSerializer: KotlinType? val KotlinType.typeSerializer: KotlinType? get() = this.annotations.serializableWith ?: (this.toClassDescriptor).classSerializer +val KotlinType.genericIndex: Int? + get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index + +fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? { + if (!thisDescriptor.isCompanionObject) return null + val classDescriptor = (thisDescriptor.containingDeclaration as? ClassDescriptor) ?: return null + if (!classDescriptor.isInternalSerializable) return null + return classDescriptor +} fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? { val serializerForClass = serializerDescriptor.annotations.serializerForClass if (serializerForClass != null) return serializerForClass.toClassDescriptor - if (!serializerDescriptor.isCompanionObject) return null + if (serializerDescriptor.name != SERIALIZER_CLASS_NAME) return null val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null if (!classDescriptor.isInternalSerializable) return null return classDescriptor diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt index e5da709df37..f04ecdf879f 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/KSerializerDescriptorResolver.kt @@ -28,8 +28,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.lazy.LazyClassContext import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.makeNullable import java.util.* @@ -39,6 +38,9 @@ object KSerializerDescriptorResolver { val SERIAL_DESC_FIELD = "serialClassDesc" val SAVE = "save" val LOAD = "load" + val SERIALIZER_CLASS = "\$serializer" + + internal val typeArgPrefix = "typeSerial" val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD) val SAVE_NAME = Name.identifier(SAVE) @@ -46,13 +48,8 @@ object KSerializerDescriptorResolver { val DUMMY_PARAM_NAME = Name.identifier("serializationConstructorMarker") val WRITE_SELF_NAME = Name.identifier("write\$Self") //todo: add it as a supertype to synthetic resolving val IMPL_NAME = Name.identifier("Impl") - - - fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList) { - if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) { - classDescriptor.getJavaSerializableType()?.let { supertypes.add(it) } - } - } + val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS) + val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer") fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean { return thisDescriptor.name == IMPL_NAME @@ -90,17 +87,40 @@ object KSerializerDescriptorResolver { false) } + fun addSerializerImplClass(thisDescriptor: ClassDescriptor, declarationProvider: ClassMemberDeclarationProvider, ctx: LazyClassContext): ClassDescriptor { + val thisDeclaration = declarationProvider.correspondingClassOrObject!! + val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor) + val serializerKind = if (thisDescriptor.declaredTypeParameters.isNotEmpty()) ClassKind.CLASS else ClassKind.OBJECT + return SyntheticClassOrObjectDescriptor(ctx, + thisDeclaration, + thisDescriptor, SERIALIZER_CLASS_NAME, thisDescriptor.source, + scope, + Modality.FINAL, Visibilities.PUBLIC, Visibilities.PRIVATE, + serializerKind, false) + } + fun generateSerializerProperties(thisDescriptor: ClassDescriptor, fromSupertypes: ArrayList, name: Name, result: MutableSet) { - val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return + val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return if (name == SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) && - fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL} ) - result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) + fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }) + result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) } + fun generateCompanionObjectMethods(thisDescriptor: ClassDescriptor, + fromSupertypes: List, + name: Name, + result: MutableCollection) { + val classDescriptor = getSerializableClassDescriptorByCompanion(thisDescriptor) ?: return + + if (name == SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == classDescriptor.declaredTypeParameters.size }) { + result.add(createSerializerGetterDescriptor(thisDescriptor, classDescriptor)) + } + } + fun generateSerializerMethods(thisDescriptor: ClassDescriptor, fromSupertypes: List, name: Name, @@ -228,6 +248,71 @@ object KSerializerDescriptorResolver { return functionDescriptor } + fun createTypedSerializerConstructorDescriptor(classDescriptor: ClassDescriptor, serializableDescriptor: ClassDescriptor): ConstructorDescriptor { + val constrDesc = ClassConstructorDescriptorImpl.createSynthesized( + classDescriptor, + Annotations.EMPTY, + false, + classDescriptor.source + ) + val serializerClass = classDescriptor.getClassFromSerializationPackage("KSerializer") + val targs = mutableListOf() + val args = serializableDescriptor.declaredTypeParameters.mapIndexed { index, _ -> + val targ = TypeParameterDescriptorImpl.createWithDefaultBound(constrDesc, Annotations.EMPTY, false, Variance.INVARIANT, + Name.identifier("T$index"), index) + + val pType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializerClass, listOf(TypeProjectionImpl(targ.defaultType))) + + targs.add(targ) + ValueParameterDescriptorImpl(constrDesc, null, index, Annotations.EMPTY, Name.identifier("$typeArgPrefix$index"), pType, + false, false, false, null, constrDesc.source) + } + + constrDesc.initialize(args, Visibilities.PUBLIC, targs) + constrDesc.returnType = classDescriptor.defaultType + return constrDesc + } + + fun createSerializerGetterDescriptor(thisClass: ClassDescriptor, serializableClass: ClassDescriptor): SimpleFunctionDescriptor { + val f = SimpleFunctionDescriptorImpl.create(thisClass, Annotations.EMPTY, SERIALIZER_PROVIDER_NAME, CallableMemberDescriptor.Kind.SYNTHESIZED, thisClass.source) + val serializerClass = thisClass.getClassFromSerializationPackage("KSerializer") + + val args = mutableListOf() + val typeArgs = mutableListOf() + var i = 0 + + serializableClass.declaredTypeParameters.forEach { it -> + val targ = TypeParameterDescriptorImpl.createWithDefaultBound(f, Annotations.EMPTY, false, Variance.INVARIANT, + Name.identifier("T$i"), i) + + val pType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializerClass, listOf(TypeProjectionImpl(targ.defaultType))) + + args.add(ValueParameterDescriptorImpl( + f, + null, + i, + Annotations.EMPTY, + Name.identifier("$typeArgPrefix${i}"), + pType, + false, + false, + false, + null, + f.source + )) + + typeArgs.add(targ) + i++ + } + + val newSerializableType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializableClass, typeArgs.map { TypeProjectionImpl(it.defaultType) }) + val serialReturnType = KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, serializerClass, listOf(TypeProjectionImpl(newSerializableType))) + + f.initialize(null, thisClass.thisAsReceiverParameter, typeArgs, args, serialReturnType, Modality.FINAL, Visibilities.PUBLIC) + return f + } + + private fun KotlinType.makeNullableIfNotPrimitive() = if (KotlinBuiltIns.isPrimitiveType(this)) this else this.makeNullable() @@ -266,6 +351,24 @@ object KSerializerDescriptorResolver { f.source) ) + val kSerialClass = thisClass.getClassFromSerializationPackage("KSerializer").toSimpleType(false) + + thisClass.declaredTypeParameters.forEach { + args.add(ValueParameterDescriptorImpl( + f, + null, + i++, + Annotations.EMPTY, + Name.identifier("$typeArgPrefix${i-3}"), + kSerialClass, + false, + false, + false, + null, + f.source + )) + } + f.initialize( null, thisClass.thisAsReceiverParameter, diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index b63fbf12b24..0fb2dc3d097 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructorParameterWithDefault: Boolean) { val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() val type = descriptor.type + val genericIndex = type.genericIndex val module = descriptor.module val serializableWith = descriptor.annotations.serializableWith val optional = descriptor.annotations.serialOptional diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/jps/KotlinSerializationJpsPlugin.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/jps/KotlinSerializationJpsPlugin.kt new file mode 100644 index 00000000000..06152e5e418 --- /dev/null +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/jps/KotlinSerializationJpsPlugin.kt @@ -0,0 +1,45 @@ +/* + * 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. + */ + + + +package org.jetbrains.kotlinx.serialization.jps + +import com.intellij.util.PathUtil +import org.jetbrains.jps.incremental.CompileContext +import org.jetbrains.jps.incremental.ModuleBuildTarget +import org.jetbrains.kotlin.jps.build.KotlinJpsCompilerArgumentsProvider +import java.io.File + +class KotlinSerializationJpsPlugin : KotlinJpsCompilerArgumentsProvider { + val JAR_FILE_NAME = "kotlinx-serialization-compiler-plugin.jar" + + override fun getExtraArguments(moduleBuildTarget: ModuleBuildTarget, context: CompileContext): List { + return emptyList() + } + + override fun getClasspath(moduleBuildTarget: ModuleBuildTarget, context: CompileContext): List { + val inJar = File(PathUtil.getJarPathForClass(this::class.java)).isFile + return listOf( + if (inJar) { + File(PathUtil.getJarPathForClass(this::class.java)).absolutePath + } else { + // We're in tests now + val kotlinProjectDirectory = File(PathUtil.getJarPathForClass(javaClass)).parentFile.parentFile.parentFile + File(kotlinProjectDirectory, "dist/artifacts/Serialization/lib/$JAR_FILE_NAME").absolutePath + }) + } +}