2011-02-18 3 views
11

기존 클래스에서 (Java 바이트 코드 생성을 통해) 새 클래스를 생성해야합니다. 클래스의 메서드의 본문 (표현식)을 분석하겠습니다. 표현식은 내가 생성 할 코드를 결정할 것입니다.행 번호를 제어 할 때 어떤 바이트 코드 라이브러리가 필요합니까?

필자에게있어 새로운 클래스 (기본 Java 파일과 동일) 및 제어 라인 번호 (예외가 발생하면 stacktrace에 기본 Java 파일의 행 번호가 포함되어야 함)의 소스 파일을 설정하는 것이 importand입니다.

예 : 파일이 입니다. BaseClass.java입니다. 컴파일러는 BaseClass.class을 생성합니다. 이 클래스 파일을 분석하고 GeneratedClass.class에 대한 바이트 코드를 생성하고 싶습니다. c에 예외가 발생하면 stacktrace에 "BaseClass.java line 3"이 포함되어야합니다.

BaseClass.java 
1: class BaseClass { 
2: void method() { 
3:  call(); 
4: } 
5:} 

GeneratesClaas.class 
a: class GeneratedClass { 
b: void generatedMethod() { 
c:  generatedCall(); 
d: } 
e:} 

내 질문 :이 요구 사항을 지원하는 라이브러리가 있습니까? Javassist, ASM 또는 BCEL? 이 목적을 위해 무엇을 사용해야합니까? 이를 수행하는 방법이나 예제 코드가 도움이 될 것입니다.

편집 : 요점은 fullfiled 할 수 없기 때문에 사용하지 말아야 할 라이브러리를 알려주는 것입니다.

+0

대부분의 디 컴파일러는 원본 코드 줄을 주석으로 (주석으로) 인쇄 할 수 있습니다. 이것을 파싱하고 코드를 재정렬 할 수 있습니다. 발견 할 수있는 문제는 디 컴파일 된 코드가 정확히 동일하지 않기 때문에 동일한 순서가 아닐 수 있다는 것입니다. –

+0

디 컴파일러는 내가 뭘하고 있는지 알지 못한다. 새로운 바이트 코드를 생성하려면 런타임에이 정보가 필요합니다. – Arne

답변

5

asm을 사용하면 visitSourcevisitLineNumber 메서드를 사용하여 생성 된 클래스에이 디버깅 정보를 만들 수 있습니다.

편집 : 이것은 RuntimeException의 막 스택 트레이스 내의 행 번호를 표시 슬로우 메인 메소드를 포함하는 클래스를 생성하는 실행

import java.io.File; 
import org.objectweb.asm.Label; 
import org.objectweb.asm.MethodVisitor; 
import java.io.FileOutputStream; 
import java.io.IOException; 
import org.objectweb.asm.ClassWriter; 
import org.objectweb.asm.util.CheckClassAdapter; 
import static org.objectweb.asm.Opcodes.*; 

public class App { 
    public static void main(String[] args) throws IOException { 
     ClassWriter cw = new ClassWriter(0); 
     CheckClassAdapter ca = new CheckClassAdapter(cw); 
     ca.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "test/Test", null, "java/lang/Object", null); 
     ca.visitSource("this/file/does/not/exist.txt", null); // Not sure what the second parameter does 
     MethodVisitor mv = ca.visitMethod(ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); 

     mv.visitCode(); 
     Label label = new Label(); 
     mv.visitLabel(label); 
     mv.visitLineNumber(123, label); 
     mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); 
     mv.visitInsn(DUP); 
     mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "()V"); 
     mv.visitInsn(ATHROW); 
     mv.visitInsn(RETURN); 
     mv.visitMaxs(2, 1); 
     mv.visitEnd(); 

     ca.visitEnd(); 

     File target = new File("target/classes/test/"); 
     target.mkdirs(); 
     FileOutputStream out = new FileOutputStream(new File(target, "Test.class")); 
     out.write(cw.toByteArray()); 
     out.close(); 
    } 
} 

: 여기서 최소한의 예이다. 먼저 디스어셈블러이의 무엇이 볼 수 있습니다 :

$ javap -classpath target/classes/ -c -l test.Test 
Compiled from "this.file.does.not.exist.txt" 
public class test.Test extends java.lang.Object{ 
public static void main(java.lang.String[]); 
    Code: 
    0: new #9; //class java/lang/RuntimeException 
    3: dup 
    4: invokespecial #13; //Method java/lang/RuntimeException."<init>":()V 
    7: athrow 
    8: return 

    LineNumberTable: 
    line 123: 0 
} 

그래서이 클래스 : 존재하지 않는 txt 파일에서 컴파일되었으며, LineNumberTable가 시작되는 바이트 코드가 0이 가상의 123 라인에 해당 오프셋 말한다 파일. 이 파일을 실행하면이 파일과 줄 번호가 스택 추적에 포함되어 있음을 알 수 있습니다.

$ java -cp target/classes/ test.Test 
Exception in thread "main" java.lang.RuntimeException 
     at test.Test.main(this/file/does/not/exist.txt:123) 
2

BCEL에는 클래스 파일의 행 번호 정보를 나타내는 클래스 LineNumber 및 LineNumberTable이 있습니다. 이 코드를 통해 코드를 생성하는 일부 클래스의 테이블을 만들고 설정할 수 있습니다. 아마도 정보는 클래스 파일에 기록됩니다.

관련 문제