New tests.
[mono.git] / mono / metadata / debug-helpers.c
1 /*
2  * console-io.c: ConsoleDriver internal calls
3  *
4  * Author:
5  *      Mono Project (http://www.mono-project.com)
6  *
7  * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
8  */
9
10 #include <string.h>
11 #include "mono/metadata/tokentype.h"
12 #include "mono/metadata/opcodes.h"
13 #include "mono/metadata/metadata-internals.h"
14 #include "mono/metadata/class-internals.h"
15 #include "mono/metadata/object-internals.h"
16 #include "mono/metadata/mono-endian.h"
17 #include "mono/metadata/debug-helpers.h"
18 #include "mono/metadata/tabledefs.h"
19 #include "mono/metadata/appdomain.h"
20
21 struct MonoMethodDesc {
22         char *namespace;
23         char *klass;
24         char *name;
25         char *args;
26         guint num_args;
27         gboolean include_namespace, klass_glob, name_glob;
28 };
29
30 #ifdef HAVE_ARRAY_ELEM_INIT
31 #define MSGSTRFIELD(line) MSGSTRFIELD1(line)
32 #define MSGSTRFIELD1(line) str##line
33 static const struct msgstr_t {
34 #define WRAPPER(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)];
35 #include "wrapper-types.h"
36 #undef WRAPPER
37 } opstr = {
38 #define WRAPPER(a,b) b,
39 #include "wrapper-types.h"
40 #undef WRAPPER
41 };
42 static const gint16 opidx [] = {
43 #define WRAPPER(a,b) [MONO_WRAPPER_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
44 #include "wrapper-types.h"
45 #undef WRAPPER
46 };
47
48 static const char*
49 wrapper_type_to_str (guint32 wrapper_type)
50 {
51         g_assert (wrapper_type < MONO_WRAPPER_NUM);
52
53         return (const char*)&opstr + opidx [wrapper_type];
54 }
55
56 #else
57 #define WRAPPER(a,b) b,
58 static const char* const
59 wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
60 #include "wrapper-types.h"
61         NULL
62 };
63
64 static const char*
65 wrapper_type_to_str (guint32 wrapper_type)
66 {
67         g_assert (wrapper_type < MONO_WRAPPER_NUM);
68
69         return wrapper_type_names [wrapper_type];
70 }
71
72 #endif
73
74 static void
75 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
76 {
77         if (!class) {
78                 g_string_append (res, "Unknown");
79                 return;
80         }
81         if (class->nested_in) {
82                 append_class_name (res, class->nested_in, include_namespace);
83                 g_string_append_c (res, '/');
84         }
85         if (include_namespace && *(class->name_space))
86                 g_string_append_printf (res, "%s.", class->name_space);
87         g_string_append_printf (res, "%s", class->name);
88 }
89
90 static MonoClass*
91 find_system_class (const char *name)
92 {
93         if (!strcmp (name, "void")) 
94                 return mono_defaults.void_class;
95         else if (!strcmp (name, "char")) return mono_defaults.char_class;
96         else if (!strcmp (name, "bool")) return mono_defaults.boolean_class;
97         else if (!strcmp (name, "byte")) return mono_defaults.byte_class;
98         else if (!strcmp (name, "sbyte")) return mono_defaults.sbyte_class;
99         else if (!strcmp (name, "uint16")) return mono_defaults.uint16_class;
100         else if (!strcmp (name, "int16")) return mono_defaults.int16_class;
101         else if (!strcmp (name, "uint")) return mono_defaults.uint32_class;
102         else if (!strcmp (name, "int")) return mono_defaults.int32_class;
103         else if (!strcmp (name, "ulong")) return mono_defaults.uint64_class;
104         else if (!strcmp (name, "long")) return mono_defaults.int64_class;
105         else if (!strcmp (name, "uintptr")) return mono_defaults.uint_class;
106         else if (!strcmp (name, "intptr")) return mono_defaults.int_class;
107         else if (!strcmp (name, "single")) return mono_defaults.single_class;
108         else if (!strcmp (name, "double")) return mono_defaults.double_class;
109         else if (!strcmp (name, "string")) return mono_defaults.string_class;
110         else if (!strcmp (name, "object")) return mono_defaults.object_class;
111         else
112                 return NULL;
113 }
114
115 void
116 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace)
117 {
118         int i;
119
120         switch (type->type) {
121         case MONO_TYPE_VOID:
122                 g_string_append (res, "void"); break;
123         case MONO_TYPE_CHAR:
124                 g_string_append (res, "char"); break;
125         case MONO_TYPE_BOOLEAN:
126                 g_string_append (res, "bool"); break;
127         case MONO_TYPE_U1:
128                 g_string_append (res, "byte"); break;
129         case MONO_TYPE_I1:
130                 g_string_append (res, "sbyte"); break;
131         case MONO_TYPE_U2:
132                 g_string_append (res, "uint16"); break;
133         case MONO_TYPE_I2:
134                 g_string_append (res, "int16"); break;
135         case MONO_TYPE_U4:
136                 g_string_append (res, "uint"); break;
137         case MONO_TYPE_I4:
138                 g_string_append (res, "int"); break;
139         case MONO_TYPE_U8:
140                 g_string_append (res, "ulong"); break;
141         case MONO_TYPE_I8:
142                 g_string_append (res, "long"); break;
143         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
144                 g_string_append (res, "*()"); break;
145         case MONO_TYPE_U:
146                 g_string_append (res, "uintptr"); break;
147         case MONO_TYPE_I:
148                 g_string_append (res, "intptr"); break;
149         case MONO_TYPE_R4:
150                 g_string_append (res, "single"); break;
151         case MONO_TYPE_R8:
152                 g_string_append (res, "double"); break;
153         case MONO_TYPE_STRING:
154                 g_string_append (res, "string"); break;
155         case MONO_TYPE_OBJECT:
156                 g_string_append (res, "object"); break;
157         case MONO_TYPE_PTR:
158                 mono_type_get_desc (res, type->data.type, include_namespace);
159                 g_string_append_c (res, '*');
160                 break;
161         case MONO_TYPE_ARRAY:
162                 mono_type_get_desc (res, &type->data.array->eklass->byval_arg, include_namespace);
163                 g_string_append_printf (res, "[%d]", type->data.array->rank);
164                 break;
165         case MONO_TYPE_SZARRAY:
166                 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
167                 g_string_append (res, "[]");
168                 break;
169         case MONO_TYPE_CLASS:
170         case MONO_TYPE_VALUETYPE:
171                 append_class_name (res, type->data.klass, include_namespace);
172                 break;
173         case MONO_TYPE_GENERICINST: {
174                 MonoGenericContext *context;
175
176                 mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace);
177                 g_string_append (res, "<");
178                 context = &type->data.generic_class->context;
179                 if (context->class_inst) {
180                         for (i = 0; i < context->class_inst->type_argc; ++i) {
181                                 if (i > 0)
182                                         g_string_append (res, ", ");
183                                 mono_type_get_desc (res, context->class_inst->type_argv [i], include_namespace);
184                         }
185                 }
186                 if (context->method_inst) {
187                         if (context->class_inst)
188                                         g_string_append (res, "; ");
189                         for (i = 0; i < context->method_inst->type_argc; ++i) {
190                                 if (i > 0)
191                                         g_string_append (res, ", ");
192                                 mono_type_get_desc (res, context->method_inst->type_argv [i], include_namespace);
193                         }
194                 }
195                 g_string_append (res, ">");
196                 break;
197         }
198         case MONO_TYPE_VAR:
199         case MONO_TYPE_MVAR:
200                 if (type->data.generic_param) {
201                         MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
202                         if (info)
203                                 g_string_append (res, info->name);
204                         else
205                                 g_string_append_printf (res, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", mono_generic_param_num (type->data.generic_param));
206                 } else {
207                         g_string_append (res, "<unknown>");
208                 }
209                 break;
210         case MONO_TYPE_TYPEDBYREF:
211                 g_string_append (res, "typedbyref");
212                 break;
213         default:
214                 break;
215         }
216         if (type->byref)
217                 g_string_append_c (res, '&');
218 }
219
220 char*
221 mono_type_full_name (MonoType *type)
222 {
223         GString *str;
224
225         str = g_string_new ("");
226         mono_type_get_desc (str, type, TRUE);
227         return g_string_free (str, FALSE);
228 }
229
230 char*
231 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
232 {
233         int i;
234         char *result;
235         GString *res;
236
237         if (!sig)
238                 return g_strdup ("<invalid signature>");
239
240         res = g_string_new ("");
241
242         for (i = 0; i < sig->param_count; ++i) {
243                 if (i > 0)
244                         g_string_append_c (res, ',');
245                 mono_type_get_desc (res, sig->params [i], include_namespace);
246         }
247         result = res->str;
248         g_string_free (res, FALSE);
249         return result;
250 }
251
252 static void
253 ginst_get_desc (GString *str, MonoGenericInst *ginst)
254 {
255         int i;
256
257         for (i = 0; i < ginst->type_argc; ++i) {
258                 if (i > 0)
259                         g_string_append (str, ", ");
260                 mono_type_get_desc (str, ginst->type_argv [i], TRUE);
261         }
262 }
263
264 char*
265 mono_context_get_desc (MonoGenericContext *context)
266 {
267         GString *str;
268         char *res;
269
270         str = g_string_new ("");
271         g_string_append (str, "<");
272
273         if (context->class_inst)
274                 ginst_get_desc (str, context->class_inst);
275         if (context->method_inst) {
276                 if (context->class_inst)
277                         g_string_append (str, "; ");
278                 ginst_get_desc (str, context->method_inst);
279         }
280
281         g_string_append (str, ">");
282         res = g_strdup (str->str);
283         g_string_free (str, TRUE);
284         return res;
285 }       
286
287 /**
288  * mono_method_desc_new:
289  * @name: the method name.
290  * @include_namespace: whether the name includes a namespace or not.
291  *
292  * Creates a method description for @name, which conforms to the following
293  * specification:
294  *
295  * [namespace.]classname:methodname[(args...)]
296  *
297  * in all the loaded assemblies.
298  *
299  * Both classname and methodname can contain '*' which matches anything.
300  *
301  * Returns: a parsed representation of the method description.
302  */
303 MonoMethodDesc*
304 mono_method_desc_new (const char *name, gboolean include_namespace)
305 {
306         MonoMethodDesc *result;
307         char *class_name, *class_nspace, *method_name, *use_args, *end;
308         int use_namespace;
309         
310         class_nspace = g_strdup (name);
311         use_args = strchr (class_nspace, '(');
312         if (use_args) {
313                 /* Allow a ' ' between the method name and the signature */
314                 if (use_args > class_nspace && use_args [-1] == ' ')
315                         use_args [-1] = 0;
316                 *use_args++ = 0;
317                 end = strchr (use_args, ')');
318                 if (!end) {
319                         g_free (class_nspace);
320                         return NULL;
321                 }
322                 *end = 0;
323         }
324         method_name = strrchr (class_nspace, ':');
325         if (!method_name) {
326                 g_free (class_nspace);
327                 return NULL;
328         }
329         *method_name++ = 0;
330         /* allow two :: to separate the method name */
331         if (*method_name == ':')
332                 method_name++;
333         class_name = strrchr (class_nspace, '.');
334         if (class_name) {
335                 *class_name++ = 0;
336                 use_namespace = 1;
337         } else {
338                 class_name = class_nspace;
339                 use_namespace = 0;
340         }
341         result = g_new0 (MonoMethodDesc, 1);
342         result->include_namespace = include_namespace;
343         result->name = method_name;
344         result->klass = class_name;
345         result->namespace = use_namespace? class_nspace: NULL;
346         result->args = use_args? use_args: NULL;
347         if (strstr (result->name, "*"))
348                 result->name_glob = TRUE;
349         if (strstr (result->klass, "*"))
350                 result->klass_glob = TRUE;
351         if (use_args) {
352                 end = use_args;
353                 if (*end)
354                         result->num_args = 1;
355                 while (*end) {
356                         if (*end == ',')
357                                 result->num_args++;
358                         ++end;
359                 }
360         }
361
362         return result;
363 }
364
365 MonoMethodDesc*
366 mono_method_desc_from_method (MonoMethod *method)
367 {
368         MonoMethodDesc *result;
369         
370         result = g_new0 (MonoMethodDesc, 1);
371         result->include_namespace = TRUE;
372         result->name = g_strdup (method->name);
373         result->klass = g_strdup (method->klass->name);
374         result->namespace = g_strdup (method->klass->name_space);
375
376         return result;
377 }
378
379 /**
380  * mono_method_desc_free:
381  * @desc: method description to be released
382  *
383  * Releases the MonoMethodDesc object @desc.
384  */
385 void
386 mono_method_desc_free (MonoMethodDesc *desc)
387 {
388         if (desc->namespace)
389                 g_free (desc->namespace);
390         else if (desc->klass)
391                 g_free (desc->klass);
392         g_free (desc);
393 }
394
395 /*
396  * namespace and class are supposed to match already if this function is used.
397  */
398 gboolean
399 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
400 {
401         char *sig;
402         gboolean name_match;
403
404         name_match = strcmp (desc->name, method->name) == 0;
405 #ifndef _EGLIB_MAJOR
406         if (!name_match && desc->name_glob)
407                 name_match = g_pattern_match_simple (desc->name, method->name);
408 #endif
409         if (!name_match)
410                 return FALSE;
411         if (!desc->args)
412                 return TRUE;
413         if (desc->num_args != mono_method_signature (method)->param_count)
414                 return FALSE;
415         sig = mono_signature_get_desc (mono_method_signature (method), desc->include_namespace);
416         if (strcmp (sig, desc->args)) {
417                 g_free (sig);
418                 return FALSE;
419         }
420         g_free (sig);
421         return TRUE;
422 }
423
424 static const char *
425 my_strrchr (const char *str, char ch, int *len)
426 {
427         int pos;
428
429         for (pos = (*len)-1; pos >= 0; pos--) {
430                 if (str [pos] != ch)
431                         continue;
432
433                 *len = pos;
434                 return str + pos;
435         }
436
437         return NULL;
438 }
439
440 static gboolean
441 match_class (MonoMethodDesc *desc, int pos, MonoClass *klass)
442 {
443         const char *p;
444
445         if (desc->klass_glob && !strcmp (desc->klass, "*"))
446                 return TRUE;
447 #ifndef _EGLIB_MAJOR
448         if (desc->klass_glob && g_pattern_match_simple (desc->klass, klass->name))
449                 return TRUE;
450 #endif
451         p = my_strrchr (desc->klass, '/', &pos);
452         if (!p) {
453                 if (strncmp (desc->klass, klass->name, pos))
454                         return FALSE;
455                 if (desc->namespace && strcmp (desc->namespace, klass->name_space))
456                         return FALSE;
457                 return TRUE;
458         }
459
460         if (strcmp (p+1, klass->name))
461                 return FALSE;
462         if (!klass->nested_in)
463                 return FALSE;
464
465         return match_class (desc, pos, klass->nested_in);
466 }
467
468 gboolean
469 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
470 {
471         if (!match_class (desc, strlen (desc->klass), method->klass))
472                 return FALSE;
473
474         return mono_method_desc_match (desc, method);
475 }
476
477 MonoMethod*
478 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
479 {
480         MonoMethod* m;
481         gpointer iter = NULL;
482         
483         while ((m = mono_class_get_methods (klass, &iter)))
484                 if (mono_method_desc_match (desc, m))
485                         return m;
486         return NULL;
487 }
488
489 MonoMethod*
490 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
491 {
492         MonoClass *klass;
493         const MonoTableInfo *tdef;
494         const MonoTableInfo *methods;
495         MonoMethod *method;
496         int i;
497
498         /* Handle short names for system classes */
499         if (!desc->namespace && image == mono_defaults.corlib) {
500                 klass = find_system_class (desc->klass);
501                 if (klass)
502                         return mono_method_desc_search_in_class (desc, klass);
503         }
504
505         if (desc->namespace && desc->klass) {
506                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
507                 if (!klass)
508                         return NULL;
509                 return mono_method_desc_search_in_class (desc, klass);
510         }
511
512         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
513         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
514         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
515                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
516                 const char *n = mono_metadata_string_heap (image, token);
517
518                 if (strcmp (n, desc->name))
519                         continue;
520                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
521                 if (mono_method_desc_full_match (desc, method))
522                         return method;
523         }
524         return NULL;
525 }
526
527 static const unsigned char*
528 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
529 {
530         MonoMethodHeader *header = mono_method_get_header (method);
531         const MonoOpcode *opcode;
532         guint32 label, token;
533         gint32 sval;
534         int i;
535         char *tmp;
536         const unsigned char* il_code = mono_method_header_get_code (header, NULL, NULL);
537
538         label = ip - il_code;
539         if (dh->indenter) {
540                 tmp = dh->indenter (dh, method, label);
541                 g_string_append (str, tmp);
542                 g_free (tmp);
543         }
544         if (dh->label_format)
545                 g_string_append_printf (str, dh->label_format, label);
546         
547         i = mono_opcode_value (&ip, end);
548         ip++;
549         opcode = &mono_opcodes [i];
550         g_string_append_printf (str, "%-10s", mono_opcode_name (i));
551
552         switch (opcode->argument) {
553         case MonoInlineNone:
554                 break;
555         case MonoInlineType:
556         case MonoInlineField:
557         case MonoInlineMethod:
558         case MonoInlineTok:
559         case MonoInlineSig:
560                 token = read32 (ip);
561                 if (dh->tokener) {
562                         tmp = dh->tokener (dh, method, token);
563                         g_string_append (str, tmp);
564                         g_free (tmp);
565                 } else {
566                         g_string_append_printf (str, "0x%08x", token);
567                 }
568                 ip += 4;
569                 break;
570         case MonoInlineString: {
571                 const char *blob;
572                 char *s;
573                 size_t len2;
574                 char *blob2 = NULL;
575
576                 if (!method->klass->image->dynamic) {
577                         token = read32 (ip);
578                         blob = mono_metadata_user_string (method->klass->image, mono_metadata_token_index (token));
579
580                         len2 = mono_metadata_decode_blob_size (blob, &blob);
581                         len2 >>= 1;
582
583 #ifdef NO_UNALIGNED_ACCESS
584                         /* The blob might not be 2 byte aligned */
585                         blob2 = g_malloc ((len2 * 2) + 1);
586                         memcpy (blob2, blob, len2 * 2);
587 #else
588                         blob2 = (char*)blob;
589 #endif
590
591 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
592                         {
593                                 guint16 *buf = g_new (guint16, len2 + 1);
594                                 int i;
595
596                                 for (i = 0; i < len2; ++i)
597                                         buf [i] = GUINT16_FROM_LE (((guint16*)blob2) [i]);
598                                 s = g_utf16_to_utf8 (buf, len2, NULL, NULL, NULL);
599                                 g_free (buf);
600                         }
601 #else
602                                 s = g_utf16_to_utf8 ((gunichar2*)blob2, len2, NULL, NULL, NULL);
603 #endif
604
605                         g_string_append_printf (str, "\"%s\"", s);
606                         g_free (s);
607                         if (blob != blob2)
608                                 g_free (blob2);
609                 }
610                 ip += 4;
611                 break;
612         }
613         case MonoInlineVar:
614                 g_string_append_printf (str, "%d", read16 (ip));
615                 ip += 2;
616                 break;
617         case MonoShortInlineVar:
618                 g_string_append_printf (str, "%d", (*ip));
619                 ip ++;
620                 break;
621         case MonoInlineBrTarget:
622                 sval = read32 (ip);
623                 ip += 4;
624                 if (dh->label_target)
625                         g_string_append_printf (str, dh->label_target, ip + sval - il_code);
626                 else
627                         g_string_append_printf (str, "%d", sval);
628                 break;
629         case MonoShortInlineBrTarget:
630                 sval = *(const signed char*)ip;
631                 ip ++;
632                 if (dh->label_target)
633                         g_string_append_printf (str, dh->label_target, ip + sval - il_code);
634                 else
635                         g_string_append_printf (str, "%d", sval);
636                 break;
637         case MonoInlineSwitch: {
638                 const unsigned char *end;
639                 sval = read32 (ip);
640                 ip += 4;
641                 end = ip + sval * 4;
642                 g_string_append_c (str, '(');
643                 for (i = 0; i < sval; ++i) {
644                         if (i > 0)
645                                 g_string_append (str, ", ");
646                         label = read32 (ip);
647                         if (dh->label_target)
648                                 g_string_append_printf (str, dh->label_target, end + label - il_code);
649                         else
650                                 g_string_append_printf (str, "%d", label);
651                         ip += 4;
652                 }
653                 g_string_append_c (str, ')');
654                 break;
655         }
656         case MonoInlineR: {
657                 double r;
658                 readr8 (ip, &r);
659                 g_string_append_printf (str, "%g", r);
660                 ip += 8;
661                 break;
662         }
663         case MonoShortInlineR: {
664                 float r;
665                 readr4 (ip, &r);
666                 g_string_append_printf (str, "%g", r);
667                 ip += 4;
668                 break;
669         }
670         case MonoInlineI:
671                 g_string_append_printf (str, "%d", (gint32)read32 (ip));
672                 ip += 4;
673                 break;
674         case MonoShortInlineI:
675                 g_string_append_printf (str, "%d", *(const signed char*)ip);
676                 ip ++;
677                 break;
678         case MonoInlineI8:
679                 ip += 8;
680                 break;
681         default:
682                 g_assert_not_reached ();
683         }
684         if (dh->newline)
685                 g_string_append (str, dh->newline);
686
687         mono_metadata_free_mh (header);
688         return ip;
689 }
690
691 static MonoDisHelper
692 default_dh = {
693         "\n",
694         "IL_%04x: ", /* label_format */
695         "IL_%04x", /* label_target */
696         NULL, /* indenter */
697         NULL, /* tokener */
698         NULL  /* user data */
699 };
700
701 char*
702 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
703 {
704         char *result;
705         GString *res = g_string_new ("");
706
707         if (!dh)
708                 dh = &default_dh;
709         /* set ip + 2 as the end: this is just a debugging method */
710         ip = dis_one (res, dh, method, ip, ip + 2);
711         if (endp)
712                 *endp = ip;
713         
714         result = res->str;
715         g_string_free (res, FALSE);
716         return result;
717 }
718
719 char*
720 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
721 {
722         char *result;
723         GString *res = g_string_new ("");
724
725         if (!dh)
726                 dh = &default_dh;
727         while (ip < end) {
728                 ip = dis_one (res, dh, method, ip, end);
729         }
730         
731         result = res->str;
732         g_string_free (res, FALSE);
733         return result;
734 }
735
736 char *
737 mono_field_full_name (MonoClassField *field)
738 {
739         char *res;
740         const char *nspace = field->parent->name_space;
741
742         res = g_strdup_printf ("%s%s%s:%s", nspace, *nspace ? "." : "",
743                                                    field->parent->name, mono_field_get_name (field));
744
745         return res;
746 }
747
748 char *
749 mono_method_full_name (MonoMethod *method, gboolean signature)
750 {
751         char *res;
752         char wrapper [64];
753         char *klass_desc = mono_type_full_name (&method->klass->byval_arg);
754         char *inst_desc = NULL;
755
756         if (method->is_inflated && ((MonoMethodInflated*)method)->context.method_inst) {
757                 GString *str = g_string_new ("");
758                 g_string_append (str, "<");
759                 ginst_get_desc (str, ((MonoMethodInflated*)method)->context.method_inst);
760                 g_string_append (str, ">");
761
762                 inst_desc = str->str;
763                 g_string_free (str, FALSE);
764         } else if (method->is_generic) {
765                 MonoGenericContainer *container = mono_method_get_generic_container (method);
766
767                 GString *str = g_string_new ("");
768                 g_string_append (str, "<");
769                 ginst_get_desc (str, container->context.method_inst);
770                 g_string_append (str, ">");
771
772                 inst_desc = str->str;
773                 g_string_free (str, FALSE);
774         }
775
776         if (method->wrapper_type != MONO_WRAPPER_NONE)
777                 sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
778         else
779                 strcpy (wrapper, "");
780
781         if (signature) {
782                 char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
783
784                 if (method->wrapper_type != MONO_WRAPPER_NONE)
785                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
786                 else
787                         strcpy (wrapper, "");
788                 res = g_strdup_printf ("%s%s:%s%s (%s)", wrapper, klass_desc, 
789                                                            method->name, inst_desc ? inst_desc : "", tmpsig);
790                 g_free (tmpsig);
791         } else {
792                 res = g_strdup_printf ("%s%s:%s%s", wrapper, klass_desc,
793                                                            method->name, inst_desc ? inst_desc : "");
794         }
795
796         g_free (klass_desc);
797         g_free (inst_desc);
798
799         return res;
800 }
801
802 static const char*
803 print_name_space (MonoClass *klass)
804 {
805         if (klass->nested_in) {
806                 print_name_space (klass->nested_in);
807                 g_print ("%s", klass->nested_in->name);
808                 return "/";
809         }
810         if (klass->name_space [0]) {
811                 g_print ("%s", klass->name_space);
812                 return ".";
813         }
814         return "";
815 }
816
817 /**
818  * mono_object_describe:
819  *
820  * Prints to stdout a small description of the object @obj.
821  * For use in a debugger.
822  */
823 void
824 mono_object_describe (MonoObject *obj)
825 {
826         MonoClass* klass;
827         const char* sep;
828         if (!obj) {
829                 g_print ("(null)\n");
830                 return;
831         }
832         klass = mono_object_class (obj);
833         if (klass == mono_defaults.string_class) {
834                 char *utf8 = mono_string_to_utf8 ((MonoString*)obj);
835                 if (strlen (utf8) > 60) {
836                         utf8 [57] = '.';
837                         utf8 [58] = '.';
838                         utf8 [59] = '.';
839                         utf8 [60] = 0;
840                 }
841                 g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8);
842                 g_free (utf8);
843         } else if (klass->rank) {
844                 MonoArray *array = (MonoArray*)obj;
845                 sep = print_name_space (klass);
846                 g_print ("%s%s", sep, klass->name);
847                 g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, (int)mono_array_length (array));
848         } else {
849                 sep = print_name_space (klass);
850                 g_print ("%s%s", sep, klass->name);
851                 g_print (" object at %p (klass: %p)\n", obj, klass);
852         }
853
854 }
855
856 static void
857 print_field_value (const char *field_ptr, MonoClassField *field, int type_offset)
858 {
859         MonoType *type;
860         g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, mono_field_get_name (field));
861         type = mono_type_get_underlying_type (field->type);
862
863         switch (type->type) {
864         case MONO_TYPE_I:
865         case MONO_TYPE_U:
866         case MONO_TYPE_PTR:
867         case MONO_TYPE_FNPTR:
868                 g_print ("%p\n", *(const void**)field_ptr);
869                 break;
870         case MONO_TYPE_STRING:
871         case MONO_TYPE_SZARRAY:
872         case MONO_TYPE_CLASS:
873         case MONO_TYPE_OBJECT:
874         case MONO_TYPE_ARRAY:
875                 mono_object_describe (*(MonoObject**)field_ptr);
876                 break;
877         case MONO_TYPE_GENERICINST:
878                 if (!mono_type_generic_inst_is_valuetype (type)) {
879                         mono_object_describe (*(MonoObject**)field_ptr);
880                         break;
881                 } else {
882                         /* fall through */
883                 }
884         case MONO_TYPE_VALUETYPE: {
885                 MonoClass *k = mono_class_from_mono_type (type);
886                 g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr);
887                 break;
888         }
889         case MONO_TYPE_I1:
890                 g_print ("%d\n", *(gint8*)field_ptr);
891                 break;
892         case MONO_TYPE_U1:
893                 g_print ("%d\n", *(guint8*)field_ptr);
894                 break;
895         case MONO_TYPE_I2:
896                 g_print ("%d\n", *(gint16*)field_ptr);
897                 break;
898         case MONO_TYPE_U2:
899                 g_print ("%d\n", *(guint16*)field_ptr);
900                 break;
901         case MONO_TYPE_I4:
902                 g_print ("%d\n", *(gint32*)field_ptr);
903                 break;
904         case MONO_TYPE_U4:
905                 g_print ("%u\n", *(guint32*)field_ptr);
906                 break;
907         case MONO_TYPE_I8:
908                 g_print ("%lld\n", (long long int)*(gint64*)field_ptr);
909                 break;
910         case MONO_TYPE_U8:
911                 g_print ("%llu\n", (long long unsigned int)*(guint64*)field_ptr);
912                 break;
913         case MONO_TYPE_R4:
914                 g_print ("%f\n", *(gfloat*)field_ptr);
915                 break;
916         case MONO_TYPE_R8:
917                 g_print ("%f\n", *(gdouble*)field_ptr);
918                 break;
919         case MONO_TYPE_BOOLEAN:
920                 g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr);
921                 break;
922         case MONO_TYPE_CHAR:
923                 g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr);
924                 break;
925         default:
926                 g_assert_not_reached ();
927                 break;
928         }
929 }
930
931 static void
932 objval_describe (MonoClass *class, const char *addr)
933 {
934         MonoClassField *field;
935         MonoClass *p;
936         const char *field_ptr;
937         gssize type_offset = 0;
938         if (class->valuetype)
939                 type_offset = -sizeof (MonoObject);
940
941         for (p = class; p != NULL; p = p->parent) {
942                 gpointer iter = NULL;
943                 int printed_header = FALSE;
944                 while ((field = mono_class_get_fields (p, &iter))) {
945                         if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
946                                 continue;
947
948                         if (p != class && !printed_header) {
949                                 const char *sep;
950                                 g_print ("In class ");
951                                 sep = print_name_space (p);
952                                 g_print ("%s%s:\n", sep, p->name);
953                                 printed_header = TRUE;
954                         }
955                         field_ptr = (const char*)addr + field->offset + type_offset;
956
957                         print_field_value (field_ptr, field, type_offset);
958                 }
959         }
960 }
961
962 /**
963  * mono_object_describe_fields:
964  *
965  * Prints to stdout a small description of each field of the object @obj.
966  * For use in a debugger.
967  */
968 void
969 mono_object_describe_fields (MonoObject *obj)
970 {
971         MonoClass *class = mono_object_class (obj);
972         objval_describe (class, (char*)obj);
973 }
974
975 /**
976  * mono_value_describe_fields:
977  *
978  * Prints to stdout a small description of each field of the value type
979  * stored at @addr of type @klass.
980  * For use in a debugger.
981  */
982 void
983 mono_value_describe_fields (MonoClass* klass, const char* addr)
984 {
985         objval_describe (klass, addr);
986 }
987
988 /**
989  * mono_class_describe_statics:
990  *
991  * Prints to stdout a small description of each static field of the type @klass
992  * in the current application domain.
993  * For use in a debugger.
994  */
995 void
996 mono_class_describe_statics (MonoClass* klass)
997 {
998         MonoClassField *field;
999         MonoClass *p;
1000         const char *field_ptr;
1001         MonoVTable *vtable = mono_class_vtable_full (mono_domain_get (), klass, FALSE);
1002         const char *addr;
1003
1004         if (!vtable)
1005                 return;
1006         if (!(addr = vtable->data))
1007                 return;
1008
1009         for (p = klass; p != NULL; p = p->parent) {
1010                 gpointer iter = NULL;
1011                 while ((field = mono_class_get_fields (p, &iter))) {
1012                         if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
1013                                 continue;
1014                         if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
1015                                 continue;
1016
1017                         field_ptr = (const char*)addr + field->offset;
1018
1019                         print_field_value (field_ptr, field, 0);
1020                 }
1021         }
1022 }
1023