Whiteship's Note


[ASM] 클래스 - 만들기

Good Tools : 2010.04.13 17:23


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

ClassWriter만 사용해서 클래스를 만들수 있다.

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

한 줄씩 살펴보자.

ClassWriter cw = new ClassWriter(0);
- ClassWriter 객체를 생성한다. 인자값 0에 대해서는 다음에 설명한단다. 다음에 읽지 뭐... @_@;

cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "pkg/Comparable", null, "java/lang/Object", new String[]{"pkg/Mesurable"});
- 저 상수들은 import static org.objectweb.asm.Opcodes.*; 이 안에 담겨있다.
- 첫번째 인자(V1_5)는 자바 클래스 버전이다. 자바 1.5 버전으로 컴파일한 클래스를 만들게 될 것이다.
- 두번째 인자(ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE)는 public interface에 해당한다. 객체를 생성할 수 없으니 abstract를 선택한다.(이부분은 좀 애매함. INTERFACE인데 ABSTRACT가 꼭 들어가야 하나;;)
- 세번째 인자("pkg/Comparable")는 클래스 이름인데 풀패키지 경로로 설정해 줘야 한다. 그렇다고 타입명(Type descriptor)은 아니고 internal name으로 표기해준다.
- 네번째 인자(null)는 제네릭이다.
- 다섯번째 인자("java/lang/Object")는 상위 클래스이다. 이 때는 internal name으로 표기해준다.
- 마지막(new String[]{"pkg/Mesurable"})은 구현할 인터페이스 목록이다. 여기서도 클래스 이름을 internal name으로 표기한다.
- visit 메서드는 리턴 타입이 void다.

cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I"
null, new Integer(-1)).visitEnd();
- 첫번째 인자는 패스.
- 두번째 인자는 필드 이름
- 세번째 인자는 타입 (여기선 int)
- 네번째 인자는 Generic
- 다섯번째 인자는 값 (여기서는 final static 값이니까 설정했지. 보통은 null)
- visitFiled 메서드의 리턴 타입은 FieldVisitor 타입 객체다. 이 뒤에 반환받은 객체로 할 일이 없으니 visitEnd() 호출한다.

cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo"
"(Ljava/lang/Object;)I", null, null).visitEnd();
- 첫번째 인자는 패스
- 두번째 인자는 메서드 이름
- 세번째 인자는 메서드 시그너처 (Object를 인자로 받고 int 타입을 반환 한다.)
- 네번째는 Generic
- 다섯번째는 해당 메서드에서 던지는 예외 타입 목록
- visitMethod 메서드의 리턴 타입은 MethodVisitor 타입 객체다. 이 뒤에 반환받은 객체로 할 일이 없으니 visitEnd() 호출한다.

cw.visitEnd();
- ClassWriter로 할 일 다 했으니 visitEnd() 호출한다.

byte[] b = cw.toByteArray();
- toByteArray()를 호출하여 생성된 클래스를 바이트 배열 형태로 얻을 수 있다. 이 녀석을 ClassLaoder의 defineClass에 넘겨주면 Class 타입을 받아서 사용할 수 있다.

저작자 표시
신고
top


[ASM] 개요

Good Tools : 2010.04.13 15:45


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

- 프로그램 분석: 파싱에서 부터 애플리케이션의 잠재적인 버그를 찾거나, 사용하지 않는 코드나 역공학에 사용할 수 있다.
- 프로그램 생성: 일반 컴파일러나 Just in Time 컴파일러 같이 분산 프로그래밍에서 사용할 스텁, 스켈레톤 컴파일러가 사용할 프로그램을 작성한다.
- 프로그램 변경: 프로그램을 최적화 하거나 AOP같이 성능 모니터링 이나 디버깅용 코드를 추가한다.

자바 소스 코드보다 컴파일된 자바 클래스를 사용할 때 얻을 수 있는 장점
- 소스코드가 필요 없다
- JVM에 로딩 되기 전에 분석, 생성, 변경 할 수 있다.(소스 코드도 가능하지만 느리고 완전한 컴파일러가 필요하다)
- 스텁 컴파일러나 Aspectj 위버 사용이 사용자로부터 감춰진다.

ASM도 그런 툴 중 하나로 컴파일 된 클래스를 사용하도록 설계되었다.
최대한 빠르고 작게 설계했다.
- 빨라야 런타임에 애플리케이션이 느려지지 않으니까
- 작아야 메모리 소비가 적을테니까

ASM 주요 장점
- 간단하다. 잘 설계되어 있고 모듈화된 API가 있어서 사용하기 쉽다. (근데 나에겐 왜케 어려운건지.. OTL)
- 문서화가 잘 되어 있고 Eclipse 플러그인도 가지고 있다. (홈피에 있는 문서 중에서는 이것밖에 볼만한게 없던데..)
- 자바 최신 버전(Java 6)을 지원한다.
- 작고 빠르고 견고하다.
- 다수의 사용자 커뮤니티가 있으니 새로운 사용자를 도울 것이다.
- 오픈 소스니까 맘대로 쓸 수 있다.

ASM 목적
- 바이트 배열로 표현되는 컴파일 된 자바 클래스를 생성, 변형, 분석 한다.
- 클래스 로딩 프로세스는 범위에 속하지 않는다.

두 종류 API를 제공한다.
- core API: 이벤트 기반 표현체
- tree API: 객체 기반 표현체

이벤트 기반 모델
- 연속적인 이벤트로 클래스를 표현한다.
- 각 이벤트는 클래스의 특정 구성 요소를 나타낸다.
- 이벤트 기반 API는 사용 가능한 이벤트, 반드시 따라야 하는 순서, 파싱할 엘리먼트 당 하나의 이벤트를 생성하는 클래스 파서, 연속적인 이벤트에서 컴파일된 클래스를 생성하는 클래스 작성기를 제공한다.

객체 기반 모델
- 객체 트리로 클래스를 표현한다.
- 각 객체가 클래스 구성 요소를 나타낸다.
- 각 객체는 자신의 하위 구성 요소에 대한 객체들을 나타내는 레퍼런스를 가지고 있다.
- 객체 기반 API는 연속적인 이벤트 모델을 객체 트리 모델로 변경하는 하거나 그 반대로 변경하는 기능을 제공한다.
- 객체 기반 API는 이벤트 기반 API를 근간으로 만들어졌다.

이벤트 기반 API
- 객체 기반 API 보다 빠르고 메모리 소비가 적다.
- 클래스 변형이 객체 기반 모델 보다 어렵다. 한번에 한 엘리먼트만 조작하니까.

ASM 아키텍처
- 이벤트 기반 API는 이벤트 공급자(클래스 파서), 이벤트 소비자(클래스 작성기), 미리 정의해둔 이벤트 필터로 구성되어 있으며 사용자 정의 생성자, 소비자, 필터도 추가할 수 있다.

이벤트 기반 API 사용법
- 이벤트 공급자, 필터, 소비자 컴포넌트를 구성한다. 
- 이벤트 공급자가 일을 시작하도록 지시한다.

구조
- asm.jar: org.objectweb.asm 와 org.objectweb.asm.signature패키지가 들어있고, 이벤트 기반 API를 정의하고 클래스 파서, 클래스 작성기를 제공한다.
- asm-util.jar: org.objectweb.asm.util 패키지는 core API를 기반으로 다양한 툴을 제공한다. (너무 대강이네;;)
- asm-commons.jar: org.objectweb.asm.commons 패키지에서 미리 만들어둔 클래스 변경기(transformer)를 제공한다.
- asm-tree.jar: org.objectweb.asm.tree 패키지는 객체 기반 API를 제공한다. 이벤트 기반 모델과 객체 기반 모델을 변환할 수 있다.
- asm-analysis.jar: org.objectweb.asm.tree.analysis 패키지는 tree API를 기반으로 클래스 분석 프레임워크와 미리 만들어 둔 클래스 분선기를 제공한다.


저작자 표시
신고
top

TAG ASM

[ASM] 클래스 - 파싱하기

Good Tools : 2010.04.01 11:16


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

ClassVisitor 인터페이스를 구현하여 간단하게 파싱하는 클래스 작성 가능.

public class ClassPrinter implements ClassVisitor {

    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        System.out.println(name + " extends " + superName + " {");
    }

    public void visitSource(String s, String s1) {
    }

    public void visitOuterClass(String s, String s1, String s2) {
    }

    public AnnotationVisitor visitAnnotation(String s, boolean b) {
        return null;
    }

    public void visitAttribute(Attribute attribute) {
    }

    public void visitInnerClass(String s, String s1, String s2, int i) {
    }

    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
        System.out.println(" " + desc + " " + name);
        return null;
    }

    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        System.out.println(" " + name + desc);
        return null;
    }

    public void visitEnd() {
    }
}

이 클래스 사용하기

    public void asm() throws IOException {
        ClassPrinter cp = new ClassPrinter();
        ClassReader cr = new ClassReader("java.lang.Runnable");
        cr.accept(cp, true);
    }

ClassReader 인스턴스를 만들 때 어떤 클래스를 파싱할지 지정한다.
accept를 호출하면 전달받은 ClassVisitor(여기서는 그 구현체 ClassPrinter)의 메서드를 호출하여 파싱할 클래스 정보를 파싱한다.(꼬인다 꼬여;)

java/lang/Runnable extends java/lang/Object {
 run()V

결과는 위와 같음.

ClassReader 객체를 만드는 방법은 여러 가지가 있다.


ClassLaoder의 getResourceAsStream 메서드를 이용하여 가져온 InputStream으로 만들 수도 있다.

cl.getResourceAsStream(classname.replace(’.’, ’/’) + ".class");

하지만 복잡하다. 너무 저수준 API 아닌가... @_@ 이렇게까지 쓰고 싶진 않다.
저작자 표시
신고
top


[ASM] 클래스 - 인터페이스와 구성요소

Good Tools : 2010.04.01 10:23


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

컴파일된 클래스 생성 또는 변경할 때 사용하는 핵심 API는 ClassVisitor 인터페이스.


이 API는 다음 순서대로 사용해야 함.

1. visit 호출.
2. visitSource 최대 한번 호출 가능.
3. visitOuterClass 최대 한번 호출 가능.
4. visitAnnotation과 visitAttribute 원하는 만큼 호출 가능.
5. visitInnerClass, visitField, visitMethod 원하는 만큼 호출 가능.
6. visitEnd 호출.

주요 구성요소
- ClassReader: 바이트 배열로 받은 컴파일된 클래스를 파싱하고 accept 메서드의 인자로 전달된 ClassVisitor 객체에 있는 visitXxx 메서드를 호출한다. 이벤트 공급자.
- CLassWriter: ClassVisitor 인터페이스 구현체로, 직접 바이트 형식으로 컴파일된 클래스를 만든다. toyteArray 메서드로 컴파일된 클래스를 담고 있는 바이트 배열을 가져올 수 있다. 이벤트 소비자.
- ClassAdapter: ClassVisitor 인터페이스 구현체로, 이 객체에 대한 모든 메서드 호출을 다른 ClassVisitor 인스턴스에 위임한다. 이벤트 필터.


저작자 표시
신고
top

TAG ASM, class

[ASM] 클래스 - 구조

Good Tools : 2010.03.31 17:32


참조: http://download.fr.forge.objectweb.org/asm/asm-guide.pdf

컴파일 된 클래스 구성요소
- 클래스의 접근자, 이름, 상위 클래스, 인터페이스, 애노테이션
- 클래스에 선언한 필드당 한 섹션. 각 섹션에는 필드의 접근자, 이름, 타입, 애노테이션
- 클래스의 메서드와 생성자 당 한 섹션. 각 섹션에는 접근자, 이름, 반환 타입, 매개변수 타입, 애노테이션, 컴파일된(바이트) 코드

소스와 컴파일된 클래스와 차이
- 컴파일된 클래스는 오직 한 클래스에 대한 정보만 나타내지만 소스는 여러 클래스 가질 수 있다.
- 컴파일된 클래스는 주석을 가지고 있지 않다. 하지만 애노테이션에 부가 정보를 담을 수 있다. 그 전에는 클래스, 메서드, 필드, 코드의 attibutes 속성에 부가 정보를 전달했지만 이젠 별로 쓸 일 없다.
- 컴파일된 클래스는 package나 import 섹션이 없다. 모든 타입 이름이 전체 경로를 가지고 있다.
- 컴파일된 클래스는 constatnt pool 섹션을 가지고 있다.

internal name
- 클래스 또는 인터페이스 타입의 이름
- String -> java/lang/String

Type descriptor
- 자바 타입 서술자

boolean->  Z
char-> C
byte-> B
short-> S
int-> I
float-> F
long-> J
double-> D
Object-> Ljava/lang/Object;
int[]-> [I
Object[][]-> [[Ljava/lang/Object;

Method descriptor
- 메서드 서술자

void m(int I, float F) -> (IF)V
int m(Object o) -> (Ljava/lang/Object;)I
int[] m(int i, String s) -> (ILjava/lang/String;)[I
Object m(int[] i) -> ([I)Ljava/lang/Object;








저작자 표시
신고
top

TAG ASM






티스토리 툴바