leibnitz27 / cfr

This is the public repository for the CFR Java decompiler
https://www.benf.org/other/cfr
MIT License
1.93k stars 249 forks source link

While loop with elvis operator in Kotlin produces incorrect code #272

Open adamciolkowski opened 2 years ago

adamciolkowski commented 2 years ago

CFR version

0.151

Compiler

kotlin compiler (from 1.6.0-M1 back to at least 1.4.32)

Description

While loop is decompiled incorrectly with kotlin elvis operator. Value returned from operator is not assigned to variable, as per attached example.

Example

Test.kt

import java.util.Queue

class Test {

    fun processAll(q: Queue<String>) {
        while (true) {
            val item = q.poll() ?: break
            process(item)
        }
    }

    private fun process(item: String) {
    }
}

Decompiled

/*
 * Decompiled with CFR 0.151.
 */
import java.util.Queue;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(/* omitted for brevity */)
public final class Test {
    public final void processAll(@NotNull Queue<String> q) {
        Intrinsics.checkNotNullParameter(q, "q");
        while (q.poll() != null) {
            String item;
            this.process(item);
        }
    }

    private final void process(String item) {
    }
}

javap -c output

  public final void processAll(java.util.Queue<java.lang.String>);
    Code:
       0: aload_1
       1: ldc           #10                 // String q
       3: invokestatic  #16                 // Method kotlin/jvm/internal/Intrinsics.checkNotNullParameter:(Ljava/lang/Object;Ljava/lang/String;)V
       6: nop
       7: aload_1
       8: invokeinterface #22,  1           // InterfaceMethod java/util/Queue.poll:()Ljava/lang/Object;
      13: checkcast     #24                 // class java/lang/String
      16: dup
      17: ifnull        23
      20: goto          27
      23: pop
      24: goto          36
      27: astore_2
      28: aload_0
      29: aload_2
      30: invokespecial #28                 // Method process:(Ljava/lang/String;)V
      33: goto          6
      36: return

Curiously, it only happens when elvis operator is used. Changing the loop to equivalent:

        while (true) {
            val item = q.poll()
            if (item == null) break
            process(item)
        }

decompiles correctly.