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