A Clojure library designed to let you inspect bytecode of functions and things.
The Most Recent Release is available on clojars
With Leiningen:
[nodisassemble "0.1.3"]
HOWEVER, don't use it this way, let lein-nodissassemble's project middleware inject it for you.
{:plugins [[lein-nodisassemble "0.1.3"]]}
no.disassemble is the runtime library created to negotiate with the agent ClassTransformer that stores class bytes globally.
In order to use no.disassemble, add lein-nodisassemble to your :plugins, which will initialize the agent transformer and make bytecode available.
WARNING: there is no cleanup of bytecode yet, so evaling a lot would surely exhaust the heap.
user=> (require 'no.disassemble)
nil
user=> (in-ns 'no.disassemble)
#<Namespace no.disassemble>
no.disassemble=> (disassemble (fn []))
"// Compiled from NO_SOURCE_FILE (version 1.5 : 49.0, super bit)\npublic final class no.disassemble$eval1170$fn__1171 extends clojure.lang.AFunction {\n \n // Method descriptor #7 ()V\n // Stack: 0, Locals: 0\n public static {};\n 0 return\n Line numbers:\n [pc: 0, line: 1]\n \n // Method descriptor #7 ()V\n // Stack: 1, Locals: 1\n public disassemble$eval1170$fn__1171();\n 0 aload_0\n 1 invokespecial clojure.lang.AFunction() [10]\n 4 return\n Line numbers:\n [pc: 0, line: 1]\n \n // Method descriptor #12 ()Ljava/lang/Object;\n // Stack: 1, Locals: 1\n public java.lang.Object invoke();\n 0 aconst_null\n 1 areturn\n Line numbers:\n [pc: 0, line: 1]\n Local variable table:\n [pc: 0, pc: 1] local: this index: 0 type: java.lang.Object\n\n}"
no.disassemble=> (println (disassemble (fn [])))
// Compiled from NO_SOURCE_FILE (version 1.5 : 49.0, super bit)
public final class no.disassemble$eval1174$fn__1175 extends clojure.lang.AFunction {
// Method descriptor #7 ()V
// Stack: 0, Locals: 0
public static {};
0 return
Line numbers:
[pc: 0, line: 1]
// Method descriptor #7 ()V
// Stack: 1, Locals: 1
public disassemble$eval1174$fn__1175();
0 aload_0
1 invokespecial clojure.lang.AFunction() [10]
4 return
Line numbers:
[pc: 0, line: 1]
// Method descriptor #12 ()Ljava/lang/Object;
// Stack: 1, Locals: 1
public java.lang.Object invoke();
0 aconst_null
1 areturn
Line numbers:
[pc: 0, line: 1]
Local variable table:
[pc: 0, pc: 1] local: this index: 0 type: java.lang.Object
}
nil
no.disassemble=>
You can also manipulate the disassembly as a Clojure data structure:
user=> (-> (fn []) disassemble-data pprint)
{:minor-version 0,
:superclass-name clojure/lang/AFunction,
:class? true,
:major-version 49,
:name user$eval1321$fn__1322,
:interface-names [],
:methods
({:access-flags #{:public :static},
:name <clinit>,
:clinit? true,
:deprecated? false,
:code
{:max-locals 0,
:max-stack 0,
:line-numbers {0 1},
:local-variables nil,
:exception-table (),
:raw-bytecode [-79],
:bytecode [[:return 0]],
:code-length 1},
:descriptor "()V",
:constructor? false,
:exception nil,
:synthetic? false}
{:access-flags #{:public},
:name <init>,
:clinit? false,
:deprecated? false,
:code
{:max-locals 1,
:max-stack 1,
:line-numbers {0 1},
:local-variables nil,
:exception-table (),
:raw-bytecode [42, -73, 0, 10, -79],
:bytecode
[[:aload_0 0]
[:invokespecial
1
10
{:kind :methodref,
:class-name "clojure/lang/AFunction",
:method-name "<init>",
:method-descriptor "()V"}]
[:return 4]],
:code-length 5},
:descriptor "()V",
:constructor? true,
:exception nil,
:synthetic? false}
{:access-flags #{:public},
:name invoke,
:clinit? false,
:deprecated? false,
:code
{:max-locals 1,
:max-stack 1,
:line-numbers {0 1},
:local-variables
({:name this,
:descriptor "Ljava/lang/Object;",
:length 1,
:start-pc 0}),
:exception-table (),
:raw-bytecode [1, -80],
:bytecode [[:aconst_null 0] [:areturn 1]],
:code-length 2},
:descriptor "()Ljava/lang/Object;",
:constructor? false,
:exception nil,
:synthetic? false}),
:attributes
("NO_SOURCE_FILE"
{:type org.eclipse.jdt.internal.core.util.ClassFileAttribute,
:name "SourceDebugExtension",
:length 94}),
:fields (),
:interface? false}- It can even disassemble itself
no.disassemble=> (println (disassemble disassemble))
// Compiled from disassemble.clj (version 1.5 : 49.0, super bit)
public final class no.disassemble$disassemble extends clojure.lang.AFunction {
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__0;
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__1;
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__2;
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__3;
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__4;
// Field descriptor #13 Lclojure/lang/Keyword;
public static final clojure.lang.Keyword const__5;
// Field descriptor #7 Lclojure/lang/Var;
public static final clojure.lang.Var const__6;
// Field descriptor #16 Lclojure/lang/KeywordLookupSite;
static final clojure.lang.KeywordLookupSite __site__0__;
// Field descriptor #18 Lclojure/lang/ILookupThunk;
static clojure.lang.ILookupThunk __thunk__0__;
// Method descriptor #20 ()V
// Stack: 4, Locals: 0
public static {};
0 ldc [22]
2 ldc [24]
4 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
7 checkcast clojure.lang.Var [32]
10 putstatic no.disassemble$disassemble.const__0 : clojure.lang.Var [34]
13 ldc [36]
15 ldc [38]
17 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
20 checkcast clojure.lang.Var [32]
23 putstatic no.disassemble$disassemble.const__1 : clojure.lang.Var [40]
26 ldc [36]
28 ldc [42]
30 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
33 checkcast clojure.lang.Var [32]
36 putstatic no.disassemble$disassemble.const__2 : clojure.lang.Var [44]
39 ldc [36]
41 ldc [46]
43 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
46 checkcast clojure.lang.Var [32]
49 putstatic no.disassemble$disassemble.const__3 : clojure.lang.Var [48]
52 ldc [22]
54 ldc [50]
56 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
59 checkcast clojure.lang.Var [32]
62 putstatic no.disassemble$disassemble.const__4 : clojure.lang.Var [52]
65 aconst_null
66 ldc [54]
68 invokestatic clojure.lang.RT.keyword(java.lang.String, java.lang.String) : clojure.lang.Keyword [58]
71 checkcast clojure.lang.Keyword [60]
74 putstatic no.disassemble$disassemble.const__5 : clojure.lang.Keyword [62]
77 ldc [22]
79 ldc [64]
81 invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
84 checkcast clojure.lang.Var [32]
87 putstatic no.disassemble$disassemble.const__6 : clojure.lang.Var [66]
90 new clojure.lang.KeywordLookupSite [68]
93 dup
94 aconst_null
95 ldc [54]
97 invokestatic clojure.lang.RT.keyword(java.lang.String, java.lang.String) : clojure.lang.Keyword [58]
100 invokespecial clojure.lang.KeywordLookupSite(clojure.lang.Keyword) [72]
103 dup
104 putstatic no.disassemble$disassemble.__site__0__ : clojure.lang.KeywordLookupSite [74]
107 putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
110 return
Line numbers:
[pc: 0, line: 19]
// Method descriptor #20 ()V
// Stack: 1, Locals: 1
public disassemble$disassemble();
0 aload_0
1 invokespecial clojure.lang.AFunction() [78]
4 return
Line numbers:
[pc: 0, line: 19]
// Method descriptor #80 (Ljava/lang/Object;)Ljava/lang/Object;
// Stack: 9, Locals: 4
public java.lang.Object invoke(java.lang.Object obj);
0 getstatic no.disassemble$disassemble.const__0 : clojure.lang.Var [34]
3 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
6 checkcast clojure.lang.IFn [86]
9 getstatic no.disassemble$disassemble.const__1 : clojure.lang.Var [40]
12 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
15 checkcast clojure.lang.IFn [86]
18 aload_1 [obj]
19 invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
24 dup
25 ifnull 40
28 getstatic java.lang.Boolean.FALSE : java.lang.Boolean [94]
31 if_acmpeq 41
34 aload_1 [obj]
35 aconst_null
36 astore_1 [obj]
37 goto 58
40 pop
41 getstatic no.disassemble$disassemble.const__2 : clojure.lang.Var [44]
44 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
47 checkcast clojure.lang.IFn [86]
50 aload_1 [obj]
51 aconst_null
52 astore_1 [obj]
53 invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
58 ldc [96]
60 invokestatic clojure.lang.Reflector.invokeNoArgInstanceMember(java.lang.Object, java.lang.String) : java.lang.Object [102]
63 invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
68 astore_2 [cls_name]
69 getstatic no.disassemble$disassemble.const__4 : clojure.lang.Var [52]
72 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
75 checkcast clojure.lang.IFn [86]
78 invokeinterface clojure.lang.IFn.invoke() : java.lang.Object [104] [nargs: 1]
83 aload_2 [cls_name]
84 aconst_null
85 astore_2 [cls_name]
86 invokestatic clojure.lang.RT.get(java.lang.Object, java.lang.Object) : java.lang.Object [107]
89 astore_3 [bytecode]
90 new org.eclipse.jdt.internal.core.util.Disassembler [109]
93 dup
94 invokespecial org.eclipse.jdt.internal.core.util.Disassembler() [110]
97 ldc [112]
99 iconst_3
100 anewarray java.lang.Object [114]
103 dup
104 iconst_0
105 aload_3 [bytecode]
106 aconst_null
107 astore_3 [bytecode]
108 aastore
109 dup
110 iconst_1
111 ldc [116]
113 aastore
114 dup
115 iconst_2
116 getstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
119 dup
120 getstatic no.disassemble$disassemble.const__6 : clojure.lang.Var [66]
123 invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
126 dup_x2
127 invokeinterface clojure.lang.ILookupThunk.get(java.lang.Object) : java.lang.Object [120] [nargs: 2]
132 dup_x2
133 if_acmpeq 140
136 pop
137 goto 162
140 swap
141 pop
142 dup
143 getstatic no.disassemble$disassemble.__site__0__ : clojure.lang.KeywordLookupSite [74]
146 swap
147 invokeinterface clojure.lang.ILookupSite.fault(java.lang.Object) : clojure.lang.ILookupThunk [126] [nargs: 2]
152 dup
153 putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
156 swap
157 invokeinterface clojure.lang.ILookupThunk.get(java.lang.Object) : java.lang.Object [120] [nargs: 2]
162 aastore
163 invokestatic clojure.lang.Reflector.invokeInstanceMethod(java.lang.Object, java.lang.String, java.lang.Object[]) : java.lang.Object [130]
166 areturn
Line numbers:
[pc: 0, line: 19]
[pc: 0, line: 21]
[pc: 9, line: 21]
[pc: 9, line: 21]
[pc: 9, line: 21]
[pc: 41, line: 21]
[pc: 69, line: 22]
[pc: 69, line: 22]
[pc: 90, line: 23]
[pc: 116, line: 23]
Local variable table:
[pc: 69, pc: 166] local: cls_name index: 2 type: java.lang.Object
[pc: 90, pc: 166] local: bytecode index: 3 type: java.lang.Object
[pc: 0, pc: 166] local: this index: 0 type: java.lang.Object
[pc: 0, pc: 166] local: obj index: 1 type: java.lang.Object
// Method descriptor #137 (ILclojure/lang/ILookupThunk;)V
// Stack: 1, Locals: 3
public void swapThunk(int arg0, clojure.lang.ILookupThunk arg1);
0 iload_1
1 tableswitch default: 27
case 0: 20
20 aload_2
21 putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
24 goto 27
27 return
}
nil
no.disassemble=>
Copyright © 2013 Gary Trakhman
Distributed under the Eclipse Public License, the same as Clojure.