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