Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Javassist sets the generic type of the class but the decompilation is lost #2272

Closed
Milory opened this issue Sep 11, 2024 · 4 comments

Comments

@Milory
Copy link

Milory commented Sep 11, 2024

Issue details

platform

  1. macos 14.6.1 (23G93)
  2. jdk (Zulu 8.78.0.19-CA-macos-aarch64)
  3. Decompilation tools (jadx-gui 1.5.0)
  4. javassist (3.30.2-GA)

test code

    public static void main(String[] args) throws Exception {
        // 获取默认的类池
        ClassPool pool = ClassPool.getDefault();

        // 创建类 Wrapper<T>
        CtClass ctClass = pool.makeClass("com.Wrapper");

        // 获取类文件的常量池
        ConstPool constPool = ctClass.getClassFile().getConstPool();

        // 定义泛型参数 T
        TypeParameter[] typeParams = new TypeParameter[] { new TypeParameter("T") };

        // 实现 Comparable<Wrapper<T>>
        ClassType[] interfaces = {
                new ClassType("java.lang.Comparable", new SignatureAttribute.TypeArgument[]{
                        new SignatureAttribute.TypeArgument(new ClassType("com.Wrapper", new SignatureAttribute.TypeArgument[]{
                                new SignatureAttribute.TypeArgument(new ClassType("T"))
                        }))
                })
        };

        // 构建类签名
        ClassSignature classSignature = new ClassSignature(typeParams, ClassType.OBJECT, interfaces);

        // 将类签名应用到类文件中
        SignatureAttribute signatureAttribute = new SignatureAttribute(constPool, classSignature.encode());
        ctClass.getClassFile().addAttribute(signatureAttribute);

        // 添加字段 T value
        CtField valueField = new CtField(pool.get("java.lang.Object"), "value", ctClass);
        valueField.setGenericSignature("TT;");
        ctClass.addField(valueField);

        // 将生成的类写入文件
        ctClass.writeFile(SAVE_PATH);
    }

Decompile and view source code

package com;

/* loaded from: Wrapper.class */
public class Wrapper<T> {
    T value;
}

After decompiling through jadx, it was found that the implemented interfaces content was lost。
However, when the code is decompiled through the javap command, the code can be displayed normally

$ javap Wrapper 
Compiled from "Wrapper.java"
public class com.Wrapper<T> implements java.lang.Comparable<com.Wrapper<T>> {
  T value;
  public com.Wrapper();
}

Relevant log output or stacktrace

No response

Provide sample and class/method full name

No response

Jadx version

1.5.0

@Milory Milory added bug Core Issues in jadx-core module labels Sep 11, 2024
@skylot
Copy link
Owner

skylot commented Sep 11, 2024

@Milory please share your sample

@Milory
Copy link
Author

Milory commented Sep 12, 2024

test.zip

The MainTest.java file in the compressed file is a complete example, but it first depends on Lombok and Javassist, for example:

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.30.2-GA</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
        </dependency>

The Wrapper.class file in the compressed package is the generated target file。

@skylot
Copy link
Owner

skylot commented Sep 12, 2024

Hm, @Milory looks like you add Comparable interface only to signature and not to actual interfaces list:

ctClass.setInterfaces(new CtClass[]{pool.get("java.lang.Comparable")});

Because of this, class doesn't implement Comparable and check at runtime:

Object obj = ctClass.toClass().getConstructor().newInstance();
System.out.println("Is instance of Comparable: " + (obj instanceof Comparable));
Comparable cmp = (Comparable) obj;

print Is instance of Comparable: false and next cast fail with ClassCastException.

It is weird that javap use info from signature without checks.
But jadx don't trust signatures because it is just an optional metadata.
I will add another check to add a warning about incorrect signature for this case.

@Milory
Copy link
Author

Milory commented Sep 13, 2024

I'm very sorry, this is my problem. I suddenly realized that there was no implementation interface for the Wrapper set in the code example. After adding this part of the code, everything became normal。

       // 获取默认的类池
        ClassPool pool = ClassPool.getDefault();

        // 创建类 Wrapper<T>
        CtClass ctClass = pool.makeClass("com.Wrapper");
        CtClass comparableCtClass = pool.get("java.lang.Comparable");

        // 设置 Wrapper 实现 Comparable
        ctClass.addInterface(comparableCtClass);

        // 获取类文件的常量池
        ConstPool constPool = ctClass.getClassFile().getConstPool();

        // 定义泛型参数 T
        SignatureAttribute.TypeParameter[] typeParams = new SignatureAttribute.TypeParameter[] { new SignatureAttribute.TypeParameter("T") };

        // 实现 Comparable<Wrapper<T>>
        SignatureAttribute.ClassType[] interfaces = {
                new SignatureAttribute.ClassType("java.lang.Comparable", new SignatureAttribute.TypeArgument[]{
                        new SignatureAttribute.TypeArgument(new SignatureAttribute.ClassType("com.Wrapper", new SignatureAttribute.TypeArgument[]{
                                new SignatureAttribute.TypeArgument(new SignatureAttribute.ClassType("T"))
                        }))
                })
        };

        // 构建类签名
        SignatureAttribute.ClassSignature classSignature = new SignatureAttribute.ClassSignature(typeParams, SignatureAttribute.ClassType.OBJECT, interfaces);

        // 将类签名应用到类文件中
        SignatureAttribute signatureAttribute = new SignatureAttribute(constPool, classSignature.encode());
        ctClass.getClassFile().addAttribute(signatureAttribute);

        // 添加字段 T value
        CtField valueField = new CtField(pool.get("java.lang.Object"), "value", ctClass);
        valueField.setGenericSignature("TT;");
        ctClass.addField(valueField);

        CtMethod method = CtMethod.make("public int compareTo(com.Wrapper o) { return 0; }", ctClass);
        method.setGenericSignature("(Lcom/Wrapper<TT;>;)I");
        ctClass.addMethod(method);

        // 将生成的类写入文件
//        ctClass.writeFile(SAVE_PATH);

        Object obj = ctClass.toClass().newInstance();
        System.out.println("Is instance of Comparable: " + (obj instanceof java.lang.Comparable));
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants