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
This commit is contained in:
Leonid Startsev
2017-10-25 16:09:23 +03:00
parent f42767fcc2
commit 703d715334
19 changed files with 474 additions and 58 deletions
@@ -5,6 +5,7 @@ apply { plugin("kotlin") }
dependencies { dependencies {
compileOnly(ideaSdkCoreDeps("intellij-core")) compileOnly(ideaSdkCoreDeps("intellij-core"))
compileOnly(project(":jps-plugin"))
compileOnly(project(":compiler:plugin-api")) compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":compiler:frontend")) compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:backend")) compileOnly(project(":compiler:backend"))
@@ -1,7 +1,7 @@
<idea-plugin> <idea-plugin>
<name>Kotlin-serialization</name> <name>Kotlin-serialization</name>
<id>org.jetbrains.kotlinx.serialization</id> <id>org.jetbrains.kotlinx.serialization</id>
<version>0.1-SNAPSHOT</version> <version>0.2</version>
<depends>org.jetbrains.kotlin</depends> <depends>org.jetbrains.kotlin</depends>
<idea-version since-build="172"/> <idea-version since-build="172"/>
@@ -11,4 +11,9 @@
<syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/> <syntheticResolveExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationResolveExtension"/>
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationJsExtension"/> <jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationJsExtension"/>
</extensions> </extensions>
<extensions defaultExtensionNs="com.intellij">
<compileServer.plugin classpath="kotlinx-serialization-compiler-plugin.jar"/>
</extensions>
</idea-plugin> </idea-plugin>
@@ -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
@@ -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)
}
@@ -25,8 +25,8 @@ import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor import org.jetbrains.kotlin.psi.synthetics.findClassDescriptor
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe 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
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.createTypedSerializerConstructorDescriptor
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) { abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContext: BindingContext) {
@@ -43,12 +43,17 @@ abstract class SerializerCodegen(declaration: KtPureClassOrObject, bindingContex
val load = generateLoadIfNeeded() val load = generateLoadIfNeeded()
if (save || load || prop) if (save || load || prop)
generateSerialDesc() generateSerialDesc()
if (serializableDescriptor.declaredTypeParameters.isNotEmpty()) {
generateGenericFieldsAndConstructor(createTypedSerializerConstructorDescriptor(serializerDescriptor, serializableDescriptor))
}
} }
protected val serialDescPropertyDescriptor = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD, protected val serialDescPropertyDescriptor = getPropertyToGenerate(serializerDescriptor, KSerializerDescriptorResolver.SERIAL_DESC_FIELD,
serializerDescriptor::checkSerializableClassPropertyResult) serializerDescriptor::checkSerializableClassPropertyResult)
protected abstract fun generateSerialDesc() protected abstract fun generateSerialDesc()
protected abstract fun generateGenericFieldsAndConstructor(typedConstructorDescriptor: ConstructorDescriptor)
protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor) protected abstract fun generateSerializableClassProperty(property: PropertyDescriptor)
protected abstract fun generateSave(function: FunctionDescriptor) protected abstract fun generateSave(function: FunctionDescriptor)
@@ -28,9 +28,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.containsTypeProjectionsInTopLevelArguments import org.jetbrains.kotlin.types.typeUtil.*
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.contextSerializerId 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.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.polymorphicSerializerId
@@ -47,6 +45,7 @@ open class SerialTypeInfo(
fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo { fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
val T = property.type val T = property.type
return when { return when {
T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null)
T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(property, T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(property,
T.nameIfStandardType.toString().capitalize()) T.nameIfStandardType.toString().capitalize())
KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String") KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String")
@@ -71,6 +70,7 @@ fun getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? { fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module) return if (kType.requiresPolymorphism()) findPolymorphicSerializer(module)
else if (kType.isTypeParameter()) return null
else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type else kType.typeSerializer.toClassDescriptor // check for serializer defined on the type
?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer ?: findStandardKotlinTypeSerializer(module, kType) // otherwise see if there is a standard serializer
?: findEnumTypeSerializer(module, kType) ?: findEnumTypeSerializer(module, kType)
@@ -89,6 +89,7 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType
"D", "kotlin.Double" -> "DoubleSerializer" "D", "kotlin.Double" -> "DoubleSerializer"
"C", "kotlin.Char" -> "CharSerializer" "C", "kotlin.Char" -> "CharSerializer"
"kotlin.String" -> "StringSerializer" "kotlin.String" -> "StringSerializer"
"kotlin.Pair" -> "PairSerializer"
"kotlin.collections.Collection", "kotlin.collections.List", "kotlin.collections.ArrayList" -> "ArrayListSerializer" "kotlin.collections.Collection", "kotlin.collections.List", "kotlin.collections.ArrayList" -> "ArrayListSerializer"
"kotlin.collections.Set", "kotlin.collections.LinkedHashSet" -> "LinkedHashSetSerializer" "kotlin.collections.Set", "kotlin.collections.LinkedHashSet" -> "LinkedHashSetSerializer"
"kotlin.collections.HashSet" -> "HashSetSerializer" "kotlin.collections.HashSet" -> "HashSetSerializer"
@@ -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()
}
}
}
@@ -19,12 +19,14 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.js
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.backend.ast.* 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.context.TranslationContext
import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor
import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtPureClassOrObject import org.jetbrains.kotlin.psi.KtPureClassOrObject
import org.jetbrains.kotlin.resolve.descriptorUtil.classId 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.enumSerializerId
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
import org.jetbrains.kotlinx.serialization.compiler.resolve.* import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix
class SerializerJsTranslator(declaration: KtPureClassOrObject, class SerializerJsTranslator(declaration: KtPureClassOrObject,
val translator: DeclarationBodyVisitor, val translator: DeclarationBodyVisitor,
@@ -94,6 +97,22 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
translator.addProperty(propDesc, getterExpr, null) 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) = private fun ClassDescriptor.getFuncDesc(funcName: String) =
unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) } unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }
@@ -120,7 +139,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
if (property.transient) continue if (property.transient) continue
// output.writeXxxElementValue(classDesc, index, value) // output.writeXxxElementValue(classDesc, index, value)
val sti = getSerialTypeInfo(property) 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) { if (innerSerial == null) {
val writeFunc = val writeFunc =
kOutputClass.getFuncDesc("write${sti.elementMethodPrefix}ElementValue").single() kOutputClass.getFuncDesc("write${sti.elementMethodPrefix}ElementValue").single()
@@ -152,9 +171,13 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
return context.getQualifiedReference(classDescriptor) return context.getQualifiedReference(classDescriptor)
} }
private fun serializerInstance(serializerClass: ClassDescriptor, module: ModuleDescriptor, kType: KotlinType): JsExpression? { private fun serializerInstance(serializerClass: ClassDescriptor?, module: ModuleDescriptor, kType: KotlinType, genericIndex: Int? = null): JsExpression? {
val nullableSerClass = getQualifiedClassReferenceName( val nullableSerClass = getQualifiedClassReferenceName(requireNotNull(
serializerClass.getClassFromInternalSerializationPackage("NullableSerializer")) 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) { if (serializerClass.kind == ClassKind.OBJECT) {
return getQualifiedClassReferenceName(serializerClass) return getQualifiedClassReferenceName(serializerClass)
} }
@@ -162,13 +185,20 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId) var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId)
listOf(createGetKClassExpression(kType.toClassDescriptor!!)) listOf(createGetKClassExpression(kType.toClassDescriptor!!))
else kType.arguments.map { else kType.arguments.map {
val argSer = findTypeSerializer(module, it.type) ?: return null val argSer = findTypeSerializer(module, it.type)
val expr = serializerInstance(argSer, module, it.type) ?: return null val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null
if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr
} }
if (serializerClass.classId == referenceArraySerializerId) if (serializerClass.classId == referenceArraySerializerId)
args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args 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)) { case(JsIntLiteral(i)) {
// input.readXxxElementValue // input.readXxxElementValue
val sti = getSerialTypeInfo(property) 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 call = if (innerSerial == null) {
val unknownSer = (sti.elementMethodPrefix.isEmpty()) val unknownSer = (sti.elementMethodPrefix.isEmpty())
val readFunc = val readFunc =
@@ -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.findPolymorphicSerializer
import org.jetbrains.kotlinx.serialization.compiler.backend.common.requiresPolymorphism import org.jetbrains.kotlinx.serialization.compiler.backend.common.requiresPolymorphism
import org.jetbrains.kotlinx.serialization.compiler.resolve.* 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.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -71,10 +72,12 @@ internal fun InstructionAdapter.genExceptionThrow(exceptionClass: String, messag
athrow() 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 propertyType = classCodegen.typeMapper.mapType(property.type)
val sti = getSerialTypeInfo(property, propertyType) 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) if (!sti.unit) classCodegen.genPropertyOnStack(this, expressionCodegen.context, property.descriptor, propertyOwnerType, ownerVar)
invokevirtual(kOutputType.internalName, invokevirtual(kOutputType.internalName,
"write" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue", "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 referenceArraySerializerId = ClassId(internalPackageFqName, Name.identifier("ReferenceArraySerializer"))
internal val contextSerializerId = ClassId(packageFqName, Name.identifier("ContextSerializer")) 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 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 // returns false is cannot not use serializer
// use iv == null to check only (do not emit serializer onto stack) // use iv == null to check only (do not emit serializer onto stack)
internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, serializer: ClassDescriptor, internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: ModuleDescriptor, kType: KotlinType, maybeSerializer: ClassDescriptor?,
iv: InstructionAdapter?): Boolean { 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) { if (serializer.kind == ClassKind.OBJECT) {
// singleton serializer -- just get it // singleton serializer -- just get it
if (iv != null) if (iv != null)
@@ -133,11 +152,12 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
// serializer is not singleton object and shall be instantiated // serializer is not singleton object and shall be instantiated
val argSerializers = kType.arguments.map { projection -> val argSerializers = kType.arguments.map { projection ->
// bail out from stackValueSerializerInstance if any type argument is not serializable // 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 // 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 return false
Pair(projection.type, argSerializer) Pair(argType, argSerializer)
} }
// new serializer if needed // new serializer if needed
iv?.apply { iv?.apply {
@@ -163,7 +183,7 @@ internal fun stackValueSerializerInstance(codegen: ClassBodyCodegen, module: Mod
} }
// all serializers get arguments with serializers of their generic types // all serializers get arguments with serializers of their generic types
argSerializers.forEach { (argType, argSerializer) -> 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 // wrap into nullable serializer if argType is nullable
if (argType.isMarkedNullable) { if (argType.isMarkedNullable) {
invokestatic("kotlinx/serialization/internal/BuiltinSerializersKt", "makeNullable", 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/Map;", "Ljava/util/LinkedHashMap;" -> "LinkedHashMapSerializer"
"Ljava/util/HashMap;" -> "HashMapSerializer" "Ljava/util/HashMap;" -> "HashMapSerializer"
"Ljava/util/Map\$Entry;" -> "MapEntrySerializer" "Ljava/util/Map\$Entry;" -> "MapEntrySerializer"
"Lkotlin/Pair;" -> "PairSerializer"
else -> null else -> null
} }
@@ -26,14 +26,13 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny 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.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen 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.anonymousInitializers
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryPropertiesDescriptorsMap
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties import org.jetbrains.kotlinx.serialization.compiler.resolve.*
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperty
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -73,22 +72,29 @@ class SerializableCodegenImpl(
val thisI = 0 val thisI = 0
val outputI = 1 val outputI = 1
val serialDescI = 2 val serialDescI = 2
// val offsetI = 3 val offsetI = 3
val superClass = serializableDescriptor.getSuperClassOrAny() val superClass = serializableDescriptor.getSuperClassOrAny()
val myPropsStart: Int val myPropsStart: Int
if (superClass.isInternalSerializable) { if (superClass.isInternalSerializable) {
myPropsStart = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties.size myPropsStart = SerializableProperties(superClass, classCodegen.bindingContext).serializableProperties.size
val superTypeArguments = serializableDescriptor.typeConstructor.supertypes.single { it.toClassDescriptor?.isInternalSerializable == true }.arguments
//super.writeSelf(output, serialDesc) //super.writeSelf(output, serialDesc)
load(thisI, thisAsmType) load(thisI, thisAsmType)
load(outputI, kOutputType) load(outputI, kOutputType)
load(serialDescI, descType) 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 { else {
myPropsStart = 0 myPropsStart = 0
// offset = 0
// iconst(0)
} }
for (i in myPropsStart until properties.serializableProperties.size) { for (i in myPropsStart until properties.serializableProperties.size) {
@@ -97,7 +103,7 @@ class SerializableCodegenImpl(
load(outputI, kOutputType) load(outputI, kOutputType)
load(serialDescI, descType) load(serialDescI, descType)
iconst(i) iconst(i)
genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI) genKOutputMethodCall(property, classCodegen, exprCodegen, thisAsmType, thisI, offsetI)
} }
areturn(Type.VOID_TYPE) areturn(Type.VOID_TYPE)
@@ -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)
}
}
}
@@ -18,6 +18,7 @@ package org.jetbrains.kotlinx.serialization.compiler.backend.jvm
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.resolve.jvm.AsmTypes 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.SerializerCodegen
import org.jetbrains.kotlinx.serialization.compiler.backend.common.annotationVarsAndDesc 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
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.typeArgPrefix
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorBySerializer
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
import org.jetbrains.org.objectweb.asm.Label 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", "<init>", "()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() { override fun generateSerialDesc() {
codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC, codegen.v.newField(OtherOrigin(codegen.myClass.psiOrParent), ACC_PRIVATE or ACC_STATIC or ACC_FINAL or ACC_SYNTHETIC,
serialDescField, descType.descriptor, null, null) serialDescField, descType.descriptor, null, null)
@@ -126,12 +147,19 @@ class SerializerCodegenImpl(
")" + kOutputType.descriptor, false) ")" + kOutputType.descriptor, false)
store(outputVar, kOutputType) store(outputVar, kOutputType)
if (serializableDescriptor.isInternalSerializable) { if (serializableDescriptor.isInternalSerializable) {
val sig = StringBuilder("(${kOutputType.descriptor}${descType.descriptor}")
// call obj.write$Self(output, classDesc) // call obj.write$Self(output, classDesc)
load(objVar, objType) load(objVar, objType)
load(outputVar, kOutputType) load(outputVar, kOutputType)
load(descVar, descType) 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(), invokevirtual(objType.internalName, KSerializerDescriptorResolver.WRITE_SELF_NAME.asString(),
"(${kOutputType.descriptor}${descType.descriptor})V", false) sig.toString(), false)
} }
else { else {
// loop for all properties // loop for all properties
@@ -235,7 +263,7 @@ class SerializerCodegenImpl(
iconst(labelNum) iconst(labelNum)
val sti = getSerialTypeInfo(property, propertyType) val sti = getSerialTypeInfo(property, propertyType)
val useSerializer = stackValueSerializerInstance(codegen, sti) val useSerializer = stackValueSerializerInstanceFromSerializer(codegen, sti)
val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty()) val unknownSer = (!useSerializer && sti.elementMethodPrefix.isEmpty())
if (unknownSer) { if (unknownSer) {
aconst(codegen.typeMapper.mapType(property.type)) aconst(codegen.typeMapper.mapType(property.type))
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerialInfoCodegenImpl 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.SerializableCodegenImpl
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCompanionCodegenImpl
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializerCodegenImpl
class SerializationCodegenExtension : ExpressionCodegenExtension { class SerializationCodegenExtension : ExpressionCodegenExtension {
@@ -27,5 +28,6 @@ class SerializationCodegenExtension : ExpressionCodegenExtension {
SerialInfoCodegenImpl.generateSerialInfoImplBody(codegen) SerialInfoCodegenImpl.generateSerialInfoImplBody(codegen)
SerializableCodegenImpl.generateSerializableExtensions(codegen) SerializableCodegenImpl.generateSerializableExtensions(codegen)
SerializerCodegenImpl.generateSerializerExtensions(codegen) SerializerCodegenImpl.generateSerializerExtensions(codegen)
SerializableCompanionCodegenImpl.generateSerializableExtensions(codegen)
} }
} }
@@ -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.declaration.DeclarationBodyVisitor
import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension import org.jetbrains.kotlin.js.translate.extensions.JsSyntheticTranslateExtension
import org.jetbrains.kotlin.psi.KtPureClassOrObject 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.SerializableJsTranslator
import org.jetbrains.kotlinx.serialization.compiler.backend.js.SerializerJsTranslator 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) { override fun generateClassSyntheticParts(declaration: KtPureClassOrObject, descriptor: ClassDescriptor, translator: DeclarationBodyVisitor, context: TranslationContext) {
SerializerJsTranslator.translate(declaration, descriptor, translator, context) SerializerJsTranslator.translate(declaration, descriptor, translator, context)
SerializableJsTranslator.translate(declaration, descriptor, translator, context) SerializableJsTranslator.translate(declaration, descriptor, translator, context)
SerializableCompanionJsTranslator.translate(declaration, descriptor, translator, context)
} }
} }
@@ -31,16 +31,17 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.serialInfoFqName
import java.util.* import java.util.*
class SerializationResolveExtension : SyntheticResolveExtension { class SerializationResolveExtension : SyntheticResolveExtension {
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> { override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName)) thisDescriptor.annotations.hasAnnotation(serialInfoFqName) -> listOf(KSerializerDescriptorResolver.IMPL_NAME)
return listOf(KSerializerDescriptorResolver.IMPL_NAME) thisDescriptor.isInternalSerializable -> listOf(KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME)
else else -> listOf()
return listOf()
} }
override fun generateSyntheticClasses(thisDescriptor: ClassDescriptor, name: Name, ctx: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider, result: MutableSet<ClassDescriptor>) { override fun generateSyntheticClasses(thisDescriptor: ClassDescriptor, name: Name, ctx: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider, result: MutableSet<ClassDescriptor>) {
if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == KSerializerDescriptorResolver.IMPL_NAME) if (thisDescriptor.annotations.hasAnnotation(serialInfoFqName) && name == KSerializerDescriptorResolver.IMPL_NAME)
result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx)) result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx))
else if (thisDescriptor.isInternalSerializable && name == KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME)
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
return return
} }
@@ -50,12 +51,12 @@ class SerializationResolveExtension : SyntheticResolveExtension {
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) { override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializableSupertypes(thisDescriptor, supertypes)
KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes) KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes)
} }
override fun generateSyntheticMethods(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: List<SimpleFunctionDescriptor>, result: MutableCollection<SimpleFunctionDescriptor>) { override fun generateSyntheticMethods(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: List<SimpleFunctionDescriptor>, result: MutableCollection<SimpleFunctionDescriptor>) {
KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result) KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result)
KSerializerDescriptorResolver.generateCompanionObjectMethods(thisDescriptor, fromSupertypes, name, result)
} }
override fun generateSyntheticProperties(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: ArrayList<PropertyDescriptor>, result: MutableSet<PropertyDescriptor>) { override fun generateSyntheticProperties(thisDescriptor: ClassDescriptor, name: Name, fromSupertypes: ArrayList<PropertyDescriptor>, result: MutableSet<PropertyDescriptor>) {
@@ -17,17 +17,16 @@
package org.jetbrains.kotlinx.serialization.compiler.resolve package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf 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 packageFqName = FqName("kotlinx.serialization")
internal val internalPackageFqName = FqName("kotlinx.serialization.internal") internal val internalPackageFqName = FqName("kotlinx.serialization.internal")
@@ -123,7 +122,9 @@ internal val ClassDescriptor?.classSerializer: KotlinType?
// serializer annotation on class? // serializer annotation on class?
annotations.serializableWith?.let { return it } annotations.serializableWith?.let { return it }
// default serializable? // default serializable?
if (isInternalSerializable) return companionObjectDescriptor?.defaultType if (isInternalSerializable) return this.unsubstitutedMemberScope
.getDescriptorsFiltered(nameFilter = {it == SERIALIZER_CLASS_NAME})
.filterIsInstance<ClassDescriptor>().singleOrNull()?.defaultType
return null return null
} }
@@ -131,11 +132,20 @@ internal val ClassDescriptor?.classSerializer: KotlinType?
val KotlinType.typeSerializer: KotlinType? val KotlinType.typeSerializer: KotlinType?
get() = this.annotations.serializableWith ?: (this.toClassDescriptor).classSerializer 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? { fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
val serializerForClass = serializerDescriptor.annotations.serializerForClass val serializerForClass = serializerDescriptor.annotations.serializerForClass
if (serializerForClass != null) return serializerForClass.toClassDescriptor 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 val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
if (!classDescriptor.isInternalSerializable) return null if (!classDescriptor.isInternalSerializable) return null
return classDescriptor return classDescriptor
@@ -28,8 +28,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.createProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.util.* import java.util.*
@@ -39,6 +38,9 @@ object KSerializerDescriptorResolver {
val SERIAL_DESC_FIELD = "serialClassDesc" val SERIAL_DESC_FIELD = "serialClassDesc"
val SAVE = "save" val SAVE = "save"
val LOAD = "load" val LOAD = "load"
val SERIALIZER_CLASS = "\$serializer"
internal val typeArgPrefix = "typeSerial"
val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD) val SERIAL_DESC_FIELD_NAME = Name.identifier(SERIAL_DESC_FIELD)
val SAVE_NAME = Name.identifier(SAVE) val SAVE_NAME = Name.identifier(SAVE)
@@ -46,13 +48,8 @@ object KSerializerDescriptorResolver {
val DUMMY_PARAM_NAME = Name.identifier("serializationConstructorMarker") 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 WRITE_SELF_NAME = Name.identifier("write\$Self") //todo: add it as a supertype to synthetic resolving
val IMPL_NAME = Name.identifier("Impl") val IMPL_NAME = Name.identifier("Impl")
val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS)
val SERIALIZER_PROVIDER_NAME = Name.identifier("serializer")
fun addSerializableSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
if (classDescriptor.isInternalSerializable && supertypes.none(::isJavaSerializable)) {
classDescriptor.getJavaSerializableType()?.let { supertypes.add(it) }
}
}
fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean { fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean {
return thisDescriptor.name == IMPL_NAME return thisDescriptor.name == IMPL_NAME
@@ -90,17 +87,40 @@ object KSerializerDescriptorResolver {
false) 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, fun generateSerializerProperties(thisDescriptor: ClassDescriptor,
fromSupertypes: ArrayList<PropertyDescriptor>, fromSupertypes: ArrayList<PropertyDescriptor>,
name: Name, name: Name,
result: MutableSet<PropertyDescriptor>) { result: MutableSet<PropertyDescriptor>) {
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
if (name == SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) && if (name == SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL} ) fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL })
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor)) result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
} }
fun generateCompanionObjectMethods(thisDescriptor: ClassDescriptor,
fromSupertypes: List<SimpleFunctionDescriptor>,
name: Name,
result: MutableCollection<SimpleFunctionDescriptor>) {
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, fun generateSerializerMethods(thisDescriptor: ClassDescriptor,
fromSupertypes: List<SimpleFunctionDescriptor>, fromSupertypes: List<SimpleFunctionDescriptor>,
name: Name, name: Name,
@@ -228,6 +248,71 @@ object KSerializerDescriptorResolver {
return functionDescriptor 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<TypeParameterDescriptor>()
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<ValueParameterDescriptor>()
val typeArgs = mutableListOf<TypeParameterDescriptor>()
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() = private fun KotlinType.makeNullableIfNotPrimitive() =
if (KotlinBuiltIns.isPrimitiveType(this)) this if (KotlinBuiltIns.isPrimitiveType(this)) this
else this.makeNullable() else this.makeNullable()
@@ -266,6 +351,24 @@ object KSerializerDescriptorResolver {
f.source) 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( f.initialize(
null, null,
thisClass.thisAsReceiverParameter, thisClass.thisAsReceiverParameter,
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.module
class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructorParameterWithDefault: Boolean) { class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructorParameterWithDefault: Boolean) {
val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString() val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
val type = descriptor.type val type = descriptor.type
val genericIndex = type.genericIndex
val module = descriptor.module val module = descriptor.module
val serializableWith = descriptor.annotations.serializableWith val serializableWith = descriptor.annotations.serializableWith
val optional = descriptor.annotations.serialOptional val optional = descriptor.annotations.serialOptional
@@ -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<String> {
return emptyList()
}
override fun getClasspath(moduleBuildTarget: ModuleBuildTarget, context: CompileContext): List<String> {
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
})
}
}