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