Chandra Sekhar
Chandra Sekhar

Reputation: 19492

Why Kotlin decompiler generates null.INSTANCE

I am trying to understand few of the Kotlin features by checking how it looks in Java side.

So as an experiment, I tried with this:

val printKotlin = fun () {
    print("Hello Kotlin")
}

So the output for the above snippet is:

public final class FunAsVariableKt {
    private static final Function0 printKotlin;

    public static final Function0 getPrintKotlin() {
        return printKotlin;
    }

    static {
        printKotlin = (Function0)null.INSTANCE;
    }
}

How do I understand the static block of the above decompiled code? Why it is generating this non working code?

Upvotes: 3

Views: 1169

Answers (2)

shubhamgarg1
shubhamgarg1

Reputation: 1995

You can use JD-GUI for this.

http://java-decompiler.github.io/

This worked better for me as compared to the inbuilt IntelliJ plugin for decompilation. You just need to feed it your *.class files.

Kotlin Code

//TryingOutLamdas.kt

class TryingOutLambdas {

    public fun performSomething()
    {
        takingAOnclickListener { x -> print("Hello $x").toString() }
    }
    
    fun takingAOnclickListener(onClickListener: (Int) -> (String))
    {
        onClickListener.invoke(6)
    }

} 

Converted bytecode for this using JD-GUI.

public final class TryingOutLambdas {


    public final void performSomething() {
        
       takingAOnclickListener(TryingOutLambdas$performSomething$1
        .INSTANCE);
    }

    public final void takingAOnclickListener(@NotNull Function1 onClickListener) {
        Intrinsics.checkParameterIsNotNull(onClickListener, "onClickListener");
        onClickListener.invoke(Integer.valueOf(6));
    }

    static final class TryingOutLambdas$performSomething$1 extends Lambda implements Function1<Integer, String> {
        public static final TryingOutLambdas$performSomething$1 INSTANCE = new TryingOutLambdas$performSomething$1();
        
        @NotNull
        public final String invoke(int x) {
          String str = "Hello " + x;
          boolean bool = false;
          System.out.print(str);
          return Unit.INSTANCE.toString();
        }
        
        TryingOutLambdas$performSomething$1() {
          super(1);
        }
    } 

} 

In case a lambda expression accesses any variables from the function it is declared, a new object implementing that interface will be created instead of creating a Singleton instance created above.

Upvotes: 1

Jayson Minard
Jayson Minard

Reputation: 85946

Use the Kotlin bytecode inspector to see what is being generated as JVM bytecode rather than trying to decompile bytecode to Java, which likely won't work for code generated by a different language compiler because it might not follow the expected patterns. A bytecode decompiler does not always generate working code.

JVM bytecode != Java language specific

Kotlin generated JVM bytecode != Java generated JVM bytecode

The NULL instance you are seeing is a misinterpretation by your decompiler, and you can see all references to INSTANCE in the bytecode are setting values correctly.

This is the actual output:

// ================Stackoverflow_53384931Kt.class =================
// class version 52.0 (52)
// access flags 0x31
public final class Stackoverflow_53384931Kt {


  // access flags 0x1A
  // signature Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
  // declaration: kotlin.jvm.functions.Function0<kotlin.Unit>
  private final static Lkotlin/jvm/functions/Function0; printKotlin
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x19
  // signature ()Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
  // declaration: kotlin.jvm.functions.Function0<kotlin.Unit> getPrintKotlin()
  public final static getPrintKotlin()Lkotlin/jvm/functions/Function0;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 3 L0
    GETSTATIC Stackoverflow_53384931Kt.printKotlin : Lkotlin/jvm/functions/Function0;
    ARETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 3 L0
    GETSTATIC Stackoverflow_53384931Kt$printKotlin$1.INSTANCE : LStackoverflow_53384931Kt$printKotlin$1;
    CHECKCAST kotlin/jvm/functions/Function0
    PUTSTATIC Stackoverflow_53384931Kt.printKotlin : Lkotlin/jvm/functions/Function0;
    RETURN
    MAXSTACK = 1
    MAXLOCALS = 0

  @Lkotlin/Metadata; ... 
  // access flags 0x18
  final static INNERCLASS Stackoverflow_53384931Kt$printKotlin$1 null null
  // compiled from: stackoverflow-53384931.kt
}


// ================Stackoverflow_53384931Kt$printKotlin$1.class =================
// class version 52.0 (52)
// access flags 0x30
// signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>;
// declaration: Stackoverflow_53384931Kt$printKotlin$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<kotlin.Unit>
final class Stackoverflow_53384931Kt$printKotlin$1 extends kotlin/jvm/internal/Lambda  implements kotlin/jvm/functions/Function0  {


  // access flags 0x1041
  public synthetic bridge invoke()Ljava/lang/Object;
    ALOAD 0
    INVOKEVIRTUAL Stackoverflow_53384931Kt$printKotlin$1.invoke ()V
    GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
    ARETURN
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x11
  public final invoke()V
   L0
    LINENUMBER 4 L0
    LDC "Hello Kotlin"
    ASTORE 1
   L1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/Object;)V
   L2
   L3
    LINENUMBER 5 L3
    RETURN
   L4
    LOCALVARIABLE this LStackoverflow_53384931Kt$printKotlin$1; L0 L4 0
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x0
  <init>()V
    ALOAD 0
    ICONST_0
    INVOKESPECIAL kotlin/jvm/internal/Lambda.<init> (I)V
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x19
  public final static LStackoverflow_53384931Kt$printKotlin$1; INSTANCE

  // access flags 0x8
  static <clinit>()V
    NEW Stackoverflow_53384931Kt$printKotlin$1
    DUP
    INVOKESPECIAL Stackoverflow_53384931Kt$printKotlin$1.<init> ()V
    PUTSTATIC Stackoverflow_53384931Kt$printKotlin$1.INSTANCE : LStackoverflow_53384931Kt$printKotlin$1;
    RETURN
    MAXSTACK = 2
    MAXLOCALS = 0

  @Lkotlin/Metadata; ...
  OUTERCLASS Stackoverflow_53384931Kt null
  // access flags 0x18
  final static INNERCLASS Stackoverflow_53384931Kt$printKotlin$1 null null
  // compiled from: stackoverflow-53384931.kt
  // debug info: SMAP
  ...

}

Upvotes: 8

Related Questions