2014-09-18

【ASM】Javaバイトコードを生成するライブラリ『ASM』が凄い

Categories: Java JVM ASM
no_image.jpg

[ PR ]


ASMというフレームワークをご存知でしょうか。

実は、以下の様な言語・フレームワークに利用され、JRubyJythonEclipseなども利用しているのです。

  • AspectWerkz
  • AspectJ
  • BeanShell
  • CGLIB
  • dynaop
  • Clojure
  • Groovy
  • Jamaica
  • JRuby
  • Jython
  • NetLogo
  • Open Quark
  • WebSphere sMash
  • Coroutines
  • fun4j
  • EclipseME
  • MicroEmulator Sun Java ME emulation for Java SE |
  • Fractal
  • Dr. Garbage
  • Proactive
  • Retrotranslator
  • RIFE
  • R-OSGi
  • Terracotta
  • Substance L&F
  • WindowBuilder
  • Javeleon
  • EasyBeans
  • Ebean
  • JDBCPersistence
  • JPOX
  • OpenEJB
  • Oracle BerkleyDB
  • Oracle TopLink
  • Speedo
  • BEA WebLogic
  • BTrace
  • Byteman
  • JiP
  • ByCounter
  • Limpid Log
  • Agitar
  • Cobertura
  • Eclipse
  • JCarder
  • SemmleCode
  • Structure101
  • SonarJ
  • Sun Microsystems, Inc.
  • TamiFlex

そこで、今回はASMの触りだけを紹介します。

Hello, World

Jarの準備

まず、asm.jarをMavenCentralからダウンロードしてください。(Ivy, Mavenでも可)

Maven Repository: org.ow2.asm » asm

元になるコード

今回は、以下のコードに当たるクラスをASMで生成してみます。

public class HelloFromASM {
    public HelloFromASM(){
        super();
    }

    public static void main(String[] args){
        System.out.println("Hello, ASM!");
    }
}

ソースコード

ASMHelloWorld.java:

import java.io.DataOutputStream;
import java.io.FileOutputStream;

import org.objectweb.asm.*;

public class ASMHelloWorld implements Opcodes {
  public static byte[] dump() throws Exception {
    ClassWriter cw = new ClassWriter(0);
    MethodVisitor mv;
    ClassVisitor cv;
    cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "HelloFromAsm", null, "java/lang/Object", null);

    mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
    mv.visitCode();
    mv.visitVarInsn(ALOAD, 0);
    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
    mv.visitInsn(RETURN);
    mv.visitMaxs(1,1);
    mv.visitEnd();

    mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
    mv.visitCode();
    mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
    mv.visitLdcInsn("Hello, ASM!");
    mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
    mv.visitInsn(RETURN);
    mv.visitMaxs(2,1);
    mv.visitEnd();

    cw.visitEnd();

    return cw.toByteArray();
  }

  public static void main(String[] args) throws Exception {
    DataOutputStream out = new DataOutputStream(new FileOutputStream("HelloFromAsm.class"));
    out.write(dump());
    out.flush();
    out.close();
  }
}

コンパイル・コード生成

このソースをコンパイルします。

javac -cp asm.jar:. ASMHelloWorld.java

ここからマジックが始まります。

java -cp asm.jar:. ASMHelloWorld

すると、なんと HelloFromASM.class が生成されています!

試しにHelloFromASMを実行してみましょう。

java HelloFromASM

# Hello, ASM!

何が起こったのか

(詳しい解説は、Jasminで同じコードの解説をしているので、そちらを参照してください。)

Javac → Java と実行したのに、classファイルが生成されたのが不思議ですが、実は、ASMが動的にクラスファイルを生成しているのです。

そのため、JavacではなくJavaで実行した際にクラスファイルが出来るのです。

不思議ですね。

>コラム:高次概念「メタ」

人間の頭は、メタな概念に関してはついていけないので、理解出来ないのは当然なのです。

要するに、ASMはメタプログラミングを行っていることが分かります。

ASMのその他の機能

  • Javaバイトコードの解析
  • 既存Javaバイトコードの変更
  • Javaバイトコードの動的生成
  • クラスファイルの動的生成

ASM - Home Page

ライセンス

BSDライセンス

デコンパイリングJava ―逆解析技術とコードの難読化 (Art Of Reversing)
Godfrey Nolan
オライリージャパン
売り上げランキング: 348,592

コメントはTwitterアカウントにお願いします。

RECENT POSTS


[ PR ]

.