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