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