Enhanced support for custom serializers definition
kotlin.Triple serializer Resolving serializers for mutable collection interfaces on JS Remove idea-related source files form maven compilation Serialization url in plugin.xml Removed note about JPS incompatibility Don't use serial names in produced JS code (https://github.com/Kotlin/kotlinx.serialization/issues/43) Generator for calling `update` functions Fix synthetic serialinfo implementations for 2-slots vars (double and long) (https://github.com/Kotlin/kotlinx.serialization/issues/60) Prohibit usage of nullable serializers for non-null fields (https://github.com/Kotlin/kotlinx.serialization/issues/59) Fix incorrect `this` referencing after update to 1.2.20 compiler
This commit is contained in:
@@ -7,9 +7,11 @@
|
||||
<properties>
|
||||
<maven.version>3.0.5</maven.version>
|
||||
<serialization.src>${basedir}/../../../plugins/kotlin-serialization/kotlin-serialization-compiler/src/</serialization.src>
|
||||
<serialization.src.kt>${basedir}/../../../plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler</serialization.src.kt>
|
||||
<serialization.maven.plugin.src>${basedir}/src/main/kotlin</serialization.maven.plugin.src>
|
||||
<serialization.maven.plugin.resources>${basedir}/src/main/resources</serialization.maven.plugin.resources>
|
||||
<serialization.target-src>${basedir}/target/src/main/kotlin</serialization.target-src>
|
||||
<serialization.target-src.kt>${basedir}/target/src/main/kotlin/org/jetbrains/kotlinx/serialization/compiler</serialization.target-src.kt>
|
||||
<serialization.target-resources>${basedir}/target/resource</serialization.target-resources>
|
||||
<kotlin.version>1.2-SNAPSHOT</kotlin.version>
|
||||
</properties>
|
||||
@@ -17,7 +19,7 @@
|
||||
<groupId>org.jetbrains.kotlinx</groupId>
|
||||
<artifactId>kotlinx-maven-serialization-plugin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.2</version>
|
||||
<version>0.3</version>
|
||||
<description>Serialization plugin for Maven</description>
|
||||
|
||||
<repositories>
|
||||
@@ -56,7 +58,7 @@
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-sources</id>
|
||||
<id>copy-maven-sources</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
@@ -64,11 +66,23 @@
|
||||
<configuration>
|
||||
<outputDirectory>${serialization.target-src}</outputDirectory>
|
||||
<resources>
|
||||
<resource><directory>${serialization.src}</directory></resource>
|
||||
<resource><directory>${serialization.maven.plugin.src}</directory></resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-sources</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${serialization.target-src.kt}</outputDirectory>
|
||||
<resources>
|
||||
<resource><directory>${serialization.src.kt}</directory></resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>validate</phase>
|
||||
|
||||
@@ -8,7 +8,7 @@ configureJvmProject(project)
|
||||
configurePublishing(project)
|
||||
|
||||
group = 'org.jetbrains.kotlinx'
|
||||
version = '0.2'
|
||||
version = '0.3'
|
||||
if (!project.hasProperty("deploy")) {
|
||||
version = "$version-SNAPSHOT"
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
# Kotlin serialization IDEA plugin
|
||||
|
||||
Kotlin serialization plugin consists of three parts: a compiler plugin, an IntelliJ plugin and a runtime library.
|
||||
This is the IDEA plugin. Gradle and Maven plugins can be found in `libraries` folder.
|
||||
|
||||
Please note that this plugin currently works only for highlighting and resolving symbols, and it doesn't work with embedded IDEA compiler.
|
||||
To build any project with serialization within IDEA, you'll need to delegate all build actions to gradle:
|
||||
`File - Settings - Build, Execution, Deployment - Build Tools - Gradle - Runner -` tick `Delegate IDE build/run actions to gradle`.
|
||||
IntelliJ projects currently are not supported.
|
||||
This is the folder with common source for all plugins, IDEA plugin is built from here. Gradle and Maven plugins can be found in `libraries` folder.
|
||||
|
||||
Compiler plugins are uploaded on bintray: https://bintray.com/kotlin/kotlinx/kotlinx.serialization.plugin
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<idea-plugin>
|
||||
<idea-plugin url="https://github.com/Kotlin/kotlinx.serialization" >
|
||||
<name>Kotlin-serialization</name>
|
||||
<id>org.jetbrains.kotlinx.serialization</id>
|
||||
<version>0.2</version>
|
||||
<version>0.3</version>
|
||||
|
||||
<depends>org.jetbrains.kotlin</depends>
|
||||
<idea-version since-build="172"/>
|
||||
|
||||
+5
-3
@@ -90,10 +90,12 @@ fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType
|
||||
"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.Triple" -> "TripleSerializer"
|
||||
"kotlin.collections.Collection", "kotlin.collections.List",
|
||||
"kotlin.collections.ArrayList", "kotlin.collections.MutableList" -> "ArrayListSerializer"
|
||||
"kotlin.collections.Set", "kotlin.collections.LinkedHashSet", "kotlin.collections.MutableSet" -> "LinkedHashSetSerializer"
|
||||
"kotlin.collections.HashSet" -> "HashSetSerializer"
|
||||
"kotlin.collections.Map", "kotlin.collections.LinkedHashMap" -> "LinkedHashMapSerializer"
|
||||
"kotlin.collections.Map", "kotlin.collections.LinkedHashMap", "kotlin.collections.MutableMap" -> "LinkedHashMapSerializer"
|
||||
"kotlin.collections.HashMap" -> "HashMapSerializer"
|
||||
"kotlin.collections.Map.Entry" -> "MapEntrySerializer"
|
||||
else -> return null
|
||||
|
||||
+11
-1
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
|
||||
internal class JsBlockBuilder {
|
||||
val block: JsBlock = JsBlock()
|
||||
@@ -81,4 +82,13 @@ internal fun TranslationContext.buildFunction(descriptor: FunctionDescriptor, bo
|
||||
b.bodyGen(functionObject, innerCtx)
|
||||
functionObject.body.statements += b.body
|
||||
return functionObject
|
||||
}
|
||||
}
|
||||
|
||||
internal fun propNotSeenTest(seenVar: JsNameRef, index: Int): JsBinaryOperation = JsAstUtils.equality(
|
||||
JsBinaryOperation(
|
||||
JsBinaryOperator.BIT_AND,
|
||||
seenVar,
|
||||
JsIntLiteral(1 shl (index % 32))
|
||||
),
|
||||
JsIntLiteral(0)
|
||||
)
|
||||
+2
-11
@@ -25,10 +25,8 @@ 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.general.Translation
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.anonymousInitializers
|
||||
@@ -53,7 +51,7 @@ class SerializableJsTranslator(val declaration: KtPureClassOrObject,
|
||||
|
||||
val f = context.buildFunction(constructorDescriptor) { jsFun, context ->
|
||||
val thiz = jsFun.scope.declareName(Namer.ANOTHER_THIS_PARAMETER_NAME).makeRef()
|
||||
val context = context.innerContextWithAliased(serializableDescriptor, thiz)
|
||||
val context = context.innerContextWithAliased(serializableDescriptor.thisAsReceiverParameter, thiz)
|
||||
|
||||
+JsVars(JsVars.JsVar(thiz.name, Namer.createObjectWithPrototypeFrom(context.getQualifiedReference(serializableDescriptor))))
|
||||
val seenVar = jsFun.parameters[0].name.makeRef()
|
||||
@@ -71,14 +69,7 @@ class SerializableJsTranslator(val declaration: KtPureClassOrObject,
|
||||
JsThrow(JsNew(missingExceptionClassRef, listOf(JsStringLiteral(prop.name))))
|
||||
}
|
||||
// (seen & 1 << i == 0) -- not seen
|
||||
val notSeenTest = JsAstUtils.equality(
|
||||
JsBinaryOperation(
|
||||
JsBinaryOperator.BIT_AND,
|
||||
seenVar,
|
||||
JsIntLiteral(1 shl (index % 32))
|
||||
),
|
||||
JsIntLiteral(0)
|
||||
)
|
||||
val notSeenTest = propNotSeenTest(seenVar, index)
|
||||
+JsIf(notSeenTest, ifNotSeenStmt, assignParamStmt)
|
||||
}
|
||||
|
||||
|
||||
+18
-6
@@ -228,7 +228,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
+JsVars(bitMasks.map { JsVars.JsVar(it.name, JsIntLiteral(0)) }, false)
|
||||
|
||||
// var localProp0, localProp1, ...
|
||||
val localProps = orderedProperties.map { JsNameRef(jsFun.scope.declareFreshName(it.name)) }
|
||||
val localProps = orderedProperties.mapIndexed {i, _ -> JsNameRef(jsFun.scope.declareFreshName("local$i")) }
|
||||
+JsVars(localProps.map { JsVars.JsVar(it.name) }, true)
|
||||
|
||||
//input = input.readBegin(...)
|
||||
@@ -266,7 +266,7 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
// input.readXxxElementValue
|
||||
val sti = getSerialTypeInfo(property)
|
||||
val innerSerial = serializerInstance(sti.serializer, property.module, property.type, property.genericIndex)
|
||||
val call = if (innerSerial == null) {
|
||||
val call: JsExpression = if (innerSerial == null) {
|
||||
val unknownSer = (sti.elementMethodPrefix.isEmpty())
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("read${sti.elementMethodPrefix}ElementValue")
|
||||
@@ -278,13 +278,25 @@ class SerializerJsTranslator(declaration: KtPureClassOrObject,
|
||||
JsInvocation(JsNameRef(readFunc, inputVar), readArgs)
|
||||
}
|
||||
else {
|
||||
val notSeenTest = propNotSeenTest(bitMasks[i / 32], i)
|
||||
val readFunc =
|
||||
inputClass.getFuncDesc("read${sti.elementMethodPrefix}SerializableElementValue").single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
JsInvocation(JsNameRef(readFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial)
|
||||
val updateFunc =
|
||||
inputClass.getFuncDesc("update${sti.elementMethodPrefix}SerializableElementValue").single()
|
||||
.let { context.getNameForDescriptor(it) }
|
||||
JsConditional(notSeenTest,
|
||||
JsInvocation(JsNameRef(readFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial),
|
||||
JsInvocation(JsNameRef(updateFunc, inputVar),
|
||||
serialClassDescRef,
|
||||
JsIntLiteral(i),
|
||||
innerSerial,
|
||||
localProps[i]
|
||||
)
|
||||
)
|
||||
}
|
||||
// localPropI = ...
|
||||
+JsAstUtils.assignment(
|
||||
|
||||
+1
@@ -282,5 +282,6 @@ internal val org.jetbrains.org.objectweb.asm.Type.standardSerializer: String? ge
|
||||
"Ljava/util/HashMap;" -> "HashMapSerializer"
|
||||
"Ljava/util/Map\$Entry;" -> "MapEntrySerializer"
|
||||
"Lkotlin/Pair;" -> "PairSerializer"
|
||||
"Lkotlin/Triple;" -> "TripleSerializer"
|
||||
else -> null
|
||||
}
|
||||
|
||||
+4
-2
@@ -76,12 +76,14 @@ class SerialInfoCodegenImpl(val codegen: ImplementationBodyCodegen, val thisClas
|
||||
codegen.generateMethod(constr, { _, _ ->
|
||||
load(0, thisAsmType)
|
||||
invokespecial("java/lang/Object", "<init>", "()V", false)
|
||||
props.forEachIndexed { index, prop ->
|
||||
var varOffset = 1
|
||||
props.forEach { prop ->
|
||||
val propType = codegen.typeMapper.mapType(prop.type)
|
||||
val propFieldName = "_" + prop.name.identifier
|
||||
load(0, thisAsmType)
|
||||
load(index + 1, propType)
|
||||
load(varOffset, propType)
|
||||
putfield(thisAsmType.internalName, propFieldName, propType.descriptor)
|
||||
varOffset += propType.size
|
||||
}
|
||||
areturn(Type.VOID_TYPE)
|
||||
})
|
||||
|
||||
+28
-6
@@ -280,12 +280,34 @@ class SerializerCodegenImpl(
|
||||
aconst(codegen.typeMapper.mapType(property.type))
|
||||
AsmUtil.wrapJavaClassIntoKClass(this)
|
||||
}
|
||||
invokevirtual(kInputType.internalName,
|
||||
"read" + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
||||
"(" + descType.descriptor + "I" +
|
||||
(if (useSerializer) kSerialLoaderType.descriptor else "")
|
||||
+ (if (unknownSer) AsmTypes.K_CLASS_TYPE.descriptor else "")
|
||||
+ ")" + (if (sti.unit) "V" else sti.type.descriptor), false)
|
||||
|
||||
fun produceCall(update: Boolean) {
|
||||
invokevirtual(kInputType.internalName,
|
||||
(if (update) "update" else "read") + sti.elementMethodPrefix + (if (useSerializer) "Serializable" else "") + "ElementValue",
|
||||
"(" + descType.descriptor + "I" +
|
||||
(if (useSerializer) kSerialLoaderType.descriptor else "")
|
||||
+ (if (unknownSer) AsmTypes.K_CLASS_TYPE.descriptor else "")
|
||||
+ (if (update) sti.type.descriptor else "")
|
||||
+ ")" + (if (sti.unit) "V" else sti.type.descriptor), false)
|
||||
}
|
||||
|
||||
if (useSerializer) {
|
||||
// we can choose either it is read or update
|
||||
val readLabel = Label()
|
||||
val endL = Label()
|
||||
genValidateProperty(index, ::bitMaskOff)
|
||||
ificmpeq(readLabel)
|
||||
load(propVar, propertyType)
|
||||
produceCall(true)
|
||||
goTo(endL)
|
||||
visitLabel(readLabel)
|
||||
produceCall(false)
|
||||
visitLabel(endL)
|
||||
} else {
|
||||
// update not supported for primitive types
|
||||
produceCall(false)
|
||||
}
|
||||
|
||||
if (sti.unit) {
|
||||
StackValue.putUnitInstance(this)
|
||||
}
|
||||
|
||||
+5
-6
@@ -25,16 +25,14 @@ import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getSerializableClassDescriptorByCompanion
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.serialInfoFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import java.util.*
|
||||
|
||||
class SerializationResolveExtension : SyntheticResolveExtension {
|
||||
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
|
||||
thisDescriptor.annotations.hasAnnotation(serialInfoFqName) -> listOf(KSerializerDescriptorResolver.IMPL_NAME)
|
||||
thisDescriptor.isInternalSerializable -> listOf(KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME)
|
||||
thisDescriptor.isInternalSerializable && !thisDescriptor.hasCompanionObjectAsSerializer ->
|
||||
listOf(KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME)
|
||||
else -> listOf()
|
||||
}
|
||||
|
||||
@@ -47,7 +45,8 @@ class SerializationResolveExtension : SyntheticResolveExtension {
|
||||
override fun generateSyntheticClasses(thisDescriptor: ClassDescriptor, name: Name, ctx: LazyClassContext, declarationProvider: ClassMemberDeclarationProvider, result: MutableSet<ClassDescriptor>) {
|
||||
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)
|
||||
else if (thisDescriptor.isInternalSerializable && name == KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME &&
|
||||
result.none { it.name == KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME })
|
||||
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
|
||||
return
|
||||
}
|
||||
|
||||
+23
-4
@@ -26,6 +26,7 @@ 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.kotlin.types.typeUtil.supertypes
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.KSerializerDescriptorResolver.SERIALIZER_CLASS_NAME
|
||||
|
||||
internal val packageFqName = FqName("kotlinx.serialization")
|
||||
@@ -122,15 +123,33 @@ internal val ClassDescriptor?.classSerializer: KotlinType?
|
||||
// serializer annotation on class?
|
||||
annotations.serializableWith?.let { return it }
|
||||
// default serializable?
|
||||
if (isInternalSerializable) return this.unsubstitutedMemberScope
|
||||
.getDescriptorsFiltered(nameFilter = {it == SERIALIZER_CLASS_NAME})
|
||||
.filterIsInstance<ClassDescriptor>().singleOrNull()?.defaultType
|
||||
if (isInternalSerializable) {
|
||||
// companion object serializer?
|
||||
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor?.defaultType
|
||||
// $serializer nested class
|
||||
return this.unsubstitutedMemberScope
|
||||
.getDescriptorsFiltered(nameFilter = {it == SERIALIZER_CLASS_NAME})
|
||||
.filterIsInstance<ClassDescriptor>().singleOrNull()?.defaultType
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
internal val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
|
||||
get() = companionObjectDescriptor?.annotations?.serializerForClass == this.defaultType
|
||||
|
||||
internal fun checkSerializerNullability(classType: KotlinType, serializerType: KotlinType): KotlinType {
|
||||
val castedToKSerial = requireNotNull(
|
||||
serializerType.supertypes().find { isKSerializer(it) },
|
||||
{ "KSerializer is not a supertype of $serializerType" }
|
||||
)
|
||||
if (!classType.isMarkedNullable && castedToKSerial.arguments.first().type.isMarkedNullable)
|
||||
throw IllegalStateException("Can't serialize non-nullable field of type ${classType} with nullable serializer ${serializerType}")
|
||||
return serializerType
|
||||
}
|
||||
|
||||
// serializer that was declared for this specific type or annotation from a class declaration
|
||||
val KotlinType.typeSerializer: KotlinType?
|
||||
get() = this.annotations.serializableWith ?: (this.toClassDescriptor).classSerializer
|
||||
get() = (this.annotations.serializableWith ?: (this.toClassDescriptor).classSerializer)?.let { checkSerializerNullability(this, it) }
|
||||
|
||||
val KotlinType.genericIndex: Int?
|
||||
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ class SerializableProperty(val descriptor: PropertyDescriptor, val isConstructor
|
||||
val type = descriptor.type
|
||||
val genericIndex = type.genericIndex
|
||||
val module = descriptor.module
|
||||
val serializableWith = descriptor.annotations.serializableWith
|
||||
val serializableWith = descriptor.annotations.serializableWith?.let { checkSerializerNullability(type, it) }
|
||||
val optional = descriptor.annotations.serialOptional
|
||||
val transient = descriptor.annotations.serialTransient
|
||||
val annotations = descriptor.annotations
|
||||
|
||||
Reference in New Issue
Block a user