├── .gitignore ├── CHANGES ├── README.md ├── build-meta.edn ├── build.org ├── doc └── intro.md ├── java └── no │ └── disassemble │ └── NoDisassemble.java ├── lein-nodisassemble ├── .gitignore ├── README.md ├── project.clj └── src │ ├── lein_nodisassemble │ └── plugin.clj │ └── leiningen │ └── nodisassemble.clj ├── project.clj ├── src └── no │ ├── disassemble.clj │ └── disassemble │ └── r.clj └── test └── nodisassemble └── core_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | *.jar 7 | *.class 8 | .lein-deps-sum 9 | .lein-failures 10 | .lein-plugins 11 | *~ 12 | *# 13 | .#* 14 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 0.1.3 March 23, 2014 2 | Aphyr's data disassembler is available as disassemble-data. 3 | 4 | 0.1.2 November 5, 2013 5 | 6 | Specified equinox maven dep. 7 | 8 | 0.1.1 March 31, 2013 9 | 10 | Switched javac options to target java 1.6. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nodisassemble 2 | 3 | A Clojure library designed to let you inspect bytecode of functions and things. 4 | 5 | ## Artifacts 6 | The Most Recent Release is available on clojars 7 | 8 | With Leiningen: 9 | 10 | [nodisassemble "0.1.3"] 11 | 12 | HOWEVER, don't use it this way, let lein-nodissassemble's project middleware inject it for you. 13 | 14 | {:plugins [[lein-nodisassemble "0.1.3"]]} 15 | 16 | ## Usage 17 | 18 | no.disassemble is the runtime library created to negotiate with the agent ClassTransformer that stores class bytes globally. 19 | 20 | In order to use no.disassemble, add lein-nodisassemble to your :plugins, which will initialize the agent transformer and make bytecode available. 21 | 22 | WARNING: there is no cleanup of bytecode yet, so evaling a lot would surely exhaust the heap. 23 | 24 | 25 | user=> (require 'no.disassemble) 26 | nil 27 | 28 | user=> (in-ns 'no.disassemble) 29 | # 30 | 31 | no.disassemble=> (disassemble (fn [])) 32 | "// 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}" 33 | 34 | no.disassemble=> (println (disassemble (fn []))) 35 | // Compiled from NO_SOURCE_FILE (version 1.5 : 49.0, super bit) 36 | public final class no.disassemble$eval1174$fn__1175 extends clojure.lang.AFunction { 37 | 38 | // Method descriptor #7 ()V 39 | // Stack: 0, Locals: 0 40 | public static {}; 41 | 0 return 42 | Line numbers: 43 | [pc: 0, line: 1] 44 | 45 | // Method descriptor #7 ()V 46 | // Stack: 1, Locals: 1 47 | public disassemble$eval1174$fn__1175(); 48 | 0 aload_0 49 | 1 invokespecial clojure.lang.AFunction() [10] 50 | 4 return 51 | Line numbers: 52 | [pc: 0, line: 1] 53 | 54 | // Method descriptor #12 ()Ljava/lang/Object; 55 | // Stack: 1, Locals: 1 56 | public java.lang.Object invoke(); 57 | 0 aconst_null 58 | 1 areturn 59 | Line numbers: 60 | [pc: 0, line: 1] 61 | Local variable table: 62 | [pc: 0, pc: 1] local: this index: 0 type: java.lang.Object 63 | 64 | } 65 | nil 66 | no.disassemble=> 67 | 68 | You can also manipulate the disassembly as a Clojure data structure: 69 | 70 | ```clj 71 | user=> (-> (fn []) disassemble-data pprint) 72 | {:minor-version 0, 73 | :superclass-name clojure/lang/AFunction, 74 | :class? true, 75 | :major-version 49, 76 | :name user$eval1321$fn__1322, 77 | :interface-names [], 78 | :methods 79 | ({:access-flags #{:public :static}, 80 | :name , 81 | :clinit? true, 82 | :deprecated? false, 83 | :code 84 | {:max-locals 0, 85 | :max-stack 0, 86 | :line-numbers {0 1}, 87 | :local-variables nil, 88 | :exception-table (), 89 | :raw-bytecode [-79], 90 | :bytecode [[:return 0]], 91 | :code-length 1}, 92 | :descriptor "()V", 93 | :constructor? false, 94 | :exception nil, 95 | :synthetic? false} 96 | {:access-flags #{:public}, 97 | :name , 98 | :clinit? false, 99 | :deprecated? false, 100 | :code 101 | {:max-locals 1, 102 | :max-stack 1, 103 | :line-numbers {0 1}, 104 | :local-variables nil, 105 | :exception-table (), 106 | :raw-bytecode [42, -73, 0, 10, -79], 107 | :bytecode 108 | [[:aload_0 0] 109 | [:invokespecial 110 | 1 111 | 10 112 | {:kind :methodref, 113 | :class-name "clojure/lang/AFunction", 114 | :method-name "", 115 | :method-descriptor "()V"}] 116 | [:return 4]], 117 | :code-length 5}, 118 | :descriptor "()V", 119 | :constructor? true, 120 | :exception nil, 121 | :synthetic? false} 122 | {:access-flags #{:public}, 123 | :name invoke, 124 | :clinit? false, 125 | :deprecated? false, 126 | :code 127 | {:max-locals 1, 128 | :max-stack 1, 129 | :line-numbers {0 1}, 130 | :local-variables 131 | ({:name this, 132 | :descriptor "Ljava/lang/Object;", 133 | :length 1, 134 | :start-pc 0}), 135 | :exception-table (), 136 | :raw-bytecode [1, -80], 137 | :bytecode [[:aconst_null 0] [:areturn 1]], 138 | :code-length 2}, 139 | :descriptor "()Ljava/lang/Object;", 140 | :constructor? false, 141 | :exception nil, 142 | :synthetic? false}), 143 | :attributes 144 | ("NO_SOURCE_FILE" 145 | {:type org.eclipse.jdt.internal.core.util.ClassFileAttribute, 146 | :name "SourceDebugExtension", 147 | :length 94}), 148 | :fields (), 149 | :interface? false} 150 | ``` 151 | 152 | * It can even disassemble itself 153 | 154 |
155 |     no.disassemble=> (println (disassemble disassemble))
156 |     // Compiled from disassemble.clj (version 1.5 : 49.0, super bit)
157 |     public final class no.disassemble$disassemble extends clojure.lang.AFunction {
158 |       
159 |       // Field descriptor #7 Lclojure/lang/Var;
160 |       public static final clojure.lang.Var const__0;
161 |       
162 |       // Field descriptor #7 Lclojure/lang/Var;
163 |       public static final clojure.lang.Var const__1;
164 |       
165 |       // Field descriptor #7 Lclojure/lang/Var;
166 |       public static final clojure.lang.Var const__2;
167 |       
168 |       // Field descriptor #7 Lclojure/lang/Var;
169 |       public static final clojure.lang.Var const__3;
170 |       
171 |       // Field descriptor #7 Lclojure/lang/Var;
172 |       public static final clojure.lang.Var const__4;
173 |       
174 |       // Field descriptor #13 Lclojure/lang/Keyword;
175 |       public static final clojure.lang.Keyword const__5;
176 |       
177 |       // Field descriptor #7 Lclojure/lang/Var;
178 |       public static final clojure.lang.Var const__6;
179 |       
180 |       // Field descriptor #16 Lclojure/lang/KeywordLookupSite;
181 |       static final clojure.lang.KeywordLookupSite __site__0__;
182 |       
183 |       // Field descriptor #18 Lclojure/lang/ILookupThunk;
184 |       static clojure.lang.ILookupThunk __thunk__0__;
185 |       
186 |       // Method descriptor #20 ()V
187 |       // Stack: 4, Locals: 0
188 |       public static {};
189 |           0  ldc  [22]
190 |           2  ldc  [24]
191 |           4  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
192 |           7  checkcast clojure.lang.Var [32]
193 |          10  putstatic no.disassemble$disassemble.const__0 : clojure.lang.Var [34]
194 |          13  ldc  [36]
195 |          15  ldc  [38]
196 |          17  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
197 |          20  checkcast clojure.lang.Var [32]
198 |          23  putstatic no.disassemble$disassemble.const__1 : clojure.lang.Var [40]
199 |          26  ldc  [36]
200 |          28  ldc  [42]
201 |          30  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
202 |          33  checkcast clojure.lang.Var [32]
203 |          36  putstatic no.disassemble$disassemble.const__2 : clojure.lang.Var [44]
204 |          39  ldc  [36]
205 |          41  ldc  [46]
206 |          43  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
207 |          46  checkcast clojure.lang.Var [32]
208 |          49  putstatic no.disassemble$disassemble.const__3 : clojure.lang.Var [48]
209 |          52  ldc  [22]
210 |          54  ldc  [50]
211 |          56  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
212 |          59  checkcast clojure.lang.Var [32]
213 |          62  putstatic no.disassemble$disassemble.const__4 : clojure.lang.Var [52]
214 |          65  aconst_null
215 |          66  ldc  [54]
216 |          68  invokestatic clojure.lang.RT.keyword(java.lang.String, java.lang.String) : clojure.lang.Keyword [58]
217 |          71  checkcast clojure.lang.Keyword [60]
218 |          74  putstatic no.disassemble$disassemble.const__5 : clojure.lang.Keyword [62]
219 |          77  ldc  [22]
220 |          79  ldc  [64]
221 |          81  invokestatic clojure.lang.RT.var(java.lang.String, java.lang.String) : clojure.lang.Var [30]
222 |          84  checkcast clojure.lang.Var [32]
223 |          87  putstatic no.disassemble$disassemble.const__6 : clojure.lang.Var [66]
224 |          90  new clojure.lang.KeywordLookupSite [68]
225 |          93  dup
226 |          94  aconst_null
227 |          95  ldc  [54]
228 |          97  invokestatic clojure.lang.RT.keyword(java.lang.String, java.lang.String) : clojure.lang.Keyword [58]
229 |         100  invokespecial clojure.lang.KeywordLookupSite(clojure.lang.Keyword) [72]
230 |         103  dup
231 |         104  putstatic no.disassemble$disassemble.__site__0__ : clojure.lang.KeywordLookupSite [74]
232 |         107  putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
233 |         110  return
234 |           Line numbers:
235 |             [pc: 0, line: 19]
236 |       
237 |       // Method descriptor #20 ()V
238 |       // Stack: 1, Locals: 1
239 |       public disassemble$disassemble();
240 |         0  aload_0
241 |         1  invokespecial clojure.lang.AFunction() [78]
242 |         4  return
243 |           Line numbers:
244 |             [pc: 0, line: 19]
245 |       
246 |       // Method descriptor #80 (Ljava/lang/Object;)Ljava/lang/Object;
247 |       // Stack: 9, Locals: 4
248 |       public java.lang.Object invoke(java.lang.Object obj);
249 |           0  getstatic no.disassemble$disassemble.const__0 : clojure.lang.Var [34]
250 |           3  invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
251 |           6  checkcast clojure.lang.IFn [86]
252 |           9  getstatic no.disassemble$disassemble.const__1 : clojure.lang.Var [40]
253 |          12  invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
254 |          15  checkcast clojure.lang.IFn [86]
255 |          18  aload_1 [obj]
256 |          19  invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
257 |          24  dup
258 |          25  ifnull 40
259 |          28  getstatic java.lang.Boolean.FALSE : java.lang.Boolean [94]
260 |          31  if_acmpeq 41
261 |          34  aload_1 [obj]
262 |          35  aconst_null
263 |          36  astore_1 [obj]
264 |          37  goto 58
265 |          40  pop
266 |          41  getstatic no.disassemble$disassemble.const__2 : clojure.lang.Var [44]
267 |          44  invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
268 |          47  checkcast clojure.lang.IFn [86]
269 |          50  aload_1 [obj]
270 |          51  aconst_null
271 |          52  astore_1 [obj]
272 |          53  invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
273 |          58  ldc  [96]
274 |          60  invokestatic clojure.lang.Reflector.invokeNoArgInstanceMember(java.lang.Object, java.lang.String) : java.lang.Object [102]
275 |          63  invokeinterface clojure.lang.IFn.invoke(java.lang.Object) : java.lang.Object [88] [nargs: 2]
276 |          68  astore_2 [cls_name]
277 |          69  getstatic no.disassemble$disassemble.const__4 : clojure.lang.Var [52]
278 |          72  invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
279 |          75  checkcast clojure.lang.IFn [86]
280 |          78  invokeinterface clojure.lang.IFn.invoke() : java.lang.Object [104] [nargs: 1]
281 |          83  aload_2 [cls_name]
282 |          84  aconst_null
283 |          85  astore_2 [cls_name]
284 |          86  invokestatic clojure.lang.RT.get(java.lang.Object, java.lang.Object) : java.lang.Object [107]
285 |          89  astore_3 [bytecode]
286 |          90  new org.eclipse.jdt.internal.core.util.Disassembler [109]
287 |          93  dup
288 |          94  invokespecial org.eclipse.jdt.internal.core.util.Disassembler() [110]
289 |          97  ldc  [112]
290 |          99  iconst_3
291 |         100  anewarray java.lang.Object [114]
292 |         103  dup
293 |         104  iconst_0
294 |         105  aload_3 [bytecode]
295 |         106  aconst_null
296 |         107  astore_3 [bytecode]
297 |         108  aastore
298 |         109  dup
299 |         110  iconst_1
300 |         111  ldc  [116]
301 |         113  aastore
302 |         114  dup
303 |         115  iconst_2
304 |         116  getstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
305 |         119  dup
306 |         120  getstatic no.disassemble$disassemble.const__6 : clojure.lang.Var [66]
307 |         123  invokevirtual clojure.lang.Var.getRawRoot() : java.lang.Object [84]
308 |         126  dup_x2
309 |         127  invokeinterface clojure.lang.ILookupThunk.get(java.lang.Object) : java.lang.Object [120] [nargs: 2]
310 |         132  dup_x2
311 |         133  if_acmpeq 140
312 |         136  pop
313 |         137  goto 162
314 |         140  swap
315 |         141  pop
316 |         142  dup
317 |         143  getstatic no.disassemble$disassemble.__site__0__ : clojure.lang.KeywordLookupSite [74]
318 |         146  swap
319 |         147  invokeinterface clojure.lang.ILookupSite.fault(java.lang.Object) : clojure.lang.ILookupThunk [126] [nargs: 2]
320 |         152  dup
321 |         153  putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
322 |         156  swap
323 |         157  invokeinterface clojure.lang.ILookupThunk.get(java.lang.Object) : java.lang.Object [120] [nargs: 2]
324 |         162  aastore
325 |         163  invokestatic clojure.lang.Reflector.invokeInstanceMethod(java.lang.Object, java.lang.String, java.lang.Object[]) : java.lang.Object [130]
326 |         166  areturn
327 |           Line numbers:
328 |             [pc: 0, line: 19]
329 |             [pc: 0, line: 21]
330 |             [pc: 9, line: 21]
331 |             [pc: 9, line: 21]
332 |             [pc: 9, line: 21]
333 |             [pc: 41, line: 21]
334 |             [pc: 69, line: 22]
335 |             [pc: 69, line: 22]
336 |             [pc: 90, line: 23]
337 |             [pc: 116, line: 23]
338 |           Local variable table:
339 |             [pc: 69, pc: 166] local: cls_name index: 2 type: java.lang.Object
340 |             [pc: 90, pc: 166] local: bytecode index: 3 type: java.lang.Object
341 |             [pc: 0, pc: 166] local: this index: 0 type: java.lang.Object
342 |             [pc: 0, pc: 166] local: obj index: 1 type: java.lang.Object
343 |       
344 |       // Method descriptor #137 (ILclojure/lang/ILookupThunk;)V
345 |       // Stack: 1, Locals: 3
346 |       public void swapThunk(int arg0, clojure.lang.ILookupThunk arg1);
347 |          0  iload_1
348 |          1  tableswitch default: 27
349 |               case 0: 20
350 |         20  aload_2
351 |         21  putstatic no.disassemble$disassemble.__thunk__0__ : clojure.lang.ILookupThunk [76]
352 |         24  goto 27
353 |         27  return
354 |     
355 |     
356 |     }
357 |     nil
358 |     no.disassemble=> 
359 | 
360 | 361 | 362 | ## License 363 | 364 | Copyright © 2013 Gary Trakhman 365 | 366 | Distributed under the Eclipse Public License, the same as Clojure. 367 | -------------------------------------------------------------------------------- /build-meta.edn: -------------------------------------------------------------------------------- 1 | {:version "0.1.3"} 2 | -------------------------------------------------------------------------------- /build.org: -------------------------------------------------------------------------------- 1 | 1. Increment build version 2 | 2. Update Documentation to reflect version 3 | 3. Create release commit, tag it with format 'v0.1.2' 4 | 5. Deploy main project to Clojars 5 | 6. Deploy plugin to Clojars 6 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to nodisassemble 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /java/no/disassemble/NoDisassemble.java: -------------------------------------------------------------------------------- 1 | package no.disassemble; 2 | 3 | import java.lang.instrument.Instrumentation; 4 | import java.lang.instrument.ClassFileTransformer; 5 | import java.lang.instrument.IllegalClassFormatException; 6 | import java.security.ProtectionDomain; 7 | 8 | import java.util.Map; 9 | 10 | public class NoDisassemble { 11 | 12 | private static Map classes = new java.util.concurrent.ConcurrentHashMap(); 13 | public static Map getClasses() {return classes;} 14 | 15 | private static void println(Object arg){ 16 | System.out.println(arg); 17 | } 18 | public static class ClojureTransformer implements ClassFileTransformer { 19 | {println("Initializing NoDisassemble Transformer");} 20 | public byte[] transform(ClassLoader loader, String className, 21 | Class classBeingRedefined, ProtectionDomain protectionDomain, 22 | byte[] classBytes) throws IllegalClassFormatException { 23 | classes.put(className, classBytes); 24 | return classBytes; 25 | } 26 | } 27 | 28 | /** 29 | * Entry point method, called when the JVM initializes this package as an instrumentation agent. 30 | * @param args The arguments for this agent. Currently unused. 31 | * @param inst The instrumentation object to register with. 32 | */ 33 | public static void premain(String args, Instrumentation inst) { 34 | inst.addTransformer(new ClojureTransformer()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /lein-nodisassemble/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | .lein-deps-sum 10 | .lein-failures 11 | .lein-plugins 12 | .lein-repl-history 13 | -------------------------------------------------------------------------------- /lein-nodisassemble/README.md: -------------------------------------------------------------------------------- 1 | # lein-nodisassemble 2 | 3 | A Leiningen plugin to add no.disassemble to your project and startup the jvm instrumentation properly. Should only be used in a dev profile of some sort. 4 | 5 | ## Artifacts 6 | The Most Recent Release is deployed to clojars 7 | 8 | With Leiningen: 9 | 10 | {:plugins [[lein-nodisassemble "0.1.3"]]} 11 | 12 | ## Usage 13 | 14 | Put `[lein-nodisassemble "0.1.3"]` into the `:plugins` vector of your project.clj. 15 | 16 | Look at the documentation for no.disassemble for usage. 17 | 18 | ## License 19 | 20 | Copyright © 2013 Gary Trakhman 21 | 22 | Distributed under the Eclipse Public License, the same as Clojure. 23 | -------------------------------------------------------------------------------- /lein-nodisassemble/project.clj: -------------------------------------------------------------------------------- 1 | (def version (:version (read-string (slurp "../build-meta.edn")))) 2 | 3 | (defproject lein-nodisassemble version 4 | :url "http://example.com/FIXME" 5 | :license {:name "Eclipse Public License" 6 | :url "http://www.eclipse.org/legal/epl-v10.html"} 7 | :eval-in-leiningen true 8 | 9 | ) 10 | -------------------------------------------------------------------------------- /lein-nodisassemble/src/lein_nodisassemble/plugin.clj: -------------------------------------------------------------------------------- 1 | (ns lein-nodisassemble.plugin 2 | (:require [leiningen.core.classpath :as cp])) 3 | 4 | (defn get-version 5 | [project] 6 | (let [[_ version] (first (filter #(re-matches #".*lein-nodisassemble.*" (str (first %))) 7 | (:plugins project)))] 8 | version)) 9 | 10 | (defn find-dep 11 | [project] 12 | (->> (cp/resolve-dependencies :dependencies project) 13 | (filter #(re-find #".*nodisassemble.*" (.getName %))) 14 | first)) 15 | 16 | (defn middleware [project] 17 | (let [version (get-version project) 18 | project (update-in project [:dependencies] conj ['nodisassemble version]) 19 | path (-> (find-dep project) .toURI .getPath)] 20 | (update-in project [:jvm-opts] conj (str "-javaagent:" path)))) 21 | -------------------------------------------------------------------------------- /lein-nodisassemble/src/leiningen/nodisassemble.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.nodisassemble) 2 | 3 | (comment 4 | (defn nodisassemble 5 | "I don't do a lot." 6 | [project & args] 7 | (println "Hi!"))) 8 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (def version (:version (read-string (slurp "build-meta.edn")))) 2 | 3 | (defproject nodisassemble version 4 | :description "FIXME: write description" 5 | :url "http://example.com/FIXME" 6 | :license {:name "Eclipse Public License" 7 | :url "http://www.eclipse.org/legal/epl-v10.html"} 8 | :dependencies [[org.eclipse.jdt/org.eclipse.jdt.core "3.7.1"] 9 | ; [org.eclipse.equinox/app "1.0.0-v20070606"] 10 | [org.clojure/clojure "1.4.0"]] 11 | 12 | :java-source-paths ["java"] 13 | :manifest {"Premain-Class" "no.disassemble.NoDisassemble" 14 | ;; "Can-Redefine-Classes" true 15 | ;; "Can-Retransform-Classes" true 16 | } 17 | 18 | :javac-options ["-target" "1.6" "-source" "1.6" "-Xlint:-options"] 19 | 20 | ) 21 | -------------------------------------------------------------------------------- /src/no/disassemble.clj: -------------------------------------------------------------------------------- 1 | (ns no.disassemble 2 | (:require [no.disassemble.r :as disassembler]) 3 | (:import [org.eclipse.jdt.internal.core.util Disassembler])) 4 | 5 | (defn- classes 6 | [] 7 | (no.disassemble.NoDisassemble/getClasses)) 8 | 9 | (defn- sanitize 10 | [classname] 11 | (.replace classname \. \/)) 12 | 13 | (def levels 14 | {:detailed 1 15 | :default 2 16 | :system 4 17 | :compact 8 18 | :working-copy 16}) 19 | 20 | (defn disassemble-str 21 | "Emits a string bytecode disassembly of an object or class." 22 | [obj] 23 | (let [cls-name (sanitize (.getCanonicalName (if (class? obj) obj (class obj)))) 24 | bytecode (get (classes) cls-name)] 25 | (.disassemble (Disassembler.) bytecode "\n" (:detailed levels)))) 26 | 27 | (defn disassemble-data 28 | "Emits a data structure disassembly of an object or class." 29 | [obj] 30 | (disassembler/disassemble obj)) 31 | 32 | (def disassemble disassemble-str) 33 | -------------------------------------------------------------------------------- /src/no/disassemble/r.clj: -------------------------------------------------------------------------------- 1 | (ns no.disassemble.r 2 | "Low-level disassembly of classes. Uses the Eclipse jdt disassembler, but 3 | coerces its object graph into idiomatic Clojure data structures. 4 | 5 | The motherlode: http://grepcode.com/file/repository.grepcode.com/java/eclipse.org/3.7/org.eclipse.jdt/core/3.7.0/org/eclipse/jdt/internal/core/util/Disassembler.java" 6 | (:refer-clojure :exclude [methods]) 7 | (:import (org.eclipse.jdt core.JavaCore 8 | core.Signature 9 | core.compiler.CharOperation 10 | internal.compiler.codegen.AttributeNamesConstants 11 | internal.compiler.lookup.TypeConstants) 12 | (org.eclipse.jdt.core.util IClassFileReader 13 | IClassFileAttribute 14 | IConstantPoolEntry 15 | IConstantPoolConstant 16 | IAnnotationComponent 17 | ISourceAttribute 18 | IAnnotationDefaultAttribute 19 | ICodeAttribute 20 | ILocalVariableAttribute 21 | IRuntimeVisibleParameterAnnotationsAttribute 22 | IAnnotation 23 | IStackMapTableAttribute 24 | ILocalVariableTableEntry 25 | IExceptionTableEntry 26 | IAnnotationComponentValue 27 | ISignatureAttribute 28 | IInnerClassesAttributeEntry 29 | ILineNumberAttribute 30 | IStackMapFrame 31 | IRuntimeInvisibleParameterAnnotationsAttribute 32 | IRuntimeVisibleAnnotationsAttribute 33 | ILocalVariableTypeTableEntry 34 | ILocalVariableTypeTableAttribute 35 | IStackMapAttribute 36 | IConstantValueAttribute 37 | IEnclosingMethodAttribute 38 | IVerificationTypeInfo 39 | IRuntimeInvisibleAnnotationsAttribute 40 | IInnerClassesAttribute 41 | IParameterAnnotation 42 | IExceptionAttribute 43 | IFieldInfo 44 | IBytecodeVisitor 45 | IMethodInfo 46 | IModifierConstants) 47 | (org.eclipse.jdt.internal.core.util Messages 48 | ClassFileReader) 49 | )) 50 | 51 | (def constant-modifiers "Maps keywords to IModifierConstants" 52 | {:public IModifierConstants/ACC_PUBLIC 53 | :private IModifierConstants/ACC_PRIVATE 54 | :protected IModifierConstants/ACC_PROTECTED 55 | :static IModifierConstants/ACC_STATIC 56 | :final IModifierConstants/ACC_FINAL 57 | :super IModifierConstants/ACC_SUPER 58 | :synchronized IModifierConstants/ACC_SYNCHRONIZED 59 | :volatile IModifierConstants/ACC_VOLATILE 60 | :bridge IModifierConstants/ACC_BRIDGE 61 | :transient IModifierConstants/ACC_TRANSIENT 62 | :varargs IModifierConstants/ACC_VARARGS 63 | :native IModifierConstants/ACC_NATIVE 64 | :interface IModifierConstants/ACC_INTERFACE 65 | :abstract IModifierConstants/ACC_ABSTRACT 66 | :strict IModifierConstants/ACC_STRICT 67 | :synthetic IModifierConstants/ACC_SYNTHETIC 68 | :annotation IModifierConstants/ACC_ANNOTATION 69 | :enum IModifierConstants/ACC_ENUM}) 70 | 71 | (defn modifiers->kw 72 | "Given a set of legal modifier keywords, and an IModifierConstant integer, 73 | returns a set of keywords that integer represents, like :public, :varags, 74 | :native, :annotation, etc." 75 | [modifiers i] 76 | (assert i) 77 | (->> modifiers 78 | (remove (fn [modifier] 79 | (if-let [m (get constant-modifiers modifier)] 80 | (zero? (bit-and i m)) 81 | (throw (IllegalArgumentException. 82 | (str "Unknown modifier " (prn-str modifier))))))) 83 | (into (sorted-set)))) 84 | 85 | (defn constant-kind->kw 86 | "Given an integer constant kind, returns a keyword like :class, :string, 87 | :name-and-type, etc from the constants in IConstantPoolConstant." 88 | [kind] 89 | (condp = kind 90 | IConstantPoolConstant/CONSTANT_Class :class 91 | IConstantPoolConstant/CONSTANT_Fieldref :fieldref 92 | IConstantPoolConstant/CONSTANT_Methodref :methodref 93 | IConstantPoolConstant/CONSTANT_InterfaceMethodref :interface-methodref 94 | IConstantPoolConstant/CONSTANT_String :string 95 | IConstantPoolConstant/CONSTANT_Integer :integer 96 | IConstantPoolConstant/CONSTANT_Float :float 97 | IConstantPoolConstant/CONSTANT_Long :long 98 | IConstantPoolConstant/CONSTANT_Double :double 99 | IConstantPoolConstant/CONSTANT_NameAndType :name-and-type 100 | IConstantPoolConstant/CONSTANT_Utf8 :utf8)) 101 | 102 | ; Coerce disassembler classes to normal clojure data structures. 103 | (defprotocol Coerce 104 | (coerce [this])) 105 | 106 | ; These protocols are required for the bytecode visitor 107 | (extend-protocol Coerce 108 | nil 109 | (coerce [_] nil) 110 | 111 | Object 112 | (coerce [x] x) 113 | 114 | IConstantPoolEntry 115 | (coerce [c] 116 | (let [kind (constant-kind->kw (.getKind c))] 117 | (assoc 118 | (case kind 119 | :class {:class-info-name (String. (.getClassInfoName c))} 120 | :fieldref {:class-name (String. (.getClassName c)) 121 | :field-name (String. (.getFieldName c)) 122 | :field-descriptor (String. (.getFieldDescriptor c))} 123 | :methodref {:class-name (String. (.getClassName c)) 124 | :method-name (String. (.getMethodName c)) 125 | :method-descriptor (String. (.getMethodDescriptor c))} 126 | :interface-methodref {:class-name (String. (.getClassName c)) 127 | :method-name (String. (.getMethodName c)) 128 | :method-descriptor (String. 129 | (.getMethodDescriptor c))} 130 | :string {:value (.getStringValue c)} 131 | :integer {:value (.getIntegerValue c)} 132 | :float {:value (.getFloatValue c)} 133 | :double {:value (.getDoubleValue c)} 134 | :long {:value (.getLongValue c)} 135 | :name-and-type {:descriptor-index 136 | (.getNameAndTypeInfoDescriptorIndex c) 137 | :info-name-index (.getNameAndTypeInfoNameIndex c)} 138 | :utf8 {:value (String. (.getUtf8Value c))}) 139 | :kind kind)))) 140 | 141 | (defmacro defbytecode-visitor 142 | "Takes a list of expressions like 143 | 144 | _aload (int pc, int index) 145 | 146 | and generates a BytecodeVisitor deftype which traverses bytecode and builds 147 | up a sequence of Clojure data structures representing each op. Deref returns 148 | that sequence. Basically just means I can copy-paste the interface with 149 | minimal changes into the source here." 150 | [classname & specs] 151 | `(deftype ~classname [~'ops] 152 | clojure.lang.IDeref 153 | (deref [~'this] (deref ~'ops)) 154 | 155 | IBytecodeVisitor 156 | ~@(->> specs 157 | (partition 2) 158 | (map (fn [[fun args]] 159 | (let [; Keyword name for fun, without leading _ 160 | kw (-> fun 161 | name 162 | (.substring 1) 163 | keyword) 164 | ; Drop types 165 | args (keep-indexed #(when (odd? %1) %2) args) 166 | 167 | values (map (partial list 'coerce) args)] 168 | `(~fun [~'this ~@args] 169 | (swap! ~'ops conj [~kw ~@values])))))))) 170 | 171 | (defbytecode-visitor BytecodeVisitor 172 | _aaload (int pc) 173 | _aastore (int pc) 174 | _aconst_null (int pc) 175 | _aload (int pc, int index) 176 | _aload_0 (int pc) 177 | _aload_1 (int pc) 178 | _aload_2 (int pc) 179 | _aload_3 (int pc) 180 | _anewarray ( 181 | int pc, 182 | int index, 183 | IConstantPoolEntry constantClass) 184 | _areturn (int pc) 185 | _arraylength (int pc) 186 | _astore (int pc, int index) 187 | _astore_0 (int pc) 188 | _astore_1 (int pc) 189 | _astore_2 (int pc) 190 | _astore_3 (int pc) 191 | _athrow (int pc) 192 | _baload (int pc) 193 | _bastore (int pc) 194 | _bipush (int pc, byte _byte) 195 | _caload (int pc) 196 | _castore (int pc) 197 | _checkcast ( 198 | int pc, 199 | int index, 200 | IConstantPoolEntry constantClass) 201 | _d2f (int pc) 202 | _d2i (int pc) 203 | _d2l (int pc) 204 | _dadd (int pc) 205 | _daload (int pc) 206 | _dastore (int pc) 207 | _dcmpg (int pc) 208 | _dcmpl (int pc) 209 | _dconst_0 (int pc) 210 | _dconst_1 (int pc) 211 | _ddiv (int pc) 212 | _dload (int pc, int index) 213 | _dload_0 (int pc) 214 | _dload_1 (int pc) 215 | _dload_2 (int pc) 216 | _dload_3 (int pc) 217 | _dmul (int pc) 218 | _dneg (int pc) 219 | _drem (int pc) 220 | _dreturn (int pc) 221 | _dstore (int pc, int index) 222 | _dstore_0 (int pc) 223 | _dstore_1 (int pc) 224 | _dstore_2 (int pc) 225 | _dstore_3 (int pc) 226 | _dsub (int pc) 227 | _dup (int pc) 228 | _dup_x1 (int pc) 229 | _dup_x2 (int pc) 230 | _dup2 (int pc) 231 | _dup2_x1 (int pc) 232 | _dup2_x2 (int pc) 233 | _f2d (int pc) 234 | _f2i (int pc) 235 | _f2l (int pc) 236 | _fadd (int pc) 237 | _faload (int pc) 238 | _fastore (int pc) 239 | _fcmpg (int pc) 240 | _fcmpl (int pc) 241 | _fconst_0 (int pc) 242 | _fconst_1 (int pc) 243 | _fconst_2 (int pc) 244 | _fdiv (int pc) 245 | _fload (int pc, int index) 246 | _fload_0 (int pc) 247 | _fload_1 (int pc) 248 | _fload_2 (int pc) 249 | _fload_3 (int pc) 250 | _fmul (int pc) 251 | _fneg (int pc) 252 | _frem (int pc) 253 | _freturn (int pc) 254 | _fstore (int pc, int index) 255 | _fstore_0 (int pc) 256 | _fstore_1 (int pc) 257 | _fstore_2 (int pc) 258 | _fstore_3 (int pc) 259 | _fsub (int pc) 260 | _getfield ( 261 | int pc, 262 | int index, 263 | IConstantPoolEntry constantFieldref) 264 | _getstatic ( 265 | int pc, 266 | int index, 267 | IConstantPoolEntry constantFieldref) 268 | _goto (int pc, int branchOffset) 269 | _goto_w (int pc, int branchOffset) 270 | _i2b (int pc) 271 | _i2c (int pc) 272 | _i2d (int pc) 273 | _i2f (int pc) 274 | _i2l (int pc) 275 | _i2s (int pc) 276 | _iadd (int pc) 277 | _iaload (int pc) 278 | _iand (int pc) 279 | _iastore (int pc) 280 | _iconst_m1 (int pc) 281 | _iconst_0 (int pc) 282 | _iconst_1 (int pc) 283 | _iconst_2 (int pc) 284 | _iconst_3 (int pc) 285 | _iconst_4 (int pc) 286 | _iconst_5 (int pc) 287 | _idiv (int pc) 288 | _if_acmpeq (int pc, int branchOffset) 289 | _if_acmpne (int pc, int branchOffset) 290 | _if_icmpeq (int pc, int branchOffset) 291 | _if_icmpne (int pc, int branchOffset) 292 | _if_icmplt (int pc, int branchOffset) 293 | _if_icmpge (int pc, int branchOffset) 294 | _if_icmpgt (int pc, int branchOffset) 295 | _if_icmple (int pc, int branchOffset) 296 | _ifeq (int pc, int branchOffset) 297 | _ifne (int pc, int branchOffset) 298 | _iflt (int pc, int branchOffset) 299 | _ifge (int pc, int branchOffset) 300 | _ifgt (int pc, int branchOffset) 301 | _ifle (int pc, int branchOffset) 302 | _ifnonnull (int pc, int branchOffset) 303 | _ifnull (int pc, int branchOffset) 304 | _iinc (int pc, int index, int _const) 305 | _iload (int pc, int index) 306 | _iload_0 (int pc) 307 | _iload_1 (int pc) 308 | _iload_2 (int pc) 309 | _iload_3 (int pc) 310 | _imul (int pc) 311 | _ineg (int pc) 312 | _instanceof ( 313 | int pc, 314 | int index, 315 | IConstantPoolEntry constantClass) 316 | ; Not defined in this version of the jar :( 317 | ;_invokedynamic ( 318 | ; int pc, 319 | ; int index, 320 | ; IConstantPoolEntry nameEntry, 321 | ; IConstantPoolEntry descriptorEntry) 322 | _invokeinterface ( 323 | int pc, 324 | int index, 325 | byte nargs, 326 | IConstantPoolEntry constantInterfaceMethodref) 327 | _invokespecial ( 328 | int pc, 329 | int index, 330 | IConstantPoolEntry constantMethodref) 331 | _invokestatic ( 332 | int pc, 333 | int index, 334 | IConstantPoolEntry constantMethodref) 335 | _invokevirtual ( 336 | int pc, 337 | int index, 338 | IConstantPoolEntry constantMethodref) 339 | _ior (int pc) 340 | _irem (int pc) 341 | _ireturn (int pc) 342 | _ishl (int pc) 343 | _ishr (int pc) 344 | _istore (int pc, int index) 345 | _istore_0 (int pc) 346 | _istore_1 (int pc) 347 | _istore_2 (int pc) 348 | _istore_3 (int pc) 349 | _isub (int pc) 350 | _iushr (int pc) 351 | _ixor (int pc) 352 | _jsr (int pc, int branchOffset) 353 | _jsr_w (int pc, int branchOffset) 354 | _l2d (int pc) 355 | _l2f (int pc) 356 | _l2i (int pc) 357 | _ladd (int pc) 358 | _laload (int pc) 359 | _land (int pc) 360 | _lastore (int pc) 361 | _lcmp (int pc) 362 | _lconst_0 (int pc) 363 | _lconst_1 (int pc) 364 | _ldc (int pc, int index, IConstantPoolEntry constantPoolEntry) 365 | _ldc_w (int pc, int index, IConstantPoolEntry constantPoolEntry) 366 | _ldc2_w (int pc, int index, IConstantPoolEntry constantPoolEntry) 367 | _ldiv (int pc) 368 | _lload (int pc, int index) 369 | _lload_0 (int pc) 370 | _lload_1 (int pc) 371 | _lload_2 (int pc) 372 | _lload_3 (int pc) 373 | _lmul (int pc) 374 | _lneg (int pc) 375 | ; Not defined in this version of the jar. :( 376 | ;_lookupswitch ( 377 | ; int pc, 378 | ; int defaultoffset, 379 | ; int npairs, 380 | ; int[][] offset_pairs) 381 | _lor (int pc) 382 | _lrem (int pc) 383 | _lreturn (int pc) 384 | _lshl (int pc) 385 | _lshr (int pc) 386 | _lstore (int pc, int index) 387 | _lstore_0 (int pc) 388 | _lstore_1 (int pc) 389 | _lstore_2 (int pc) 390 | _lstore_3 (int pc) 391 | _lsub (int pc) 392 | _lushr (int pc) 393 | _lxor (int pc) 394 | _monitorenter (int pc) 395 | _monitorexit (int pc) 396 | _multianewarray ( 397 | int pc, 398 | int index, 399 | int dimensions, 400 | IConstantPoolEntry constantClass) 401 | _new ( 402 | int pc, 403 | int index, 404 | IConstantPoolEntry constantClass) 405 | _newarray (int pc, int atype) 406 | _nop (int pc) 407 | _pop (int pc) 408 | _pop2 (int pc) 409 | _putfield ( 410 | int pc, 411 | int index, 412 | IConstantPoolEntry constantFieldref) 413 | _putstatic ( 414 | int pc, 415 | int index, 416 | IConstantPoolEntry constantFieldref) 417 | _ret (int pc, int index) 418 | _return (int pc) 419 | _saload (int pc) 420 | _sastore (int pc) 421 | _sipush (int pc, short value) 422 | _swap (int pc) 423 | _tableswitch ( 424 | int pc, 425 | int defaultoffset, 426 | int low, 427 | int high, 428 | int[] jump_offsets) 429 | _wide ( 430 | int pc, 431 | int opcode, 432 | int index) 433 | _wide ( 434 | int pc, 435 | int iincopcode, 436 | int index, 437 | int _const) 438 | _breakpoint (int pc) 439 | _impdep1 (int pc) 440 | _impdep2 (int pc)) 441 | 442 | (extend-protocol Coerce 443 | IExceptionTableEntry 444 | (coerce [e] 445 | {:start-pc (.getStartPC e) 446 | :end-pc (.getEndPC e) 447 | :handler-pc (.getHandlerPC e) 448 | :catch-type (String. (.getCatchType e))}) 449 | 450 | ILocalVariableTableEntry 451 | (coerce [v] 452 | {:name (symbol (String. (.getName v))) 453 | :descriptor (String. (.getDescriptor v)) 454 | :length (.getLength v) 455 | :start-pc (.getStartPC v) 456 | :index (.getIndex v) 457 | :name-index (.getNameIndex v)}) 458 | 459 | ILocalVariableTypeTableEntry 460 | (coerce [v] 461 | {:start-pc (.getStartPC v) 462 | :length (.getLength v) 463 | :name (symbol (String. (.getName v))) 464 | :signature (String. (.getSignature v))}) 465 | 466 | IInnerClassesAttributeEntry 467 | (coerce [ic] 468 | {:access-flags (->> (.getAccessFlags ic) 469 | (modifiers->kw [:public :protected :private :abstract 470 | :static :final])) 471 | :inner-name (when (.getInnerName ic) (String. (.getInnerName ic))) 472 | :outer-class-name (when (.getOuterClassName ic) 473 | (String. (.getOuterClassName ic))) 474 | :inner-class-name (when (.getInnerClassName ic) 475 | (String. (.getInnerClassName ic)))}) 476 | 477 | IAnnotation 478 | (coerce [a] 479 | {:type-name (String. (.getTypeName a)) 480 | :components (map coerce (.getComponents a))}) 481 | 482 | IAnnotationComponent 483 | (coerce [c] 484 | {:name (String. (.getComponentName c)) 485 | :value (coerce (.getComponentValue c))}) 486 | 487 | IAnnotationComponentValue 488 | (coerce [c] 489 | {:values (map coerce (.getAnnotationComponentValues c)) 490 | :value (coerce (.getAnnotationValue c)) 491 | :class-info (coerce (.getClassInfo c)) 492 | :constant-value (coerce (.getConstantValue c)) 493 | :enum-constant-name (when (.getEnumConstantName c) 494 | (String. (.getEnumConstantName c))) 495 | :enum-constant-type-name (when (.getEnumConstantTypeName c) 496 | (String. (.getEnumConstantTypeName c))) 497 | :tag (.getTag c)}) 498 | 499 | IParameterAnnotation 500 | (coerce [c] (map coerce (.getAnnotations c))) 501 | 502 | IStackMapFrame 503 | (coerce [f] 504 | {:type (.getFrameType f) 505 | :offset-delta (.getOffsetDelta f) 506 | :locals (map coerce (.getLocals f)) 507 | :stack-items (map coerce (.getStackItems f))}) 508 | 509 | IVerificationTypeInfo 510 | (coerce [i] 511 | {:tag (.getTag i) 512 | :offset (.getOffset i) 513 | :class-type-name (String. (.getClassTypeName i))}) 514 | 515 | IClassFileAttribute 516 | (coerce [a] 517 | {:type (type a) 518 | :name (String. (.getAttributeName a)) 519 | :length (.getAttributeLength a)}) 520 | 521 | IAnnotationDefaultAttribute 522 | (coerce [a] (coerce (.getMemberValue a))) 523 | 524 | ICodeAttribute 525 | (coerce [c] 526 | {:max-locals (.getMaxLocals c) 527 | :max-stack (.getMaxStack c) 528 | :line-numbers (coerce (.getLineNumberAttribute c)) 529 | :local-variables (coerce (.getLocalVariableAttribute c)) 530 | :exception-table (map coerce (.getExceptionTable c)) 531 | :raw-bytecode (.getBytecodes c) 532 | :bytecode (let [v (BytecodeVisitor. (atom []))] 533 | (.traverse c v) 534 | @v) 535 | :code-length (.getCodeLength c)}) 536 | 537 | IConstantValueAttribute 538 | (coerce [c] 539 | (coerce (.getConstantValue c))) 540 | 541 | IEnclosingMethodAttribute 542 | (coerce [em] 543 | {:enclosing-class (symbol (String. (.getEnclosingClass em))) 544 | :method-descriptor (String. (.getMethodDescriptor em)) 545 | :method-name (symbol (String. (.getMethodName em)))}) 546 | 547 | IExceptionAttribute 548 | (coerce [e] (map #(String.) (.getExceptionNames e))) 549 | 550 | IInnerClassesAttribute 551 | (coerce [ic] (map coerce (.getInnerClassAttributesEntries ic))) 552 | 553 | ILineNumberAttribute 554 | (coerce [l] 555 | (->> l 556 | .getLineNumberTable 557 | (mapcat identity) 558 | (apply sorted-map))) 559 | 560 | ILocalVariableAttribute 561 | (coerce [v] (map coerce (.getLocalVariableTable v))) 562 | 563 | ILocalVariableTypeTableAttribute 564 | (coerce [lvtt] (map coerce (.getLocalVariableTypeTable lvtt))) 565 | 566 | IRuntimeInvisibleAnnotationsAttribute 567 | (coerce [a] (map coerce (.getAnnotations a))) 568 | 569 | IRuntimeInvisibleParameterAnnotationsAttribute 570 | (coerce [a] (map coerce (.getParameterAnnotations a))) 571 | 572 | IRuntimeVisibleAnnotationsAttribute 573 | (coerce [a] (map coerce (.getAnnotations a))) 574 | 575 | IRuntimeVisibleParameterAnnotationsAttribute 576 | (coerce [a] (map coerce (.getParameterAnnotations a))) 577 | 578 | ISignatureAttribute 579 | (coerce [a] (String. (.getSignature a))) 580 | 581 | ISourceAttribute 582 | (coerce [s] (String. (.getSourceFileName s))) 583 | 584 | IStackMapAttribute 585 | (coerce [s] (map coerce (.getStackMapFrame s))) 586 | 587 | IStackMapTableAttribute 588 | (coerce [t] (map coerce (.getStackMapFrame t))) 589 | 590 | IMethodInfo 591 | (coerce [m] 592 | {:descriptor (String. (.getDescriptor m)) 593 | :access-flags (->> (.getAccessFlags m) 594 | (modifiers->kw [:public :protected :private 595 | :abstract :static :final 596 | :synchronized :native :strict 597 | :bridge])) 598 | :name (symbol (String. (.getName m))) 599 | :clinit? (.isClinit m) 600 | :constructor? (.isConstructor m) 601 | :synthetic? (.isSynthetic m) 602 | :deprecated? (.isDeprecated m) 603 | :code (coerce (.getCodeAttribute m)) 604 | :exception (.getExceptionAttribute m)}) 605 | 606 | IFieldInfo 607 | (coerce [f] 608 | (let [base {:access-flags (->> (.getAccessFlags f) 609 | (modifiers->kw [:public :protected :private 610 | :static :final :transient 611 | :volatile :enum])) 612 | :name (symbol (String. (.getName f))) 613 | :descriptor (String. (.getDescriptor f)) 614 | :synthetic? (.isSynthetic f) 615 | :deprecated? (.isDeprecated f) 616 | :attributes (map coerce (.getAttributes f))} 617 | base 618 | (if (.hasConstantValueAttribute f) 619 | (assoc base :constant-value-attribute 620 | (.getConstantValueAttribute f)) 621 | base)] 622 | base))) 623 | 624 | (defn class-name 625 | "Finds the sanitized canonical name of the class of an object. If obj is a 626 | class, uses obj, otherwise finds the class of that object." 627 | [obj] 628 | (-> (if (class? obj) 629 | obj 630 | (class obj)) 631 | .getCanonicalName 632 | (.replace \. \/))) 633 | 634 | (defn class-file-bytes 635 | "Returns a byte array for a class (or an object's class)." 636 | [obj] 637 | (get (no.disassemble.NoDisassemble/getClasses) 638 | (class-name obj))) 639 | 640 | (defn ^ClassFileReader class-file-reader 641 | [obj] 642 | (ClassFileReader. (class-file-bytes obj) IClassFileReader/ALL)) 643 | 644 | 645 | (defn disassemble 646 | "Returns a structure representing the bytecode of an object. TODO: inner classes. Maybe broken: interface-names?" 647 | [obj] 648 | (let [r (class-file-reader obj)] 649 | {:attributes (->> r .getAttributes (map coerce)) 650 | :major-version (.getMajorVersion r) 651 | :minor-version (.getMinorVersion r) 652 | :class? (.isClass r) 653 | :interface? (.isInterface r) 654 | :name (symbol (String. (.getClassName r))) 655 | :superclass-name (symbol (String. (.getSuperclassName r))) 656 | :interface-names (.getInterfaceNames r) 657 | :fields (->> r .getFieldInfos (map coerce)) 658 | :methods (->> r .getMethodInfos (map coerce))})) 659 | -------------------------------------------------------------------------------- /test/nodisassemble/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns nodisassemble.core-test 2 | (:use clojure.test 3 | nodisassemble.core)) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | --------------------------------------------------------------------------------