2005-04-04 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mono / dis / dis-cil.c
1 /*
2  * dis-cil.c: Disassembles CIL byte codes
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9 #include <config.h>
10 #include <glib.h>
11 #include <stdio.h>
12 #include <math.h>
13 #ifdef  HAVE_WCHAR_H
14 #include <wchar.h>
15 #endif
16 #include "meta.h"
17 #include "get.h"
18 #include "dump.h"
19 #include "dis-cil.h"
20 #include "mono/metadata/opcodes.h"
21 #include "mono/utils/mono-compiler.h"
22
23 #ifdef HAVE_IEEEFP_H
24 #include <ieeefp.h>
25 int isinf (double);
26 int
27 isinf (double num)
28 {
29         fpclass_t klass;
30
31         klass = fpclass (num);
32         if (klass == FP_NINF)
33                 return -1;
34
35         if (klass == FP_PINF)
36                 return 1;
37
38         return 0;
39 }
40 #else
41 #ifndef HAVE_ISINF
42 #error "Don't know how to implement isinf for this platform."
43 #endif
44 #endif
45 /*
46  * Strings on the US heap are encoded using UTF-16.  Poor man's
47  * UTF-16 to UTF-8.  I know its broken, use libunicode later.
48  */
49 static char *
50 get_encoded_user_string (const char *ptr)
51 {
52         char *res, *result;
53         int len, i, j;
54
55         len = mono_metadata_decode_blob_size (ptr, &ptr);
56         res = g_malloc (len + 1);
57
58         /*
59          * I should really use some kind of libunicode here
60          */
61         for (i = 0, j = 0; i < len; j++, i += 2)
62                 res [j] = ptr [i];
63
64         res [j] = 0;
65
66         result = g_strescape (res, NULL);
67         g_free (res);
68         
69         return result;
70 }
71
72 #define CODE_INDENT g_assert (indent_level < 512); \
73         indent[indent_level*2] = ' ';   \
74         indent[indent_level*2+1] = ' '; \
75         ++indent_level; \
76         indent[indent_level*2] = 0;
77 #define CODE_UNINDENT g_assert (indent_level);  \
78         --indent_level; \
79         indent[indent_level*2] = 0;
80
81 void
82 dissasemble_cil (MonoImage *m, MonoMethodHeader *mh, MonoGenericContext *context)
83 {
84         const unsigned char *start = mh->code;
85         int size = mh->code_size;
86         const unsigned char *end = start + size;
87         const unsigned char *ptr = start;
88         const MonoOpcode *entry;
89         char indent[1024];
90         int i, indent_level = 0;
91         gboolean in_fault = 0;
92         const char *clause_names[] = {"catch", "filter", "finally", "", "fault"};
93         gboolean *trys = NULL;
94         indent [0] = 0;
95
96 #ifdef DEBUG
97         for (i = 0; i < mh->num_clauses; ++i) {
98 #define clause mh->clauses [i]
99                g_print ("/* out clause %d: from %d len=%d, handler at %d, %d */\n",
100                         clause.flags, clause.try_offset, clause.try_len, clause.handler_offset, clause.handler_len);
101 #undef clause
102         }
103 #endif
104
105        if (mh->num_clauses) {
106                trys = g_malloc0 (sizeof (gboolean) * mh->num_clauses);
107                trys [0] = 1;
108                for (i=1; i < mh->num_clauses; ++i) {
109 #define pcl mh->clauses [i-1]   
110 #define cl mh->clauses [i]      
111                        if (pcl.try_offset != cl.try_offset || pcl.try_len != cl.try_len)
112                                trys [i] = 1;
113 #undef pcl
114 #undef cl
115                }
116        }
117
118         while (ptr < end){
119                 for (i = mh->num_clauses - 1; i >= 0 ; --i) {
120                         if (ptr == start + mh->clauses[i].try_offset && trys [i]) {
121                                 fprintf (output, "\t%s.try { // %d\n", indent, i);
122                                 CODE_INDENT;
123                         }
124                         
125                         if (ptr == start + mh->clauses[i].handler_offset) {
126                                 if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FILTER) {
127                                         CODE_UNINDENT;
128                                         fprintf (output, "\t%s} { // %d\n", indent, i);
129                                 } else {
130                                         char * klass = mh->clauses[i].flags ? g_strdup ("") :
131                                                 dis_stringify_object_with_class (m, mh->clauses[i].data.catch_class,
132                                                                                  FALSE, FALSE);
133                                         fprintf (output, "\t%s%s %s { // %d\n", indent,
134                                                         clause_names [mh->clauses[i].flags], klass, i);
135                                         g_free (klass);
136                                 }
137                                 CODE_INDENT;
138                                 if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FAULT)
139                                         in_fault = 1;
140                         } 
141                         if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FILTER && ptr == start + mh->clauses[i].data.filter_offset) {
142                                 fprintf (output, "\t%s%s {\n", indent, clause_names[1]);
143                                 CODE_INDENT;
144                         }
145                 }
146                 fprintf (output, "\t%sIL_%04x: ", indent, (int) (ptr - start));
147                 i = *ptr;
148                 if (*ptr == 0xfe){
149                         ptr++;
150                         i = *ptr + 256;
151                 } 
152                 entry = &mono_opcodes [i];
153
154                 if (in_fault && entry->opval == 0xDC)
155                         fprintf (output, " %s", "endfault");
156                 else
157                         fprintf (output, " %s ", mono_opcode_name (i));
158                 ptr++;
159                 switch (entry->argument){
160                 case MonoInlineBrTarget: {
161                         gint target = read32 (ptr);
162                         fprintf (output, "IL_%04x\n", ((int) (ptr - start)) + 4 + target);
163                         ptr += 4;
164                         break;
165                 }
166                         
167                 case MonoInlineField: {
168                         guint32 token = read32 (ptr);
169                         char *s;
170                         
171                         s = get_field (m, token, context);
172                         fprintf (output, "%s", s);
173                         g_free (s);
174                         ptr += 4;
175                         break;
176                 }
177                 
178                 case MonoInlineI: {
179                         int value = read32 (ptr);
180
181                         fprintf (output, "%d", value);
182                         ptr += 4;
183                         break;
184                 }
185                 
186                 case MonoInlineI8: {
187                         gint64 top = read64 (ptr);
188
189                         fprintf (output, "0x%llx", (long long) top);
190                         ptr += 8;
191                         break;
192                 }
193                 
194                 case MonoInlineMethod: {
195                         guint32 token = read32 (ptr);
196                         char *s;
197
198                         s = get_method (m, token, context);
199                         fprintf (output, "%s", s);
200                         g_free (s);
201                         ptr += 4;
202                         break;
203                 }
204                 
205                 case MonoInlineNone:
206                         break;
207                         
208                 case MonoInlineR: {
209                         double r;
210                         int inf;
211                         readr8 (ptr, &r);
212                         inf = isinf (r);
213                         if (inf == -1) 
214                                 fprintf (output, "(00 00 00 00 00 00 f0 ff)"); /* negative infinity */
215                         else if (inf == 1)
216                                 fprintf (output, "(00 00 00 00 00 00 f0 7f)"); /* positive infinity */
217                         else if (isnan (r))
218                                 fprintf (output, "(00 00 00 00 00 00 f8 ff)"); /* NaN */
219                         else
220                                 fprintf (output, "%g", r);
221                         ptr += 8;
222                         break;
223                 }
224                 
225                 case MonoInlineSig: {
226                         guint32 token = read32 (ptr);
227                         fprintf (output, "signature-0x%08x", token);
228                         ptr += 4;
229                         break;
230                 }
231                 
232                 case MonoInlineString: {
233                         guint32 token = read32 (ptr);
234                         
235                         char *s = get_encoded_user_string (
236                                 mono_metadata_user_string (m, token & 0xffffff));
237                         
238                         /*
239                          * See section 23.1.4 on the encoding of the #US heap
240                          */
241                         fprintf (output, "\"%s\"", s);
242                         g_free (s);
243                         ptr += 4;
244                         break;
245                 }
246
247                 case MonoInlineSwitch: {
248                         guint32 count = read32 (ptr);
249                         const unsigned char *endswitch;
250                         guint32 n;
251                         
252                         ptr += 4;
253                         endswitch = ptr + sizeof (guint32) * count;
254                         fprintf (output, count > 0 ? "(\n" : "( )");
255                         CODE_INDENT;
256                         for (n = 0; n < count; n++){
257                                 fprintf (output, "\t%sIL_%04x%s", indent, 
258                                                  (int)(endswitch-start+read32 (ptr)), 
259                                                  n == count - 1 ? ")" : ",\n");
260                                 ptr += 4;
261                         }
262                         CODE_UNINDENT;
263                         break;
264                 }
265
266                 case MonoInlineTok: {
267                         guint32 token = read32 (ptr);
268                         char *s;
269                         
270                         s = get_token (m, token, context);
271                         fprintf (output, "%s", s);
272                         g_free (s);
273                         
274                         ptr += 4;
275                         break;
276                 }
277                 
278                 case MonoInlineType: {
279                         guint32 token = read32 (ptr);
280                         char *s = get_token_type (m, token, context);
281                         fprintf (output, "%s", s);
282                         g_free (s);
283                         ptr += 4;
284                         break;
285                 }
286
287                 case MonoInlineVar: {
288                         guint16 var_idx = read16 (ptr);
289
290                         fprintf (output, "%d\n", var_idx);
291                         ptr += 2;
292                         break;
293                 }
294
295                 case MonoShortInlineBrTarget: {
296                         signed char x = *ptr;
297                         
298                         fprintf (output, "IL_%04x\n", (int)(ptr - start + 1 + x));
299                         ptr++;
300                         break;
301                 }
302
303                 case MonoShortInlineI: {
304                         char x = *ptr;
305
306                         fprintf (output, "0x%02x", x);
307                         ptr++;
308                         break;
309                 }
310
311                 case MonoShortInlineR: {
312                         float f;
313                         int inf;
314                         
315                         readr4 (ptr, &f);
316
317                         inf = isinf (f);
318                         if (inf == -1) 
319                                 fprintf (output, "(00 00 80 ff)"); /* negative infinity */
320                         else if (inf == 1)
321                                 fprintf (output, "(00 00 80 7f)"); /* positive infinity */
322                         else if (isnan (f))
323                                 fprintf (output, "(00 00 c0 ff)"); /* NaN */
324                         else
325                                 fprintf (output, "%g", (double) f);
326                         ptr += 4;
327                         break;
328                 }
329
330                 case MonoShortInlineVar: {
331                         unsigned char x = *ptr;
332
333                         fprintf (output, "%d", (int) x);
334                         ptr++;
335                         break;
336                 }
337                 default:
338                         break;
339                 }
340
341                 fprintf (output, "\n");
342                 for (i = 0; i < mh->num_clauses; ++i) {
343                         if (ptr == start + mh->clauses[i].try_offset + mh->clauses[i].try_len && trys [i]) {
344                                 CODE_UNINDENT;
345                                 fprintf (output, "\t%s} // end .try %d\n", indent, i);
346                         }
347                         if (ptr == start + mh->clauses[i].handler_offset + mh->clauses[i].handler_len) {
348                                 CODE_UNINDENT;
349                                 fprintf (output, "\t%s} // end handler %d\n", indent, i);
350                                 if (mh->clauses[i].flags == MONO_EXCEPTION_CLAUSE_FAULT)
351                                         in_fault = 0;
352                         }
353                 }
354         }
355         if (trys)
356                 g_free (trys);
357 }