This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mono / metadata / debug-helpers.c
1
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"
8
9 struct MonoMethodDesc {
10         char *namespace;
11         char *klass;
12         char *name;
13         char *args;
14         guint num_args;
15         gboolean include_namespace;
16 };
17
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         "unknown"
33 };
34
35 static void
36 append_class_name (GString *res, MonoClass *class, gboolean include_namespace)
37 {
38         if (!class) {
39                 g_string_append (res, "Unknown");
40                 return;
41         }
42         if (class->nested_in) {
43                 append_class_name (res, class->nested_in, include_namespace);
44                 g_string_append_c (res, '/');
45         }
46         if (include_namespace && *(class->name_space))
47                 g_string_sprintfa (res, "%s.", class->name_space);
48         g_string_sprintfa (res, "%s", class->name);
49 }
50
51 void
52 mono_type_get_desc (GString *res, MonoType *type, gboolean include_namespace) {
53         switch (type->type) {
54         case MONO_TYPE_VOID:
55                 g_string_append (res, "void"); break;
56         case MONO_TYPE_CHAR:
57                 g_string_append (res, "char"); break;
58         case MONO_TYPE_BOOLEAN:
59                 g_string_append (res, "bool"); break;
60         case MONO_TYPE_U1:
61                 g_string_append (res, "byte"); break;
62         case MONO_TYPE_I1:
63                 g_string_append (res, "sbyte"); break;
64         case MONO_TYPE_U2:
65                 g_string_append (res, "uint16"); break;
66         case MONO_TYPE_I2:
67                 g_string_append (res, "int16"); break;
68         case MONO_TYPE_U4:
69                 g_string_append (res, "uint"); break;
70         case MONO_TYPE_I4:
71                 g_string_append (res, "int"); break;
72         case MONO_TYPE_U8:
73                 g_string_append (res, "ulong"); break;
74         case MONO_TYPE_I8:
75                 g_string_append (res, "long"); break;
76         case MONO_TYPE_FNPTR: /* who cares for the exact signature? */
77                 g_string_append (res, "*()"); break;
78         case MONO_TYPE_U:
79                 g_string_append (res, "uintptr"); break;
80         case MONO_TYPE_I:
81                 g_string_append (res, "intptr"); break;
82         case MONO_TYPE_R4:
83                 g_string_append (res, "single"); break;
84         case MONO_TYPE_R8:
85                 g_string_append (res, "double"); break;
86         case MONO_TYPE_STRING:
87                 g_string_append (res, "string"); break;
88         case MONO_TYPE_OBJECT:
89                 g_string_append (res, "object"); break;
90         case MONO_TYPE_PTR:
91                 mono_type_get_desc (res, type->data.type, include_namespace);
92                 g_string_append_c (res, '*');
93                 break;
94         case MONO_TYPE_ARRAY:
95                 append_class_name (res, type->data.array->eklass, include_namespace);
96                 g_string_sprintfa (res, "[%d]", type->data.array->rank);
97                 break;
98         case MONO_TYPE_SZARRAY:
99                 mono_type_get_desc (res, &type->data.klass->byval_arg, include_namespace);
100                 g_string_append (res, "[]");
101                 break;
102         case MONO_TYPE_CLASS:
103         case MONO_TYPE_VALUETYPE:
104                 append_class_name (res, type->data.klass, include_namespace);
105                 break;
106         default:
107                 break;
108         }
109         if (type->byref)
110                 g_string_append_c (res, '&');
111 }
112
113 char*
114 mono_type_full_name (MonoType *type)
115 {
116         GString *str;
117         char *res;
118
119         str = g_string_new ("");
120         mono_type_get_desc (str, type, TRUE);
121
122         res = g_strdup (str->str);
123         g_string_free (str, TRUE);
124         return res;
125 }
126
127 char*
128 mono_signature_get_desc (MonoMethodSignature *sig, gboolean include_namespace)
129 {
130         int i;
131         char *result;
132         GString *res = g_string_new ("");
133
134         for (i = 0; i < sig->param_count; ++i) {
135                 if (i > 0)
136                         g_string_append_c (res, ',');
137                 mono_type_get_desc (res, sig->params [i], include_namespace);
138         }
139         result = res->str;
140         g_string_free (res, FALSE);
141         return result;
142 }
143
144 /**
145  * mono_method_desc_new:
146  *
147  * Creates a method description for `name', which conforms to the following
148  * specification:
149  *
150  * [namespace.]classname:methodname[(args...)]
151  *
152  * in all the loaded assemblies.
153  *
154  * Returns a parsed representation of the method description.
155  */
156 MonoMethodDesc*
157 mono_method_desc_new (const char *name, gboolean include_namespace)
158 {
159         MonoMethodDesc *result;
160         char *class_name, *class_nspace, *method_name, *use_args, *end;
161         int use_namespace;
162         
163         class_nspace = g_strdup (name);
164         use_args = strchr (class_nspace, '(');
165         if (use_args) {
166                 *use_args++ = 0;
167                 end = strchr (use_args, ')');
168                 if (!end) {
169                         g_free (class_nspace);
170                         return NULL;
171                 }
172                 *end = 0;
173         }
174         method_name = strrchr (class_nspace, ':');
175         if (!method_name) {
176                 g_free (class_nspace);
177                 return NULL;
178         }
179         *method_name++ = 0;
180         /* allow two :: to separate the method name */
181         if (*method_name == ':')
182                 method_name++;
183         class_name = strrchr (class_nspace, '.');
184         if (class_name) {
185                 *class_name++ = 0;
186                 use_namespace = 1;
187         } else {
188                 class_name = class_nspace;
189                 use_namespace = 0;
190         }
191         result = g_new0 (MonoMethodDesc, 1);
192         result->include_namespace = include_namespace;
193         result->name = method_name;
194         result->klass = class_name;
195         result->namespace = use_namespace? class_nspace: NULL;
196         result->args = use_args? use_args: NULL;
197         if (use_args) {
198                 end = use_args;
199                 if (*end)
200                         result->num_args = 1;
201                 while (*end) {
202                         if (*end == ',')
203                                 result->num_args++;
204                         ++end;
205                 }
206         }
207
208         return result;
209 }
210
211 MonoMethodDesc*
212 mono_method_desc_from_method (MonoMethod *method)
213 {
214         MonoMethodDesc *result;
215         
216         result = g_new0 (MonoMethodDesc, 1);
217         result->include_namespace = TRUE;
218         result->name = g_strdup (method->name);
219         result->klass = g_strdup (method->klass->name);
220         result->namespace = g_strdup (method->klass->name_space);
221
222         return result;
223 }
224
225 /**
226  * mono_method_desc_free:
227  *
228  * Releases the MonoMethodDesc object `desc'.
229  */
230 void
231 mono_method_desc_free (MonoMethodDesc *desc)
232 {
233         if (desc->namespace)
234                 g_free (desc->namespace);
235         else if (desc->klass)
236                 g_free (desc->klass);
237         g_free (desc);
238 }
239
240 /*
241  * namespace and class are supposed to match already if this function is used.
242  */
243 gboolean
244 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
245 {
246         char *sig;
247         if (strcmp (desc->name, method->name))
248                 return FALSE;
249         if (!desc->args)
250                 return TRUE;
251         if (desc->num_args != method->signature->param_count)
252                 return FALSE;
253         sig = mono_signature_get_desc (method->signature, desc->include_namespace);
254         if (strcmp (sig, desc->args)) {
255                 g_free (sig);
256                 return FALSE;
257         }
258         g_free (sig);
259         return TRUE;
260 }
261
262 gboolean
263 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
264 {
265         if (strcmp (desc->klass, method->klass->name))
266                 return FALSE;
267         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
268                 return FALSE;
269         return mono_method_desc_match (desc, method);
270 }
271
272 MonoMethod*
273 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
274 {
275         int i;
276
277         mono_class_init (klass);
278         for (i = 0; i < klass->method.count; ++i) {
279                 if (mono_method_desc_match (desc, klass->methods [i]))
280                         return klass->methods [i];
281         }
282         return NULL;
283 }
284
285 MonoMethod*
286 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
287 {
288         MonoClass *klass;
289         MonoTableInfo *tdef;
290         MonoTableInfo *methods;
291         MonoMethod *method;
292         int i;
293
294         if (desc->namespace && desc->klass) {
295                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
296                 if (!klass)
297                         return NULL;
298                 return mono_method_desc_search_in_class (desc, klass);
299         }
300
301         tdef = mono_image_get_table_info (image, MONO_TABLE_TYPEDEF);
302         methods = mono_image_get_table_info (image, MONO_TABLE_METHOD);
303         for (i = 0; i < mono_table_info_get_rows (methods); ++i) {
304                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
305                 const char *n = mono_metadata_string_heap (image, token);
306
307                 if (strcmp (n, desc->name))
308                         continue;
309                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
310                 if (mono_method_desc_full_match (desc, method))
311                         return method;
312         }
313         return NULL;
314 }
315
316 static const unsigned char*
317 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip, const unsigned char *end)
318 {
319         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
320         const MonoOpcode *opcode;
321         guint32 i, label, token;
322         gint32 sval;
323         char *tmp;
324
325         label = ip - header->code;
326         if (dh->indenter) {
327                 tmp = dh->indenter (dh, method, label);
328                 g_string_append (str, tmp);
329                 g_free (tmp);
330         }
331         if (dh->label_format)
332                 g_string_sprintfa (str, dh->label_format, label);
333         
334         i = mono_opcode_value (&ip, end);
335         ip++;
336         opcode = &mono_opcodes [i];
337         g_string_sprintfa (str, "%-10s", mono_opcode_names [i]);
338
339         switch (opcode->argument) {
340         case MonoInlineNone:
341                 break;
342         case MonoInlineType:
343         case MonoInlineField:
344         case MonoInlineMethod:
345         case MonoInlineTok:
346         case MonoInlineSig:
347                 token = read32 (ip);
348                 if (dh->tokener) {
349                         tmp = dh->tokener (dh, method, token);
350                         g_string_append (str, tmp);
351                         g_free (tmp);
352                 } else {
353                         g_string_sprintfa (str, "0x%08x", token);
354                 }
355                 ip += 4;
356                 break;
357         case MonoInlineString:
358                 /* TODO */
359                 ip += 4;
360                 break;
361         case MonoInlineVar:
362                 g_string_sprintfa (str, "%d", read16 (ip));
363                 ip += 2;
364                 break;
365         case MonoShortInlineVar:
366                 g_string_sprintfa (str, "%d", (*ip));
367                 ip ++;
368                 break;
369         case MonoInlineBrTarget:
370                 sval = read32 (ip);
371                 ip += 4;
372                 if (dh->label_target)
373                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
374                 else
375                         g_string_sprintfa (str, "%d", sval);
376                 break;
377         case MonoShortInlineBrTarget:
378                 sval = *(const signed char*)ip;
379                 ip ++;
380                 if (dh->label_target)
381                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
382                 else
383                         g_string_sprintfa (str, "%d", sval);
384                 break;
385         case MonoInlineSwitch: {
386                 const unsigned char *end;
387                 sval = read32 (ip);
388                 ip += 4;
389                 end = ip + sval * 4;
390                 g_string_append_c (str, '(');
391                 for (i = 0; i < sval; ++i) {
392                         if (i > 0)
393                                 g_string_append (str, ", ");
394                         label = read32 (ip);
395                         if (dh->label_target)
396                                 g_string_sprintfa (str, dh->label_target, end + label - header->code);
397                         else
398                                 g_string_sprintfa (str, "%d", label);
399                         ip += 4;
400                 }
401                 g_string_append_c (str, ')');
402                 break;
403         }
404         case MonoInlineR: {
405                 double r;
406                 readr8 (ip, &r);
407                 g_string_sprintfa (str, "%g", r);
408                 ip += 8;
409                 break;
410         }
411         case MonoShortInlineR: {
412                 float r;
413                 readr4 (ip, &r);
414                 g_string_sprintfa (str, "%g", r);
415                 ip += 4;
416                 break;
417         }
418         case MonoInlineI:
419                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
420                 ip += 4;
421                 break;
422         case MonoShortInlineI:
423                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
424                 ip ++;
425                 break;
426         case MonoInlineI8:
427                 ip += 8;
428                 break;
429         default:
430                 g_assert_not_reached ();
431         }
432         if (dh->newline)
433                 g_string_append (str, dh->newline);
434
435         return ip;
436 }
437
438 static MonoDisHelper
439 default_dh = {
440         "\n",
441         "IL_%04x: ", /* label_format */
442         "IL_%04x", /* label_target */
443         NULL, /* indenter */
444         NULL, /* tokener */
445         NULL  /* user data */
446 };
447
448 char*
449 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar **endp)
450 {
451         char *result;
452         GString *res = g_string_new ("");
453
454         if (!dh)
455                 dh = &default_dh;
456         /* set ip + 2 as the end: this is just a debugging method */
457         ip = dis_one (res, dh, method, ip, ip + 2);
458         if (endp)
459                 *endp = ip;
460         
461         result = res->str;
462         g_string_free (res, FALSE);
463         return result;
464 }
465
466 char*
467 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
468 {
469         char *result;
470         GString *res = g_string_new ("");
471
472         if (!dh)
473                 dh = &default_dh;
474         while (ip < end) {
475                 ip = dis_one (res, dh, method, ip, end);
476         }
477         
478         result = res->str;
479         g_string_free (res, FALSE);
480         return result;
481 }
482
483 static const char*
484 wrapper_type_to_str (guint32 wrapper_type)
485 {
486         g_assert (wrapper_type < sizeof (wrapper_type_names) / sizeof (char*));
487
488         return wrapper_type_names [wrapper_type];
489 }
490
491 char *
492 mono_method_full_name (MonoMethod *method, gboolean signature)
493 {
494         char *res;
495         char wrapper [64];
496         char *nspace;
497
498         nspace = method->klass->name_space;
499
500         if (signature) {
501                 char *tmpsig = mono_signature_get_desc (method->signature, TRUE);
502
503                 if (method->wrapper_type != MONO_WRAPPER_NONE)
504                         sprintf (wrapper, "(wrapper %s) ", wrapper_type_to_str (method->wrapper_type));
505                 else
506                         strcpy (wrapper, "");
507                 res = g_strdup_printf ("%s%s%s%s:%s (%s)", wrapper, 
508                                                            nspace, *nspace ? "." : "",
509                                                            method->klass->name, method->name, tmpsig);
510                 g_free (tmpsig);
511         } else {
512
513                 res = g_strdup_printf ("%02d %s%s%s:%s", method->wrapper_type,
514                                                            nspace, *nspace ? "." : "",
515                                                            method->klass->name, method->name);
516         }
517
518         return res;
519 }
520
521