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