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