Kotlin / kotlinx.serialization

Kotlin multiplatform / multi-format serialization
Apache License 2.0
5.39k stars 620 forks source link

IR: Serialization results in "IndexOutOfBoundsException: Index 0 out of bounds for length 0" #1438

Closed Karn closed 2 years ago

Karn commented 3 years ago

Describe the bug The kotlinx-serialization library seems to be having issues when used in conjunction with the useIR flag or with Jetpack compose which also enables useIR.

Related: https://youtrack.jetbrains.com/issue/KT-43910

To Reproduce In the project-level build.gradle, import the kotlin-serialization and kotlin gradle plugin as usual.

In the app level build.gradle

plugins {
  id 'com.android.application'
  id 'kotlin-android'
  id 'kotlinx-serialization'
}

android {
  // ...

  kotlinOptions {
    useIR = true // or enable Jetpack Compose which automatically enables IR.
  }
}

Anywhere in the codebase add the following chunk:

import kotlinx.serialization.Serializable

@Serializable
sealed class SampleSealedClassWithGeneric<T>(val name: String, val result: T)

@Serializable
class SampleSubClass : SampleSealedClassWithGeneric<String>("hello", "world")

Build and observe the following error:

Click to expand

Ref: [GeneratorHelpers.kt:593](https://github.com/JetBrains/kotlin/blob/9e50a239b4f34406a6c6f987b29d5ceedca777a8/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/GeneratorHelpers.kt#L593) ```log java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64) at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70) at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248) at java.base/java.util.Objects.checkIndex(Objects.java:372) at java.base/java.util.ArrayList.get(ArrayList.java:459) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.IrBuilderExtension$serializerInstance$1.invoke(GeneratorHelpers.kt:593) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.IrBuilderExtension$serializerInstance$1.invoke(GeneratorHelpers.kt:592) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.IrBuilderExtension$DefaultImpls.serializerInstance(GeneratorHelpers.kt:609) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator.serializerInstance(SerializerIrGenerator.kt:47) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.IrBuilderExtension$DefaultImpls.serializerInstance(GeneratorHelpers.kt:586) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator.serializerInstance(SerializerIrGenerator.kt:47) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator$generateSave$1.invoke(SerializerIrGenerator.kt:577) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator$generateSave$1.invoke(SerializerIrGenerator.kt:244) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.IrBuilderExtension$DefaultImpls.contributeFunction(GeneratorHelpers.kt:55) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator.contributeFunction(SerializerIrGenerator.kt:47) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator.generateSave(SerializerIrGenerator.kt:244) at org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen.generateSaveIfNeeded(SerializerCodegen.kt:109) at org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializerCodegen.generate(SerializerCodegen.kt:37) at org.jetbrains.kotlinx.serialization.compiler.backend.ir.SerializerIrGenerator$Companion.generate(SerializerIrGenerator.kt:521) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializerClassLowering.lower(SerializationLoweringExtension.kt:53) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:37) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitClass(IrElementVisitorVoid.kt:44) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:55) at org.jetbrains.kotlin.ir.declarations.IrClass.acceptChildren(IrClass.kt:60) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoidKt.acceptChildrenVoid(IrElementVisitorVoid.kt:275) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:38) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitClass(IrElementVisitorVoid.kt:44) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitClass(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlin.ir.declarations.IrClass.accept(IrClass.kt:55) at org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl.acceptChildren(IrFileImpl.kt:66) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoidKt.acceptChildrenVoid(IrElementVisitorVoid.kt:275) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitElement(SerializationLoweringExtension.kt:33) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitPackageFragment(IrElementVisitorVoid.kt:30) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitPackageFragment(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitFile(IrElementVisitorVoid.kt:37) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitFile(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid$DefaultImpls.visitFile(IrElementVisitorVoid.kt:38) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitFile(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt$runOnFileInOrder$1.visitFile(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl.accept(IrFileImpl.kt:63) at org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoidKt.acceptVoid(IrElementVisitorVoid.kt:271) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtensionKt.runOnFileInOrder(SerializationLoweringExtension.kt:31) at org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationLoweringExtension.generate(SerializationLoweringExtension.kt:71) at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory$generateModule$1.invoke(JvmIrCodegenFactory.kt:93) at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory$generateModule$1.invoke(JvmIrCodegenFactory.kt:89) at org.jetbrains.kotlin.psi2ir.Psi2IrTranslator.generateModuleFragment(Psi2IrTranslator.kt:91) at org.jetbrains.kotlin.backend.jvm.JvmIrCodegenFactory.generateModule(JvmIrCodegenFactory.kt:106) at org.jetbrains.kotlin.codegen.KotlinCodegenFacade.compileCorrectFiles(KotlinCodegenFacade.java:35) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.generate(KotlinToJVMBytecodeCompiler.kt:595) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:211) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:154) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:169) at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52) at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:88) 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:386) at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:110) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:286) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:111) at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:74) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:607) at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:96) at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1659) at jdk.internal.reflect.GeneratedMethodAccessor104.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) 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(Native Method) 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(Native Method) 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:834) ```

Observe also that applying the following diff to the code sample results in a successful build:

import kotlinx.serialization.Serializable

@Serializable
sealed class SampleSealedClassWithGeneric<T>(val name: String, val result: T)

@Serializable
- class SampleSubClass : SampleSealedClassWithGeneric<String>("hello", "world")
+ sealed class SampleSubClass : SampleSealedClassWithGeneric<String>("hello", "world")

Expected behavior The build completes successfully.

Environment

Karn commented 3 years ago

For anyone looking for a workaround you can try to structure your object as follows:

import kotlinx.serialization.Serializable

@Serializable
- sealed class SampleSealedClassWithGeneric<T>(val name: String, val result: T)
+ sealed class SampleSealedClassWithGeneric<T>(val name: String) {
+   abstract val result: T
+ }

@Serializable
- class SampleSubClass : SampleSealedClassWithGeneric<String>("hello", "world")
+ class SampleSubClass(override val result: String = "world") : SampleSealedClassWithGeneric<String>("hello")

Which should yield the desired results.

sandwwraith commented 3 years ago

Duplicate of #1264