2016-10-21 5 views
0

각 메소드 본문을 기본 throw 새 exception() 행으로 대체하는 스크립트를 작성하려고합니다. 나는 ASM을 배울 수있는 시작 단계에 있으므로 어디서나 볼 수있는 포인터가 있으면 감사하겠습니다.ASM을 통한 전체 메소드 대체

package methodtester; 

import java.io.DataOutputStream; 
import java.io.File; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import java.io.InputStream; 
import org.objectweb.asm.ClassReader; 
import org.objectweb.asm.ClassVisitor; 
import org.objectweb.asm.ClassWriter; 
import org.objectweb.asm.MethodVisitor; 
import static org.objectweb.asm.Opcodes.ACC_INTERFACE; 
import static org.objectweb.asm.Opcodes.ASM4; 
import static org.objectweb.asm.Opcodes.ATHROW; 
import static org.objectweb.asm.Opcodes.DUP; 
import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 
import static org.objectweb.asm.Opcodes.NEW; 

public class MethodTransformer { 
    public static void main(String[] args) throws IOException { 
     InputStream in = MethodTester.class.getResourceAsStream("/methodtester/testingMethod.class"); 

     ClassReader classReader = new ClassReader(in); 

     ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 

     ExceptionThrower exceptionThrower = new ExceptionThrower(classWriter); 

     classReader.accept(exceptionThrower, 0); 

     File outputDir = new File("build/classes/methodtester/"); 

     outputDir.mkdirs(); 

     DataOutputStream dataOutputStream = 
      new DataOutputStream(
       new FileOutputStream(
        new File(outputDir,"testingMethod-postASM.class"))); 

     dataOutputStream.write(classWriter.toByteArray()); 
    } 

    public static class ExceptionThrower extends ClassVisitor { 
     private String _className; 
     private boolean _isInterface; 

     public ExceptionThrower(ClassVisitor classVisitor) { 
      super(ASM4, classVisitor); 
     } 

     @Override 
     public void visit(
      int version, int access, String name, String signature, 
      String superName, String[] interfaces) { 

      cv.visit(version, access, name, signature, superName, interfaces); 

      _className = name; 

      _isInterface = (access & ACC_INTERFACE) != 0; 
     } 

     @Override 
     public MethodVisitor visitMethod(
      int access, String name, String desc, String signature, 
      String[] exceptions) { 

      MethodVisitor mv = cv.visitMethod(access, name, desc, signature, 
       exceptions); 

      if (!_isInterface && mv != null && !name.equals("<init>")) { 
       ExceptionThrowerMethod exceptionThrowerMethod = 
        new ExceptionThrowerMethod(mv); 

       return exceptionThrowerMethod; 
      } 

      return mv; 
     } 

     public static class ExceptionThrowerMethod extends MethodVisitor { 
      public ExceptionThrowerMethod(MethodVisitor methodVisitor) { 
       super(ASM4, methodVisitor); 
      } 

      @Override 
      public void visitCode() { 
       mv.visitCode(); 
       mv.visitTypeInsn(NEW, "java/io/IOException"); 
       mv.visitInsn(DUP); 
       mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V", false); 
       mv.visitInsn(ATHROW); 
       mv.visitMaxs(2, 0); 
       mv.visitEnd(); 
      } 
     } 
    } 
} 

지금까지 내가 방법의 시작 부분에 던져 새로운 IOException이() 명령을 삽입 할 수 있어요,하지만 난 주어진하고 같이이 런타임에 실행되지 않습니다 : 내가 지금까지했던 어떤

이 오류 :

Exception in thread "main" java.lang.ClassFormatError: Invalid start_pc 8 in LocalVariableTable in class file methodtester/testingMethod 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467) 
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
    at methodtester.MethodTester.main(MethodTester.java:27) 

은 javap 보면 내가 얻을 -cv :

public void testing() throws java.io.IOException; 
    descriptor:()V 
    flags: ACC_PUBLIC 
    Code: 
     stack=2, locals=1, args_size=1 
     0: new   #14     // class java/io/IOException 
     3: dup 
     4: invokespecial #15     // Method java/io/IOException."<init>":()V 
     7: athrow 
     LocalVariableTable: 
     Start Length Slot Name Signature 
      8  0  0 this Lmethodtester/testingMethod; 
     LineNumberTable: 
     line 24: 8 
     line 25: 8 
    Exceptions: 
     throws java.io.IOException 
} 

는 CLA를 보면 ASMifier와 SES가 나는 방법임을 알 : 나는 길이 0 0

감사를 시작하는 얻을 수있는 LocalVariableTable을 변경해야처럼

mv = cw.visitMethod(ACC_PUBLIC, "testing", "()V", null, null); 
mv.visitCode(); 
mv.visitTypeInsn(NEW, "java/io/IOException"); 
mv.visitInsn(DUP); 
mv.visitMethodInsn(INVOKESPECIAL, "java/io/IOException", "<init>", "()V", false); 
mv.visitInsn(ATHROW); 
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 
mv.visitLdcInsn("testing method"); 
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 
mv.visitInsn(RETURN); 
mv.visitMaxs(2, 1); 
mv.visitEnd(); 
} 

가 보이는!

편집 : COMPUTE_FRAMES를 사용하여 업데이트 및 사용자 정의 MethodVisitor의 슈퍼 생성자에 새로운 에러 코드

+0

바꾸려는 메서드에 인수가 없습니까? – saka1029

+0

@ saka1029 그것은 가능 하겠지만, 지금은 간단한 방법으로 논증없이 테스트 해보고 먼저 작동하도록하고 싶습니다. – tom

+0

ClassWriter 옵션'ClassWriter.COMPUTE_MAXS'를'ClassWriter.COMPUTE_FRAMES'으로 변경하십시오. [ClassWriter options] (http://download.forge.objectweb.org/asm/asm4-guide.pdf#page=50) – saka1029

답변

1

당신은 원래 MethodVisitor, 당신은 ClassWriter로부터 전달되는 업데이트. 즉, 모든 visit… 호출은 사용자가 재정의하지 않고 원래 코드 전체를 복제하여 해당 MethodVisitor에 위임됩니다.

은 물론, 첫째, 둘째, 유효한 코드를 생성 도움이되지 않습니다 visitMaxsvisitEnd의 당신의 호출 후 작가 visit… 전화 모순 수신, 원본 코드를 복제하고 싶지 않아요.

당신이 완전히 방법을 교체 할 때 대상 작가의 MethodVisitor 기본 클래스에 있지만, 자신의 코드 생성으로 만 사용 나타내지한다 : 방법 그들은함으로써

public static class ExceptionThrowerMethod extends MethodVisitor { 
    private final MethodVisitor target; 

    public ExceptionThrowerMethod(MethodVisitor methodVisitor) { 
     super(ASM4, null); 
     this.target=methodVisitor; 
    } 

    @Override 
    public void visitCode() { 
     target.visitCode(); 
     target.visitTypeInsn(NEW, "java/io/IOException"); 
     target.visitInsn(DUP); 
     target.visitMethodInsn(INVOKESPECIAL,"java/io/IOException","<init>","()V",false); 
     target.visitInsn(ATHROW); 
     target.visitMaxs(2, 0); 
     target.visitEnd(); 
    } 
} 

, 당신 Class이 리소스를 로컬에서 해결하도록함으로써 리소스 액세스를 단순화 할 수 있습니다. testingMethod.class.getResourceAsStream("testingMethod.class").

+0

감사합니다. 모든 방문 * 메소드의 대체 버전에 부울 검사를 추가하여 작동하도록했습니다.이 방법은 엉망이었고이 솔루션은 훨씬 더 깨끗합니다! – tom