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