Clojure Hacking Guide

    xiaoxiao2024-04-20  5

    这题目起的哗众取宠,其实只是想介绍下怎么查看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方法。

    文章转自庄周梦蝶  ,原文发布时间2010-07-11

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)