2016-12-14 1 views
0

Javassist 3.20.0.GA를 사용하여 빈 루프에 코드를 삽입하려고합니다.Javassist를 사용하여 빈 루프에 코드를 추가하려면 어떻게해야합니까?

내가 시도한 모든 것은 수정 된 클래스의 새 인스턴스를 만들려고 할 때 java.lang.VerifyError 오류로 계속 실행됩니다.

성공적으로 실행되지 않는 작은 프로그램으로 문제를 격리하려고 시도했습니다. 모든 것이 예상 일하고 같은 경우

import javassist.ClassPool; 
import javassist.CtClass; 
import javassist.CtMethod; 
import javassist.bytecode.Bytecode; 
import javassist.bytecode.CodeAttribute; 
import javassist.bytecode.CodeIterator; 
import javassist.bytecode.InstructionPrinter; 
import javassist.bytecode.MethodInfo; 
import javassist.compiler.Javac; 

public class EmptyLoopTest { 
    private void testEmptyLoopModification() throws Exception { 
     ClassPool cp = ClassPool.getDefault(); 
     CtClass cc = cp.get(EmptyLoopTest.class.getName() + "$EmptyLoopClass"); 
     CtMethod m = cc.getDeclaredMethod("emptyLoopMethod"); 
     printMethod("Before modifications", m); 

     MethodInfo methodInfo = m.getMethodInfo(); 
     CodeAttribute ca = methodInfo.getCodeAttribute(); 
     CodeIterator ci = ca.iterator(); 

     Javac jv = new Javac(cc); 
     jv.compileStmnt("System.out.println(\"injected into loop\");"); 

     Bytecode b = jv.getBytecode(); 
     adjustCodeAttributeIfNeeded(b, ca); 
     ci.insertAt(0, b.get()); 
     printMethod("After modifications", m); 
     Class c = cc.toClass(); 

     logInfo("Attempting to create instance of modified class"); 
     c.newInstance(); 
    } 
    private void adjustCodeAttributeIfNeeded(Bytecode b, CodeAttribute ca){ 
     int locals = b.getMaxLocals(); 
     int stack = b.getMaxStack(); 
     if(stack > ca.getMaxStack()) { 
      ca.setMaxStack(stack); 
     } 
     if(locals > ca.getMaxLocals()) { 
      ca.setMaxLocals(locals); 
     } 
    } 

    private void printMethod(String title, CtMethod m){ 
     logInfo(title); 
     InstructionPrinter instructionPrinter = new InstructionPrinter(System.out); 
     instructionPrinter.print(m); 
    } 
    private void logInfo(String message){ 
     System.out.println(""); 
     System.out.println("------" + message); 
    } 

    public class EmptyLoopClass { 
     public void emptyLoopMethod() { 
      for(;;){ 
      } 
     } 
    } 

    public static void main(String[] args) throws Exception { 
     // have errors written to sysout so all output from this program is in order 
     System.setErr(System.out); 
     new EmptyLoopTest().testEmptyLoopModification(); 
    } 
} 

내가이 프로그램을 실행하면 다음이 콘솔에 기록됩니다 ..

------Before modifications 
0: goto 0 

------After modifications 
0: getstatiC#32 = Field java.lang.System.out(Ljava/io/PrintStream;) 
3: ldC#34 = "injected into loop" 
5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V) 
8: goto 8 

------Attempting to create instance of modified class 
Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 8 
Exception Details: 
    Location: 
    EmptyLoopTest$EmptyLoopClass.emptyLoopMethod()V @8: goto 
    Reason: 
    Expected stackmap frame at this location. 
    Bytecode: 
    0x0000000: b200 2012 22b6 0028 a700 00    
    Stackmap Table: 
    same_frame(@0) 

    at java.lang.Class.getDeclaredConstructors0(Native Method) 
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671) 
    at java.lang.Class.getConstructor0(Class.java:3075) 
    at java.lang.Class.newInstance(Class.java:412) 
    at EmptyLoopTest.testEmptyLoopModification(EmptyLoopTest.java:35) 
    at EmptyLoopTest.main(EmptyLoopTest.java:68) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) 

Process finished with exit code 1 

, 나는 고토 0 대신 수정 후 고토 명령을 기대할 수 현재 보여지고있는 것, goto 8. javassist.bytecode.CodeIterator # insertAt를 호출했을 때 StackMapTable이 적절하게 조정되지 않은 것처럼 보입니다.

누구나 내가 잘못하고있는 것을 발견 할 수 있습니까?

감사합니다,

에릭

답변

0
  1. 당신은 그래서 0 매개 변수 생성자가 가정이 c.newInstance(); 같은 클래스를 생성 - 일하기를 정적 내부 클래스

    public static class EmptyLoopClass { 
    public void emptyLoopMethod() { 
        for(;;){ 
        } 
    } 
    
  2. 당신에게 코드를 수정 한 후 methodInfo.rebuildStackMap(cp);으로 전화해야합니다. JVM은 StackMap을 사용하여 클래스 파일의 유효성을 검사합니다.

  3. 어쨌든 당신이 바이트 코드

    3: ldC#34 = "injected into loop" 
        5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V) 
        8: goto 8 
    

    이 코드는 루프에 있지 함께 종료된다, 그것은 루프 전에이다. 실제 루프는 단일 명령어 8: goto 8이며 내부에 명령어를 삽입하려는 경우 루프 코드를 추가로 생성해야합니다.

관련 문제