Describe the bug
I'm trying to workaround current limitation on inline class serialization (#259). I'm adding my own KSerializer for some inline class but JVM backend fails to compile it. It stops with the message org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated.
Stacktrace
e: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
ILOAD 1
ICONST_1
IAND
ICONST_0
IF_ICMPNE L1
NEW kotlinx/serialization/MissingFieldException
DUP
LDC "innerValue"
INVOKESPECIAL kotlinx/serialization/MissingFieldException.<init> (Ljava/lang/String;)V
CHECKCAST java/lang/Throwable
ATHROW
L1
ALOAD 0
DLOAD 2
PUTFIELD DataClass.innerValue : D
RETURN
L2
LOCALVARIABLE this LDataClass; L0 L2 0
LOCALVARIABLE seen1 I L0 L2 1
LOCALVARIABLE innerValue LInlineClass; L0 L2 2
LOCALVARIABLE serializationConstructorMarker Lkotlinx/serialization/internal/SerializationConstructorMarker; L0 L2 3
MAXSTACK = 3
MAXLOCALS = 4
File being compiled: (16,1) in /Users/vsga/Workspace/playgrounds/kotlin-jvm/src/main/kotlin/Main.kt
The root cause org.jetbrains.kotlin.codegen.CompilationException was thrown at: org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.endVisit(FunctionCodegen.java:959)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethodBody(FunctionCodegen.java:481)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethod(FunctionCodegen.java:249)
at org.jetbrains.kotlin.codegen.FunctionCodegen.generateMethod(FunctionCodegen.java:165)
at org.jetbrains.kotlinx.serialization.compiler.backend.jvm.JVMCodegenUtilKt.generateMethod(JVMCodegenUtil.kt:138)
at org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCodegenImpl.generateInternalConstructor(SerializableCodegenImpl.kt:81)
at org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen.generateSyntheticInternalConstructor(SerializableCodegen.kt:41)
at org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializableCodegen.generate(SerializableCodegen.kt:33)
at org.jetbrains.kotlinx.serialization.compiler.backend.jvm.SerializableCodegenImpl$Companion.generateSerializableExtensions(SerializableCodegenImpl.kt:48)
at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationCodegenExtension.generateClassSyntheticParts(SerializationCodegenExtension.kt:29)
at org.jetbrains.kotlin.codegen.ImplementationBodyCodegen.generateSyntheticPartsAfterBody(ImplementationBodyCodegen.java:437)
at org.jetbrains.kotlin.codegen.MemberCodegen.generate(MemberCodegen.java:131)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:301)
at org.jetbrains.kotlin.codegen.MemberCodegen.genClassOrObject(MemberCodegen.java:285)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateClassesAndObjectsInFile(PackageCodegenImpl.java:119)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generateFile(PackageCodegenImpl.java:138)
at org.jetbrains.kotlin.codegen.PackageCodegenImpl.generate(PackageCodegenImpl.java:70)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generatePackage(CodegenFactory.kt:88)
at org.jetbrains.kotlin.codegen.DefaultCodegenFactory.generateModule(CodegenFactory.kt:67)
at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:35)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:616)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:203)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:164)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:86)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:346)
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:102)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:240)
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:90)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:601)
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:93)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1633)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:830)
Caused by: org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: Couldn't transform method node:
<init> (ILInlineClass;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V:
L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
ILOAD 1
ICONST_1
IAND
ICONST_0
IF_ICMPNE L1
NEW kotlinx/serialization/MissingFieldException
DUP
LDC "innerValue"
INVOKESPECIAL kotlinx/serialization/MissingFieldException.<init> (Ljava/lang/String;)V
CHECKCAST java/lang/Throwable
ATHROW
L1
ALOAD 0
DLOAD 2
PUTFIELD DataClass.innerValue : D
RETURN
L2
LOCALVARIABLE this LDataClass; L0 L2 0
LOCALVARIABLE seen1 I L0 L2 1
LOCALVARIABLE innerValue LInlineClass; L0 L2 2
LOCALVARIABLE serializationConstructorMarker Lkotlinx/serialization/internal/SerializationConstructorMarker; L0 L2 3
MAXSTACK = 3
MAXLOCALS = 4
File is unknown
The root cause java.lang.AssertionError was thrown at: org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:28)
at org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:92)
at org.jetbrains.kotlin.codegen.FunctionCodegen.endVisit(FunctionCodegen.java:952)
... 50 more
Caused by: java.lang.AssertionError: AFTER mandatory stack transformations: incorrect bytecode
at org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:28)
at org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer.transform(CompositeMethodTransformer.kt:25)
at org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor.performTransformations(OptimizationMethodVisitor.kt:62)
at org.jetbrains.kotlin.codegen.TransformationMethodVisitor.visitEnd(TransformationMethodVisitor.kt:70)
... 51 more
Caused by: java.lang.RuntimeException: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 16: Expected D, but found R
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.runAnalyzer(MethodTransformer.java:34)
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.analyze(MethodTransformer.java:44)
at org.jetbrains.kotlin.codegen.optimization.MethodVerifier.transform(MethodVerifier.kt:26)
... 54 more
Caused by: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 16: Expected D, but found R
at org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:291)
at org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer.runAnalyzer(MethodTransformer.java:31)
... 56 more
Caused by: org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException: Expected D, but found R
at org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(BasicVerifier.java:102)
at org.jetbrains.org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(BasicVerifier.java:43)
at org.jetbrains.org.objectweb.asm.tree.analysis.Frame.execute(Frame.java:288)
at org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze(Analyzer.java:187)
... 57 more
Interesting that if I move @Serializable(with = InlineClassSerializer::class) to inline class site instead of property site, the compiler stops with another message Inline classes are not supported by kotlinx.serialization yet. Am I not supposed to workaround this limitation?
The real world situation is that I'm trying to write serialization for external inner class (namely kotlin.time.Duration). In another case I would just change my class to be non-inline.
It might be a duplicate of #1060 yet I didn't see any inline classes there.
To Reproduce
fun main() {
val value = Json.decodeFromString<DataClass>("""
{ "innerValue" : 69.0 }
""".trimIndent())
println(value.innerValue.innerInnerValue)
}
@Serializable
data class DataClass(@Serializable(with = InlineClassSerializer::class) val innerValue: InlineClass)
inline class InlineClass(val innerInnerValue: Double)
object InlineClassSerializer : KSerializer<InlineClass> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("InlineClass", PrimitiveKind.DOUBLE)
override fun deserialize(decoder: Decoder): InlineClass {
return InlineClass(decoder.decodeDouble())
}
override fun serialize(encoder: Encoder, value: InlineClass) {
encoder.encodeDouble(value.innerInnerValue)
}
}
Expected behavior
69.0
Environment
No, inline classes are not supported even with custom serializers :(
Btw found a pretty straightforward workaround. Just wrap inline class in another non-inline class then add custom serializer for that and it works.
In case of kotlin.time.Duration:
@Serializable(with = DurationSerializer::class)
public class SerializableDuration(
public val value: Duration
)
public object DurationSerializer : KSerializer<SerializableDuration> {
override val descriptor: SerialDescriptor
get() = PrimitiveSerialDescriptor(SerializableDuration::class.simpleName!!, PrimitiveKind.DOUBLE)
override fun deserialize(decoder: Decoder): SerializableDuration {
return SerializableDuration(decoder.decodeDouble().nanoseconds)
}
override fun serialize(encoder: Encoder, value: SerializableDuration) {
encoder.encodeDouble(value.value.inNanoseconds)
}
}
Hello, @sandwwraith , is there some news about inline classes support?
@InsanusMokrassar Yes, we'll share the details later. They will be working in the new IR backend
@InsanusMokrassar Yes, we'll share the details later. They will be working in the new IR backend
Thank you for the update:)
It looks like #1244 may be relevant.
Most helpful comment
@InsanusMokrassar Yes, we'll share the details later. They will be working in the new IR backend