Guardsquare/proguard

java.lang.VerifyError: Bad type on operand stack for Kotlin 2.0 and KotlinX Serialization

Opened this issue · 1 comments

The Kotlin code compiled by Kotlin 2.0 with KotlinX serialization applied is executed with an error if ProGuard optimization is enabled.

Error

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    dev/mikchan/misc/ksp/model/Model$$serializer.<clinit>()V @38: putstatic
  Reason:
    Type 'dev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor' (current frame, stack[0]) is not assignable to 'dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor'
  Current Frame:
    bci: @38
    flags: { }
    locals: { 'dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor' }
    stack: { 'dev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor' }

Reproducer

Reproducer project: https://github.com/wtlgo/Kotlin-Serialization-Proguard

To reproduce:

  • call proguardJar Gradle task
  • launch application java -jar build/libs/Kotlin-Serialization-Proguard-1.0-SNAPSHOT-pro.jar

Details

As an example, we can consider the class dev.mikchan.misc.ksp.model.Model$$serializer

The problem is in the <clinit> method

Original method bytecode

  static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=5, locals=1, args_size=0
         0: new           #2                  // class dev/mikchan/misc/ksp/model/Model$$serializer
         3: dup
         4: invokespecial #144                // Method "<init>":()V
         7: putstatic     #146                // Field INSTANCE:Ldev/mikchan/misc/ksp/model/Model$$serializer;
        10: new           #148                // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor
        13: dup
        14: ldc           #150                // String dev.mikchan.misc.ksp.model.Model
        16: getstatic     #146                // Field INSTANCE:Ldev/mikchan/misc/ksp/model/Model$$serializer;
        19: checkcast     #7                  // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/GeneratedSerializer
        22: iconst_1
        23: invokespecial #153                // Method dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor."<init>":(Ljava/lang/String;Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/GeneratedSerializer;I)V
        26: astore_0
        27: aload_0
        28: ldc           #155                // String lets
        30: iconst_0
        31: invokevirtual #159                // Method dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.addElement:(Ljava/lang/String;Z)V
        34: aload_0
        35: checkcast     #113                // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor
        38: putstatic     #73                 // Field descriptor:Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor;
        41: return
      LineNumberTable:
        line 5: 10
        line 6: 41
}

optimyzed bytecode

  static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=5, locals=1, args_size=0
         0: new           #7                  // class dev/mikchan/misc/ksp/model/Model$$serializer
         3: dup
         4: invokespecial #24                 // Method "<init>":()V
         7: putstatic     #20                 // Field INSTANCE:Ldev/mikchan/misc/ksp/model/Model$$serializer;
        10: new           #16                 // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor
        13: dup
        14: ldc           #2                  // String dev.mikchan.misc.ksp.model.Model
        16: getstatic     #20                 // Field INSTANCE:Ldev/mikchan/misc/ksp/model/Model$$serializer;
        19: checkcast     #15                 // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/GeneratedSerializer
        22: iconst_1
        23: invokespecial #27                 // Method dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor."<init>":(Ljava/lang/String;Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/GeneratedSerializer;I)V
        26: dup
        27: astore_0
        28: ldc           #3                  // String lets
        30: iconst_0
        31: invokevirtual #28                 // Method dev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor.addElement:(Ljava/lang/String;Z)V
        34: aload_0
        35: checkcast     #12                 // class dev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor
        38: putstatic     #21                 // Field descriptor$2e3cfc03:Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor;
        41: return
      LineNumberTable:
        line 5: 10
        line 6: 41
}

As you can see, the type of the descriptor field has changed from Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/descriptors/SerialDescriptor; to Ldev/mikchan/misc/ksp/shadow/kotlinx/serialization/internal/PluginGeneratedSerialDescriptor;

The problem goes away if you add the rule

-keepclassmembers public class **$$serializer {
    private ** descriptor;
}

I've removed my repository, but here's a here's a zip of it. I wasn't aware it was referenced here when I did it, I'm sorry.