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