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