* list :
[mono.git] / mono / metadata / debug-helpers.c
1
2 #include <string.h>
3 #include "mono/metadata/tokentype.h"
4 #include "mono/metadata/opcodes.h"
5 #include "mono/metadata/mono-endian.h"
6 #include "mono/metadata/debug-helpers.h"
7
8 struct MonoMethodDesc {
9         char *namespace;
10         char *klass;
11         char *name;
12         char *args;
13         guint num_args;
14         gboolean include_namespace;
15 };
16
17 void
18 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
19         switch (type->type) {
20         case MONO_TYPE_VOID:
21                 g_string_append (res, "void"); break;
22         case MONO_TYPE_CHAR:
23                 g_string_append (res, "char"); break;
24         case MONO_TYPE_BOOLEAN:
25                 g_string_append (res, "bool"); break;
26         case MONO_TYPE_U1:
27                 g_string_append (res, "byte"); break;
28         case MONO_TYPE_I1:
29                 g_string_append (res, "sbyte"); break;
30         case MONO_TYPE_U2:
31                 g_string_append (res, "uint16"); break;
32         case MONO_TYPE_I2:
33                 g_string_append (res, "int16"); break;
34         case MONO_TYPE_U4:
35                 g_string_append (res, "uint"); break;
36         case MONO_TYPE_I4:
37                 g_string_append (res, "int"); break;
38         case MONO_TYPE_U8:
39                 g_string_append (res, "ulong"); break;
40         case MONO_TYPE_I8:
41                 g_string_append (res, "long"); break;
42         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
43                 g_string_append (res, "*()"); break;
44         case MONO_TYPE_U:
45                 g_string_append (res, "uintptr"); break;
46         case MONO_TYPE_I:
47                 g_string_append (res, "intptr"); break;
48         case MONO_TYPE_R4:
49                 g_string_append (res, "single"); break;
50         case MONO_TYPE_R8:
51                 g_string_append (res, "double"); break;
52         case MONO_TYPE_STRING:
53                 g_string_append (res, "string"); break;
54         case MONO_TYPE_OBJECT:
55                 g_string_append (res, "object"); break;
56         case MONO_TYPE_PTR:
57                 mono_type_get_desc (res, type->data.type, include_namespace);
58                 g_string_append_c (res, '*');
59                 break;
60         case MONO_TYPE_ARRAY:
61                 mono_type_get_desc (res, type->data.array->type, include_namespace);
62                 g_string_sprintfa (res, "[%d]", type->data.array->rank);
63                 break;
64         case MONO_TYPE_SZARRAY:
65                 mono_type_get_desc (res, type->data.type, include_namespace);
66                 g_string_append (res, "[]");
67                 break;
68         case MONO_TYPE_CLASS:
69         case MONO_TYPE_VALUETYPE: {
70                 MonoClass *class = type->data.klass;
71                 if (!class) {
72                         g_string_append (res, "Unknown");
73                         break;
74                 }
75                 if (include_namespace && *(class->name_space))
76                         g_string_sprintfa (res, "%s.", class->name_space);
77                 g_string_sprintfa (res, "%s", class->name);
78                 break;
79         }
80         default:
81                 break;
82         }
83         if (type->byref)
84                 g_string_append_c (res, '&');
85 }
86
87 char*
88 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
89 {
90         int i;
91         char *result;
92         GString *res = g_string_new ("");
93
94         for (i = 0; i < sig->param_count; ++i) {
95                 if (i > 0)
96                         g_string_append_c (res, ',');
97                 mono_type_get_desc (res, sig->params [i], include_namespace);
98         }
99         result = res->str;
100         g_string_free (res, FALSE);
101         return result;
102 }
103
104 /*
105  * The allowed format of a method description string is:
106  * [namespace.]classname:methodname[(args...)]
107  * TODO: describe instance methods.
108  */
109 MonoMethodDesc*
110 mono_method_desc_new (const char *name, gboolean include_namespace)
111 {
112         MonoMethodDesc *result;
113         char *class_name, *class_nspace, *method_name, *use_args, *end;
114         int use_namespace;
115         
116         class_nspace = g_strdup (name);
117         use_args = strchr (class_nspace, '(');
118         if (use_args) {
119                 *use_args++ = 0;
120                 end = strchr (use_args, ')');
121                 if (!end) {
122                         g_free (class_nspace);
123                         return NULL;
124                 }
125                 *end = 0;
126         }
127         method_name = strrchr (class_nspace, ':');
128         if (!method_name) {
129                 g_free (class_nspace);
130                 return NULL;
131         }
132         *method_name++ = 0;
133         /* allow two :: to separate the method name */
134         if (*method_name == ':')
135                 method_name++;
136         class_name = strrchr (class_nspace, '.');
137         if (class_name) {
138                 *class_name++ = 0;
139                 use_namespace = 1;
140         } else {
141                 class_name = class_nspace;
142                 use_namespace = 0;
143         }
144         result = g_new0 (MonoMethodDesc, 1);
145         result->include_namespace = include_namespace;
146         result->name = method_name;
147         result->klass = class_name;
148         result->namespace = use_namespace? class_nspace: NULL;
149         result->args = use_args? use_args: NULL;
150         if (use_args) {
151                 end = use_args;
152                 if (*end)
153                         result->num_args = 1;
154                 while (*end) {
155                         if (*end == ',')
156                                 result->num_args++;
157                         ++end;
158                 }
159         }
160
161         return result;
162 }
163
164 MonoMethodDesc*
165 mono_method_desc_from_method (MonoMethod *method)
166 {
167         MonoMethodDesc *result;
168         
169         result = g_new0 (MonoMethodDesc, 1);
170         result->include_namespace = TRUE;
171         result->name = g_strdup (method->name);
172         result->klass = g_strdup (method->klass->name);
173         result->namespace = g_strdup (method->klass->name_space);
174
175         return result;
176 }
177
178 void
179 mono_method_desc_free (MonoMethodDesc *desc)
180 {
181         if (desc->namespace)
182                 g_free (desc->namespace);
183         else if (desc->klass)
184                 g_free (desc->klass);
185         g_free (desc);
186 }
187
188 /*
189  * namespace and class are supposed to match already if this function is used.
190  */
191 gboolean
192 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
193 {
194         char *sig;
195         if (strcmp (desc->name, method->name))
196                 return FALSE;
197         if (!desc->args)
198                 return TRUE;
199         if (desc->num_args != method->signature->param_count)
200                 return FALSE;
201         sig = mono_signature_get_desc (method->signature, desc->include_namespace);
202         if (strcmp (sig, desc->args)) {
203                 g_free (sig);
204                 return FALSE;
205         }
206         g_free (sig);
207         return TRUE;
208 }
209
210 gboolean
211 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
212 {
213         if (strcmp (desc->klass, method->klass->name))
214                 return FALSE;
215         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
216                 return FALSE;
217         return mono_method_desc_match (desc, method);
218 }
219
220 MonoMethod*
221 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
222 {
223         int i;
224
225         mono_class_init (klass);
226         for (i = 0; i < klass->method.count; ++i) {
227                 if (mono_method_desc_match (desc, klass->methods [i]))
228                         return klass->methods [i];
229         }
230         return NULL;
231 }
232
233 MonoMethod*
234 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
235 {
236         MonoClass *klass;
237         MonoTableInfo *tdef;
238         MonoTableInfo *methods;
239         MonoMethod *method;
240         int i;
241
242         if (desc->namespace && desc->klass) {
243                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
244                 if (!klass)
245                         return NULL;
246                 return mono_method_desc_search_in_class (desc, klass);
247         }
248
249         tdef = &image->tables [MONO_TABLE_TYPEDEF];
250         methods = &image->tables [MONO_TABLE_METHOD];
251         for (i = 0; i < methods->rows; ++i) {
252                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
253                 const char *n = mono_metadata_string_heap (image, token);
254
255                 if (strcmp (n, desc->name))
256                         continue;
257                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
258                 if (mono_method_desc_full_match (desc, method))
259                         return method;
260         }
261         return NULL;
262 }
263
264 static const unsigned char*
265 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip)
266 {
267         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
268         const MonoOpcode *opcode;
269         guint32 i, label, token;
270         gint32 sval;
271         char *tmp;
272
273         label = ip - header->code;
274         if (dh->indenter) {
275                 tmp = dh->indenter (dh, method, label);
276                 g_string_append (str, tmp);
277                 g_free (tmp);
278         }
279         if (dh->label_format)
280                 g_string_sprintfa (str, dh->label_format, label);
281         
282         i = mono_opcode_value (&ip);
283         ip++;
284         opcode = &mono_opcodes [i];
285         g_string_sprintfa (str, "%-10s", mono_opcode_names [i]);
286
287         switch (opcode->argument) {
288         case MonoInlineNone:
289                 break;
290         case MonoInlineType:
291         case MonoInlineField:
292         case MonoInlineMethod:
293         case MonoInlineTok:
294         case MonoInlineSig:
295                 token = read32 (ip);
296                 if (dh->tokener) {
297                         tmp = dh->tokener (dh, method, token);
298                         g_string_append (str, tmp);
299                         g_free (tmp);
300                 } else {
301                         g_string_sprintfa (str, "0x%08x", token);
302                 }
303                 ip += 4;
304                 break;
305         case MonoInlineString:
306                 /* TODO */
307                 ip += 4;
308                 break;
309         case MonoInlineVar:
310                 g_string_sprintfa (str, "%d", read16 (ip));
311                 ip += 2;
312                 break;
313         case MonoShortInlineVar:
314                 g_string_sprintfa (str, "%d", (*ip));
315                 ip ++;
316                 break;
317         case MonoInlineBrTarget:
318                 sval = read32 (ip);
319                 ip += 4;
320                 if (dh->label_target)
321                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
322                 else
323                         g_string_sprintfa (str, "%d", sval);
324                 break;
325         case MonoShortInlineBrTarget:
326                 sval = *(const signed char*)ip;
327                 ip ++;
328                 if (dh->label_target)
329                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
330                 else
331                         g_string_sprintfa (str, "%d", sval);
332                 break;
333         case MonoInlineSwitch: {
334                 const unsigned char *end;
335                 sval = read32 (ip);
336                 ip += 4;
337                 end = ip + sval * 4;
338                 g_string_append_c (str, '(');
339                 for (i = 0; i < sval; ++i) {
340                         if (i > 0)
341                                 g_string_append (str, ", ");
342                         label = read32 (ip);
343                         if (dh->label_target)
344                                 g_string_sprintfa (str, dh->label_target, end + label - header->code);
345                         else
346                                 g_string_sprintfa (str, "%d", label);
347                         ip += 4;
348                 }
349                 g_string_append_c (str, ')');
350                 break;
351         }
352         case MonoInlineR: {
353                 double r;
354                 readr8 (ip, &r);
355                 g_string_sprintfa (str, "%g", r);
356                 ip += 8;
357                 break;
358         }
359         case MonoShortInlineR: {
360                 float r;
361                 readr4 (ip, &r);
362                 g_string_sprintfa (str, "%g", r);
363                 ip += 4;
364                 break;
365         }
366         case MonoInlineI:
367                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
368                 ip += 4;
369                 break;
370         case MonoShortInlineI:
371                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
372                 ip ++;
373                 break;
374         case MonoInlineI8:
375                 ip += 8;
376                 break;
377         default:
378                 g_assert_not_reached ();
379         }
380         if (dh->newline)
381                 g_string_append (str, dh->newline);
382
383         return ip;
384 }
385
386 static MonoDisHelper
387 default_dh = {
388         "\n",
389         "IL_%04x: ", /* label_format */
390         "IL_%04x", /* label_target */
391         NULL, /* indenter */
392         NULL, /* tokener */
393         NULL  /* user data */
394 };
395
396 char*
397 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
398 {
399         char *result;
400         GString *res = g_string_new ("");
401
402         if (!dh)
403                 dh = &default_dh;
404         ip = dis_one (res, dh, method, ip);
405         if (endp)
406                 *endp = ip;
407         
408         result = res->str;
409         g_string_free (res, FALSE);
410         return result;
411 }
412
413 char*
414 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
415 {
416         char *result;
417         GString *res = g_string_new ("");
418
419         if (!dh)
420                 dh = &default_dh;
421         while (ip < end) {
422                 ip = dis_one (res, dh, method, ip);
423         }
424         
425         result = res->str;
426         g_string_free (res, FALSE);
427         return result;
428 }
429
430 char *
431 mono_method_full_name (MonoMethod *method, gboolean signature)
432 {
433         char *res;
434
435         if (signature) {
436                 char *tmpsig = mono_signature_get_desc (method->signature, TRUE);
437
438                 res = g_strdup_printf ("%02d %s.%s:%s (%s)", method->wrapper_type, method->klass->name_space, 
439                                        method->klass->name, method->name, tmpsig);
440                 g_free (tmpsig);
441         } else {
442
443                 res = g_strdup_printf ("%02d %s.%s:%s", method->wrapper_type, method->klass->name_space, 
444                                        method->klass->name, method->name);
445         }
446
447         return res;
448 }
449
450