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

Type argument of null reference generic class variables is converted to Object #340

Open xxh160 opened 1 year ago

xxh160 commented 1 year ago

Hi! I am doing some research based on CFR! I have found a potential issue in the decompilation process of CFR: It seems that when a null reference generic class variable is decompiled, its type argument is being converted to Object.

Here is an example:

class T1<K> {
}

class T2<Q> extends T1<Integer> {
}

class Demo1 {
    public T1<Integer> foo(Boolean b1) {
        final T1<Integer> t1 = (T1<Integer>) null;
        return ((b1) ? t1 : new T2<Double>());
    }
}

After being decompiled with CFR, the output is:

/*
 * Decompiled with CFR 0.153-SNAPSHOT (24c7433-dirty).
 */
class Demo1 {
    Demo1() {
    }

    public T1<Integer> foo(Boolean bl) {
        T1<Object> t1 = null;
        return bl != false ? t1 : new T2();
    }
}

The variable t1 loses its type argument Integer, and its type is transformed into T1<Object> undesirably.

In addition, during extended research on this sample, it was discovered that if the reference value is not null, it may trigger semantic errors:

class T1<K extends Integer, U extends Double> {
    public String print() {
        return "T1";
    }
}

class T2<P, Q> extends T1<Integer, Double> {
    public String print() {
        return "T2";
    }
}

class Demo2 {
    public T1<Integer, Double> foo(Boolean b1) {
        final T1<Integer, Double> t1 = new T1<>();
        return ((b1) ? t1 : new T2<Integer, Double>());
    }

    public static void main(String[] args) {
        Demo2 d = new Demo2();
        Boolean b1 = true;
        T1<Integer, Double> t1 = d.foo(b1);
        // Should be true
        System.out.println("T1".equals(t1.print()));
    }
}

After being decompiled with CFR, the output is:

/*
 * Decompiled with CFR 0.153-SNAPSHOT (24c7433-dirty).
 */
class Demo2 {
    Demo2() {
    }

    public T1<Integer, Double> foo(Boolean bl) {
        T2 t2 = new T2();
        return bl != false ? t2 : new T2();
    }

    public static void main(String[] stringArray) {
        Demo2 demo2 = new Demo2();
        Boolean bl = true;
        T1<Integer, Double> t1 = demo2.foo(bl);
        System.out.println("T1".equals(t1.print()));
    }
}

The results are different between running the source code directly and running the decompiled code: the result of running the source code is true, while the result of running the decompiled code is false.

Would you please have a look at this? It is important for me! Thanks a lot!

CFR version: CFR 0.153-SNAPSHOT (24c7433-dirty). Javac version: openjdk 11.0.18 2023-01-17. I also attach the source files and I hope they will be helpful: demo.zip.