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