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