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