/*
 * get.c: Functions to get stringified values from the metadata tables.
 *
 * Author:
 *   Miguel de Icaza (miguel@ximian.com)
 *
 * (C) 2001 Ximian, Inc.
 * Copyright 2012 Xamarin Inc
 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
 */
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <glib.h>
#include <math.h>
#include "meta.h"
#include "util.h"
#include "get.h"
#include <mono/utils/mono-compiler.h>
#include <mono/metadata/class.h>
#include <mono/metadata/marshal.h>
#include <mono/metadata/metadata-internals.h>
#include <mono/utils/mono-math.h>

extern gboolean substitute_with_mscorlib_p;

static char *
get_token_comment (const char *prefix, guint32 token);

static MonoGenericContainer *
get_memberref_container (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container, MonoError *error);

static char *
get_memberref_parent (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container);
 
static gboolean
cant_print_generic_param_name (MonoGenericParam *gparam);

GHashTable *key_table = NULL;
GHashTable *mono_generic_params_with_ambiguous_names = NULL;
GHashTable *generic_containers = NULL;
gboolean show_method_tokens = FALSE;
gboolean show_tokens = FALSE;

char *
get_typedef (MonoImage *m, int idx)
{
	guint32 cols [MONO_TYPEDEF_SIZE];
	const char *ns;
	char *tstring, *result;
        guint32 token;
        
	if (idx == 1)
		/* <Module> */
		return NULL;

	mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPEDEF], idx - 1, cols, MONO_TYPEDEF_SIZE);

        ns = mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAMESPACE]);

        /* Check if this is a nested type */
        token = MONO_TOKEN_TYPE_DEF | (idx);
        token = mono_metadata_nested_in_typedef (m, token);
	tstring = get_token_comment (NULL, token);
        if (token) {
                char *outer;
                
                outer = get_typedef (m, mono_metadata_token_index (token));
                result = g_strdup_printf (
                        "%s/%s%s", outer,
                        mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]),
			tstring ? tstring : "");
		g_free (outer);
		g_free (tstring);
		return result;
        }
        
	
	result = g_strdup_printf (
		"%s%s%s%s", ns, *ns?".":"",
		mono_metadata_string_heap (m, cols [MONO_TYPEDEF_NAME]),
		tstring ? tstring : "");
	g_free (tstring);

	return result;
}

char *
get_module (MonoImage *m, int idx)
{
	guint32 cols [MONO_MODULE_SIZE];
	
	/*
	 * There MUST BE only one module in the Module table
	 */
	g_assert (idx == 1);
	    
	mono_metadata_decode_row (&m->tables [MONO_TABLE_MODULE], idx - 1, cols, MONO_MODULE_SIZE);

	return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MODULE_NAME]));
}

char *
get_moduleref (MonoImage *m, int idx)
{
	guint32 cols [MONO_MODULEREF_SIZE];
	
	mono_metadata_decode_row (&m->tables [MONO_TABLE_MODULEREF], idx - 1, cols, MONO_MODULEREF_SIZE);

	return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MODULEREF_NAME]));
}

char *
get_assemblyref (MonoImage *m, int idx)
{
	guint32 cols [MONO_ASSEMBLYREF_SIZE];
	
	mono_metadata_decode_row (&m->tables [MONO_TABLE_ASSEMBLYREF], idx - 1, cols, MONO_ASSEMBLYREF_SIZE);

	return get_escaped_name (mono_metadata_string_heap (m, cols [MONO_ASSEMBLYREF_NAME]));
}

static char *
stringify_array (guint32 rank, guint32 num_sizes, guint32 num_lo_bounds, gint32 *sizes, gint32 *lo_bounds)
{
	GString *res = g_string_new ("[");
	int i;

	for (i = 0; i < rank; i++) {
		if (i)
			g_string_append_c (res, ',');
		if (i < num_lo_bounds)
			g_string_append_printf (res, "%d...", lo_bounds [i]);
		if (i < num_sizes) {
			if (i < num_lo_bounds)
				g_string_append_printf (res, "%d", lo_bounds [i] + sizes [i] - 1);
			else
				g_string_append_printf (res, "%d", sizes [i]);
		}

	}
	g_string_append (res, "]");

	return g_string_free (res, FALSE);
}

/*
 *
 * Returns a string representing the ArrayShape (22.2.16).
 */
static const char *
get_array_shape (MonoImage *m, const char *ptr, char **result)
{
	guint32 rank, num_sizes, num_lo_bounds;
	gint32 *sizes = NULL, *lo_bounds = NULL;
	int i;
	
	rank = mono_metadata_decode_value (ptr, &ptr);
	num_sizes = mono_metadata_decode_value (ptr, &ptr);

	if (num_sizes > 0)
		sizes = g_new (gint32, num_sizes);
	
	for (i = 0; i < num_sizes; i++)
		sizes [i] = mono_metadata_decode_value (ptr, &ptr);

	num_lo_bounds = mono_metadata_decode_value (ptr, &ptr);
	if (num_lo_bounds > 0)
		lo_bounds = g_new (gint32, num_lo_bounds);
	
	for (i = 0; i < num_lo_bounds; i++)
		lo_bounds [i] = mono_metadata_decode_signed_value (ptr, &ptr);

	*result = stringify_array (rank, num_sizes, num_lo_bounds, sizes, lo_bounds);

	g_free (sizes);
	g_free (lo_bounds);

	return ptr;
}

/**
 * get_typespec:
 * @m: metadata context
 * @blob_idx: index into the blob heap
 *
 * Returns the stringified representation of a TypeSpec signature (22.2.17)
 */
char *
get_typespec (MonoImage *m, guint32 idx, gboolean is_def, MonoGenericContainer *container)
{
	guint32 cols [MONO_TYPESPEC_SIZE];
	const char *ptr;
	char *s, *result;
	GString *res = g_string_new ("");
	MonoMethodSignature *sig;

	mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPESPEC], idx-1, cols, MONO_TYPESPEC_SIZE);
	ptr = mono_metadata_blob_heap (m, cols [MONO_TYPESPEC_SIGNATURE]);
	/* len = */ mono_metadata_decode_value (ptr, &ptr);

	switch (*ptr++){
	case MONO_TYPE_PTR:
		ptr = get_custom_mod (m, ptr, &s);
		if (s){
			g_string_append (res, s);
			g_string_append_c (res, ' ');
			g_free (s);
		}

		if (*ptr == MONO_TYPE_VOID)
			g_string_append (res, "void");
		else {
			ptr = get_type (m, ptr, &s, is_def, container);
			if (s)
				g_string_append (res, s);
		}
		g_string_append (res, "*");
		break;

	case MONO_TYPE_FNPTR: {
		ERROR_DECL (error);
		sig = mono_metadata_parse_method_signature_full (m, container, 0, ptr, &ptr, error);
		g_assert (is_ok (error)); /*FIXME don't swallow the error message*/
		s = dis_stringify_function_ptr (m, sig);
		g_string_append (res, "method ");
		g_string_append (res, s);
		g_free (s);
		break;
	}
	case MONO_TYPE_ARRAY:
		ptr = get_type (m, ptr, &s, is_def, container);
		g_string_append (res, s);
		g_free (s);
		g_string_append_c (res, ' ');
		ptr = get_array_shape (m, ptr, &s);
		g_string_append (res, s);
		g_free (s);
		break;

	case MONO_TYPE_SZARRAY:
		ptr = get_custom_mod (m, ptr, &s);
		if (s){
			g_string_append (res, s);
			g_string_append_c (res, ' ');
			g_free (s);
		}
		ptr = get_type (m, ptr, &s, is_def, container);
		g_string_append (res, s);
		g_string_append (res, "[]");
		g_free (s);
		break;

	default:
		ptr = get_type (m, ptr - 1, &s, is_def, container);
		g_string_append (res, s);
		g_free (s);
		break;
	}

	if (show_tokens) {
		int token = mono_metadata_make_token (MONO_TABLE_TYPESPEC, idx);
		result = get_token_comment (res->str, token);
		g_string_free (res, TRUE);
	} else {
		result = res->str;
		g_string_free (res, FALSE);
	}

	return result;
}

char *
get_typeref (MonoImage *m, int idx)
{
	guint32 cols [MONO_TYPEREF_SIZE];
	const char *s, *t;
	char *x, *ret;
	guint32 rs_idx, table;
	
	mono_metadata_decode_row (&m->tables [MONO_TABLE_TYPEREF], idx - 1, cols, MONO_TYPEREF_SIZE);

	t = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAME]);
	s = mono_metadata_string_heap (m, cols [MONO_TYPEREF_NAMESPACE]);

	rs_idx = cols [MONO_TYPEREF_SCOPE] >> MONO_RESOLTION_SCOPE_BITS;
	table = cols [MONO_TYPEREF_SCOPE] & MONO_RESOLTION_SCOPE_MASK;
	
	switch (table){
	case MONO_RESOLTION_SCOPE_MODULE: /* Module */
		x = get_module (m, rs_idx);
		ret = g_strdup_printf ("[%s] %s%s%s", x, s, *s?".":"", t);
		g_free (x);
		break;

	case MONO_RESOLTION_SCOPE_MODULEREF: /* ModuleRef */
		x = get_moduleref (m, rs_idx);
		ret = g_strdup_printf ("[.module %s]%s%s%s", x, s, *s ? "." : "", t);
		break;
			      
	case MONO_RESOLTION_SCOPE_ASSEMBLYREF: /*
		 * AssemblyRef (ECMA docs claim it is 3, but it looks to
		 * me like it is 2 (tokens are prefixed with 0x23)
		 */
		x = get_assemblyref (m, rs_idx);
		ret = g_strdup_printf ("[%s]%s%s%s", x, s, *s?".":"", t);
		g_free (x);
		break;
		
	case MONO_RESOLTION_SCOPE_TYPEREF: /* TypeRef */
		x = get_typeref (m, rs_idx);
		ret =  g_strdup_printf ("%s/%s", x, t);
		g_free (x);
		break;
		
	default:
		ret = g_strdup_printf ("Unknown table in TypeRef %d", table);
	}

	if (show_tokens) {
		int token = mono_metadata_make_token (MONO_TABLE_TYPEREF, idx);
		char *temp = get_token_comment (ret, token);
		g_free (ret);
		ret = temp;
	}

	return ret;
}

/**
 * get_typedef_or_ref:
 * @m: metadata context
 * @dor_token: def or ref encoded index
 *
 * Low two bits contain table to lookup from
 * high bits contain the index into the def or ref table
 *
 * Returns: a stringified version of the MethodDef or MethodRef
 * at (dor_token >> 2) 
 */
char *
get_typedef_or_ref (MonoImage *m, guint32 dor_token, MonoGenericContainer *container)
{
	char *temp = NULL, *s = NULL;
	int table, idx;

	/*
	 * low 2 bits contain encoding
	 */
	table = dor_token & MONO_TYPEDEFORREF_MASK;
	idx = dor_token >> MONO_TYPEDEFORREF_BITS;
	
	switch (table){
	case 0: /* TypeDef */
		temp = get_typedef (m, idx);
		s = g_strdup_printf ("%s", temp);
		break;
		
	case 1: /* TypeRef */
		temp = get_typeref (m, idx);
		s = g_strdup_printf ("%s", temp);
		break;
		
	case 2: /* TypeSpec */
		s = get_typespec (m, idx, FALSE, container);
		break;

	default:
		g_error ("Unhandled encoding for typedef-or-ref coded index 0x%08x", dor_token);

	}
	
	g_free (temp);

	return s;
}

/**
 * get_type_or_methdef
 * @m: metadata context
 * @dor_token: type or method def encoded index
 *
 * Low bit contains the table to lookup from
 * high bits contain the index into the type def or method def table
 *
 * Returns: a stringified version of the TypeOrMethodDef token
 */
char *
get_type_or_methdef (MonoImage *m, guint32 dor_token)
{
        if (dor_token & MONO_TYPEORMETHOD_METHOD) /* MethodDef */
		return get_methoddef (m, dor_token >> MONO_TYPEORMETHOD_BITS);
        else  /* TypeDef */
                return get_typedef (m, dor_token >> MONO_TYPEORMETHOD_BITS);
}

/** 
 * get_encoded_typedef_or_ref:
 * @m: metadata context 
 * @ptr: location to decode from.
 * @result: pointer to string where resulting decoded string is stored
 *
 * result will point to a g_malloc()ed string.
 *
 * Returns: the new ptr to continue decoding
 */
const char *
get_encoded_typedef_or_ref (MonoImage *m, const char *ptr, char **result)
{
	guint32 token;
	
	token = mono_metadata_decode_value (ptr, &ptr);

	*result = get_typedef_or_ref (m, token, NULL);

	return ptr;
}

/**
 * get_custom_mod:
 *
 * Decodes a CustomMod (22.2.7)
 *
 * Returns: updated pointer location
 */
const char *
get_custom_mod (MonoImage *m, const char *ptr, char **return_value)
{
	char *s;
	const char *mod;
	
	*return_value = NULL;
	while ((*ptr == MONO_TYPE_CMOD_OPT) ||
		   (*ptr == MONO_TYPE_CMOD_REQD)) {
		mod = (*ptr == MONO_TYPE_CMOD_REQD) ? "modreq" : "modopt";
		ptr++;
		ptr = get_encoded_typedef_or_ref (m, ptr, &s);

		/* cmods are encoded in reverse order from how we want to print them.
		 * "int32 modopt (Foo) modopt (Bar)" is encoded as "cmod_opt [typedef_or_ref "Bar"] cmod_opt [typedef_or_ref "Foo"] I4"
		 */
		if (*return_value == NULL)
			*return_value = g_strconcat (" ", mod, " (", s, ")", (const char*)NULL);
		else
			*return_value = g_strconcat (mod, " (", s, ") ", *return_value, (const char*)NULL);
		g_free (s);
	}
	return ptr;
}


static dis_map_t element_type_map [] = {
	{ MONO_TYPE_END        , "end" },
	{ MONO_TYPE_VOID       , "void" },
	{ MONO_TYPE_BOOLEAN    , "bool" },
	{ MONO_TYPE_CHAR       , "char" }, 
	{ MONO_TYPE_I1         , "int8" },
	{ MONO_TYPE_U1         , "unsigned int8" }, 
	{ MONO_TYPE_I2         , "int16" },
	{ MONO_TYPE_U2         , "unsigned int16" },
	{ MONO_TYPE_I4         , "int32" },
	{ MONO_TYPE_U4         , "unsigned int32" },
	{ MONO_TYPE_I8         , "int64" },
	{ MONO_TYPE_U8         , "unsigned int64" },
	{ MONO_TYPE_R4         , "float32" },
	{ MONO_TYPE_R8         , "float64" },
	{ MONO_TYPE_STRING     , "string" },
	{ MONO_TYPE_TYPEDBYREF , "typedref" },
	{ MONO_TYPE_I          , "native int" },
	{ MONO_TYPE_U          , "native unsigned int" },
	{ MONO_TYPE_OBJECT     , "object" },
	{ 0, NULL }
};

static dis_map_t call_conv_type_map [] = {
	{ MONO_CALL_DEFAULT     , "default" },
	{ MONO_CALL_C           , "unmanaged cdecl" },
	{ MONO_CALL_STDCALL     , "unmanaged stdcall" },
	{ MONO_CALL_THISCALL    , "unmanaged thiscall" },
	{ MONO_CALL_FASTCALL    , "unmanaged fastcall" },
	{ MONO_CALL_VARARG      , "vararg" },
	{ 0, NULL }
};

char*
dis_stringify_token (MonoImage *m, guint32 token)
{
	guint idx = token & 0xffffff;
	switch (token >> 24) {
	case MONO_TABLE_TYPEDEF: return get_typedef (m, idx);
	case MONO_TABLE_TYPEREF: return get_typeref (m, idx);
	case MONO_TABLE_TYPESPEC: return get_typespec (m, idx, FALSE, NULL);
	default:
		 break;
	}
	return g_strdup_printf("0x%08x", token);
}

char*
dis_stringify_array (MonoImage *m, MonoArrayType *array, gboolean is_def)
{
	char *type, *arr_str, *ret;
	
	type = dis_stringify_type (m, m_class_get_byval_arg (array->eklass), is_def);
	arr_str = stringify_array (array->rank, array->numsizes, array->numlobounds, array->sizes, array->lobounds);

	ret = g_strconcat (type, arr_str, (const char*)NULL);
	
	g_free (arr_str);
	g_free (type);
	return ret;
}

char*
dis_stringify_modifiers (MonoImage *m, int n, MonoCustomMod *mod)
{
	GString *s = g_string_new("");
	char *result;
	int i;
	for (i = 0; i < n; ++i) {
		char *tok = dis_stringify_token (m, mod[i].token);
		if (i > 0)
			g_string_append_printf (s, " ");
		g_string_append_printf (s, " %s (%s)", mod[i].required ? "modreq": "modopt", tok);
		g_free (tok);
	}
	g_string_append_c (s, ' ');
	result = s->str;
	g_string_free (s, FALSE);
	return result;
}

char*
dis_stringify_param (MonoImage *m, MonoType *param) 
{
	char *t;
	char *result;
	char *attribs;
	const char *in = param->attrs & PARAM_ATTRIBUTE_IN ? "[in]" : "";
	const char *out = param->attrs & PARAM_ATTRIBUTE_OUT ? "[out]": "";
	const char *opt = param->attrs & PARAM_ATTRIBUTE_OPTIONAL ? "[opt]": "";
	attribs = g_strconcat(in, out, opt, (const char*)NULL);
	t = dis_stringify_type (m, param, TRUE);
	result = g_strjoin(attribs[0] ? " ":"", attribs, t, (const char*)NULL);
	g_free (t);
	g_free (attribs);
	return result;
}

static char*
dis_stringify_variant_type (MonoMarshalVariant variant)
{
	switch (variant) {
	case MONO_VARIANT_EMPTY:
		return g_strdup ("");
	case MONO_VARIANT_NULL:
		return g_strdup ("null");
	case MONO_VARIANT_I2:
		return g_strdup ("int16");
	case MONO_VARIANT_I4:
		return g_strdup ("int32");
	case MONO_VARIANT_R4:
		return g_strdup ("float32");
	case MONO_VARIANT_R8:
		return g_strdup ("float64");
	case MONO_VARIANT_CY:
		return g_strdup ("currency");
	case MONO_VARIANT_DATE:
		return g_strdup ("date");
	case MONO_VARIANT_BSTR:
		return g_strdup ("bstr");
	case MONO_VARIANT_DISPATCH:
		return g_strdup ("idispatch");
	case MONO_VARIANT_ERROR:
		return g_strdup ("error");
	case MONO_VARIANT_BOOL:
		return g_strdup ("bool");
	case MONO_VARIANT_VARIANT:
		return g_strdup ("variant");
	case MONO_VARIANT_UNKNOWN:
		return g_strdup ("iunknown");
	case MONO_VARIANT_DECIMAL:
		return g_strdup ("decimal");
	case MONO_VARIANT_I1:
		return g_strdup ("int8");
	case MONO_VARIANT_UI1:
		return g_strdup ("unsigned int8");
	case MONO_VARIANT_UI2:
		return g_strdup ("unsigned int16");
	case MONO_VARIANT_UI4:
		return g_strdup ("unsigned int32");
	case MONO_VARIANT_I8:
		return g_strdup ("int64");
	case MONO_VARIANT_UI8:
		return g_strdup ("unsigned int64");
	case MONO_VARIANT_INT:
		return g_strdup ("int");
	case MONO_VARIANT_UINT:
		return g_strdup ("unsigned int");
	case MONO_VARIANT_VOID:
		return g_strdup ("void");
	case MONO_VARIANT_HRESULT:
		return g_strdup ("hresult");
	case MONO_VARIANT_PTR:
		return g_strdup ("*");
	case MONO_VARIANT_SAFEARRAY:
		return g_strdup ("safearray");
	case MONO_VARIANT_CARRAY:
		return g_strdup ("carray");
	case MONO_VARIANT_USERDEFINED:
		return g_strdup ("userdefined");
	case MONO_VARIANT_LPSTR:
		return g_strdup ("lpstr");
	case MONO_VARIANT_LPWSTR:
		return g_strdup ("lpwstr");
	case MONO_VARIANT_RECORD:
		return g_strdup ("record");
	case MONO_VARIANT_FILETIME:
		return g_strdup ("filetime");
	case MONO_VARIANT_BLOB:
		return g_strdup ("blob");
	case MONO_VARIANT_STREAM:
		return g_strdup ("stream");
	case MONO_VARIANT_STORAGE:
		return g_strdup ("storage");
	case MONO_VARIANT_STREAMED_OBJECT:
		return g_strdup ("streamed_object");
	case MONO_VARIANT_STORED_OBJECT:
		return g_strdup ("stored_object");
	case MONO_VARIANT_BLOB_OBJECT:
		return g_strdup ("blob_object");
	case MONO_VARIANT_CF:
		return g_strdup ("cf");
	case MONO_VARIANT_CLSID:
		return g_strdup ("clsid");
	case MONO_VARIANT_VECTOR:
		/* FIXME: output: <v_type> vector */
		return g_strdup ("vector");
	case MONO_VARIANT_ARRAY:
		/* FIXME: output: <v_type> [ ] */
		return g_strdup ("[]");
	case MONO_VARIANT_BYREF:
		/* FIXME: output: <v_type> & */
		return g_strdup ("&");
	default:
		return g_strdup ("unknown");
	}
}

static char*
dis_stringify_native_type (MonoMarshalNative native)
{
	switch (native) {
	case MONO_NATIVE_BOOLEAN:
		return g_strdup ("bool");
	case MONO_NATIVE_I1:
		return g_strdup ("int8");
	case MONO_NATIVE_U1:
		return g_strdup ("unsigned int8");
	case MONO_NATIVE_I2:
		return g_strdup ("int16");
	case MONO_NATIVE_U2:
		return g_strdup ("unsigned int16");
	case MONO_NATIVE_I4:
		return g_strdup ("int32");
	case MONO_NATIVE_U4:
		return g_strdup ("unsigned int32");
	case MONO_NATIVE_I8:
		return g_strdup ("int64");
	case MONO_NATIVE_U8:
		return g_strdup ("unsigned int64");
	case MONO_NATIVE_R4:
		return g_strdup ("float32");
	case MONO_NATIVE_R8:
		return g_strdup ("float64");
	case MONO_NATIVE_CURRENCY:
		return g_strdup ("currency");
	case MONO_NATIVE_BSTR:
		return g_strdup ("bstr");
	case MONO_NATIVE_LPSTR:
		return g_strdup ("lpstr");
	case MONO_NATIVE_LPWSTR:
		return g_strdup ("lpwstr");
	case MONO_NATIVE_LPTSTR:
		return g_strdup ("lptstr");
	case MONO_NATIVE_IUNKNOWN:
		return g_strdup ("iunknown");
	case MONO_NATIVE_IDISPATCH:
		return g_strdup ("idispatch");
	case MONO_NATIVE_STRUCT:
		return g_strdup ("struct");
	case MONO_NATIVE_INTERFACE:
		return g_strdup ("interface");
	case MONO_NATIVE_SAFEARRAY:
		return g_strdup ("safearray");
	case MONO_NATIVE_INT:
		return g_strdup ("int");
	case MONO_NATIVE_UINT:
		return g_strdup ("unsigned int");
	case MONO_NATIVE_VBBYREFSTR:
		return g_strdup ("vbbyrefstr");
	case MONO_NATIVE_ANSIBSTR:
		return g_strdup ("ansi bstr");
	case MONO_NATIVE_TBSTR:
		return g_strdup ("tbstr");
	case MONO_NATIVE_VARIANTBOOL:
		return g_strdup ("variant bool");
	case MONO_NATIVE_FUNC:
		return g_strdup ("method");
	case MONO_NATIVE_ASANY:
		return g_strdup ("as any");
	case MONO_NATIVE_LPSTRUCT:
		return g_strdup ("lpstruct");
	case MONO_NATIVE_CUSTOM:
		return g_strdup ("custom");
	case MONO_NATIVE_ERROR:
		return g_strdup ("error");
	case MONO_NATIVE_MAX:
		return g_strdup ("");
	default:
		return g_strdup ("unknown");
	}
}

char*
dis_stringify_marshal_spec (MonoMarshalSpec *spec)
{
	switch (spec->native) {
	case MONO_NATIVE_BYVALTSTR:
		return g_strdup_printf (" marshal (fixed sysstring [%d])", spec->data.array_data.num_elem);
	case MONO_NATIVE_BYVALARRAY:
		return g_strdup_printf (" marshal (fixed array [%d])", spec->data.array_data.num_elem);
	case MONO_NATIVE_LPARRAY: {
		char *elem_type, *elems, *ret;
		guint32 num_elem = spec->data.array_data.num_elem;
		guint32 param_num = spec->data.array_data.param_num;

		elem_type = dis_stringify_native_type (spec->data.array_data.elem_type);
		if (num_elem == -1 && param_num == -1)
			elems = g_strdup ("");
		else if ((param_num == -1) || (spec->data.array_data.elem_mult == 0))
			elems = g_strdup_printf ("%d", num_elem);
		else if ((num_elem == -1) || (num_elem == 0))
			elems = g_strdup_printf ("+ %d", param_num);
		else
			elems = g_strdup_printf ("%d + %d", num_elem, param_num);
			
		ret = g_strdup_printf (" marshal (%s[%s])", elem_type, elems);
		g_free (elem_type);
		g_free (elems);
		return ret;
	}
	case MONO_NATIVE_SAFEARRAY: {
		char *elem_type = NULL, *ret;
		
		if (spec->data.safearray_data.elem_type != 0)
			elem_type = dis_stringify_variant_type (spec->data.safearray_data.elem_type);
		ret = g_strdup_printf (" marshal (safearray %s)", elem_type ? elem_type : "");
		
		g_free (elem_type);
		return ret;
	}
	case MONO_NATIVE_CUSTOM:
		return g_strdup_printf (" marshal (custom (\"%s\", \"%s\"))", 
			spec->data.custom_data.custom_name ? spec->data.custom_data.custom_name : "", 
			spec->data.custom_data.cookie ? spec->data.custom_data.cookie : "");
	default: {
		char *native_type, *ret;
		native_type = dis_stringify_native_type (spec->native);
		ret = g_strdup_printf (" marshal (%s)", native_type);
		g_free (native_type);
		return ret;
	}	
	}
}

/**
 * get_generic_param
 * @m: metadata context 
 * @table_type: The type of table we are getting generics for (0 for typedef, 1 for method)
 * @row: The row in the table
 *
 * Returns: Allocated stringified generic parameters
 */
char*
get_generic_param (MonoImage *m, MonoGenericContainer *container)
{
        GString *result;
	char *retval;
	int i;

	if (!container)
		return NULL;

	result = g_string_new ("");

	g_string_append_c (result, '<');
	for (i = 0; i < container->type_argc; i++) {
		MonoGenericParam *param = mono_generic_container_get_param (container, i);
		MonoGenericParamInfo *param_info = mono_generic_param_info (param);
		MonoClass **constr;
		int first = 1;
		guint16 flags;
		char *esname;

		if (i > 0)
			g_string_append (result, ",");
		
		flags = param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK;
		if ((flags & GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) == GENERIC_PARAMETER_ATTRIBUTE_COVARIANT)
			g_string_append (result, "+ ");
		if ((flags & GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT) == GENERIC_PARAMETER_ATTRIBUTE_CONTRAVARIANT)
			g_string_append (result, "- ");

		flags = param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
		if ((flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT)
			g_string_append (result, "class ");
		if ((flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT)
			g_string_append (result, "valuetype ");
		if ((flags & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) == GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT)
			g_string_append (result, ".ctor ");

		for (constr = param_info->constraints; constr && *constr; constr++) {
			char *sig;

			if (first) {
				g_string_append_c (result, '(');
				first = 0;
			} else
				g_string_append (result, ", ");
			sig = dis_stringify_type (m, m_class_get_byval_arg (*constr), TRUE);
			g_string_append (result, sig);
			g_free (sig);
                }

		if (!first)
			g_string_append (result, ") ");

		esname = get_escaped_name (mono_generic_param_info (param)->name);
		g_string_append (result, esname);
		g_free (esname);
	}

	g_string_append_c (result, '>');

        retval = result->str;
        g_string_free (result, FALSE);
        return retval;
}

char*
dis_stringify_method_signature (MonoImage *m, MonoMethodSignature *method, int methoddef_row,
				MonoGenericContainer *container, gboolean fully_qualified)
{
	return dis_stringify_method_signature_full (m, method, methoddef_row, container, fully_qualified, TRUE);
}

/* 
 * @m: metadata context
 * @method: MonoMethodSignature to dis-stringify
 * @methoddef_row: row index in the Method table
 * @context: generic context, generic method's context in case of a Generic method
 *	     or a generic type's context. if !@context, treats it as a non-generic method
 * @fully_qualified: TRUE to print type name also.
 *
 * Returns: Allocated stringified method signature
 */
char*
dis_stringify_method_signature_full (MonoImage *m, MonoMethodSignature *method, int methoddef_row,
				MonoGenericContainer *container, gboolean fully_qualified, gboolean with_marshal_info)
{
	guint32 cols [MONO_METHOD_SIZE];
	guint32 pcols [MONO_PARAM_SIZE];
	guint32 param_index = 0, next_param_index = 0;
	gboolean has_param_row;
	const char *method_name = "";
	int free_method = 0;
	char *retval, *esname;
	char *type = NULL;
	char *marshal_info = NULL, *ret_marshal_info = NULL;
	char *gen_param = NULL;
	GString *result = g_string_new ("");
	GString *result_ret = g_string_new ("");
	int i, start;
        
	g_assert (method || methoddef_row);

	if (methoddef_row) {
		mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], methoddef_row -1, cols, MONO_METHOD_SIZE);
		if (fully_qualified) {
			guint32 type_idx = mono_metadata_typedef_from_method (m, methoddef_row);
			if (type_idx)
				type = get_typedef (m, type_idx);
			else
				type = g_strdup ("<invalid>");
		}
		method_name = mono_metadata_string_heap (m, cols [MONO_METHOD_NAME]);
		param_index = cols [MONO_METHOD_PARAMLIST];
		if (!method) {
			ERROR_DECL (error);
			const char *sig = mono_metadata_blob_heap (m, cols [MONO_METHOD_SIGNATURE]);

			container = mono_metadata_load_generic_params (m, MONO_TOKEN_METHOD_DEF | methoddef_row, container, NULL);
			if (container) {
				mono_metadata_load_generic_param_constraints_checked (m, MONO_TOKEN_METHOD_DEF | methoddef_row, container, error);
				g_assert (is_ok (error)); /*FIXME don't swallow the error message*/
			}

			mono_metadata_decode_blob_size (sig, &sig);
			method = mono_metadata_parse_method_signature_full (m, container, methoddef_row, sig, &sig, error);
			g_assert (is_ok (error)); /*FIXME don't swallow the error message*/
			free_method = 1;
		}

		if (container && container->is_method)
			gen_param = get_generic_param (m, container);
		
		if (methoddef_row < m->tables [MONO_TABLE_METHOD].rows) {
			mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], methoddef_row, cols, MONO_METHOD_SIZE);
			next_param_index = cols [MONO_METHOD_PARAMLIST];
		} else {
			next_param_index = m->tables [MONO_TABLE_PARAM].rows + 1;
		}
	} 

	start = method->hasthis ? 0 : 1;
	for (i = 0; i < method->param_count + 1; ++i) {
		marshal_info = NULL;
		has_param_row = param_index && param_index < next_param_index;
		esname = NULL;

		if (method->param_count == 0 && !has_param_row)
			/* method has zero parameters, and no row for return val in the PARAM table */
			continue;
		
		if (has_param_row)
			mono_metadata_decode_row (&m->tables [MONO_TABLE_PARAM], param_index - 1, pcols, MONO_PARAM_SIZE);
		
		if (has_param_row && i == pcols [MONO_PARAM_SEQUENCE]) {
			if (i)
				esname = get_escaped_name (
						mono_metadata_string_heap (m, pcols [MONO_PARAM_NAME]));

			if (with_marshal_info && (pcols [MONO_PARAM_FLAGS] & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL)) {
				const char *tp;
				MonoMarshalSpec *spec;
				tp = mono_metadata_get_marshal_info (m, param_index - 1, FALSE);
				if (tp) {
					spec = mono_metadata_parse_marshal_spec (m, tp);
	
					if (i)
						marshal_info = dis_stringify_marshal_spec (spec);
					else
						ret_marshal_info = dis_stringify_marshal_spec (spec);
				} else {
					if (i)
						marshal_info = g_strdup ("(missing)");
					else
						ret_marshal_info = g_strdup ("(missing)");
				}
			}
			param_index ++;
		} else {
			if (i)
				/* A_[0-9]* does not require escaping */
				esname = g_strdup_printf ("A_%i", i - start);
		}

		if (!i)
			continue;

		if (i > 1)
			g_string_append (result, ", ");

		retval = dis_stringify_param (m, method->params [i - 1]);

		g_string_append_printf (result, "%s%s %s", retval, marshal_info ? marshal_info : "", esname);
		g_free (retval);
		g_free (esname);
		g_free (marshal_info);
	}
	g_string_append (result, ") ");

	retval = dis_stringify_param (m, method->ret);

	if (method->hasthis)
		g_string_append (result_ret, "instance ");
	g_string_append (result_ret, map (method->call_convention, call_conv_type_map));
	g_string_append_printf (result_ret, " %s%s ", retval, ret_marshal_info ? ret_marshal_info :"");
	g_free (ret_marshal_info);
	if (type) {
		char *estype = get_escaped_name (type);
		g_string_append_printf (result_ret, "%s::", estype);
		g_free (estype);
		g_free (type);
	}
	esname = get_escaped_name (method_name);
	g_string_append (result_ret, esname);
	g_free (esname);
	if (gen_param) {
		g_string_append (result_ret, gen_param);
		g_free (gen_param);
	}
	g_string_append (result_ret, " (");
	g_free (retval);

	g_string_append (result_ret, result->str);
	g_string_free (result, TRUE);
	result = result_ret;

	if (show_method_tokens && methoddef_row)
		g_string_append_printf (result, " /* 0x%X */ ",
				(methoddef_row >> MONO_TYPEORMETHOD_BITS) | MONO_TOKEN_METHOD_DEF);

	if (free_method)
		mono_metadata_free_method_signature (method);
	retval = result->str;
	g_string_free (result, FALSE);

	return retval;
}

char*
dis_stringify_function_ptr (MonoImage *m, MonoMethodSignature *method)
{
	char *retval;
	GString *result = g_string_new ("");
	int i;
	
	g_assert (method);

	g_string_append (result, map (method->call_convention, call_conv_type_map));

	retval = dis_stringify_param (m, method->ret);
	g_string_append_printf (result, " %s ", retval);
	g_free (retval);

	g_string_append (result, " *(");
	for (i = 0; i < method->param_count; ++i) {
		if (i)
			g_string_append (result, ", ");
		retval = dis_stringify_param (m, method->params [i]);
		g_string_append (result, retval);
		g_free (retval);
	}
	g_string_append (result, ") ");

	retval = result->str;
	g_string_free (result, FALSE);

	return retval;
}

static char *
get_escaped_class_name (MonoClass *c)
{
	char *result, *esname;

	if (m_class_get_type_token (c) == mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1))
		/* <Module> */
		return NULL;

	if (m_class_get_rank (c) || m_class_get_byval_arg (c)->type == MONO_TYPE_PTR) 
		g_assert (0);

	esname = get_escaped_name (m_class_get_name (c));

	if (m_class_get_nested_in (c)){
		char *part_a = get_escaped_class_name (m_class_get_nested_in (c));

		result = g_strdup_printf ("%s/%s", part_a, esname);
		g_free (part_a);
	} else if (*m_class_get_name_space (c))
		result = g_strdup_printf ("%s.%s", m_class_get_name_space (c), esname);
	else
		result = g_strdup (esname);

	g_free (esname);
	return result;
}

char *
dis_stringify_object_with_class (MonoImage *m, MonoClass *c, gboolean prefix, gboolean is_def)
{
	/* FIXME: handle MONO_TYPE_OBJECT ... */
	MonoType *type = m_class_get_byval_arg (c);
	const char *otype = type->type == MONO_TYPE_VALUETYPE ? "valuetype " : "class " ;
	char *assemblyref = NULL, *result, *esname, *generic = NULL;
	
	if (m_class_get_type_token (c) == mono_metadata_make_token (MONO_TABLE_TYPEDEF, 1))
		/* <Module> */
		return NULL;

	if (m != m_class_get_image (c)) {
		if (m_class_get_image (c)->assembly_name) {
			/* we cheat */
			if (substitute_with_mscorlib_p && !strcmp ("corlib", m_class_get_image (c)->assembly_name))
				assemblyref = g_strdup_printf ("[%s]", "mscorlib");
			else {
				char *esc = get_escaped_name (m_class_get_image (c)->assembly->aname.name);
				
				assemblyref = g_strdup_printf ("[%s]", esc);
				g_free (esc);
			}
		} else {
			assemblyref = g_strdup_printf ("[.module %s]", m_class_get_image (c)->module_name);
		}
	}

	esname = get_escaped_class_name (c);

	if (mono_class_is_ginst (c)) {
		MonoGenericClass *gclass = mono_class_get_generic_class (c);
		MonoGenericInst *inst = gclass->context.class_inst;
		GString *str = g_string_new ("");
		int i;

		for (i = 0; i < inst->type_argc; i++){
			char *t = dis_stringify_type (m, inst->type_argv [i], is_def);

			g_string_append (str, t);
			if (i+1 != inst->type_argc)
				g_string_append (str, ", ");
			g_free (t);
		}
		generic = g_strdup_printf ("<%s>", str->str);
		g_string_free (str, TRUE);
	}


	result = g_strdup_printf ("%s%s%s%s", prefix ? otype : "", assemblyref?assemblyref:"",
				  esname, generic?generic:"");
	
	g_free (generic);
	g_free (assemblyref);
	g_free (esname);
	
	return result;
}

static char *
dis_stringify_object (MonoImage *m, MonoType *type, gboolean is_def)
{
	MonoClass *c = mono_class_from_mono_type_internal (type);
	return dis_stringify_object_with_class (m, c, TRUE, is_def);
}

char*
dis_stringify_type (MonoImage *m, MonoType *type, gboolean is_def)
{
	const char *pinned = "", *byref = "";
	char *bare = NULL, *mods = NULL;
	char *result;

	if (type->has_cmods) {
		MonoCustomModContainer *cmods = mono_type_get_cmods (type);
		mods = dis_stringify_modifiers (cmods->image, cmods->count, cmods->modifiers);
	}

	switch (type->type){
	case MONO_TYPE_BOOLEAN:
	case MONO_TYPE_CHAR:
	case MONO_TYPE_I1:
	case MONO_TYPE_U1:
	case MONO_TYPE_I2:
	case MONO_TYPE_U2:
	case MONO_TYPE_I4:
	case MONO_TYPE_U4:
	case MONO_TYPE_I8:
	case MONO_TYPE_U8:
	case MONO_TYPE_R4:
	case MONO_TYPE_R8:
	case MONO_TYPE_I:
	case MONO_TYPE_U:
	case MONO_TYPE_STRING:
	case MONO_TYPE_OBJECT:
	case MONO_TYPE_TYPEDBYREF:
		bare = g_strdup (map (type->type, element_type_map));
		break;
		
	case MONO_TYPE_VALUETYPE:
	case MONO_TYPE_CLASS:
		bare = dis_stringify_object (m, type, is_def);
		break;
	case MONO_TYPE_FNPTR: {
		char *child_type;
		child_type = dis_stringify_function_ptr (m, type->data.method);
		bare = g_strdup_printf ("method %s", child_type);
		g_free (child_type);
		break;
	}
	case MONO_TYPE_PTR: {
		char *child_type;
		child_type = dis_stringify_type (m, type->data.type, is_def);
		
		bare = g_strdup_printf ("%s*", child_type);
		g_free (child_type);
		break;
	}
	case MONO_TYPE_SZARRAY: {
		char *child_type;
		child_type = dis_stringify_type (m, m_class_get_byval_arg (type->data.klass), is_def);
		
		bare = g_strdup_printf ("%s[]", child_type);
		g_free (child_type);
		break;
	}
	case MONO_TYPE_ARRAY:
		bare = dis_stringify_array (m, type->data.array, is_def);
		break;
	case MONO_TYPE_VOID:
		bare = g_strdup ("void");
		break;
	case MONO_TYPE_MVAR:
		if (is_def && !cant_print_generic_param_name (type->data.generic_param))
			bare = g_strdup_printf ("!!%s", get_escaped_name (mono_generic_param_info (type->data.generic_param)->name));
		else
			bare = g_strdup_printf ("!!%d", mono_type_get_generic_param_num (type));
		break;
	case MONO_TYPE_VAR:
		if (is_def && !cant_print_generic_param_name (type->data.generic_param))
			bare = g_strdup_printf ("!%s", get_escaped_name (mono_generic_param_info (type->data.generic_param)->name));
		else
			bare = g_strdup_printf ("!%d", mono_type_get_generic_param_num (type));
		break;
	case MONO_TYPE_GENERICINST: {
		GString *str = g_string_new ("");
		MonoGenericInst *inst;
		int i;
		char *generic_type = dis_stringify_type (
			m, m_class_get_byval_arg (type->data.generic_class->container_class), is_def);
		inst = type->data.generic_class->context.class_inst;
		for (i = 0; i < inst->type_argc; i++){
			char *t = dis_stringify_type (m, inst->type_argv [i], is_def);

			g_string_append (str, t);
			if (i+1 != inst->type_argc)
				g_string_append (str, ", ");
			g_free (t);
		}
		bare = g_strdup_printf ("%s<%s>", generic_type, str->str);
		g_string_free (str, TRUE);
		break;
	}
		
	default:
		g_error ("Do not know how to stringify type 0x%x", type->type);
	}
	
	if (type->pinned)
		pinned = " pinned";

	if (type->byref)
		byref = "&";
	
	if (!bare)
		/* bare is NULL, for <Module> */
		return bare;
		
	result = g_strconcat (bare, byref, pinned, mods ? mods : "", (const char*)NULL);

	g_free (bare);

	return result;
}

/**
 * get_type:
 * @m: metadata context 
 * @ptr: location to decode from.
 * @result: pointer to string where resulting decoded string is stored
 *
 * This routine returs in @result the stringified type pointed by @ptr.
 * (22.2.12)
 *
 * Returns: the new ptr to continue decoding
 */
const char *
get_type (MonoImage *m, const char *ptr, char **result, gboolean is_def, MonoGenericContainer *container)
{
	ERROR_DECL (error);
	const char *start = ptr;
	guint32 type;
	MonoType *t;

	if (*ptr == MONO_TYPE_BYREF)
		++ptr;

	type = mono_metadata_decode_value (ptr, &ptr);

	switch (type){
	case MONO_TYPE_VALUETYPE:
	case MONO_TYPE_CLASS: {
		guint32 token = mono_metadata_parse_typedef_or_ref (m, ptr, &ptr);
		MonoClass *klass = mono_class_get_checked (m, token, error);
		char *temp;
		if (klass) {
			temp = dis_stringify_object_with_class (m, klass, TRUE, FALSE);
		} else  {
			temp = g_strdup_printf ("<BROKEN CLASS token_%8x due to %s>", token, mono_error_get_message (error));
			mono_error_cleanup (error);
		}

		if (show_tokens) {
			*result = get_token_comment (temp, token);
			g_free (temp);
		} else
			*result = temp;
		break;
	}

	case MONO_TYPE_GENERICINST: {
		GString *str = g_string_new ("");
		int count, i;
		char *temp;

		ptr = get_type (m, ptr, &temp, is_def, container);
		g_string_append (str, temp);
		g_free (temp);

		count = mono_metadata_decode_value (ptr, &ptr);
		g_string_append (str, "<");

		for (i = 0; i < count; i++) {
			if (i)
				g_string_append (str, ",");
			ptr = get_type (m, ptr, &temp, is_def, container);
			g_string_append (str, temp);
		}

		g_string_append (str, ">");
		*result = str->str;
		g_string_free (str, FALSE);
		break;
	}

	default:
		t = mono_metadata_parse_type_checked (m, container, 0, FALSE, start, &ptr, error);
		if (t) {
			*result = dis_stringify_type (m, t, is_def);
		} else {
			*result = g_strdup_printf ("Invalid type due to %s", mono_error_get_message (error));
			mono_error_cleanup (error);
		}

		break;
	}

	return ptr;
}

/**
 * 
 * Returns a stringified representation of a FieldSig (22.2.4)
 */
char *
get_field_signature (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container)
{
	char *allocated_modifier_string, *allocated_type_string;
	const char *ptr = mono_metadata_blob_heap (m, blob_signature);
	char *res;
	int len;
	
	len = mono_metadata_decode_value (ptr, &ptr);
	/* FIELD is 0x06 */
	g_assert (*ptr == 0x06);
/*	hex_dump (ptr, 0, len); */
	ptr++; len--;
	
	ptr = get_custom_mod (m, ptr, &allocated_modifier_string);
	ptr = get_type (m, ptr, &allocated_type_string, FALSE, container);

	res = g_strdup_printf (
		"%s%s",
		allocated_type_string,
		allocated_modifier_string ? allocated_modifier_string : "");
	
	g_free (allocated_modifier_string);
	g_free (allocated_type_string);
	
	return res;
}

MonoTypeEnum
get_field_literal_type (MonoImage *m, guint32 blob_signature)
{
	const char *ptr = mono_metadata_blob_heap (m, blob_signature);
	int len;
	char *allocated_modifier_string;
	
	len = mono_metadata_decode_value (ptr, &ptr);

	/* FIELD is 0x06 */
	g_assert (*ptr == 0x06);
	ptr++; len--;
	
	ptr = get_custom_mod (m, ptr, &allocated_modifier_string);
	g_free (allocated_modifier_string);

	return (MonoTypeEnum) *ptr;
	
}

/**
 * decode_literal:
 * @m: metadata context
 * @token: token to decode
 *
 * decodes the literal indexed by @token.
 */
char *
decode_literal (MonoImage *m, guint32 token)
{
	return g_strdup ("LITERAL_VALUE");
}

/**
 * get_ret_type:
 * @m: metadata context 
 * @ptr: location to decode from.
 * @result: pointer to string where resulting decoded string is stored
 *
 * This routine returns in @result the stringified RetType (22.2.11)
 *
 * Returns: the new ptr to continue decoding.
 */
const char *
get_ret_type (MonoImage *m, const char *ptr, char **ret_type, MonoGenericContainer *container)
{
	GString *str = g_string_new ("");
	char *mod = NULL;
	char *allocated_type_string;
	int has_byref = 0;
	
	ptr = get_custom_mod (m, ptr, &mod);

	if (*ptr == MONO_TYPE_TYPEDBYREF){
		g_string_append (str, "typedref");
		ptr++;
	} else if (*ptr == MONO_TYPE_VOID){
		 g_string_append (str, "void");
		 ptr++;
	} else {
                if (*ptr == MONO_TYPE_BYREF){
			has_byref = 1;
			ptr++;
		}

		ptr = get_type (m, ptr, &allocated_type_string, FALSE, container);
		g_string_append (str, allocated_type_string);
		if (has_byref)
			g_string_append (str, "& ");
		g_free (allocated_type_string);
	}

	if (mod){
		g_string_append (str, mod);
		g_string_append_c (str, ' ');
		g_free (mod);
	}

	*ret_type = str->str;
	g_string_free (str, FALSE);

	return ptr;
}

/**
 * get_param:
 * @m: metadata context 
 * @ptr: location to decode from.
 * @result: pointer to string where resulting decoded string is stored
 *
 * This routine returns in @result the stringified Param (22.2.10)
 *
 * Returns: the new ptr to continue decoding.
 */
const char *
get_param (MonoImage *m, const char *ptr, char **retval, MonoGenericContainer *container)
{
	GString *str = g_string_new ("");
	char *allocated_mod_string, *allocated_type_string;
	
	ptr = get_custom_mod (m, ptr, &allocated_mod_string);
	
	if (*ptr == MONO_TYPE_TYPEDBYREF){
		g_string_append (str, " typedref ");
		ptr++;
	} else {
		gboolean by_ref = 0;
		if (*ptr == MONO_TYPE_BYREF){
			g_string_append (str, "[out] ");
			ptr++;
			by_ref = 1;
		}
		ptr = get_type (m, ptr, &allocated_type_string, FALSE, container);
		g_string_append (str, allocated_type_string);
		if (by_ref)
			g_string_append_c (str, '&');
		g_free (allocated_type_string);
	}

	if (allocated_mod_string){
		g_string_append (str, allocated_mod_string);
		g_string_append_c (str, ' ');
		g_free (allocated_mod_string);
	}

	*retval = str->str;
	g_string_free (str, FALSE);
	return ptr;
}

/**
 * str_escape 
 * 
 * @str: string to process
 * @list: list of chars to escape
 * 
 * Returns: an allocated escaped string.
 */
static char*
str_escape (const char *str, const char *list)
{
	const char *p = str;
	GString *res;

	res = g_string_sized_new (strlen (str));

	for (;;) {
		while (*p && !strchr (list, *p))
			++p;
		g_string_append_len (res, str, p - str);
		if (!*p)
			break;
		g_string_append_c (res, '\\');
		str = p;
		++p;
	}
			
	return g_string_free (res, FALSE);
}

/**
 * get_escaped_name
 *
 * Returns: An allocated escaped name. A name needs to be escaped
 * because it might be an ilasm keyword.
 */
char*
get_escaped_name (const char *name)
{
	const char *s;
	char *ret, *esc;

	if (!name)
		return NULL;

	g_assert (key_table);

	if (strlen (name) == 0)
		return g_strdup (name);

	for (s = name; *s; s++) {
		char *first, *result;

		if (*s != '/')
			continue;

		first = g_strndup (name, s-name);
		result = g_strdup_printf ("%s/%s", get_escaped_name (first), get_escaped_name (s+1));
		g_free (first);

		return result;
	}

	for (s = name; *s; s++) {
		if (isalnum (*s) || *s == '_' || *s == '$' || *s == '@' ||
		    *s == '?' || (*s == '.' && s != name) || *s == 0 || *s == '!' || *s == '`')
			continue;

		esc = str_escape (name, "'\\");
		ret = g_strdup_printf ("'%s'", esc);
		g_free (esc);
		return ret;
	}
	
	if (g_hash_table_lookup (key_table, name))
		return g_strdup_printf ("'%s'", name);
			
	return str_escape (name, "'\\");
}

static dis_map_t param_map [] = {
	{ PARAM_ATTRIBUTE_IN,                "[in] " },
	{ PARAM_ATTRIBUTE_OUT,               "[out] " },
	{ PARAM_ATTRIBUTE_OPTIONAL,          "optional " },
	{ PARAM_ATTRIBUTE_HAS_DEFAULT,       "hasdefault " },
	{ PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL, "fieldmarshal " },
	{ 0, NULL }
};

char *
param_flags (guint32 f)
{
	return g_strdup (flags (f, param_map));
}

static dis_map_t field_access_map [] = {
	{ FIELD_ATTRIBUTE_COMPILER_CONTROLLED, "privatescope " },
	{ FIELD_ATTRIBUTE_PRIVATE,             "private " },
	{ FIELD_ATTRIBUTE_FAM_AND_ASSEM,       "famandassem " },
	{ FIELD_ATTRIBUTE_ASSEMBLY,            "assembly " },
	{ FIELD_ATTRIBUTE_FAMILY,              "family " },
	{ FIELD_ATTRIBUTE_FAM_OR_ASSEM,        "famorassem " },
	{ FIELD_ATTRIBUTE_PUBLIC,              "public " },
	{ 0, NULL }
};

static dis_map_t field_flags_map [] = {
	{ FIELD_ATTRIBUTE_STATIC,              "static " },
	{ FIELD_ATTRIBUTE_INIT_ONLY,           "initonly " },
	{ FIELD_ATTRIBUTE_LITERAL,             "literal " },
	{ FIELD_ATTRIBUTE_NOT_SERIALIZED,      "notserialized " },
	{ FIELD_ATTRIBUTE_SPECIAL_NAME,        "specialname " },
	{ FIELD_ATTRIBUTE_PINVOKE_IMPL,        "FIXME:pinvokeimpl " },
	{ FIELD_ATTRIBUTE_RT_SPECIAL_NAME,        "rtspecialname " },

	/* This is set when a MarshalAs attribute is seen. FIXME: round-trip?  */
	{ FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL,        "" },

	/* This seems to be set if LITERAL is set. FIXME: round-trip? */
	{ FIELD_ATTRIBUTE_HAS_DEFAULT, 		"" },

	/* This seems to be set on compiler-generated array initializer fields. FIXME: round-trip? */
	{ FIELD_ATTRIBUTE_HAS_FIELD_RVA, 		"" },
	{ 0, NULL }
};

/**
 * field_flags:
 *
 * Returns a stringified version of a Field's flags
 */
char *
field_flags (guint32 f)
{
	char buffer [1024];
	int access = f & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
	int rest = f & ~access;
	
	buffer [0] = 0;

	strcat (buffer, map (access, field_access_map));
	strcat (buffer, flags (rest, field_flags_map));
	return g_strdup (buffer);
}

/**
 * Returns a stringifed representation of a MethodRefSig (22.2.2)
 */
char *
get_methodref_signature (MonoImage *m, guint32 blob_signature, const char *fancy_name)
{
	GString *res = g_string_new ("");
	const char *ptr = mono_metadata_blob_heap (m, blob_signature);
	char *allocated_ret_type, *s;
	const char *cconv_str;
	gboolean seen_vararg = 0;
	int param_count;
	int i, gen_count = 0;
	int cconv;

	/* signature_len = */ mono_metadata_decode_value (ptr, &ptr);

	if (*ptr & 0x20){
		if (*ptr & 0x40)
			g_string_append (res, "explicit-this ");
		else
			g_string_append (res, "instance "); /* has-this */
	}

	if (*ptr & 0x10)
		gen_count = 1;
	cconv = *ptr & 0x0f;
	cconv_str = map (cconv, call_conv_type_map);
	if (strcmp (cconv_str, "default") != 0) {
		g_string_append (res, cconv_str);
		g_string_append (res, " ");
	}

	ptr++;
	if (gen_count)
		gen_count = mono_metadata_decode_value (ptr, &ptr);
	param_count = mono_metadata_decode_value (ptr, &ptr);
	if (cconv != 0xa) {
		ptr = get_ret_type (m, ptr, &allocated_ret_type, NULL);
		g_string_append (res, allocated_ret_type);
		g_free (allocated_ret_type);
	}

	if (fancy_name){
		g_string_append_c (res, ' ');
		g_string_append (res, fancy_name);
	}
        
	g_string_append (res, "(");

	/*
	 * param_count describes parameters *before* and *after*
	 * the vararg sentinel
	 */
	for (i = 0; i < param_count; i++){
		char *param = NULL;
		
		/*
		 * If ptr is a SENTINEL
		 */
		if (*ptr == 0x41){
			if (!seen_vararg){
				g_string_append (res, "..., ");
				seen_vararg = 1;
			}

			ptr++;
		}

		ptr = get_param (m, ptr, &param, NULL);
		g_string_append (res, param);
		if (i+1 != param_count)
			g_string_append (res, ", ");
		g_free (param);
	}
	g_string_append (res, ")");
	
	/*
	 * cleanup and return
	 */
	s = res->str;
	g_string_free (res, FALSE);
	return s;
}

/**
 * Returns a stringifed representation of a field ref
 */
char *
get_fieldref_signature (MonoImage *m, int idx, MonoGenericContainer *container)
{
	guint32 cols [MONO_MEMBERREF_SIZE];
	ERROR_DECL (error);
	MonoGenericContainer *new_container;
	char *type, *esname;
        char *sig;
	char *full_sig;

        mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF],
				  idx - 1, cols, MONO_MEMBERREF_SIZE);

	new_container = get_memberref_container (m, cols [MONO_MEMBERREF_CLASS], container, error);
	mono_error_assert_ok (error);
        sig = get_field_signature (m, cols [MONO_MEMBERREF_SIGNATURE], new_container);

	type = get_memberref_parent (m, cols [MONO_MEMBERREF_CLASS], container);
	esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_MEMBERREF_NAME]));

	full_sig = g_strdup_printf ("%s %s%s%s", 
			sig, 
			type ? type : "", 
			type ? "::" : "",
			esname);
        g_free (sig);
	g_free (type);
	g_free (esname);
        
        return full_sig;
}

/**
 * get_token_comment:
 *
 * If show_tokens is TRUE, return "prefix""token(table)".
 * If show_tokens is FALSE, return "prefix" or NULL if prefix is NULL.
 * Caller is responsible for freeing.
 */
char *
get_token_comment (const char *prefix, guint32 token)
{
	if (!show_tokens)
		return prefix ? g_strdup_printf ("%s", prefix) : NULL;
	gint32 tableidx = mono_metadata_token_table (token);
	if ((tableidx < 0) || (tableidx > MONO_TABLE_LAST))
		return g_strdup_printf ("%s/*%08x*/", prefix ? prefix : "", token);
	else
		return g_strdup_printf ("%s/*%08x(%s)*/", prefix ? prefix : "", token, mono_meta_table_name (tableidx));
}

/**
 * get_field:
 * @m: metadata context
 * @token: a FIELD_DEF token
 *
 * This routine has to locate the TypeDef that "owns" this Field.
 * Since there is no backpointer in the Field table, we have to scan
 * the TypeDef table and locate the actual "owner" of the field
 */
char *
get_field (MonoImage *m, guint32 token, MonoGenericContainer *container)
{
	int idx = mono_metadata_token_index (token);
	guint32 cols [MONO_FIELD_SIZE];
	char *sig, *res, *type, *estype, *esname, *token_comment;
	guint32 type_idx;

	/*
	 * We can get here also with a MenberRef token (for a field
	 * defined in another module/assembly, just like in get_method ()
	 */
	if (mono_metadata_token_code (token) == MONO_TOKEN_MEMBER_REF) {
                return get_fieldref_signature (m, idx, container);
	}
	g_assert (mono_metadata_token_code (token) == MONO_TOKEN_FIELD_DEF);

	mono_metadata_decode_row (&m->tables [MONO_TABLE_FIELD], idx - 1, cols, MONO_FIELD_SIZE);
	sig = get_field_signature (m, cols [MONO_FIELD_SIGNATURE], container);

	/*
	 * To locate the actual "container" for this field, we have to scan
	 * the TypeDef table.  LAME!
	 */
	type_idx = mono_metadata_typedef_from_field (m, idx);
	if (!type_idx) {
		res = g_strdup_printf ("<invalid> %s", sig);
		g_free (sig);
		return res;
	}

	type = get_typedef (m, type_idx);
	estype = get_escaped_name (type);
	esname = get_escaped_name (mono_metadata_string_heap (m, cols [MONO_FIELD_NAME]));
	token_comment = get_token_comment (NULL, token);
	res = g_strdup_printf ("%s %s%s%s%s",
			sig, 
			estype ? estype : "",
			estype ? "::" : "",
			esname,
			token_comment ? token_comment : ""
		);

	g_free (type);
	g_free (sig);
	g_free (estype);
	g_free (esname);
	g_free (token_comment);

	return res;
}

/**
 * get_memberref_container:
 * \param m The \c MonoImage
 * \param mrp_token a \c MemberRefParent token in \p m
 * \param container the parent generic container
 * \param error set on error
 *
 * \returns the generic container given a MemberRefParent token, or \c NULL if
 * the \c MemberRefParent is not a generic type.
 *
 * On error returns NULL and sets \p error.
 */
static MonoGenericContainer *
get_memberref_container (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container, MonoError *error)
{
	MonoClass *klass;

	/*
	 * mrp_index is a MemberRefParent coded index
	 */
	guint32 table = mrp_token & 7;
	guint32 idx = mrp_token >> 3;

	switch (table){
	case 0: /* TypeDef */
		return mono_metadata_load_generic_params (m, MONO_TOKEN_TYPE_DEF | idx, NULL, NULL);
		
	case 1: /* TypeRef */
		return NULL;
		
	case 4: /* TypeSpec */
		klass = mono_class_get_and_inflate_typespec_checked (m, MONO_TOKEN_TYPE_SPEC | idx, &container->context, error);
		return_val_if_nok (error, NULL);
		g_assert (klass);
		return mono_class_is_ginst (klass) ? mono_class_get_generic_container (mono_class_get_generic_class (klass)->container_class) : NULL;
	}
	g_assert_not_reached ();
	return NULL;
}

static char *
get_memberref_parent (MonoImage *m, guint32 mrp_token, MonoGenericContainer *container)
{
	/*
	 * mrp_index is a MemberRefParent coded index
	 */
	guint32 table = mrp_token & 7;
	guint32 idx = mrp_token >> 3;

	switch (table){
	case 0: /* TypeDef */
		return get_typedef (m, idx);
		
	case 1: /* TypeRef */
		return get_typeref (m, idx);
		
	case 2: /* ModuleRef */
		return g_strdup_printf ("TODO:MemberRefParent-ModuleRef");
		
	case 3: /* MethodDef */
		return g_strdup ("TODO:MethodDef");
		
	case 4: /* TypeSpec */
		return get_typespec (m, idx, FALSE, container);
	}
	g_assert_not_reached ();
	return NULL;
}

/**
 * get_method:
 * @m: metadata context
 * @token: a METHOD_DEF or MEMBER_REF token
 *
 * This routine has to locate the TypeDef that "owns" this Field.
 * Since there is no backpointer in the Field table, we have to scan
 * the TypeDef table and locate the actual "owner" of the field
 */
static char *
get_method_core (MonoImage *m, guint32 token, gboolean fullsig, MonoGenericContainer *container)
{
	ERROR_DECL (error);
	int idx = mono_metadata_token_index (token);
	guint32 member_cols [MONO_MEMBERREF_SIZE], method_cols [MONO_METHOD_SIZE];
	char *sig = NULL, *esname;
	char *name;

	MonoMethod *mh;
	MonoGenericContainer *type_container = container;

	mh = mono_get_method_checked (m, token, NULL, (MonoGenericContext *) container, error);
	if (mh) {
		if (mono_method_signature_internal (mh)->is_inflated)
			container = mono_method_get_generic_container (((MonoMethodInflated *) mh)->declaring);
		esname = get_escaped_name (mh->name);
		sig = dis_stringify_type (m, m_class_get_byval_arg (mh->klass), TRUE);
		char *token_comment = get_token_comment (NULL, token);
		name = g_strdup_printf ("%s%s%s%s", sig ? sig : "", token_comment ? token_comment : "", sig ? "::" : "", esname);
		g_free (sig);
		g_free (esname);
		g_free (token_comment);
	} else {
		name = NULL;
		mono_error_cleanup (error);
	}

	switch (mono_metadata_token_code (token)){
	case MONO_TOKEN_METHOD_DEF:
		mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], 
					  idx - 1, method_cols, MONO_METHOD_SIZE);

		sig = get_methodref_signature (m, method_cols [MONO_METHOD_SIGNATURE], name);
		break;
		
	case MONO_TOKEN_MEMBER_REF: {
		mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF],
					  idx - 1, member_cols, MONO_MEMBERREF_SIZE);
		if (!name) {
			char *parent = get_memberref_parent (m, member_cols [MONO_MEMBERREF_CLASS], container);
			name = g_strdup_printf ("%s%s%s",
						parent ? parent : "", 
						parent ? "::" : "",
						mono_metadata_string_heap (m, member_cols [MONO_MEMBERREF_NAME]));
			g_free (parent);
		}
		if (mh) {
			int arity = 0;

			if (mh->is_generic)
				arity = mono_method_get_generic_container (mh)->type_argc;
			else
			if (mh->is_inflated && ((MonoMethodInflated *)mh)->declaring->is_generic)
				arity = mono_method_get_generic_container (((MonoMethodInflated*) mh)->declaring)->type_argc;

			if (arity > 0) {
				char *str = g_strdup_printf ("%s <[%d]>", name, arity);
				g_free (name);
				name = str;
			}
		}
				
		sig = get_methodref_signature (
			m, member_cols [MONO_MEMBERREF_SIGNATURE], name);
		break;
	}
	case MONO_TOKEN_METHOD_SPEC: {
                mono_metadata_decode_row (&m->tables [MONO_TABLE_METHODSPEC],
                                idx - 1, member_cols, MONO_METHODSPEC_SIZE);
		token = member_cols [MONO_METHODSPEC_METHOD];
                sig = get_methodspec (m, idx, token, name, type_container);
		break;
	}

	default:
		g_assert_not_reached ();
	}

	if (fullsig)
		g_free (name);
	else {
		g_free (sig);
		return name;
	}
	
	if (show_tokens) {
		char *retval = get_token_comment (sig, token);
		g_free (sig);
		return retval;
	} else
		return sig;
}

char *
get_method (MonoImage *m, guint32 token, MonoGenericContainer *container)
{
	return get_method_core (m, token, TRUE, container);
}

/**
 * get_methoddef
 * @m: metadata context
 * @idx: index into the method table
 *
 * Returns: A stringified version of the method signature.
 */
char *
get_methoddef (MonoImage *m, guint32 idx)
{
	ERROR_DECL (error);
	guint32 cols [MONO_METHOD_SIZE];
	char *sig;
	const char *name;

	MonoMethod *mh;

	mh = mono_get_method_checked (m, MONO_TOKEN_METHOD_DEF | idx, NULL, NULL, error);
	if (mh) {
		sig = dis_stringify_type (m, m_class_get_byval_arg (mh->klass), FALSE);
		name = g_strdup_printf ("%s%s%s", 
				sig ? sig : "",
				sig ? "::" : "",
				mh->name);
		g_free (sig);
	} else {
		name = g_strdup_printf ("!bad-method-name!");
		mono_error_cleanup (error);
	}
    mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], 
                    idx - 1, cols, MONO_METHOD_SIZE);
    sig = get_methodref_signature (m, cols [MONO_METHOD_SIGNATURE], name);
        
	return sig;
}

char *
get_method_type_param (MonoImage *m, guint32 blob_signature, MonoGenericContainer *container)
{
	GString *res = g_string_new ("");
	const char *ptr = mono_metadata_blob_heap (m, blob_signature);
	int param_count;
	int i = 0;
	char *s;
	
	mono_metadata_decode_value (ptr, &ptr);
	ptr++;
	param_count = mono_metadata_decode_value (ptr, &ptr);
	
	g_string_append_c (res, '<');
        
	for (i = 0; i < param_count; i++){
		char *param = NULL;

		ptr = get_param (m, ptr, &param, container);
		g_string_append (res, param);
		if (i+1 != param_count)
			g_string_append (res, ", ");
		g_free (param);
	}
	g_string_append_c (res, '>');
        
	s = res->str;
	g_string_free (res, FALSE);
	return s;
}

/**
 * get_methodspec
 *
 * Returns: An allocated stringified version of the methodspec signature.
 */

char *
get_methodspec (MonoImage *m, int idx, guint32 token, const char *fancy_name, MonoGenericContainer *type_container)
{
	ERROR_DECL (error);
	GString *res = g_string_new ("");
	guint32 member_cols [MONO_MEMBERREF_SIZE], method_cols [MONO_METHOD_SIZE];
        char *s, *type_param;
        const char *ptr;
	guint32 sig = 0;
	int param_count, cconv, i, gen_count = 0;
	MonoGenericContainer *container;
	MonoMethod *mh = NULL;

	switch (token & MONO_METHODDEFORREF_MASK) {
	case MONO_METHODDEFORREF_METHODDEF:
		mono_metadata_decode_row (&m->tables [MONO_TABLE_METHOD], 
					  (token >> MONO_METHODDEFORREF_BITS) - 1,
					  method_cols, MONO_METHOD_SIZE);
		sig = method_cols [MONO_METHOD_SIGNATURE];
		break;
	case MONO_METHODDEFORREF_METHODREF:
		mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], 
					  (token >> MONO_METHODDEFORREF_BITS) - 1,
					  member_cols, MONO_MEMBERREF_SIZE);
		sig = member_cols [MONO_MEMBERREF_SIGNATURE];
		break;
	default:
		g_assert_not_reached ();
	}

	ptr = mono_metadata_blob_heap (m, sig);
	mono_metadata_decode_value (ptr, &ptr);
	
	mh = mono_get_method_checked (m, method_dor_to_token (token), NULL, (MonoGenericContext *) type_container, error);
	if (!mh) {
		g_string_append_printf (res, "Could not decode method token 0x%x due to %s", token, mono_error_get_message (error));
		mono_error_cleanup (error);
		return g_string_free (res, FALSE);
	}

	container = mono_method_get_generic_container (mh);
	if (!container)
		container = type_container;

	if (*ptr & 0x20){
		if (*ptr & 0x40)
			g_string_append (res, "explicit-this ");
		else
			g_string_append (res, "instance "); /* has-this */
	}

	if (*ptr & 0x10)
		gen_count = 1;
	cconv = *ptr & 0x0f;
	ptr++;
	if (gen_count)
		gen_count = mono_metadata_decode_value (ptr, &ptr);
	param_count = mono_metadata_decode_value (ptr, &ptr);
	if (cconv != 0xa) {
                char *allocated_ret_type;
		ptr = get_ret_type (m, ptr, &allocated_ret_type, container);
		g_string_append (res, allocated_ret_type);
		g_free (allocated_ret_type);
	}

	if (fancy_name){
		g_string_append_c (res, ' ');
		g_string_append (res, fancy_name);
	}

        mono_metadata_decode_row (&m->tables [MONO_TABLE_METHODSPEC],
                        idx - 1, member_cols, MONO_METHODSPEC_SIZE);
        token = member_cols [MONO_METHODSPEC_SIGNATURE];
        type_param = get_method_type_param (m, token, type_container);
        g_string_append (res, type_param);
	g_string_append (res, " (");

	/*
         * methodspecs can not be varargs so we don't need to worry about that here
         */
         
	for (i = 0; i < param_count; i++){
		char *param = NULL;
		
		ptr = get_param (m, ptr, &param, container);
		g_string_append (res, param);
		if (i+1 != param_count)
			g_string_append (res, ", ");
		g_free (param);
	}
	g_string_append (res, ")");
	
	/*
	 * cleanup and return
	 */
	s = res->str;
	g_string_free (res, FALSE);
	return s;
}

/*
 * get_encoded_user_string_bytearray:
 * @ptr: pointer into the US heap
 * @len: length of string in the heap.
 *
 * Strings on the US heap are encoded using UTF-16. Print a bytearray.
 */
static char*
get_encoded_user_string_bytearray (const unsigned char* ptr, int len)
{
	gchar *str;
	int i, j, tspaces = (len % 16);
	GString *res;

	if (len == 0)
		return g_strdup_printf ("\"\"");

	res = g_string_new ("bytearray (\n\t");

	for (i = 1; i <= len; ++i) {
		g_string_append_printf (res, "%02x ", ptr [i-1]);

		if (i % 16 == 0) {
			if (i == len)
				g_string_append (res, ")// ");
			else
				g_string_append (res, " // ");

			for(j = i - 16; j < i; ++j) 
				g_string_append_printf (res, "%c", isprint (ptr [j]) ? ptr [j] : '.');
			g_string_append (res, "\n\t");
		}
	}

	if (tspaces) {
		g_string_append (res, ")  ");
		for (i = tspaces + 1; i < 16; ++i)
			g_string_append_printf (res, "   ");

		g_string_append (res, " // ");
		for(i = len - tspaces; i < len; ++i)
			g_string_append_printf (res, "%c", isprint (ptr [i]) ? ptr [i] : '.');
		g_string_append (res, "\n\t");
	} 

	str = res->str;
	g_string_free (res, FALSE);
	return str;
}

/*
 * get_encoded_user_string_or_bytearray:
 * @ptr: pointer into the US heap
 *
 * Strings on the US heap are encoded using UTF-16. Print as string
 * if possible, else emit a bytearray.
 */
char*
get_encoded_user_string_or_bytearray (const unsigned char *ptr, int len)
{
	char *res, *eres, *result;
	int i;

	res = (char *)g_malloc ((len >> 1) + 1);

	/*
	 * I should really use some kind of libunicode here
	 */
	for (i = 0; i + 1 < len; i += 2) {
		if (ptr [i + 1] || 
		    (!isprint (ptr [i]) && ptr [i] != '\\' && ptr [i] != '"' && 
		     ptr [i] != '\r' && ptr [i] != '\n' && ptr [i] != '\t')) {
			g_free (res);
			return get_encoded_user_string_bytearray (ptr, len);
		}

		res [i >> 1] = ptr [i];
	}	

	res [len >> 1] = 0;

	eres = g_strescape (res, NULL);
	result = g_strdup_printf ("\"%s\"", eres);
	g_free (res);
	g_free (eres);
	
	return result;
}

char *
stringify_double (double r)
{
	char *ret, *ptr;

	ret = g_strdup_printf ("%.17g.", r);
	ptr = ret + strlen (ret) - 1;
	if (strpbrk (ret, ".eE") != ptr)
		*ptr = '\0';

	return ret;
}

/**
 * get_constant:
 * @m: metadata context
 * @blob_index: index into the blob where the constant is stored
 *
 * Returns: An allocated value representing a stringified version of the
 * constant.
 */
char *
get_constant (MonoImage *m, MonoTypeEnum t, guint32 blob_index)
{
	const char *ptr = mono_metadata_blob_heap (m, blob_index);
	int len;
	
	len = mono_metadata_decode_value (ptr, &ptr);
	
	switch (t){
	case MONO_TYPE_BOOLEAN:
		return g_strdup_printf ("%s", *ptr ? "bool(true)" : "bool(false)");
		
	case MONO_TYPE_CHAR:
		return g_strdup_printf ("char(0x%04x)", read16(ptr)); 
		
	case MONO_TYPE_U1:
	case MONO_TYPE_I1:
		return g_strdup_printf ("int8(0x%02x)", (int) ((*ptr) & 0xFF));
		break;
		
	case MONO_TYPE_U2:
	case MONO_TYPE_I2:
		return g_strdup_printf ("int16(0x%08x)", (int) read16 (ptr));
		
	case MONO_TYPE_U4:
	case MONO_TYPE_I4:
		return g_strdup_printf ("int32(0x%08x)", read32 (ptr));
		
	case MONO_TYPE_U8:
	case MONO_TYPE_I8: {
		guint32 low, high;
		low = read32 (ptr);
		high = read32 (ptr + 4);
		return g_strdup_printf ("int64(0x%08x%08x)", high, low);
	}
	case MONO_TYPE_R4: {
		float r;
		readr4 (ptr, &r);

		if (!mono_isfinite (r)) {
			return g_strdup_printf ("float32(0x%08x)", read32 (ptr));
		} else {
			char *str = stringify_double ((double) r);
			char *ret = g_strdup_printf ("float32(%s)", str);
			g_free (str);
			return ret;
		}
	}	
	case MONO_TYPE_R8: {
		double r;
		readr8 (ptr, &r);

		if (!mono_isfinite (r)) {
			guint32 low, high;
			low = read32 (ptr);
			high = read32 (ptr + 4);
			return g_strdup_printf ("float64(0x%08x%08x)", high, low);
		} else {
			char *str = stringify_double (r);
			char *ret = g_strdup_printf ("float64(%s)", str);
			g_free (str);
			return ret;
		}
	}
	case MONO_TYPE_STRING:
		return get_encoded_user_string_or_bytearray ((const guchar*)ptr, len);
		
	case MONO_TYPE_CLASS:
		return g_strdup ("nullref");
		
	default:
		g_error ("Unknown MONO_TYPE (%d) on constant at Blob index (0x%08x)\n",
			 (int) *ptr, blob_index);
		return g_strdup_printf ("Unknown");
	}

}

/**
 * get_token:
 * @m: metadata context
 * @token: token that we want to decode.
 *
 * Returns: An allocated value representing a stringified version of the
 * constant.
 */
char *
get_token (MonoImage *m, guint32 token, MonoGenericContainer *container)
{
	char *temp, *result;
	guint32 idx = mono_metadata_token_index (token);

	switch (mono_metadata_token_code (token)){
	case MONO_TOKEN_FIELD_DEF:
		temp = get_field (m, token, container);
		result = g_strdup_printf ("field %s", temp);
		g_free (temp);
		return result;
	case MONO_TOKEN_METHOD_DEF:
	case MONO_TOKEN_METHOD_SPEC:
		temp = get_method (m, token, container);
		result = g_strdup_printf ("method %s", temp);
		g_free (temp);
		return result;
	case MONO_TOKEN_TYPE_DEF:
		temp = get_typedef (m, idx);
		result = get_escaped_name (temp);
		g_free (temp);
		return result;
	case MONO_TOKEN_TYPE_REF:
		return get_typeref (m, idx);
	case MONO_TOKEN_TYPE_SPEC:
		return get_typespec (m, idx, TRUE, container);
	case MONO_TOKEN_MEMBER_REF: {
		guint32 cols [MONO_MEMBERREF_SIZE];
		const char *sig;
		mono_metadata_decode_row (&m->tables [MONO_TABLE_MEMBERREF], mono_metadata_token_index (token) - 1, cols, MONO_MEMBERREF_SIZE);
		sig = mono_metadata_blob_heap (m, cols [MONO_MEMBERREF_SIGNATURE]);
		mono_metadata_decode_blob_size (sig, &sig);
		if (*sig == 0x6) { /* it's a field */
			temp = get_field (m, token, container);
			result = g_strdup_printf ("field %s", temp);
			g_free (temp);
			return result;
		} else {
			temp = get_method (m, token, container);
			result = g_strdup_printf ("method %s", temp);
			g_free (temp);
			return result;
		}
		break;
	}
	default:		
		g_error ("Do not know how to decode tokens of type 0x%08x", token);
	}

	g_assert_not_reached ();
	return g_strdup ("ERROR");
}

/**
 * get_token_type:
 * @m: metadata context
 * @token: the token can belong to any of the following tables:
 * MONO_TOKEN_TYPE_REF, MONO_TOKEN_TYPE_DEF, MONO_TOKEN_TYPE_SPEC
 *
 * Returns: a stringified version of the MethodDef or MethodRef or TypeSpecn
 * at (token & 0xffffff) 
 */
char *
get_token_type (MonoImage *m, guint32 token, MonoGenericContainer *container)
{
	char *temp = NULL, *s = NULL;
	int idx;

	idx = mono_metadata_token_index (token);
	
	switch (mono_metadata_token_code (token)){
	case MONO_TOKEN_TYPE_DEF:
		temp = get_typedef (m, idx);
		s = g_strdup_printf ("%s", temp);
		break;
		
	case MONO_TOKEN_TYPE_REF: 
		temp = get_typeref (m, idx);
		s = g_strdup_printf ("%s", temp);
		break;
		
	case MONO_TOKEN_TYPE_SPEC:
		s = get_typespec (m, idx, FALSE, container);
		break;

	default:
		g_error ("Unhandled encoding for token 0x%08x", token);

	}
	
	g_free (temp);

	return s;
}

char *
get_guid (MonoImage *m, guint32 guid_index)
{
	const unsigned char *guid;
	char *result;

	guid = (const guchar*)mono_metadata_guid_heap (m, guid_index);

	result = g_strdup_printf ("{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X}", 
			guid [3], guid [2], guid [1], guid [0], guid [5], guid [4], guid [7], guid [6],
			guid [8], guid [9], guid [10], guid [11], guid [12], guid [13], guid [14], guid [15]);
	return result;
}

GList *
dis_get_custom_attrs (MonoImage *m, guint32 token)
{
	GList *list = NULL;
	guint32 idx, i, len, mtoken;
	guint32 cols [MONO_CUSTOM_ATTR_SIZE];
	MonoTableInfo *ca;
	char *method;
	GString *attr;
	const char *val;

	idx = mono_metadata_token_index (token);
	idx <<= MONO_CUSTOM_ATTR_BITS;
	
	switch (mono_metadata_token_table (token)) {
	case MONO_TABLE_TYPEDEF:
		idx |= MONO_CUSTOM_ATTR_TYPEDEF;
		break;
	case MONO_TABLE_ASSEMBLY:
		idx |= MONO_CUSTOM_ATTR_ASSEMBLY;
		break;
	case MONO_TABLE_ASSEMBLYREF:
		idx |= MONO_CUSTOM_ATTR_ASSEMBLYREF;
		break;
	case MONO_TABLE_MODULE:
		idx |= MONO_CUSTOM_ATTR_MODULE;
		break;
	case MONO_TABLE_PROPERTY:
		idx |= MONO_CUSTOM_ATTR_PROPERTY;
		break;
	case MONO_TABLE_EVENT:
		idx |= MONO_CUSTOM_ATTR_EVENT;
		break;
	case MONO_TABLE_FIELD:
		idx |= MONO_CUSTOM_ATTR_FIELDDEF;
		break;
	case MONO_TABLE_METHOD:
		idx |= MONO_CUSTOM_ATTR_METHODDEF;
		break;
	case MONO_TABLE_PARAM:
		idx |= MONO_CUSTOM_ATTR_PARAMDEF;
		break;
	case MONO_TABLE_GENERICPARAM:
		idx |= MONO_CUSTOM_ATTR_GENERICPAR;
		break;
	default:
		g_print ("Missing custom attr get support for token 0x%08x\n", token);
		return NULL;
	}

	ca = &m->tables [MONO_TABLE_CUSTOMATTRIBUTE];
	/* the table is not sorted */
	for (i = 0; i < ca->rows; ++i) {
		char *dump;
		mono_metadata_decode_row (ca, i, cols, MONO_CUSTOM_ATTR_SIZE);
		if (cols [MONO_CUSTOM_ATTR_PARENT] != idx)
			continue;
		mtoken = cols [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS;
		switch (cols [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) {
		case MONO_CUSTOM_ATTR_TYPE_METHODDEF:
			mtoken |= MONO_TOKEN_METHOD_DEF;
			break;
		case MONO_CUSTOM_ATTR_TYPE_MEMBERREF:
			mtoken |= MONO_TOKEN_MEMBER_REF;
			break;
		default:
			g_error ("Unknown table for custom attr type %08x", cols [MONO_CUSTOM_ATTR_TYPE]);
			break;
		}
		method = get_method (m, mtoken, NULL);
		val = mono_metadata_blob_heap (m, cols [MONO_CUSTOM_ATTR_VALUE]);
		len = mono_metadata_decode_value (val, &val);
		attr = g_string_new (".custom ");
		dump = data_dump (val, len, "\t\t");
		g_string_append_printf (attr, "%s = %s", method, dump);
		g_free (dump);
		list = g_list_append (list, attr->str);
		g_string_free (attr, FALSE);
		g_free (method);
	}
	return list;
}

char*
get_marshal_info (MonoImage *m, const char *blob) {
	int size = 0;

	/* len = */ mono_metadata_decode_blob_size (blob, &blob);

	switch (*blob) {
	case MONO_NATIVE_BOOLEAN:
		return g_strdup ("bool");
	case MONO_NATIVE_I1:
		return g_strdup ("int8");
	case MONO_NATIVE_U1:
		return g_strdup ("unsigned int8");
	case MONO_NATIVE_I2:
		return g_strdup ("int16");
	case MONO_NATIVE_U2:
		return g_strdup ("unsigned int16");
	case MONO_NATIVE_I4:
		return g_strdup ("int32");
	case MONO_NATIVE_U4:
		return g_strdup ("unsigned int32");
	case MONO_NATIVE_I8:
		return g_strdup ("int64");
	case MONO_NATIVE_U8:
		return g_strdup ("unsigned int64");
	case MONO_NATIVE_R4:
		return g_strdup ("float32");
	case MONO_NATIVE_R8:
		return g_strdup ("float64");
	case MONO_NATIVE_CURRENCY:
		return g_strdup ("currency");
	case MONO_NATIVE_BSTR:
		return g_strdup ("bstr");
	case MONO_NATIVE_LPSTR:
		return g_strdup ("lpstr");
	case MONO_NATIVE_LPWSTR:
		return g_strdup ("lpwstr");
	case MONO_NATIVE_LPTSTR:
		return g_strdup ("lptstr");
	case MONO_NATIVE_BYVALTSTR:
		size = mono_metadata_decode_value (blob + 1, &blob);
		return g_strdup_printf ("fixed sysstring [%d]", size);
	case MONO_NATIVE_IUNKNOWN:
		return g_strdup ("iunknown");
	case MONO_NATIVE_IDISPATCH:
		return g_strdup ("idispatch");
	case MONO_NATIVE_STRUCT:
		return g_strdup ("struct");
	case MONO_NATIVE_INTERFACE:
		return g_strdup ("interface");
	case MONO_NATIVE_SAFEARRAY:
		return g_strdup ("safearray");
	case MONO_NATIVE_BYVALARRAY:
		size = mono_metadata_decode_value (blob + 1, &blob);
		return g_strdup_printf ("fixed array [%d]", size);
	case MONO_NATIVE_INT:
		return g_strdup ("int");
	case MONO_NATIVE_UINT:
		return g_strdup ("unsigned int");
	case MONO_NATIVE_VBBYREFSTR:
		return g_strdup ("vbbyrefstr");
	case MONO_NATIVE_ANSIBSTR:
		return g_strdup ("ansi bstr");
	case MONO_NATIVE_TBSTR:
		return g_strdup ("tbstr");
	case MONO_NATIVE_VARIANTBOOL:
		return g_strdup ("variant bool");
	case MONO_NATIVE_FUNC:
		return g_strdup ("method");
	case MONO_NATIVE_ASANY:
		return g_strdup ("as any");
	case MONO_NATIVE_LPARRAY:
		return g_strdup ("[]");
	case MONO_NATIVE_LPSTRUCT:
		return g_strdup ("lpstruct");
	case MONO_NATIVE_CUSTOM:
		return g_strdup ("custom");
	case MONO_NATIVE_ERROR:
		return g_strdup ("error");
	default:
		return g_strdup ("unknown");
	}
}

void
init_key_table (void)
{
	key_table = g_hash_table_new (g_str_hash, g_str_equal);

	g_hash_table_insert (key_table, (char *) "9", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "abstract", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "add", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "add.ovf", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "add.ovf.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "algorithm", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "alignment", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "and", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ansi", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "any", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "arglist", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "array", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "as", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "assembly", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "assert", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "at", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "autochar", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "auto", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "beforefieldinit", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bestfit", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "beq", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "beq.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bge", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bge.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bge.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bge.un.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bgt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bgt.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bgt.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bgt.un.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ble", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ble.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ble.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ble.un.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blob", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blob_object", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blt.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blt.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "blt.un.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bne.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bne.un.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bool", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "box", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "break", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brfalse", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brfalse.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "br", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brinst", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brinst.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brnull", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brnull.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "br.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brtrue", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brtrue.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brzero", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "brzero.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "bytearray", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "byvalstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "call", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "callconv", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "calli", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "callmostderived", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "callvirt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "carray", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "castclass", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "catch", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cdecl", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ceq", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cf", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cgt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cgt.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "char", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "charmaperror", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cil", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ckfinite", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "class", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "clsid", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "clt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "clt.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "Compilercontrolled", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "const", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i1.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i2.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i4.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i8.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.i.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u1.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u2.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u4.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u8.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.ovf.u.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.r.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.u1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.u2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.u4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.u8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "conv.u", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cpblk", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "cpobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "currency", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "custom", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "date", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "decimal", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "default", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "demand", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "deny", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "div", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "div.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "dup", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "endfault", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "endfilter", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "endfinally", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "endmac", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "enum", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "error", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "explicit", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "extends", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "extern", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "false", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "famandassem", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "family", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "famorassem", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "fastcall", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "fault", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "field", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "filetime", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "filter", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "final", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "finally", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "fixed", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "flags", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "float32", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "float64", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "float", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "forwardref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "fromunmanaged", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "handler", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "hidebysig", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "hresult", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "idispatch", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "il", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "illegal", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "implements", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "implicitcom", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "implicitres", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "import", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "in", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "inheritcheck", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "initblk", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "init", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "initobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "initonly", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "instance", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "int16", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "int32", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "int64", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "int8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "interface", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "internalcall", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "int", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "isinst", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "iunknown", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "jmp", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lasterr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lcid", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg.0", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg.1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg.2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg.3", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarga", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarga.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldarg.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.0", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.3", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.5", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.6", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.7", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.m1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.M1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i4.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldc.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelema", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.ref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.u1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.u2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.u4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldelem.u8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldflda", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldfld", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldftn", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.ref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.u1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.u2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.u4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldind.u8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldlen", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc.0", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc.1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc.2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc.3", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloca", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloca.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldloc.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldnull", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldsflda", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldsfld", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldtoken", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ldvirtftn", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "leave", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "leave.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "legacy", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "linkcheck", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "literal", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "localloc", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lpstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lpstruct", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lptstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lpvoid", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "lpwstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "managed", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "marshal", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "method", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "mkrefany", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "modopt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "modreq", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "mul", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "mul.ovf", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "mul.ovf.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "native", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "neg", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nested", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "newarr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "newobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "newslot", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noappdomain", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noinlining", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nomachine", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nomangle", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nometadata", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noncasdemand", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noncasinheritance", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noncaslinkdemand", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nop", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "noprocess", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "not", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "not_in_gc_heap", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "notremotable", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "notserialized", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "null", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "nullref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "object", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "objectref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "off", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "on", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "opt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "optil", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "or", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "out", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "permitonly", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "pinned", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "pinvokeimpl", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "pop", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix3", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix5", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix6", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefix7", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prefixref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prejitdeny", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "prejitgrant", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "preservesig", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "private", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "privatescope", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "property", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "protected", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "public", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "readonly", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "record", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "refany", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "refanytype", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "refanyval", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "rem", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "rem.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "reqmin", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "reqopt", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "reqrefuse", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "reqsecobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "request", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "ret", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "rethrow", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "retval", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "rtspecialname", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "runtime", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "safearray", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sealed", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sequential", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "serializable", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "shl", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "shr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "shr.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sizeof", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "special", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "specialname", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "starg", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "starg.s", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "static", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stdcall", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stelem.ref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stfld", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.i1", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.i2", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.i4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.i8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.i", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.r4", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stind.r8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stloc", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stobj", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "storage", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stored_object", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "streamed_object", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stream", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "strict", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "string", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "struct", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "stsfld", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sub", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sub.ovf", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sub.ovf.un", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "switch", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "synchronized", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "syschar", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "sysstring", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "tbstr", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "thiscall", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "tls", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "to", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "true", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "type", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "typedref", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "uint", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "uint8", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "uint16", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "uint32", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "uint64", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "unbox", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "unicode", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "unmanagedexp", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "unmanaged", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "unsigned", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "userdefined", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "value", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "valuetype", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "vararg", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "variant", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "vector", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "virtual", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "void", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "wchar", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "winapi", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "with", GINT_TO_POINTER (TRUE));
	g_hash_table_insert (key_table, (char *) "xor", GINT_TO_POINTER (TRUE));
}

guint32
method_dor_to_token (guint32 idx) {
	switch (idx & MONO_METHODDEFORREF_MASK) {
	case MONO_METHODDEFORREF_METHODDEF:
		return MONO_TOKEN_METHOD_DEF | (idx >> MONO_METHODDEFORREF_BITS);
	case MONO_METHODDEFORREF_METHODREF:
		return MONO_TOKEN_MEMBER_REF | (idx >> MONO_METHODDEFORREF_BITS);
	}
	return -1;
}

char *
get_method_override (MonoImage *m, guint32 token, MonoGenericContainer *container)
{
	MonoTableInfo *t = &m->tables [MONO_TABLE_METHODIMPL];
	int i;

	for (i = 1; i <= t->rows; i++){
		guint32 cols [MONO_METHODIMPL_SIZE];
		guint32 decl, impl;

		mono_metadata_decode_row (t, i - 1, cols, MONO_METHODIMPL_SIZE);

		impl = method_dor_to_token (cols [MONO_METHODIMPL_BODY]);
		decl = method_dor_to_token (cols [MONO_METHODIMPL_DECLARATION]);

		if (token == impl) {
			ERROR_DECL (error);
			MonoMethod *mh = NULL;
			mh = mono_get_method_checked (m, decl, NULL, (MonoGenericContext *) container, error);

			if (mh && (mh->klass && (mono_class_is_ginst (mh->klass) || mono_class_is_gtd (mh->klass)))) {
				char *meth_str;
				char *ret;
				
				meth_str = get_method_core (m, decl, TRUE, container);
				ret = g_strdup_printf ("method %s", meth_str);
				g_free (meth_str);
				return ret;
			} else {
				if (!is_ok (error)) {
					char *meth_str = get_method_core (m, decl, FALSE, container);
					char *ret = g_strdup_printf ("Could not decode method override %s due to %s", meth_str, mono_error_get_message (error));

					mono_error_cleanup (error);
					g_free (meth_str);
					return ret;
				} else {
					return get_method_core (m, decl, FALSE, container);
				}
			}
		}
	}

	return NULL;
}

static void
check_ambiguous_genparams (MonoGenericContainer *container)
{
	GSList *dup_list = NULL, *l;
	GHashTable *table = NULL;
	gpointer *p;
	int i;

	if (!container)
		return;
	
	if (generic_containers && g_hash_table_lookup (generic_containers, container))
		/* Already been checked for ambiguous gen params */
		return;

	table = g_hash_table_new (g_str_hash, g_str_equal);
	for (i = 0; i < container->type_argc; i++) {
		MonoGenericParam *param = mono_generic_container_get_param (container, i);

		if ((p = (gpointer *)g_hash_table_lookup (table, mono_generic_param_info (param)->name)))
			dup_list = g_slist_prepend (g_slist_prepend (dup_list, GUINT_TO_POINTER (i + 1)), p);
		else
			g_hash_table_insert (table, (char*)mono_generic_param_info (param)->name, GUINT_TO_POINTER (i + 1));
	}

	if (dup_list) {
		if (!mono_generic_params_with_ambiguous_names)
			mono_generic_params_with_ambiguous_names = g_hash_table_new (NULL, NULL);
		for (l = dup_list; l; l = l->next) {
			int param = GPOINTER_TO_UINT (l->data);
			g_hash_table_insert (mono_generic_params_with_ambiguous_names,
				mono_generic_container_get_param (container, param-1),
				mono_generic_container_get_param (container, param-1));
		}
		g_slist_free (dup_list);
	}

	if (!generic_containers)
		generic_containers = g_hash_table_new (NULL, NULL);

	g_hash_table_insert (generic_containers, container, container);
	g_hash_table_destroy (table);
}
	
static gboolean
cant_print_generic_param_name (MonoGenericParam *gparam)
{
	MonoGenericContainer *container;
	g_assert (gparam);

	container = mono_generic_param_owner (gparam);
	check_ambiguous_genparams (container);
	return (!container || (mono_generic_params_with_ambiguous_names &&
			g_hash_table_lookup (mono_generic_params_with_ambiguous_names, gparam)) || !mono_generic_param_info (gparam));
}


static dis_map_t method_impl_map [] = {
	{ METHOD_IMPL_ATTRIBUTE_IL,              "cil " },
	{ METHOD_IMPL_ATTRIBUTE_NATIVE,          "native " },
	{ METHOD_IMPL_ATTRIBUTE_OPTIL,           "optil " },
	{ METHOD_IMPL_ATTRIBUTE_RUNTIME,         "runtime " },
	{ 0, NULL }
};

static dis_map_t managed_type_map [] = {
	{ METHOD_IMPL_ATTRIBUTE_UNMANAGED,       "unmanaged " },
	{ METHOD_IMPL_ATTRIBUTE_MANAGED,         "managed " },
	{ 0, NULL }
};

static dis_map_t managed_impl_flags [] = {
	{ METHOD_IMPL_ATTRIBUTE_FORWARD_REF,     "fwdref " },
	{ METHOD_IMPL_ATTRIBUTE_PRESERVE_SIG,    "preservesig " },
	{ METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL,   "internalcall " },
	{ METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED,    "synchronized " },
	{ METHOD_IMPL_ATTRIBUTE_NOINLINING,      "noinlining " },
	{ METHOD_IMPL_ATTRIBUTE_NOOPTIMIZATION,  "nooptimization " },
	{ METHOD_IMPL_ATTRIBUTE_AGGRESSIVE_INLINING,  "agressive-inlining" },
	{ 0, NULL }
};

char *
get_method_impl_flags (guint32 f)
{
	GString *str = g_string_new ("");
	char *s;
	int code_type = f & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
	int managed_type = f & METHOD_IMPL_ATTRIBUTE_MANAGED_MASK;
	int rest = f & ~(code_type | managed_type);

	g_string_append (str, map (code_type, method_impl_map));
	g_string_append (str, map (managed_type, managed_type_map));
	g_string_append (str, flags (rest, managed_impl_flags));
	
	s = str->str;
	g_string_free (str, FALSE);
	return s;
}

