Create a static constructor clone with Javassist

It seems that the Javassist API allows us to create an exact copy of the class initializer (i.e. the static constructor) declared in the class:

CtClass cc = ...;
CtConstructor staticConstructor = cc.getClassInitializer();
if (staticConstructor != null) {
  CtConstructor staticConstructorClone = new CtConstructor(staticConstructor, cc, null);
  staticConstructorClone.getMethodInfo().setName(__NEW_NAME__);
  staticConstructorClone.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
  cc.addConstructor(staticConstructorClone);
}

however, this copy also includes (public / private) static final fields. For example, the static constructor of the following class:

public class Example {
  public static final Example ex1 = new Example("__EX_1__");

  private String name;

  private Example(String name) {
    this.name = name;
  }
}

in fact:

static {
  Example.ex1 = "__NAME__";
}

and therefore, exactly the copy of the static constructor will also include this call in the final name field.

Is there a way to create a copy of the static constructor that does not include calls in the final fields?

- Thank

+4
source share
1 answer

Introduction

, , , - ExprEditor. API Javassist , -.

, API , -, .

Example, :

public class Example {
 public static final Example ex1 = new Example("__EX_1__");
 public static String DEFAULT_NAME = "Paulo"; // <-- change 1

 private String name;

 static {
     System.out.println("Class inited");  // <-- change 2
 }

 public Example(String name) {
     this.name = name;
 }
}

, , , reset . , System.out, , , , ( ' ).

, :

public class Test {

   public static void main(String[] args) throws Throwable {
    System.out.println(Example.DEFAULT_NAME);
    Example.DEFAULT_NAME = "Jose";
    System.out.println(Example.DEFAULT_NAME);
    try {
        reset();
    } catch (Throwable t) {
        System.out.println("Problems calling reset, maybe not injected?");
    }
    System.out.println(Example.DEFAULT_NAME);
   }

   private static void reset() throws Throwable {
    Method declaredMethod = Example.class.getDeclaredMethod("__NEW_NAME__", new Class[] {});
    declaredMethod.invoke(null, new Object[] {});
   }
}

, :

Class inited
Paulo
Jose
Problems calling reset, maybe not injected?
Jose

, (, , !: P)

, , - , ? javap - Example :

javap -c -l -v -p Example.class

, .

  • c: -
  • l:
  • v: be verbose , ..
  • p:

- ( ):

 static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: new           #1                  // class test7/Example
         3: dup
         4: ldc           #13                 // String __EX_1__
         6: invokespecial #15                 // Method "<init>":(Ljava/lang/String;)V
         9: putstatic     #19                 // Field ex1:Ltest7/Example;
        12: ldc           #21                 // String Paulo
        14: putstatic     #23                 // Field DEFAULT_NAME:Ljava/lang/String;
        17: getstatic     #25                 // Field java/lang/System.out:Ljava/io/PrintStream;
        20: ldc           #31                 // String Class inited
        22: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 4: 0
        line 5: 12
        line 10: 17
        line 11: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

, , 9, a putstatic ex1, , , , .

, , , -. NEW_NAME() bytecode:

 public static void __NEW_NAME__();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: new           #1                  // class test7/Example
         3: dup
         4: ldc           #13                 // String __EX_1__
         6: invokespecial #15                 // Method "<init>":(Ljava/lang/String;)V
         9: putstatic     #19                 // Field ex1:Ltest7/Example;
        12: ldc           #21                 // String Paulo
        14: putstatic     #23                 // Field DEFAULT_NAME:Ljava/lang/String;
        17: getstatic     #25                 // Field java/lang/System.out:Ljava/io/PrintStream;
        20: ldc           #31                 // String Class inited
        22: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 4: 0
        line 5: 12
        line 10: 17
        line 11: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

- 9 , .

. , - final. , "", ? "", , - , :)

, , , , , , . :

public class Injector {

 public static void main(String[] args) throws Throwable {
    CtClass cc = ClassPool.getDefault().get(Example.class.getName());
    CtConstructor staticConstructor = cc.getClassInitializer();
    if (staticConstructor != null) {
        CtConstructor staticConstructorClone = new CtConstructor(staticConstructor, cc, null);
        staticConstructorClone.getMethodInfo().setName("__NEW_NAME__");
        staticConstructorClone.setModifiers(Modifier.PUBLIC | Modifier.STATIC);
        cc.addConstructor(staticConstructorClone);

        // Here the trick :-)

        staticConstructorClone.instrument(new ExprEditor() {

            @Override
            public void edit(FieldAccess f) throws CannotCompileException {
                try {
                    if (f.isStatic() && f.isWriter() && Modifier.isFinal(f.getField().getModifiers())) {
                        System.out.println("Found field");
                        f.replace("{  }");
                    }
                } catch (NotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });

        cc.writeFile("...);
    }
  }
}

, , instrument ExprEditor, . , , , , "{}", " ".

- :

  public static void __NEW_NAME__();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=0
         0: new           #1                  // class test7/Example
         3: dup
         4: ldc           #13                 // String __EX_1__
         6: invokespecial #15                 // Method "<init>":(Ljava/lang/String;)V
         9: astore_1
        10: aconst_null
        11: astore_0
        12: ldc           #21                 // String Paulo
        14: putstatic     #23                 // Field DEFAULT_NAME:Ljava/lang/String;
        17: getstatic     #25                 // Field java/lang/System.out:Ljava/io/PrintStream;
        20: ldc           #31                 // String Class inited
        22: invokevirtual #33                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 4: 0
        line 5: 12
        line 10: 17
        line 11: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

, stackframe 9 putstatic, astore_1, javassist 3 , 9 11:

         9: astore_1
        10: aconst_null
        11: astore_0

, , :

Class inited
Paulo
Jose
Class inited
Paulo

, , , ​​ , - ... , ExprEditor, , .

resetState(), , , , , -.

, . , .

+4

Source: https://habr.com/ru/post/1629788/


All Articles