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