์ด๋ฒˆ์—๋Š” ์ด์ „ ์‹œ๊ฐ„์— ๋งŒ๋“  writeClass()๋ฅผ ์ด์šฉํ•ด์„œ ์ƒ์„ฑ๋œ ํด๋ž˜์Šค๋ฅผ ๋ณ€ํ˜•์„ ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


ํ˜น์‹œ๋‚˜ "ํด๋ž˜์Šค ์ƒ์„ฑํ•˜๊ธฐ" ์ฑ•ํ„ฐ๋ฅผ ๊ฑด๋„ˆ๋›ฐ์‹  ๋ถ„๋“ค์„ ์œ„ํ•ด์„œ ์ €~~~~ ์•„๋ž˜์ชฝ์— writeClass() ๋ฉ”์†Œ๋“œ ์†Œ์Šค๋ฅผ ์˜ฌ๋ ค๋†“๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


์ž ๊ทธ๋Ÿผ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”...


๋ณ€ํ˜•ํ•˜๋Š”๊ฑฐ๋Š” ๊ทธ๋ฆฌ ์–ด๋ ต์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ClassVisitor๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ œ์ผ ์ฒซ๋ฒˆ์งธ ์‹œ๊ฐ„์— MyClassVisitor๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ ์ด๊ฑธ ์ด์šฉํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


ํฐ ๊ทธ๋ฆผ๋ถ€ํ„ฐ ํ•œ๋ฒˆ ๋ณผ๊นŒ์š”?? ์ˆœ์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1. ClassWriter ์ƒ์„ฑ

2. ClassWriter๋ฅผ ์ด์šฉํ•˜์—ฌ ClassVisitor ์ƒ์„ฑ ( ClassWriter๋ฅผ ์ธ์ž๋กœ ๋„˜๊ฒจ์คŒ )

3. ClassReader๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ณ€ํ˜•ํ•˜๊ณ ์žํ•˜๋Š” ํด๋ž˜์Šค ์ •๋ณด๋ฅผ ์ฝ์–ด๋“ค์ž„

4. cr.accept( ClassVisitor, int ) ๋ฉ”์†Œ๋“œ๋ฅผ ์ด์šฉํ•ด์„œ ํด๋ž˜์Šค ๋ณ€ํ˜•


์‹ค์งˆ์ ์œผ๋กœ ๋ณ€ํ˜•์„ ํ•˜๋Š” ๋…€์„์€ ๋ฐ”๋กœ ์ด ClassVisitor๋ผ๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.


์ž, ๊ทธ๋Ÿผ ์šฐ์„  ํด๋ž˜์Šค๋ฅผ ๋ณ€ํ˜•ํ•˜๊ธฐ์œ„ํ•œ ๋ฉ”์†Œ๋“œ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋ณด์ฃ .

private byte[] transformClass(){
        ClassWriter cw = new ClassWriter(0);
        ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw){};
        ClassReader cr = new ClassReader(writeClass());
        cr.accept(cv, 0);
        return cw.toByteArray();
}


์—ฌ๊ธฐ์„œ writeClass()์˜ ์†Œ์Šค๋Š” ์ด ํฌ์ŠคํŠธ ํ•˜๋‹จ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ„๋‹จํ•˜๊ฒŒ ์†Œ์Šค์„ค๋ช…์„ ๋“œ๋ฆฌ์ž๋ฉด ์šฐ์„  cw๋ฅผ ๋งŒ๋“ค๊ณ  cv๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์ธ์ž๋กœ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ๊ธฐ์กด์— ๋งŒ๋“ค์—ˆ๋˜ MyClassVisitor์—๋Š” ์œ„ ์†Œ์Šค์—์„œ ์‚ฌ์šฉํ•œ ์ƒ์„ฑ์ž๊ฐ€ ์—†์œผ๋ฏ€๋กœ ํ•˜๋‚˜๋ฅผ ์ถ”๊ฐ€๋กœ ์ƒ์„ฑํ•ด์ฃผ์…”์•ผ ํ•ฉ๋‹ˆ๋‹ค.

MyClassVisitorํด๋ž˜์Šค์— ์•„๋ž˜ ์ƒ์„ฑ์ž๋ฅผ ํ•˜๋‚˜ ์ถ”๊ฐ€ํ•ด์ฃผ์„ธ์š”.


public MyClassVisitor(int version, ClassWriter cw) {
        super(version, cw);
}


๋‹ค์‹œ ์†Œ์Šค์„ค๋ช…์œผ๋กœ ๋„˜์–ด์™€์„œ....

์ด๋ ‡๊ฒŒ cv๋ฅผ ์ƒ์„ฑ์„ ํ–ˆ์œผ๋ฉด ์ด์ œ ๋ณ€ํ˜•ํ•  ํด๋ž˜์Šค๋ฅผ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  cr์˜ accept()๋ฅผ ์ด์šฉํ•ด์„œ ๋ฐฉ๋ฌธ(?)ํ•˜๋ฉด์„œ ๋ณ€ํ™˜์„ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋ณ€ํ™˜์ด ๋œ ํด๋ž˜์Šค ์ •๋ณด๋Š” cw์— ๋“ค์–ด๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.


๋ญ”๊ฐ€ ๊ฐ„๋‹จํ•œ๊ฒƒ ๊ฐ™์œผ๋ฉด์„œ๋„ ์–ด์ง€๋Ÿฌ์šด๊ฒƒ ๊ฐ™์ฃ ??


์ž, ๊ทธ๋Ÿผ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ด์•ผ ํ•˜๊ฒ ์ฃ ..

์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“ค์—ˆ๋˜ MyClassVisitor์˜ visit() ๋ฉ”์†Œ๋“œ๋ฅผ ํ•œ๋ฒˆ ๋ณผ๊นŒ์š”?


@Override
    public void visit(int version, int access, String name,
            String signature, String superName, String[] interfaces) {
        name = "BCItest/Modified";
        super.visit(version, access, name, signature, superName, interfaces);
        System.out.println(name + " extends " + superName + " {");
}


์ž, ์œ„์—์„œ ์˜ค๋ Œ์ง€์ƒ‰์œผ๋กœ ํ‘œ์‹œ๋œ ๋ถ€๋ถ„์ด ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์ด๊ฒŒ ๋ญ˜ ํ• ๊ฑด์ง€ ์ง์ž‘์ด ๊ฐ€์‹œ๋‚˜์š”?

์ž ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์—์„œ ์ง€๋‚œ ์‹œ๊ฐ„์— ํ–ˆ๋˜ ์ƒ์„ฑ๋œ ํด๋ž˜์Šค ํ…Œ์ŠคํŠธํ•ด๋ณด๋Š” ์†Œ์Šค๋ฅผ ์กฐ๊ธˆ๋งŒ ์ˆ˜์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


// ๊ธฐ์กด ํ…Œ์ŠคํŠธ ์†Œ์Šค

Class c = new MyClassLoader().defineClass("BCItest.Comparable", writeClass());
System.out.println(c.getName());


// ์ˆ˜์ •ํ•œ ํ…Œ์ŠคํŠธ ์†Œ์Šค

Class c = new MyClassLoader().defineClass("BCItest.Comparable", transformClass());
System.out.println(c.getName());


๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„์€ ์˜ค๋ Œ์ง€์ƒ‰์œผ๋กœ ํ‘œ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•ด์„œ ์‹คํ–‰์„ ํ•˜๋ฉด ๋ญ๊ฐ€ ๋‚˜์˜ฌ๊นŒ์š”?


ClassLoader๋กœ ์ฝ์–ด์˜ค๋Š” ๊ณผ์ •์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.

java.lang.NoClassDefFoundError: BCItest/Comparable (wrong name: BCItest/Modified)


์ž....์šฐ๋ฆฌ๊ฐ€ ํด๋ž˜์Šค๋กœ๋”๋ฅผ ์ด์šฉํ•ด์„œ ๋กœ๋“œํ•˜๋ ค๊ณ ํ•œ ํด๋ž˜์Šค๋ช…์€ Comparable์ด์—ˆ๋Š”๋ฐ transformClass()์—์„œ ๋งŒ๋“ค์–ด์ง„ ํด๋ž˜์Šค๋Š” Modified์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด๋ฒˆ์—๋Š” BCItest.Modified๋ฅผ ์ฝ์–ด์˜ค๋„๋ก ํ•ด๋ด…์‹œ๋‹ค.


Class c = new MyClassLoader().defineClass("BCItest.Modified", transformClass());
 

์œ„ ์ฒ˜๋Ÿผ defineClass() ๋ฉ”์†Œ๋“œ์— ๋“ค์–ด๊ฐ€๋Š” ์ธ์ž๋ฅผ ๋ฐ”๊ฟ”์ฃผ์„ธ์š”.


์ด์ œ ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋ณด๋ฉด BCItest.Modified ๋ผ๊ณ  ์ถœ๋ ฅ์ด ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•˜์‹ค ์ˆ˜ ์žˆ์„๊ฒ๋‹ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์ง€๋‚œ๋ฒˆ์— ํ–ˆ๋˜ MyClassVisitor๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜์‹ ๋‹ค๋ฉด ํด๋ž˜์Šค ๊ตฌ์กฐ๊ฐ€ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ๋„ ๋ณด์‹ค ์ˆ˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.


์–ด๋ ต์ง€ ์•Š์ฃ ?


์ž ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ BCI์˜ ๊ธฐ๋ณธ๊ธฐ๋ฅผ ๋ชจ๋‘ ์ตํžŒ ์…ˆ์ž…๋‹ˆ๋‹ค.

ํด๋ž˜์Šค ์ฝ๊ธฐ, ์“ฐ๊ธฐ, ์ˆ˜์ • ๊นŒ์ง€ ํ–ˆ์œผ๋‹ˆ๊นŒ์š”.


ASM์„ ์ด์šฉํ•ด์„œ ํด๋ž˜์Šค๋ฅผ ์ฝ์–ด์˜ค๊ณ , ์ƒ์„ฑ ๋ฐ ๋ณ€ํ™˜ํ•˜๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ๊ณต๋ถ€๋ฅผ ํ•ด๋ณด์•˜๋Š”๋ฐ์š”


์ œ๊ฐ€ ์„ค๋ช…ํ•˜๋Š” ๋Šฅ๋ ฅ์ด ์•ฝํ•ด์„œ ์ฒซ์‹œ๊ฐ„๋ถ€ํ„ฐ ์ง€๊ธˆ๊นŒ์ง€ ํ•œ ๋‚ด์šฉ์„ ์†Œ์Šค๋ฅผ ์ฒจ๋ถ€ํ–ˆ์œผ๋‹ˆ ์†Œ์Šค๋ฅผ ๋ณด๋ฉด์„œ ์ฒœ์ฒœํžˆ ํ•œ๋ฒˆ ๊ณต๋ถ€ํ•ด๋ณด์„ธ์š” ^_^


๋‹ค์Œ์‹œ๊ฐ„์—๋Š” ์ˆ˜์ •ํ• ๋•Œ ์ตœ์ ํ™” ๊ด€๋ จ๋œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


๊ทธ๋Ÿผ ์—ฌ๋Ÿฌ๋ถ„ ์•ˆ๋…€~์—‰~









์ถœ์ฒ˜ : ASM 4 ๊ฐ€์ด๋“œ ๋ฌธ์„œ






-----------------writeClass()-----------------------

BCItest ํŒจํ‚ค์ง€๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์†Œ์Šค๊ฐ€ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.


private byte[] writeClass(){
        ClassWriter cw = new ClassWriter(0);
        cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT + Opcodes.ACC_INTERFACE,
                "BCItest/Comparable", null, "java/lang/Object",
                null);
        cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS", "I",
        null, new Integer(-1)).visitEnd();
        cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I",
        null, new Integer(0)).visitEnd();
        cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I",
        null, new Integer(1)).visitEnd();
        cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo",
        "(Ljava/lang/Object;)I", null, null).visitEnd();
        cw.visitEnd();

        return cw.toByteArray();
}