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