Mon Apr 22 19:44:16 CEST 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         /* allow two :: to separate the method name */
134         if (*method_name == ':')
135                 method_name++;
136         class_name = strrchr (class_nspace, '.');
137         if (class_name) {
138                 *class_name++ = 0;
139                 use_namespace = 1;
140         } else {
141                 class_name = class_nspace;
142                 use_namespace = 0;
143         }
144         result = g_new0 (MonoMethodDesc, 1);
145         result->include_namespace = include_namespace;
146         result->name = method_name;
147         result->klass = class_name;
148         result->namespace = use_namespace? class_nspace: NULL;
149         result->args = use_args? use_args: NULL;
150         if (use_args) {
151                 end = use_args;
152                 if (*end)
153                         result->num_args = 1;
154                 while (*end) {
155                         if (*end == ',')
156                                 result->num_args++;
157                 }
158         }
159
160         return result;
161 }
162
163 void
164 mono_method_desc_free (MonoMethodDesc *desc)
165 {
166         if (desc->namespace)
167                 g_free (desc->namespace);
168         else if (desc->klass)
169                 g_free (desc->klass);
170         g_free (desc);
171 }
172
173 /*
174  * namespace and class are supposed to match already if this function is used.
175  */
176 gboolean
177 mono_method_desc_match (MonoMethodDesc *desc, MonoMethod *method)
178 {
179         char *sig;
180         if (strcmp (desc->name, method->name))
181                 return FALSE;
182         if (!desc->args)
183                 return TRUE;
184         if (desc->num_args != method->signature->param_count)
185                 return FALSE;
186         sig = mono_signature_get_desc (method->signature, desc->include_namespace);
187         if (strcmp (sig, desc->args)) {
188                 g_free (sig);
189                 return FALSE;
190         }
191         g_free (sig);
192         return TRUE;
193 }
194
195 gboolean
196 mono_method_desc_full_match (MonoMethodDesc *desc, MonoMethod *method)
197 {
198         if (strcmp (desc->klass, method->klass->name))
199                 return FALSE;
200         if (desc->namespace && strcmp (desc->namespace, method->klass->name_space))
201                 return FALSE;
202         return mono_method_desc_match (desc, method);
203 }
204
205 MonoMethod*
206 mono_method_desc_search_in_class (MonoMethodDesc *desc, MonoClass *klass)
207 {
208         int i;
209
210         mono_class_init (klass);
211         for (i = 0; i < klass->method.count; ++i) {
212                 if (mono_method_desc_match (desc, klass->methods [i]))
213                         return klass->methods [i];
214         }
215         return NULL;
216 }
217
218 MonoMethod*
219 mono_method_desc_search_in_image (MonoMethodDesc *desc, MonoImage *image)
220 {
221         MonoClass *klass;
222         MonoTableInfo *tdef;
223         MonoTableInfo *methods;
224         MonoMethod *method;
225         int i;
226
227         if (desc->namespace && desc->klass) {
228                 klass = mono_class_from_name (image, desc->namespace, desc->klass);
229                 if (!klass)
230                         return NULL;
231                 return mono_method_desc_search_in_class (desc, klass);
232         }
233
234         tdef = &image->tables [MONO_TABLE_TYPEDEF];
235         methods = &image->tables [MONO_TABLE_METHOD];
236         for (i = 0; i < methods->rows; ++i) {
237                 guint32 token = mono_metadata_decode_row_col (methods, i, MONO_METHOD_NAME);
238                 const char *n = mono_metadata_string_heap (image, token);
239
240                 if (strcmp (n, desc->name))
241                         continue;
242                 method = mono_get_method (image, MONO_TOKEN_METHOD_DEF | (i + 1), NULL);
243                 if (mono_method_desc_full_match (desc, method))
244                         return method;
245         }
246         return NULL;
247 }
248
249 static const unsigned char*
250 dis_one (GString *str, MonoDisHelper *dh, MonoMethod *method, const unsigned char *ip)
251 {
252         MonoMethodHeader *header = ((MonoMethodNormal*)method)->header;
253         const MonoOpcode *opcode;
254         guint32 i, label, token;
255         gint32 sval;
256         char *tmp;
257
258         label = ip - header->code;
259         if (dh->indenter) {
260                 tmp = dh->indenter (dh, method, label);
261                 g_string_append (str, tmp);
262                 g_free (tmp);
263         }
264         if (dh->label_format)
265                 g_string_sprintfa (str, dh->label_format, label);
266         
267         i = *ip;
268         if (*ip == 0xfe) {
269                 ip++;
270                 i = *ip + 256;
271         }
272         ++ip;
273         opcode = &mono_opcodes [i];
274         g_string_sprintfa (str, "%-10s", mono_opcode_names [i]);
275
276         switch (opcode->argument) {
277         case MonoInlineNone:
278                 break;
279         case MonoInlineType:
280         case MonoInlineField:
281         case MonoInlineMethod:
282         case MonoInlineTok:
283         case MonoInlineSig:
284                 token = read32 (ip);
285                 if (dh->tokener) {
286                         tmp = dh->tokener (dh, method, token);
287                         g_string_append (str, tmp);
288                         g_free (tmp);
289                 } else {
290                         g_string_sprintfa (str, "0x%08x", token);
291                 }
292                 ip += 4;
293                 break;
294         case MonoInlineString:
295                 /* TODO */
296                 ip += 4;
297                 break;
298         case MonoInlineVar:
299                 g_string_sprintfa (str, "%d", read16 (ip));
300                 ip += 2;
301                 break;
302         case MonoShortInlineVar:
303                 g_string_sprintfa (str, "%d", (*ip));
304                 ip ++;
305                 break;
306         case MonoInlineBrTarget:
307                 sval = read32 (ip);
308                 ip += 4;
309                 if (dh->label_target)
310                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
311                 else
312                         g_string_sprintfa (str, "%d", sval);
313                 break;
314         case MonoShortInlineBrTarget:
315                 sval = *(const signed char*)ip;
316                 ip ++;
317                 if (dh->label_target)
318                         g_string_sprintfa (str, dh->label_target, ip + sval - header->code);
319                 else
320                         g_string_sprintfa (str, "%d", sval);
321                 break;
322         case MonoInlineSwitch: {
323                 const unsigned char *end;
324                 sval = read32 (ip);
325                 ip += 4;
326                 end = ip + sval * 4;
327                 g_string_append_c (str, '(');
328                 for (i = 0; i < sval; ++i) {
329                         if (i > 0)
330                                 g_string_append (str, ", ");
331                         label = read32 (ip);
332                         if (dh->label_target)
333                                 g_string_sprintfa (str, dh->label_target, end + label - header->code);
334                         else
335                                 g_string_sprintfa (str, "%d", label);
336                         ip += 4;
337                 }
338                 g_string_append_c (str, ')');
339                 break;
340         }
341         case MonoInlineR: {
342                 double r;
343                 readr8 (ip, &r);
344                 g_string_sprintfa (str, "%g", r);
345                 ip += 8;
346                 break;
347         }
348         case MonoShortInlineR: {
349                 float r;
350                 readr4 (ip, &r);
351                 g_string_sprintfa (str, "%g", r);
352                 ip += 4;
353                 break;
354         }
355         case MonoInlineI:
356                 g_string_sprintfa (str, "%d", (gint32)read32 (ip));
357                 ip += 4;
358                 break;
359         case MonoShortInlineI:
360                 g_string_sprintfa (str, "%d", *(const signed char*)ip);
361                 ip ++;
362                 break;
363         case MonoInlineI8:
364                 ip += 8;
365                 break;
366         default:
367                 g_assert_not_reached ();
368         }
369         if (dh->newline)
370                 g_string_append (str, dh->newline);
371
372         return ip;
373 }
374
375 static MonoDisHelper
376 default_dh = {
377         "\n",
378         "IL_%04x: ", /* label_format */
379         "IL_%04x", /* label_target */
380         NULL, /* indenter */
381         NULL, /* tokener */
382         NULL  /* user data */
383 };
384
385 char*
386 mono_disasm_code_one (MonoDisHelper *dh, MonoMethod *method, const guchar *ip)
387 {
388         char *result;
389         GString *res = g_string_new ("");
390
391         if (!dh)
392                 dh = &default_dh;
393         dis_one (res, dh, method, ip);
394         
395         result = res->str;
396         g_string_free (res, FALSE);
397         return result;
398 }
399
400 char*
401 mono_disasm_code (MonoDisHelper *dh, MonoMethod *method, const guchar *ip, const guchar* end)
402 {
403         char *result;
404         GString *res = g_string_new ("");
405
406         if (!dh)
407                 dh = &default_dh;
408         while (ip < end) {
409                 ip = dis_one (res, dh, method, ip);
410         }
411         
412         result = res->str;
413         g_string_free (res, FALSE);
414         return result;
415 }
416