论坛首页 综合技术论坛

Clojure Hacking Guide

浏览 3684 次
精华帖 (11) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-07-11   最后修改:2010-07-11
FP

   这题目起的哗众取宠,其实只是想介绍下怎么查看Clojure动态生成的字节码,这对分析Clojure的内部实现很重要。

    第一步,下载最新的Clojure 1.1.0源码并解压,并导入到你喜欢的IDE。

    其次,下载asm 3.0的源码并解压。

    第三,删除Clojure 1.1.0源码中的clojure.asm包。clojure并不是引用asm的jar包,而是将asm的源码合并到clojure中,并且删除一些只会 在调试阶段用到的package和class,保留使用asm的最小源码集合,这可能是处于防止asm不同版本的jar包冲突以及缩小clojure大小 的考虑。

    第四,将asm 3.0源码拷入clojure的源码中,并将包org.objectweb.asm包括子包整体重名名为clojure.asm。

    第五步,修改Clojure源码,加入TraceClassVisitor的适配器,用于跟踪字节码生成,这需要修改 clojure.lang.Compiler类中的两个compile方法,找到类似

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
// ClassWriter cw = new ClassWriter(0);
ClassVisitor cv = cw;


这样的代码,将cv修改为TraceClassVisitor:

 ClassVisitor cv = new TraceClassVisitor(new CheckClassAdapter(cw), new PrintWriter(System.out));


    TraceClassVisitor的第二个参数指定将跟踪到的字节码输出到哪里,这里简单地输出到标准输出方便查看。

    第六步,接下来可以尝试下我们修改过的clojure怎么动态生成字节码,启动REPL,

java clojure.main


启动阶段就会输出一些字节码信息,主要预先加载的一些标准库函数,如clojure.core中的函数等,REPL启动完毕,随便输入一个表达式都将看到 生成的字节码

user=> (+ 1 2)


输出类似

compile 1
// class version 49.0 (49)
// access flags 33
public class user$eval__4346 extends clojure/lang/AFunction  {

  
// compiled from: NO_SOURCE_FILE
  
// debug info: SMAP
eval__4346.java
Clojure
*S Clojure
*F
+ 1 NO_SOURCE_FILE
NO_SOURCE_PATH
*L
0#1,1:0
*E

  
// access flags 25
  public final static Lclojure/lang/Var; const__0

  
// access flags 25
  public final static Ljava/lang/Object; const__1

  
// access flags 25
  public final static Ljava/lang/Object; const__2

  
// access flags 9
  public static <clinit>()V
   L0
    LINENUMBER 
2 L0
    LDC 
"clojure.core"
    LDC 
"+"
    INVOKESTATIC clojure
/lang/RT.var (Ljava/lang/String;Ljava/lang/String;)Lclojure/lang/Var;
    CHECKCAST clojure
/lang/Var
    PUTSTATIC user$eval__4346.const__0 : Lclojure
/lang/Var;
    ICONST_1
    INVOKESTATIC java
/lang/Integer.valueOf (I)Ljava/lang/Integer;
    PUTSTATIC user$eval__4346.const__1 : Ljava
/lang/Object;
    ICONST_2
    INVOKESTATIC java
/lang/Integer.valueOf (I)Ljava/lang/Integer;
    PUTSTATIC user$eval__4346.const__2 : Ljava
/lang/Object;
    RETURN
    MAXSTACK 
= 0
    MAXLOCALS 
= 0

  
// access flags 1
  public <init>()V
   L0
    LINENUMBER 
2 L0
   L1
    ALOAD 
0
    INVOKESPECIAL clojure
/lang/AFunction.<init> ()V
   L2
    RETURN
    MAXSTACK 
= 0
    MAXLOCALS 
= 0

  
// access flags 1
  public invoke()Ljava/lang/Object; throws java/lang/Exception 
   L0
    LINENUMBER 
2 L0
   L1
    LINENUMBER 
2 L1
    GETSTATIC user$eval__4346.const__1 : Ljava/lang/Object;
    GETSTATIC user$eval__4346.const__2 : Ljava
/lang/Object;
    INVOKESTATIC clojure
/lang/Numbers.add (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Number;

   L2
    LOCALVARIABLE 
this Ljava/lang/Object; L0 L2 0
    ARETURN
    MAXSTACK 
= 0
    MAXLOCALS 
= 0
}
3


3就是表达式的结果。可以看到,一个表达式生成了一个class。其中<clinit>是静态初始化块,主要是初始化表达式中的字面常 量;<init>不用说,默认的构造函数;invoke是核心方法,表达式生成的class,new一个实例后调用的就是invoke方法, 执行实际的代码,高亮部分加载了两个常量,并执行Number.add方法。

最后,请Happy hacking!。

   发表时间:2010-07-12  
建议加一个副标题 ---- 怎么查看Clojure动态生成的字节码
0 请登录后投票
   发表时间:2010-07-18  
感觉很不错,研究一下。有没有对于asm的介绍啊
0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics