2004-11-07 Ben Maurer <bmaurer@ximian.com>
[mono-project.git] / mono / metadata / debug-helpers.c
blob284ddf1b2254916fe521c1fd8991552b75dce01d
2 #include <string.h>
3 #include "mono/metadata/tokentype.h"
4 #include "mono/metadata/opcodes.h"
5 #include "mono/metadata/class-internals.h"
6 #include "mono/metadata/mono-endian.h"
7 #include "mono/metadata/debug-helpers.h"
9 struct MonoMethodDesc {
10 char *namespace;
11 char *klass;
12 char *name;
13 char *args;
14 guint num_args;
15 gboolean include_namespace;
18 static const char *wrapper_type_names [] = {
19 "none",
20 "delegate-invoke",
21 "delegate-begin-invoke",
22 "delegate-end-invoke",
23 "runtime-invoke",
24 "native-to-managed",
25 "managed-to-native",
26 "remoting-invoke",
27 "remoting-invoke-with-check",
28 "ldfld",
29 "stfld",
30 "synchronized",
31 "dynamic-method",
32 "isinst",
33 "cancast",
34 "proxy_isinst",
35 "stelemref",
36 "unknown"
39 static void
40 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
42 if (!class) {
43 g_string_append (res, "Unknown");
44 return;
46 if (class->nested_in) {
47 append_class_name (res, class->nested_in, include_namespace);
48 g_string_append_c (res, '/');
50 if (include_namespace && *(class->name_space))
51 g_string_sprintfa (res, "%s.", class->name_space);
52 g_string_sprintfa (res, "%s", class->name);
55 void
56 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
57 switch (type->type) {
58 case MONO_TYPE_VOID:
59 g_string_append (res, "void"); break;
60 case MONO_TYPE_CHAR:
61 g_string_append (res, "char"); break;
62 case MONO_TYPE_BOOLEAN:
63 g_string_append (res, "bool"); break;
64 case MONO_TYPE_U1:
65 g_string_append (res, "byte"); break;
66 case MONO_TYPE_I1:
67 g_string_append (res, "sbyte"); break;
68 case MONO_TYPE_U2:
69 g_string_append (res, "uint16"); break;
70 case MONO_TYPE_I2:
71 g_string_append (res, "int16"); break;
72 case MONO_TYPE_U4:
73 g_string_append (res, "uint"); break;
74 case MONO_TYPE_I4:
75 g_string_append (res, "int"); break;
76 case MONO_TYPE_U8:
77 g_string_append (res, "ulong"); break;
78 case MONO_TYPE_I8:
79 g_string_append (res, "long"); break;
80 case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
81 g_string_append (res, "*()"); break;
82 case MONO_TYPE_U:
83 g_string_append (res, "uintptr"); break;
84 case MONO_TYPE_I:
85 g_string_append (res, "intptr"); break;
86 case MONO_TYPE_R4:
87 g_string_append (res, "single"); break;
88 case MONO_TYPE_R8:
89 g_string_append (res, "double"); break;
90 case MONO_TYPE_STRING:
91 g_string_append (res, "string"); break;
92 case MONO_TYPE_OBJECT:
93 g_string_append (res, "object"); break;
94 case MONO_TYPE_PTR:
95 mono_type_get_desc (res, type->data.type, include_namespace);
96 g_string_append_c (res, '*');
97 break;
98 case MONO_TYPE_ARRAY:
99 append_class_name (res, type->data.array->eklass, include_namespace);
100 g_string_sprintfa (res, "[%d]", type->data.array->rank);
101 break;
102 case MONO_TYPE_SZARRAY:
103 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
104 g_string_append (res, "[]");
105 break;
106 case MONO_TYPE_CLASS:
107 case MONO_TYPE_VALUETYPE:
108 append_class_name (res, type->data.klass, include_namespace);
109 break;
110 case MONO_TYPE_GENERICINST:
111 mono_type_get_desc (res, type->data.generic_inst->generic_type, include_namespace);
112 break;
113 default:
114 break;
116 if (type->byref)
117 g_string_append_c (res, '&');
120 char*
121 mono_type_full_name (MonoType *type)
123 GString *str;
124 char *res;
126 str = g_string_new ("");
127 mono_type_get_desc (str, type, TRUE);
129 res = g_strdup (str->str);
130 g_string_free (str, TRUE);
131 return res;
134 char*
135 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
137 int i;
138 char *result;
139 GString *res = g_string_new ("");
141 for (i = 0; i < sig->param_count; ++i) {
142 if (i > 0)
143 g_string_append_c (res, ',');
144 mono_type_get_desc (res, sig->params [i], include_namespace);
146 result = res->str;
147 g_string_free (res, FALSE);
148 return result;
152 * mono_method_desc_new:
154 * Creates a method description for `name', which conforms to the following
155 * specification:
157 * [namespace.]classname:methodname[(args...)]
159 * in all the loaded assemblies.
161 * Returns a parsed representation of the method description.
163 MonoMethodDesc*
164 mono_method_desc_new (const char *name, gboolean include_namespace)
166 MonoMethodDesc *result;
167 char *class_name, *class_nspace, *method_name, *use_args, *end;
168 int use_namespace;
170 class_nspace = g_strdup (name);
171 use_args = strchr (class_nspace, '(');
172 if (use_args) {
173 *use_args++ = 0;
174 end = strchr (use_args, ')');
175 if (!end) {
176 g_free (class_nspace);
177 return NULL;
179 *end = 0;
181 method_name = strrchr (class_nspace, ':');
182 if (!method_name) {
183 g_free (class_nspace);
184 return NULL;
186 *method_name++ = 0;
187 /* allow two :: to separate the method name */
188 if (*method_name == ':')
189 method_name++;
190 class_name = strrchr (class_nspace, '.');
191 if (class_name) {
192 *class_name++ = 0;
193 use_namespace = 1;
194 } else {
195 class_name = class_nspace;
196 use_namespace = 0;
198 result = g_new0 (MonoMethodDesc, 1);
199 result->include_namespace = include_namespace;
200 result->name = method_name;
201 result->klass = class_name;
202 result->namespace = use_namespace? class_nspace: NULL;
203 result->args = use_args? use_args: NULL;
204 if (use_args) {
205 end = use_args;
206 if (*end)
207 result->num_args = 1;
208 while (*end) {
209 if (*end == ',')
210 result->num_args++;
211 ++end;
215 return result;
218 MonoMethodDesc*
219 mono_method_desc_from_method (MonoMethod *method)
221 MonoMethodDesc *result;
223 result = g_new0 (MonoMethodDesc, 1);
224 result->include_namespace = TRUE;
225 result->name = g_strdup (method->name);
226 result->klass = g_strdup (method->klass->name);
227 result->namespace = g_strdup (method->klass->name_space);
229 return result;
233 * mono_method_desc_free:
235 * Releases the MonoMethodDesc object `desc'.
237 void
238 mono_method_desc_free (MonoMethodDesc *desc)
240 if (desc->namespace)
241 g_free (desc->namespace);
242 else if (desc->klass)
243 g_free (desc->klass);
244 g_free (desc);
248 * namespace and class are supposed to match already if this function is used.
250 gboolean
251 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
253 char *sig;
254 if (strcmp (desc->name, method->name))
255 return FALSE;
256 if (!desc->args)
257 return TRUE;
258 if (desc->num_args != method->signature->param_count)
259 return FALSE;
260 sig = mono_signature_get_desc (method->signature, desc->include_namespace);
261 if (strcmp (sig, desc->args)) {
262 g_free (sig);
263 return FALSE;
265 g_free (sig);
266 return TRUE;
269 gboolean
270 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
272 if (strcmp (desc->klass, method->klass->name))
273 return FALSE;
274 if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
275 return FALSE;
276 return mono_method_desc_match (desc, method);
279 MonoMethod*
280 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
282 int i;
284 mono_class_init (klass);
285 for (i = 0; i < klass->method.count; ++i) {
286 if (mono_method_desc_match (desc, klass->methods [i]))
287 return klass->methods [i];
289 return NULL;
292 MonoMethod*
293 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
295 MonoClass *klass;
296 const MonoTableInfo *tdef;
297 const MonoTableInfo *methods;
298 MonoMethod *method;
299 int i;
301 if (desc->namespace && desc->klass) {
302 klass = mono_class_from_name (image, desc->namespace, desc->klass);
303 if (!klass)
304 return NULL;
305 return mono_method_desc_search_in_class (desc, klass);
308 tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
309 methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
310 for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
311 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
312 const char *n = mono_metadata_string_heap (image, token);
314 if (strcmp (n, desc->name))
315 continue;
316 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
317 if (mono_method_desc_full_match (desc, method))
318 return method;
320 return NULL;
323 static const unsigned char*
324 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
326 MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
327 const MonoOpcode *opcode;
328 guint32 i, label, token;
329 gint32 sval;
330 char *tmp;
332 label = ip - header->code;
333 if (dh->indenter) {
334 tmp = dh->indenter (dh, method, label);
335 g_string_append (str, tmp);
336 g_free (tmp);
338 if (dh->label_format)
339 g_string_sprintfa (str, dh->label_format, label);
341 i = mono_opcode_value (&ip, end);
342 ip++;
343 opcode = &mono_opcodes [i];
344 g_string_sprintfa (str, "%-10s", mono_opcode_name (i));
346 switch (opcode->argument) {
347 case MonoInlineNone:
348 break;
349 case MonoInlineType:
350 case MonoInlineField:
351 case MonoInlineMethod:
352 case MonoInlineTok:
353 case MonoInlineSig:
354 token = read32 (ip);
355 if (dh->tokener) {
356 tmp = dh->tokener (dh, method, token);
357 g_string_append (str, tmp);
358 g_free (tmp);
359 } else {
360 g_string_sprintfa (str, "0x%08x", token);
362 ip += 4;
363 break;
364 case MonoInlineString:
365 /* TODO */
366 ip += 4;
367 break;
368 case MonoInlineVar:
369 g_string_sprintfa (str, "%d", read16 (ip));
370 ip += 2;
371 break;
372 case MonoShortInlineVar:
373 g_string_sprintfa (str, "%d", (*ip));
374 ip ++;
375 break;
376 case MonoInlineBrTarget:
377 sval = read32 (ip);
378 ip += 4;
379 if (dh->label_target)
380 g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
381 else
382 g_string_sprintfa (str, "%d", sval);
383 break;
384 case MonoShortInlineBrTarget:
385 sval = *(const signed char*)ip;
386 ip ++;
387 if (dh->label_target)
388 g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
389 else
390 g_string_sprintfa (str, "%d", sval);
391 break;
392 case MonoInlineSwitch: {
393 const unsigned char *end;
394 sval = read32 (ip);
395 ip += 4;
396 end = ip + sval * 4;
397 g_string_append_c (str, '(');
398 for (i = 0; i < sval; ++i) {
399 if (i > 0)
400 g_string_append (str, ", ");
401 label = read32 (ip);
402 if (dh->label_target)
403 g_string_sprintfa (str, dh->label_target, end + label - header->code);
404 else
405 g_string_sprintfa (str, "%d", label);
406 ip += 4;
408 g_string_append_c (str, ')');
409 break;
411 case MonoInlineR: {
412 double r;
413 readr8 (ip, &r);
414 g_string_sprintfa (str, "%g", r);
415 ip += 8;
416 break;
418 case MonoShortInlineR: {
419 float r;
420 readr4 (ip, &r);
421 g_string_sprintfa (str, "%g", r);
422 ip += 4;
423 break;
425 case MonoInlineI:
426 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
427 ip += 4;
428 break;
429 case MonoShortInlineI:
430 g_string_sprintfa (str, "%d", *(const signed char*)ip);
431 ip ++;
432 break;
433 case MonoInlineI8:
434 ip += 8;
435 break;
436 default:
437 g_assert_not_reached ();
439 if (dh->newline)
440 g_string_append (str, dh->newline);
442 return ip;
445 static MonoDisHelper
446 default_dh = {
447 "\n",
448 "IL_%04x: ", /* label_format */
449 "IL_%04x", /* label_target */
450 NULL, /* indenter */
451 NULL, /* tokener */
452 NULL /* user data */
455 char*
456 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
458 char *result;
459 GString *res = g_string_new ("");
461 if (!dh)
462 dh = &default_dh;
463 /* set ip + 2 as the end: this is just a debugging method */
464 ip = dis_one (res, dh, method, ip, ip + 2);
465 if (endp)
466 *endp = ip;
468 result = res->str;
469 g_string_free (res, FALSE);
470 return result;
473 char*
474 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
476 char *result;
477 GString *res = g_string_new ("");
479 if (!dh)
480 dh = &default_dh;
481 while (ip < end) {
482 ip = dis_one (res, dh, method, ip, end);
485 result = res->str;
486 g_string_free (res, FALSE);
487 return result;
490 static const char*
491 wrapper_type_to_str (guint32 wrapper_type)
493 g_assert (wrapper_type < sizeof (wrapper_type_names) / sizeof (char*));
495 return wrapper_type_names [wrapper_type];
498 char *
499 mono_method_full_name (MonoMethod *method, gboolean signature)
501 char *res;
502 char wrapper [64];
503 const char *nspace = method->klass->name_space;
505 if (signature) {
506 char *tmpsig = mono_signature_get_desc (method->signature, TRUE);
508 if (method->wrapper_type != MONO_WRAPPER_NONE)
509 sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
510 else
511 strcpy (wrapper, "");
512 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper,
513 nspace, *nspace ? "." : "",
514 method->klass->name, method->name, tmpsig);
515 g_free (tmpsig);
516 } else {
518 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
519 nspace, *nspace ? "." : "",
520 method->klass->name, method->name);
523 return res;
526 MonoMethod *
527 mono_find_method_by_name (MonoClass *klass, const char *name, int param_count)
529 MonoMethod *res = NULL;
530 int i;
532 mono_class_init (klass);
534 for (i = 0; i < klass->method.count; ++i) {
535 if (klass->methods [i]->name[0] == name [0] &&
536 !strcmp (name, klass->methods [i]->name) &&
537 klass->methods [i]->signature->param_count == param_count) {
538 res = klass->methods [i];
539 break;
542 return res;