+/*
+ * console-io.c: ConsoleDriver internal calls
+ *
+ * Author:
+ * Mono Project (http://www.mono-project.com)
+ *
+ * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
+ */
#include <string.h>
#include "mono/metadata/tokentype.h"
#include "mono/metadata/opcodes.h"
+#include "mono/metadata/metadata-internals.h"
#include "mono/metadata/class-internals.h"
+#include "mono/metadata/object-internals.h"
#include "mono/metadata/mono-endian.h"
#include "mono/metadata/debug-helpers.h"
+#include "mono/metadata/tabledefs.h"
+#include "mono/metadata/appdomain.h"
struct MonoMethodDesc {
char *namespace;
char *name;
char *args;
guint num_args;
- gboolean include_namespace;
+ gboolean include_namespace, klass_glob, name_glob;
+};
+
+#ifdef HAVE_ARRAY_ELEM_INIT
+#define MSGSTRFIELD(line) MSGSTRFIELD1(line)
+#define MSGSTRFIELD1(line) str##line
+static const struct msgstr_t {
+#define WRAPPER(a,b) char MSGSTRFIELD(__LINE__) [sizeof (b)];
+#include "wrapper-types.h"
+#undef WRAPPER
+} opstr = {
+#define WRAPPER(a,b) b,
+#include "wrapper-types.h"
+#undef WRAPPER
+};
+static const gint16 opidx [] = {
+#define WRAPPER(a,b) [MONO_WRAPPER_ ## a] = offsetof (struct msgstr_t, MSGSTRFIELD(__LINE__)),
+#include "wrapper-types.h"
+#undef WRAPPER
};
-static const char *wrapper_type_names [] = {
- "none",
- "delegate-invoke",
- "delegate-begin-invoke",
- "delegate-end-invoke",
- "runtime-invoke",
- "native-to-managed",
- "managed-to-native",
- "remoting-invoke",
- "remoting-invoke-with-check",
- "xdomain-invoke",
- "xdomain-dispatch",
- "ldfld",
- "stfld",
- "ldfld-remote",
- "stfld-remote",
- "synchronized",
- "dynamic-method",
- "isinst",
- "cancast",
- "proxy_isinst",
- "stelemref",
- "unbox",
- "unknown"
+static const char*
+wrapper_type_to_str (guint32 wrapper_type)
+{
+ g_assert (wrapper_type < MONO_WRAPPER_NUM);
+
+ return (const char*)&opstr + opidx [wrapper_type];
+}
+
+#else
+#define WRAPPER(a,b) b,
+static const char* const
+wrapper_type_names [MONO_WRAPPER_NUM + 1] = {
+#include "wrapper-types.h"
+ NULL
};
+static const char*
+wrapper_type_to_str (guint32 wrapper_type)
+{
+ g_assert (wrapper_type < MONO_WRAPPER_NUM);
+
+ return wrapper_type_names [wrapper_type];
+}
+
+#endif
+
static void
append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
{
g_string_append_c (res, '/');
}
if (include_namespace && *(class->name_space))
- g_string_sprintfa (res, "%s.", class->name_space);
- g_string_sprintfa (res, "%s", class->name);
+ g_string_append_printf (res, "%s.", class->name_space);
+ g_string_append_printf (res, "%s", class->name);
+}
+
+static MonoClass*
+find_system_class (const char *name)
+{
+ if (!strcmp (name, "void"))
+ return mono_defaults.void_class;
+ else if (!strcmp (name, "char")) return mono_defaults.char_class;
+ else if (!strcmp (name, "bool")) return mono_defaults.boolean_class;
+ else if (!strcmp (name, "byte")) return mono_defaults.byte_class;
+ else if (!strcmp (name, "sbyte")) return mono_defaults.sbyte_class;
+ else if (!strcmp (name, "uint16")) return mono_defaults.uint16_class;
+ else if (!strcmp (name, "int16")) return mono_defaults.int16_class;
+ else if (!strcmp (name, "uint")) return mono_defaults.uint32_class;
+ else if (!strcmp (name, "int")) return mono_defaults.int32_class;
+ else if (!strcmp (name, "ulong")) return mono_defaults.uint64_class;
+ else if (!strcmp (name, "long")) return mono_defaults.int64_class;
+ else if (!strcmp (name, "uintptr")) return mono_defaults.uint_class;
+ else if (!strcmp (name, "intptr")) return mono_defaults.int_class;
+ else if (!strcmp (name, "single")) return mono_defaults.single_class;
+ else if (!strcmp (name, "double")) return mono_defaults.double_class;
+ else if (!strcmp (name, "string")) return mono_defaults.string_class;
+ else if (!strcmp (name, "object")) return mono_defaults.object_class;
+ else
+ return NULL;
}
void
-mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
+mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace)
+{
+ int i;
+
switch (type->type) {
case MONO_TYPE_VOID:
g_string_append (res, "void"); break;
g_string_append_c (res, '*');
break;
case MONO_TYPE_ARRAY:
- append_class_name (res, type->data.array->eklass, include_namespace);
- g_string_sprintfa (res, "[%d]", type->data.array->rank);
+ mono_type_get_desc (res, &type->data.array->eklass->byval_arg, include_namespace);
+ g_string_append_printf (res, "[%d]", type->data.array->rank);
break;
case MONO_TYPE_SZARRAY:
mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
case MONO_TYPE_VALUETYPE:
append_class_name (res, type->data.klass, include_namespace);
break;
- case MONO_TYPE_GENERICINST:
+ case MONO_TYPE_GENERICINST: {
+ MonoGenericContext *context;
+
mono_type_get_desc (res, &type->data.generic_class->container_class->byval_arg, include_namespace);
+ g_string_append (res, "<");
+ context = &type->data.generic_class->context;
+ if (context->class_inst) {
+ for (i = 0; i < context->class_inst->type_argc; ++i) {
+ if (i > 0)
+ g_string_append (res, ", ");
+ mono_type_get_desc (res, context->class_inst->type_argv [i], include_namespace);
+ }
+ }
+ if (context->method_inst) {
+ if (context->class_inst)
+ g_string_append (res, "; ");
+ for (i = 0; i < context->method_inst->type_argc; ++i) {
+ if (i > 0)
+ g_string_append (res, ", ");
+ mono_type_get_desc (res, context->method_inst->type_argv [i], include_namespace);
+ }
+ }
+ g_string_append (res, ">");
+ break;
+ }
+ case MONO_TYPE_VAR:
+ case MONO_TYPE_MVAR:
+ if (type->data.generic_param) {
+ MonoGenericParamInfo *info = mono_generic_param_info (type->data.generic_param);
+ if (info)
+ g_string_append (res, info->name);
+ else
+ g_string_append_printf (res, "%s%d", type->type == MONO_TYPE_VAR ? "!" : "!!", mono_generic_param_num (type->data.generic_param));
+ } else {
+ g_string_append (res, "<unknown>");
+ }
+ break;
+ case MONO_TYPE_TYPEDBYREF:
+ g_string_append (res, "typedbyref");
break;
default:
break;
mono_type_full_name (MonoType *type)
{
GString *str;
- char *res;
str = g_string_new ("");
mono_type_get_desc (str, type, TRUE);
-
- res = g_strdup (str->str);
- g_string_free (str, TRUE);
- return res;
+ return g_string_free (str, FALSE);
}
char*
{
int i;
char *result;
- GString *res = g_string_new ("");
+ GString *res;
+
+ if (!sig)
+ return g_strdup ("<invalid signature>");
+
+ res = g_string_new ("");
for (i = 0; i < sig->param_count; ++i) {
if (i > 0)
return result;
}
+static void
+ginst_get_desc (GString *str, MonoGenericInst *ginst)
+{
+ int i;
+
+ for (i = 0; i < ginst->type_argc; ++i) {
+ if (i > 0)
+ g_string_append (str, ", ");
+ mono_type_get_desc (str, ginst->type_argv [i], TRUE);
+ }
+}
+
+char*
+mono_context_get_desc (MonoGenericContext *context)
+{
+ GString *str;
+ char *res;
+
+ str = g_string_new ("");
+ g_string_append (str, "<");
+
+ if (context->class_inst)
+ ginst_get_desc (str, context->class_inst);
+ if (context->method_inst) {
+ if (context->class_inst)
+ g_string_append (str, "; ");
+ ginst_get_desc (str, context->method_inst);
+ }
+
+ g_string_append (str, ">");
+ res = g_strdup (str->str);
+ g_string_free (str, TRUE);
+ return res;
+}
+
/**
* mono_method_desc_new:
* @name: the method name.
*
* in all the loaded assemblies.
*
+ * Both classname and methodname can contain '*' which matches anything.
+ *
* Returns: a parsed representation of the method description.
*/
MonoMethodDesc*
class_nspace = g_strdup (name);
use_args = strchr (class_nspace, '(');
if (use_args) {
+ /* Allow a ' ' between the method name and the signature */
+ if (use_args > class_nspace && use_args [-1] == ' ')
+ use_args [-1] = 0;
*use_args++ = 0;
end = strchr (use_args, ')');
if (!end) {
result->klass = class_name;
result->namespace = use_namespace? class_nspace: NULL;
result->args = use_args? use_args: NULL;
+ if (strstr (result->name, "*"))
+ result->name_glob = TRUE;
+ if (strstr (result->klass, "*"))
+ result->klass_glob = TRUE;
if (use_args) {
end = use_args;
if (*end)
mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
{
char *sig;
- if (strcmp (desc->name, method->name))
+ gboolean name_match;
+
+ name_match = strcmp (desc->name, method->name) == 0;
+#ifndef _EGLIB_MAJOR
+ if (!name_match && desc->name_glob)
+ name_match = g_pattern_match_simple (desc->name, method->name);
+#endif
+ if (!name_match)
return FALSE;
if (!desc->args)
return TRUE;
return TRUE;
}
+static const char *
+my_strrchr (const char *str, char ch, int *len)
+{
+ int pos;
+
+ for (pos = (*len)-1; pos >= 0; pos--) {
+ if (str [pos] != ch)
+ continue;
+
+ *len = pos;
+ return str + pos;
+ }
+
+ return NULL;
+}
+
+static gboolean
+match_class (MonoMethodDesc *desc, int pos, MonoClass *klass)
+{
+ const char *p;
+
+ if (desc->klass_glob && !strcmp (desc->klass, "*"))
+ return TRUE;
+#ifndef _EGLIB_MAJOR
+ if (desc->klass_glob && g_pattern_match_simple (desc->klass, klass->name))
+ return TRUE;
+#endif
+ p = my_strrchr (desc->klass, '/', &pos);
+ if (!p) {
+ if (strncmp (desc->klass, klass->name, pos))
+ return FALSE;
+ if (desc->namespace && strcmp (desc->namespace, klass->name_space))
+ return FALSE;
+ return TRUE;
+ }
+
+ if (strcmp (p+1, klass->name))
+ return FALSE;
+ if (!klass->nested_in)
+ return FALSE;
+
+ return match_class (desc, pos, klass->nested_in);
+}
+
gboolean
mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
{
- if (strcmp (desc->klass, method->klass->name))
- return FALSE;
- if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
+ if (!match_class (desc, strlen (desc->klass), method->klass))
return FALSE;
+
return mono_method_desc_match (desc, method);
}
MonoMethod *method;
int i;
+ /* Handle short names for system classes */
+ if (!desc->namespace && image == mono_defaults.corlib) {
+ klass = find_system_class (desc->klass);
+ if (klass)
+ return mono_method_desc_search_in_class (desc, klass);
+ }
+
if (desc->namespace && desc->klass) {
klass = mono_class_from_name (image, desc->namespace, desc->klass);
if (!klass)
{
MonoMethodHeader *header = mono_method_get_header (method);
const MonoOpcode *opcode;
- guint32 i, label, token;
+ guint32 label, token;
gint32 sval;
+ int i;
char *tmp;
+ const unsigned char* il_code = mono_method_header_get_code (header, NULL, NULL);
- label = ip - header->code;
+ label = ip - il_code;
if (dh->indenter) {
tmp = dh->indenter (dh, method, label);
g_string_append (str, tmp);
g_free (tmp);
}
if (dh->label_format)
- g_string_sprintfa (str, dh->label_format, label);
+ g_string_append_printf (str, dh->label_format, label);
i = mono_opcode_value (&ip, end);
ip++;
opcode = &mono_opcodes [i];
- g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
+ g_string_append_printf (str, "%-10s", mono_opcode_name (i));
switch (opcode->argument) {
case MonoInlineNone:
g_string_append (str, tmp);
g_free (tmp);
} else {
- g_string_sprintfa (str, "0x%08x", token);
+ g_string_append_printf (str, "0x%08x", token);
}
ip += 4;
break;
- case MonoInlineString:
- /* TODO */
+ case MonoInlineString: {
+ const char *blob;
+ char *s;
+ size_t len2;
+ char *blob2 = NULL;
+
+ if (!method->klass->image->dynamic) {
+ token = read32 (ip);
+ blob = mono_metadata_user_string (method->klass->image, mono_metadata_token_index (token));
+
+ len2 = mono_metadata_decode_blob_size (blob, &blob);
+ len2 >>= 1;
+
+#ifdef NO_UNALIGNED_ACCESS
+ /* The blob might not be 2 byte aligned */
+ blob2 = g_malloc ((len2 * 2) + 1);
+ memcpy (blob2, blob, len2 * 2);
+#else
+ blob2 = (char*)blob;
+#endif
+
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ {
+ guint16 *buf = g_new (guint16, len2 + 1);
+ int i;
+
+ for (i = 0; i < len2; ++i)
+ buf [i] = GUINT16_FROM_LE (((guint16*)blob2) [i]);
+ s = g_utf16_to_utf8 (buf, len2, NULL, NULL, NULL);
+ g_free (buf);
+ }
+#else
+ s = g_utf16_to_utf8 ((gunichar2*)blob2, len2, NULL, NULL, NULL);
+#endif
+
+ g_string_append_printf (str, "\"%s\"", s);
+ g_free (s);
+ if (blob != blob2)
+ g_free (blob2);
+ }
ip += 4;
break;
+ }
case MonoInlineVar:
- g_string_sprintfa (str, "%d", read16 (ip));
+ g_string_append_printf (str, "%d", read16 (ip));
ip += 2;
break;
case MonoShortInlineVar:
- g_string_sprintfa (str, "%d", (*ip));
+ g_string_append_printf (str, "%d", (*ip));
ip ++;
break;
case MonoInlineBrTarget:
sval = read32 (ip);
ip += 4;
if (dh->label_target)
- g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
+ g_string_append_printf (str, dh->label_target, ip + sval - il_code);
else
- g_string_sprintfa (str, "%d", sval);
+ g_string_append_printf (str, "%d", sval);
break;
case MonoShortInlineBrTarget:
sval = *(const signed char*)ip;
ip ++;
if (dh->label_target)
- g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
+ g_string_append_printf (str, dh->label_target, ip + sval - il_code);
else
- g_string_sprintfa (str, "%d", sval);
+ g_string_append_printf (str, "%d", sval);
break;
case MonoInlineSwitch: {
const unsigned char *end;
g_string_append (str, ", ");
label = read32 (ip);
if (dh->label_target)
- g_string_sprintfa (str, dh->label_target, end + label - header->code);
+ g_string_append_printf (str, dh->label_target, end + label - il_code);
else
- g_string_sprintfa (str, "%d", label);
+ g_string_append_printf (str, "%d", label);
ip += 4;
}
g_string_append_c (str, ')');
case MonoInlineR: {
double r;
readr8 (ip, &r);
- g_string_sprintfa (str, "%g", r);
+ g_string_append_printf (str, "%g", r);
ip += 8;
break;
}
case MonoShortInlineR: {
float r;
readr4 (ip, &r);
- g_string_sprintfa (str, "%g", r);
+ g_string_append_printf (str, "%g", r);
ip += 4;
break;
}
case MonoInlineI:
- g_string_sprintfa (str, "%d", (gint32)read32 (ip));
+ g_string_append_printf (str, "%d", (gint32)read32 (ip));
ip += 4;
break;
case MonoShortInlineI:
- g_string_sprintfa (str, "%d", *(const signed char*)ip);
+ g_string_append_printf (str, "%d", *(const signed char*)ip);
ip ++;
break;
case MonoInlineI8:
if (dh->newline)
g_string_append (str, dh->newline);
+ mono_metadata_free_mh (header);
return ip;
}
return result;
}
-static const char*
-wrapper_type_to_str (guint32 wrapper_type)
+char *
+mono_field_full_name (MonoClassField *field)
{
- g_assert (wrapper_type < sizeof (wrapper_type_names) / sizeof (char*));
+ char *res;
+ const char *nspace = field->parent->name_space;
- return wrapper_type_names [wrapper_type];
+ res = g_strdup_printf ("%s%s%s:%s", nspace, *nspace ? "." : "",
+ field->parent->name, mono_field_get_name (field));
+
+ return res;
}
char *
{
char *res;
char wrapper [64];
- const char *nspace = method->klass->name_space;
+ char *klass_desc = mono_type_full_name (&method->klass->byval_arg);
+ char *inst_desc = NULL;
+
+ if (method->is_inflated && ((MonoMethodInflated*)method)->context.method_inst) {
+ GString *str = g_string_new ("");
+ g_string_append (str, "<");
+ ginst_get_desc (str, ((MonoMethodInflated*)method)->context.method_inst);
+ g_string_append (str, ">");
+
+ inst_desc = str->str;
+ g_string_free (str, FALSE);
+ } else if (method->is_generic) {
+ MonoGenericContainer *container = mono_method_get_generic_container (method);
+
+ GString *str = g_string_new ("");
+ g_string_append (str, "<");
+ ginst_get_desc (str, container->context.method_inst);
+ g_string_append (str, ">");
+
+ inst_desc = str->str;
+ g_string_free (str, FALSE);
+ }
+
+ if (method->wrapper_type != MONO_WRAPPER_NONE)
+ sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
+ else
+ strcpy (wrapper, "");
if (signature) {
char *tmpsig = mono_signature_get_desc (mono_method_signature (method), TRUE);
sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
else
strcpy (wrapper, "");
- res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper,
- nspace, *nspace ? "." : "",
- method->klass->name, method->name, tmpsig);
+ res = g_strdup_printf ("%s%s:%s%s (%s)", wrapper, klass_desc,
+ method->name, inst_desc ? inst_desc : "", tmpsig);
g_free (tmpsig);
} else {
-
- res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
- nspace, *nspace ? "." : "",
- method->klass->name, method->name);
+ res = g_strdup_printf ("%s%s:%s%s", wrapper, klass_desc,
+ method->name, inst_desc ? inst_desc : "");
}
+ g_free (klass_desc);
+ g_free (inst_desc);
+
return res;
}
+
+static const char*
+print_name_space (MonoClass *klass)
+{
+ if (klass->nested_in) {
+ print_name_space (klass->nested_in);
+ g_print ("%s", klass->nested_in->name);
+ return "/";
+ }
+ if (klass->name_space [0]) {
+ g_print ("%s", klass->name_space);
+ return ".";
+ }
+ return "";
+}
+
+/**
+ * mono_object_describe:
+ *
+ * Prints to stdout a small description of the object @obj.
+ * For use in a debugger.
+ */
+void
+mono_object_describe (MonoObject *obj)
+{
+ MonoClass* klass;
+ const char* sep;
+ if (!obj) {
+ g_print ("(null)\n");
+ return;
+ }
+ klass = mono_object_class (obj);
+ if (klass == mono_defaults.string_class) {
+ char *utf8 = mono_string_to_utf8 ((MonoString*)obj);
+ if (strlen (utf8) > 60) {
+ utf8 [57] = '.';
+ utf8 [58] = '.';
+ utf8 [59] = '.';
+ utf8 [60] = 0;
+ }
+ g_print ("String at %p, length: %d, '%s'\n", obj, mono_string_length ((MonoString*) obj), utf8);
+ g_free (utf8);
+ } else if (klass->rank) {
+ MonoArray *array = (MonoArray*)obj;
+ sep = print_name_space (klass);
+ g_print ("%s%s", sep, klass->name);
+ g_print (" at %p, rank: %d, length: %d\n", obj, klass->rank, (int)mono_array_length (array));
+ } else {
+ sep = print_name_space (klass);
+ g_print ("%s%s", sep, klass->name);
+ g_print (" object at %p (klass: %p)\n", obj, klass);
+ }
+
+}
+
+static void
+print_field_value (const char *field_ptr, MonoClassField *field, int type_offset)
+{
+ MonoType *type;
+ g_print ("At %p (ofs: %2d) %s: ", field_ptr, field->offset + type_offset, mono_field_get_name (field));
+ type = mono_type_get_underlying_type (field->type);
+
+ switch (type->type) {
+ case MONO_TYPE_I:
+ case MONO_TYPE_U:
+ case MONO_TYPE_PTR:
+ case MONO_TYPE_FNPTR:
+ g_print ("%p\n", *(const void**)field_ptr);
+ break;
+ case MONO_TYPE_STRING:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_OBJECT:
+ case MONO_TYPE_ARRAY:
+ mono_object_describe (*(MonoObject**)field_ptr);
+ break;
+ case MONO_TYPE_GENERICINST:
+ if (!mono_type_generic_inst_is_valuetype (type)) {
+ mono_object_describe (*(MonoObject**)field_ptr);
+ break;
+ } else {
+ /* fall through */
+ }
+ case MONO_TYPE_VALUETYPE: {
+ MonoClass *k = mono_class_from_mono_type (type);
+ g_print ("%s ValueType (type: %p) at %p\n", k->name, k, field_ptr);
+ break;
+ }
+ case MONO_TYPE_I1:
+ g_print ("%d\n", *(gint8*)field_ptr);
+ break;
+ case MONO_TYPE_U1:
+ g_print ("%d\n", *(guint8*)field_ptr);
+ break;
+ case MONO_TYPE_I2:
+ g_print ("%d\n", *(gint16*)field_ptr);
+ break;
+ case MONO_TYPE_U2:
+ g_print ("%d\n", *(guint16*)field_ptr);
+ break;
+ case MONO_TYPE_I4:
+ g_print ("%d\n", *(gint32*)field_ptr);
+ break;
+ case MONO_TYPE_U4:
+ g_print ("%u\n", *(guint32*)field_ptr);
+ break;
+ case MONO_TYPE_I8:
+ g_print ("%lld\n", (long long int)*(gint64*)field_ptr);
+ break;
+ case MONO_TYPE_U8:
+ g_print ("%llu\n", (long long unsigned int)*(guint64*)field_ptr);
+ break;
+ case MONO_TYPE_R4:
+ g_print ("%f\n", *(gfloat*)field_ptr);
+ break;
+ case MONO_TYPE_R8:
+ g_print ("%f\n", *(gdouble*)field_ptr);
+ break;
+ case MONO_TYPE_BOOLEAN:
+ g_print ("%s (%d)\n", *(guint8*)field_ptr? "True": "False", *(guint8*)field_ptr);
+ break;
+ case MONO_TYPE_CHAR:
+ g_print ("'%c' (%d 0x%04x)\n", *(guint16*)field_ptr, *(guint16*)field_ptr, *(guint16*)field_ptr);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+objval_describe (MonoClass *class, const char *addr)
+{
+ MonoClassField *field;
+ MonoClass *p;
+ const char *field_ptr;
+ gssize type_offset = 0;
+ if (class->valuetype)
+ type_offset = -sizeof (MonoObject);
+
+ for (p = class; p != NULL; p = p->parent) {
+ gpointer iter = NULL;
+ int printed_header = FALSE;
+ while ((field = mono_class_get_fields (p, &iter))) {
+ if (field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA))
+ continue;
+
+ if (p != class && !printed_header) {
+ const char *sep;
+ g_print ("In class ");
+ sep = print_name_space (p);
+ g_print ("%s%s:\n", sep, p->name);
+ printed_header = TRUE;
+ }
+ field_ptr = (const char*)addr + field->offset + type_offset;
+
+ print_field_value (field_ptr, field, type_offset);
+ }
+ }
+}
+
+/**
+ * mono_object_describe_fields:
+ *
+ * Prints to stdout a small description of each field of the object @obj.
+ * For use in a debugger.
+ */
+void
+mono_object_describe_fields (MonoObject *obj)
+{
+ MonoClass *class = mono_object_class (obj);
+ objval_describe (class, (char*)obj);
+}
+
+/**
+ * mono_value_describe_fields:
+ *
+ * Prints to stdout a small description of each field of the value type
+ * stored at @addr of type @klass.
+ * For use in a debugger.
+ */
+void
+mono_value_describe_fields (MonoClass* klass, const char* addr)
+{
+ objval_describe (klass, addr);
+}
+
+/**
+ * mono_class_describe_statics:
+ *
+ * Prints to stdout a small description of each static field of the type @klass
+ * in the current application domain.
+ * For use in a debugger.
+ */
+void
+mono_class_describe_statics (MonoClass* klass)
+{
+ MonoClassField *field;
+ MonoClass *p;
+ const char *field_ptr;
+ MonoVTable *vtable = mono_class_vtable_full (mono_domain_get (), klass, FALSE);
+ const char *addr;
+
+ if (!vtable)
+ return;
+ if (!(addr = vtable->data))
+ return;
+
+ for (p = klass; p != NULL; p = p->parent) {
+ gpointer iter = NULL;
+ while ((field = mono_class_get_fields (p, &iter))) {
+ if (field->type->attrs & FIELD_ATTRIBUTE_LITERAL)
+ continue;
+ if (!(field->type->attrs & (FIELD_ATTRIBUTE_STATIC | FIELD_ATTRIBUTE_HAS_FIELD_RVA)))
+ continue;
+
+ field_ptr = (const char*)addr + field->offset;
+
+ print_field_value (field_ptr, field, 0);
+ }
+ }
+}
+