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