4831a85bd93517a4c4492ff059c0be7226423df9
[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/class-internals.h"
6 #include "mono/metadata/mono-endian.h"
7 #include "mono/metadata/debug-helpers.h"
8
9 struct MonoMethodDesc {
10         char *namespace;
11         char *klass;
12         char *name;
13         char *args;
14         guint num_args;
15         gboolean include_namespace;
16 };
17
18 static const char *wrapper_type_names [] = {
19         "none",
20         "delegate-invoke",
21         "delegate-begin-invoke",
22         "delegate-end-invoke",
23         "runtime-invoke",
24         "native-to-managed",
25         "managed-to-native",
26         "remoting-invoke",
27         "remoting-invoke-with-check",
28         "xdomain-invoke",
29         "xdomain-dispatch",
30         "ldfld",
31         "stfld",
32         "ldfld-remote",
33         "stfld-remote",
34         "synchronized",
35         "dynamic-method",
36         "isinst",
37         "cancast",
38         "proxy_isinst",
39         "stelemref",
40         "unbox",
41         "ldflda",
42         "unknown"
43 };
44
45 static void
46 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
47 {
48         if (!class) {
49                 g_string_append (res, "Unknown");
50                 return;
51         }
52         if (class->nested_in) {
53                 append_class_name (res, class->nested_in, include_namespace);
54                 g_string_append_c (res, '/');
55         }
56         if (include_namespace && *(class->name_space))
57                 g_string_sprintfa (res, "%s.", class->name_space);
58         g_string_sprintfa (res, "%s", class->name);
59 }
60
61 void
62 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
63         switch (type->type) {
64         case MONO_TYPE_VOID:
65                 g_string_append (res, "void"); break;
66         case MONO_TYPE_CHAR:
67                 g_string_append (res, "char"); break;
68         case MONO_TYPE_BOOLEAN:
69                 g_string_append (res, "bool"); break;
70         case MONO_TYPE_U1:
71                 g_string_append (res, "byte"); break;
72         case MONO_TYPE_I1:
73                 g_string_append (res, "sbyte"); break;
74         case MONO_TYPE_U2:
75                 g_string_append (res, "uint16"); break;
76         case MONO_TYPE_I2:
77                 g_string_append (res, "int16"); break;
78         case MONO_TYPE_U4:
79                 g_string_append (res, "uint"); break;
80         case MONO_TYPE_I4:
81                 g_string_append (res, "int"); break;
82         case MONO_TYPE_U8:
83                 g_string_append (res, "ulong"); break;
84         case MONO_TYPE_I8:
85                 g_string_append (res, "long"); break;
86         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
87                 g_string_append (res, "*()"); break;
88         case MONO_TYPE_U:
89                 g_string_append (res, "uintptr"); break;
90         case MONO_TYPE_I:
91                 g_string_append (res, "intptr"); break;
92         case MONO_TYPE_R4:
93                 g_string_append (res, "single"); break;
94         case MONO_TYPE_R8:
95                 g_string_append (res, "double"); break;
96         case MONO_TYPE_STRING:
97                 g_string_append (res, "string"); break;
98         case MONO_TYPE_OBJECT:
99                 g_string_append (res, "object"); break;
100         case MONO_TYPE_PTR:
101                 mono_type_get_desc (res, type->data.type, include_namespace);
102                 g_string_append_c (res, '*');
103                 break;
104         case MONO_TYPE_ARRAY:
105                 append_class_name (res, type->data.array->eklass, include_namespace);
106                 g_string_sprintfa (res, "[%d]", type->data.array->rank);
107                 break;
108         case MONO_TYPE_SZARRAY:
109                 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
110                 g_string_append (res, "[]");
111                 break;
112         case MONO_TYPE_CLASS:
113         case MONO_TYPE_VALUETYPE:
114                 append_class_name (res, type->data.klass, include_namespace);
115                 break;
116         case MONO_TYPE_GENERICINST:
117                 mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace);
118                 break;
119         default:
120                 break;
121         }
122         if (type->byref)
123                 g_string_append_c (res, '&');
124 }
125
126 char*
127 mono_type_full_name (MonoType *type)
128 {
129         GString *str;
130         char *res;
131
132         str = g_string_new ("");
133         mono_type_get_desc (str, type, TRUE);
134
135         res = g_strdup (str->str);
136         g_string_free (str, TRUE);
137         return res;
138 }
139
140 char*
141 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
142 {
143         int i;
144         char *result;
145         GString *res = g_string_new ("");
146
147         for (i = 0; i < sig->param_count; ++i) {
148                 if (i > 0)
149                         g_string_append_c (res, ',');
150                 mono_type_get_desc (res, sig->params [i], include_namespace);
151         }
152         result = res->str;
153         g_string_free (res, FALSE);
154         return result;
155 }
156
157 /**
158  * mono_method_desc_new:
159  * @name: the method name.
160  * @include_namespace: whether the name includes a namespace or not.
161  *
162  * Creates a method description for @name, which conforms to the following
163  * specification:
164  *
165  * [namespace.]classname:methodname[(args...)]
166  *
167  * in all the loaded assemblies.
168  *
169  * Returns: a parsed representation of the method description.
170  */
171 MonoMethodDesc*
172 mono_method_desc_new (const char *name, gboolean include_namespace)
173 {
174         MonoMethodDesc *result;
175         char *class_name, *class_nspace, *method_name, *use_args, *end;
176         int use_namespace;
177         
178         class_nspace = g_strdup (name);
179         use_args = strchr (class_nspace, '(');
180         if (use_args) {
181                 *use_args++ = 0;
182                 end = strchr (use_args, ')');
183                 if (!end) {
184                         g_free (class_nspace);
185                         return NULL;
186                 }
187                 *end = 0;
188         }
189         method_name = strrchr (class_nspace, ':');
190         if (!method_name) {
191                 g_free (class_nspace);
192                 return NULL;
193         }
194         *method_name++ = 0;
195         /* allow two :: to separate the method name */
196         if (*method_name == ':')
197                 method_name++;
198         class_name = strrchr (class_nspace, '.');
199         if (class_name) {
200                 *class_name++ = 0;
201                 use_namespace = 1;
202         } else {
203                 class_name = class_nspace;
204                 use_namespace = 0;
205         }
206         result = g_new0 (MonoMethodDesc, 1);
207         result->include_namespace = include_namespace;
208         result->name = method_name;
209         result->klass = class_name;
210         result->namespace = use_namespace? class_nspace: NULL;
211         result->args = use_args? use_args: NULL;
212         if (use_args) {
213                 end = use_args;
214                 if (*end)
215                         result->num_args = 1;
216                 while (*end) {
217                         if (*end == ',')
218                                 result->num_args++;
219                         ++end;
220                 }
221         }
222
223         return result;
224 }
225
226 MonoMethodDesc*
227 mono_method_desc_from_method (MonoMethod *method)
228 {
229         MonoMethodDesc *result;
230         
231         result = g_new0 (MonoMethodDesc, 1);
232         result->include_namespace = TRUE;
233         result->name = g_strdup (method->name);
234         result->klass = g_strdup (method->klass->name);
235         result->namespace = g_strdup (method->klass->name_space);
236
237         return result;
238 }
239
240 /**
241  * mono_method_desc_free:
242  * @desc: method description to be released
243  *
244  * Releases the MonoMethodDesc object @desc.
245  */
246 void
247 mono_method_desc_free (MonoMethodDesc *desc)
248 {
249         if (desc->namespace)
250                 g_free (desc->namespace);
251         else if (desc->klass)
252                 g_free (desc->klass);
253         g_free (desc);
254 }
255
256 /*
257  * namespace and class are supposed to match already if this function is used.
258  */
259 gboolean
260 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
261 {
262         char *sig;
263         if (strcmp (desc->name, method->name))
264                 return FALSE;
265         if (!desc->args)
266                 return TRUE;
267         if (desc->num_args != mono_method_signature (method)->param_count)
268                 return FALSE;
269         sig = mono_signature_get_desc (mono_method_signature (method), desc->include_namespace);
270         if (strcmp (sig, desc->args)) {
271                 g_free (sig);
272                 return FALSE;
273         }
274         g_free (sig);
275         return TRUE;
276 }
277
278 gboolean
279 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
280 {
281         if (strcmp (desc->klass, method->klass->name))
282                 return FALSE;
283         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
284                 return FALSE;
285         return mono_method_desc_match (desc, method);
286 }
287
288 MonoMethod*
289 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
290 {
291         MonoMethod* m;
292         gpointer iter = NULL;
293         
294         while ((m = mono_class_get_methods (klass, &iter)))
295                 if (mono_method_desc_match (desc, m))
296                         return m;
297         return NULL;
298 }
299
300 MonoMethod*
301 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
302 {
303         MonoClass *klass;
304         const MonoTableInfo *tdef;
305         const MonoTableInfo *methods;
306         MonoMethod *method;
307         int i;
308
309         if (desc->namespace && desc->klass) {
310                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
311                 if (!klass)
312                         return NULL;
313                 return mono_method_desc_search_in_class (desc, klass);
314         }
315
316         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
317         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
318         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
319                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
320                 const char *n = mono_metadata_string_heap (image, token);
321
322                 if (strcmp (n, desc->name))
323                         continue;
324                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
325                 if (mono_method_desc_full_match (desc, method))
326                         return method;
327         }
328         return NULL;
329 }
330
331 static const unsigned char*
332 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
333 {
334         MonoMethodHeader *header = mono_method_get_header (method);
335         const MonoOpcode *opcode;
336         guint32 i, label, token;
337         gint32 sval;
338         char *tmp;
339
340         label = ip - header->code;
341         if (dh->indenter) {
342                 tmp = dh->indenter (dh, method, label);
343                 g_string_append (str, tmp);
344                 g_free (tmp);
345         }
346         if (dh->label_format)
347                 g_string_sprintfa (str, dh->label_format, label);
348         
349         i = mono_opcode_value (&ip, end);
350         ip++;
351         opcode = &mono_opcodes [i];
352         g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
353
354         switch (opcode->argument) {
355         case MonoInlineNone:
356                 break;
357         case MonoInlineType:
358         case MonoInlineField:
359         case MonoInlineMethod:
360         case MonoInlineTok:
361         case MonoInlineSig:
362                 token = read32 (ip);
363                 if (dh->tokener) {
364                         tmp = dh->tokener (dh, method, token);
365                         g_string_append (str, tmp);
366                         g_free (tmp);
367                 } else {
368                         g_string_sprintfa (str, "0x%08x", token);
369                 }
370                 ip += 4;
371                 break;
372         case MonoInlineString:
373                 /* TODO */
374                 ip += 4;
375                 break;
376         case MonoInlineVar:
377                 g_string_sprintfa (str, "%d", read16 (ip));
378                 ip += 2;
379                 break;
380         case MonoShortInlineVar:
381                 g_string_sprintfa (str, "%d", (*ip));
382                 ip ++;
383                 break;
384         case MonoInlineBrTarget:
385                 sval = read32 (ip);
386                 ip += 4;
387                 if (dh->label_target)
388                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
389                 else
390                         g_string_sprintfa (str, "%d", sval);
391                 break;
392         case MonoShortInlineBrTarget:
393                 sval = *(const signed char*)ip;
394                 ip ++;
395                 if (dh->label_target)
396                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
397                 else
398                         g_string_sprintfa (str, "%d", sval);
399                 break;
400         case MonoInlineSwitch: {
401                 const unsigned char *end;
402                 sval = read32 (ip);
403                 ip += 4;
404                 end = ip + sval * 4;
405                 g_string_append_c (str, '(');
406                 for (i = 0; i < sval; ++i) {
407                         if (i > 0)
408                                 g_string_append (str, ", ");
409                         label = read32 (ip);
410                         if (dh->label_target)
411                                 g_string_sprintfa (str, dh->label_target, end + label - header->code);
412                         else
413                                 g_string_sprintfa (str, "%d", label);
414                         ip += 4;
415                 }
416                 g_string_append_c (str, ')');
417                 break;
418         }
419         case MonoInlineR: {
420                 double r;
421                 readr8 (ip, &r);
422                 g_string_sprintfa (str, "%g", r);
423                 ip += 8;
424                 break;
425         }
426         case MonoShortInlineR: {
427                 float r;
428                 readr4 (ip, &r);
429                 g_string_sprintfa (str, "%g", r);
430                 ip += 4;
431                 break;
432         }
433         case MonoInlineI:
434                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
435                 ip += 4;
436                 break;
437         case MonoShortInlineI:
438                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
439                 ip ++;
440                 break;
441         case MonoInlineI8:
442                 ip += 8;
443                 break;
444         default:
445                 g_assert_not_reached ();
446         }
447         if (dh->newline)
448                 g_string_append (str, dh->newline);
449
450         return ip;
451 }
452
453 static MonoDisHelper
454 default_dh = {
455         "\n",
456         "IL_%04x: ", /* label_format */
457         "IL_%04x", /* label_target */
458         NULL, /* indenter */
459         NULL, /* tokener */
460         NULL  /* user data */
461 };
462
463 char*
464 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
465 {
466         char *result;
467         GString *res = g_string_new ("");
468
469         if (!dh)
470                 dh = &default_dh;
471         /* set ip + 2 as the end: this is just a debugging method */
472         ip = dis_one (res, dh, method, ip, ip + 2);
473         if (endp)
474                 *endp = ip;
475         
476         result = res->str;
477         g_string_free (res, FALSE);
478         return result;
479 }
480
481 char*
482 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
483 {
484         char *result;
485         GString *res = g_string_new ("");
486
487         if (!dh)
488                 dh = &default_dh;
489         while (ip < end) {
490                 ip = dis_one (res, dh, method, ip, end);
491         }
492         
493         result = res->str;
494         g_string_free (res, FALSE);
495         return result;
496 }
497
498 static const char*
499 wrapper_type_to_str (guint32 wrapper_type)
500 {
501         g_assert (wrapper_type < sizeof (wrapper_type_names) / sizeof (char*));
502
503         return wrapper_type_names [wrapper_type];
504 }
505
506 char *
507 mono_method_full_name (MonoMethod *method, gboolean signature)
508 {
509         char *res;
510         char wrapper [64];
511         const char *nspace = method->klass->name_space;
512
513         if (signature) {
514                 char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
515
516                 if (method->wrapper_type != MONO_WRAPPER_NONE)
517                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
518                 else
519                         strcpy (wrapper, "");
520                 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper, 
521                                                            nspace, *nspace ? "." : "",
522                                                            method->klass->name, method->name, tmpsig);
523                 g_free (tmpsig);
524         } else {
525
526                 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
527                                                            nspace, *nspace ? "." : "",
528                                                            method->klass->name, method->name);
529         }
530
531         return res;
532 }