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

javassist creates invalid class files for Java records when injecting static fields and adding static initializer code #481

Open
xzel23 opened this issue Jan 20, 2024 · 0 comments

Comments

@xzel23
Copy link

xzel23 commented Jan 20, 2024

Description of problem

When injecting a field and a static initializer into a Java record class, Javassist generates invalid class files. I have seen this for several record classes and the error seems to vary. I have created a minimal example to reproduce this.

It works with normal classes and enums. I have provided both a normal class that pass enum classes share the property of being special classes in Java, but since the static initializer handling differs, I also added a normal class for reference.

File that is processed with Javassist

public record InvalidClassFile_Record(Object arg) {
    public static void main(String[] args) {
        InvalidClassFile_Record instance = new InvalidClassFile_Record(InvalidClassFile_Record.class.getName());
        System.out.println(instance.arg());
    }
}

Manipulating code

        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get(className);
        CtField f = new CtField(CtClass.booleanType, "f", cc);
        int modifiers = Modifier.STATIC | Modifier.FINAL;
        f.setModifiers(modifiers);
        cc.addField(f);

        String init = "{ " + className + ".f = true; }";
        CtConstructor initializer = cc.getClassInitializer();
        if (initializer == null) {
            System.out.println("generating initializer");
            initializer = cc.makeClassInitializer();
            initializer.setBody(init);
        } else {
            System.out.println("modifying existing initializer");
            initializer.insertBefore(init);
        }

        cc.writeFile();

How to reprodcue

A minimal example is contained in the attached javassist_invalid_classfile.zip. It contains of four classes:

  • ValidClassFile_Class demonstrates that the injection works for normal classes
  • ValidClassFile_Enum demonstrates that the injection works for enum classes
  • InvalidClassFile_Record demonstrates the error when using enum classes
  • Main contains the code that uses Javassist to manipulate the classes and write out the modified class files.
  1. Make sure you have at least Java 17 and Maven installed (I did this on Java 21 but I think 17 should work)
  2. extract the zip and change into the newly created folder
  3. Run mvn compile to compile
  4. Run mvn exec:java to write out the modified class files
  5. Run java ValidClassFile_Class, java ValidClassFile_Enum, and java InvalidClassFile_Record. The first two output a line of text, the third one fails to run

Example output

% javassist_invalid_classfile % java ValidClassFile_Class
ValidClassFile_Class@1dbd16a6
% javassist_invalid_classfile % java ValidClassFile_Enum
A
% javassist_invalid_classfile % java InvalidClassFile_Record
Fehler: Beim Laden der Klasse InvalidClassFile_Record ist ein LinkageError aufgetreten
java.lang.ClassFormatError: Illegal field name "LInvalidClassFile_Record;" in class InvalidClassFile_Record

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

No branches or pull requests

1 participant