Fernflower 反编译switch语句报错 问题分析

当switch里面包含方法调用时,反编译会报错:

java.lang.NullPointerException: Cannot read field "type" because the return value of "org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent.getInstance()" is null
        at org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper$StringSwitchRecognizer$JavacStringRecognizer.recognize(SwitchHelper.java:427)
        at org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper$StringSwitchRecognizer$JavacStringRecognizer.recognize(SwitchHelper.java:404)
        at org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.collectSwitchesOn(SwitchHelper.java:130)
        at org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.collectSwitchesOn(SwitchHelper.java:139)
        at org.jetbrains.java.decompiler.modules.decompiler.SwitchHelper.simplifySwitchesOnReferences(SwitchHelper.java:59)
        at org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable.codeToJava(MethodProcessorRunnable.java:216)
        at org.jetbrains.java.decompiler.main.rels.MethodProcessorRunnable.run(MethodProcessorRunnable.java:55)
        at org.jetbrains.java.decompiler.main.rels.ClassWrapper.init(ClassWrapper.java:73)
        at org.jetbrains.java.decompiler.main.ClassesProcessor.initWrappers(ClassesProcessor.java:396)
        at org.jetbrains.java.decompiler.main.ClassesProcessor.writeClass(ClassesProcessor.java:350)
        at org.jetbrains.java.decompiler.main.Fernflower.getClassContent(Fernflower.java:123)
        at org.jetbrains.java.decompiler.struct.ContextUnit.save(ContextUnit.java:100)
        at org.jetbrains.java.decompiler.struct.StructContext.saveContext(StructContext.java:57)
        at org.jetbrains.java.decompiler.main.Fernflower.decompileContext(Fernflower.java:96)
        at org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler.decompileContext(ConsoleDecompiler.java:126)
        at org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler.main(ConsoleDecompiler.java:83)

在官方YouTrack上提了一个issue(https://youtrack.jetbrains.com/issue/IDEA-346717/Decompiler-fails-to-decompile-switch-statements-containing-static-method-calls),不过一周过去了好像也没有回应,干脆自己研究一下。

测试用例:

public class A {
    public static void main(String[] args) {
        a();
        b();
        c();
    }

    public static void a() {
        switch (aa()) {
        case "1":
            System.out.println("a");
            break;
        case "2":
            System.out.println("b");
            break;
        }
    }

    public static String aa() {
        return "1";
    }

    public static void b() {
        switch (bb()) {
        case 1:
            System.out.println("a");
            break;
        case 2:
            System.out.println("b");
            break;
        }
    }

    public static int bb() {
        return 1;
    }

    public static void c() {
        switch (new A().cc()) {
        case 1:
            System.out.println("a");
            break;
        case 2:
            System.out.println("b");
            break;
        }
    }

    public int cc() {
        return 1;
    }
}

其中,a()和c()能够正常反编译,而b()不行。发现是调用静态方法导致的。

在Fernflower中,如果是静态调用,那么InvocationExprent的instance成员变量为null(InvocationExprent.java:121):

else if (opcode == CodeConstants.opc_invokestatic) {
  isStatic = true;
}
else {
  instance = stack.pop();
}

而在处理switch时,会获取InvocationExprent的instance成员的type成员(SwitchHelper.java:427):

if (switchSelector.getInstance().type != Exprent.EXPRENT_VAR) return null;

此时,如果instance是null,就会报错。解决方法为直接先在前面判断instance是否为null:

if (switchSelector.getInstance() == null || switchSelector.getInstance().type != Exprent.EXPRENT_VAR) return null;

提交了一个PR,目前已修复(就是不知道什么时候release新版本):https://github.com/JetBrains/intellij-community/pull/2688

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注