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