247c749bdedad87bad1932fc978dea8bb7f81eb9
[mono.git] / mono / metadata / method-builder.c
1 /*
2  * method-builder.c: Functions for creating IL methods at runtime.
3  * 
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
8  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9  */
10
11 #include "config.h"
12 #include "loader.h"
13 #include "mono/metadata/method-builder.h"
14 #include "mono/metadata/tabledefs.h"
15 #include "mono/metadata/exception.h"
16 #include "mono/metadata/appdomain.h"
17 #include "mono/metadata/debug-helpers.h"
18 #include "mono/metadata/metadata-internals.h"
19 #include "mono/metadata/domain-internals.h"
20 #include <string.h>
21 #include <errno.h>
22
23 /* #define DEBUG_RUNTIME_CODE */
24
25 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
26         a = i,
27
28 enum {
29 #include "mono/cil/opcode.def"
30         LAST = 0xff
31 };
32 #undef OPDEF
33
34 #ifdef DEBUG_RUNTIME_CODE
35 static char*
36 indenter (MonoDisHelper *dh, MonoMethod *method, guint32 ip_offset)
37 {
38         return g_strdup (" ");
39 }
40
41 static MonoDisHelper marshal_dh = {
42         "\n",
43         "IL_%04x: ",
44         "IL_%04x",
45         indenter, 
46         NULL,
47         NULL
48 };
49 #endif 
50
51 static MonoMethodBuilder *
52 mono_mb_new_base (MonoClass *klass, MonoWrapperType type)
53 {
54         MonoMethodBuilder *mb;
55         MonoMethod *m;
56
57         g_assert (klass != NULL);
58
59         mb = g_new0 (MonoMethodBuilder, 1);
60
61         mb->method = m = (MonoMethod *)g_new0 (MonoMethodWrapper, 1);
62
63         m->klass = klass;
64         m->inline_info = 1;
65         m->wrapper_type = type;
66
67         mb->code_size = 40;
68         mb->code = g_malloc (mb->code_size);
69         /* placeholder for the wrapper always at index 1 */
70         mono_mb_add_data (mb, NULL);
71
72         return mb;
73 }
74
75 MonoMethodBuilder *
76 mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type)
77 {
78         MonoMethodBuilder *mb = mono_mb_new_base (klass, type);
79         mb->name = (char*)name;
80         mb->no_dup_name = TRUE;
81         return mb;
82 }
83
84 MonoMethodBuilder *
85 mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type)
86 {
87         MonoMethodBuilder *mb = mono_mb_new_base (klass, type);
88         mb->name = g_strdup (name);
89         return mb;
90 }
91
92 void
93 mono_mb_free (MonoMethodBuilder *mb)
94 {
95         g_list_free (mb->locals_list);
96         if (!mb->dynamic) {
97                 g_free (mb->method);
98                 if (!mb->no_dup_name)
99                         g_free (mb->name);
100                 g_free (mb->code);
101         }
102         g_free (mb);
103 }
104
105 int
106 mono_mb_add_local (MonoMethodBuilder *mb, MonoType *type)
107 {
108         int res;
109
110         g_assert (mb != NULL);
111         g_assert (type != NULL);
112
113         res = mb->locals;
114         mb->locals_list = g_list_append (mb->locals_list, type);
115         mb->locals++;
116
117         return res;
118 }
119
120 /**
121  * mono_mb_create_method:
122  *
123  * Create a MonoMethod from this method builder.
124  * Returns: the newly created method.
125  *
126  * LOCKING: Takes the loader lock.
127  */
128 MonoMethod *
129 mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack)
130 {
131         MonoMethodHeader *header;
132         MonoMethodWrapper *mw;
133         MonoImage *image;
134         MonoMethod *method;
135         GList *l;
136         int i;
137
138         g_assert (mb != NULL);
139
140         image = mb->method->klass->image;
141
142         mono_loader_lock (); /*FIXME I think this lock can go.*/
143         if (mb->dynamic) {
144                 method = mb->method;
145                 mw = (MonoMethodWrapper*)method;
146
147                 method->name = mb->name;
148                 method->dynamic = TRUE;
149
150                 mw->header = header = (MonoMethodHeader *) 
151                         g_malloc0 (MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *));
152
153                 header->code = mb->code;
154
155                 for (i = 0, l = mb->locals_list; l; l = l->next, i++) {
156                         header->locals [i] = mono_metadata_type_dup (NULL, (MonoType*)l->data);
157                 }
158         } else {
159                 /* Realloc the method info into a mempool */
160
161                 method = mono_image_alloc0 (image, sizeof (MonoMethodWrapper));
162                 memcpy (method, mb->method, sizeof (MonoMethodWrapper));
163                 mw = (MonoMethodWrapper*) method;
164
165                 if (mb->no_dup_name)
166                         method->name = mb->name;
167                 else
168                         method->name = mono_image_strdup (image, mb->name);
169
170                 mw->header = header = (MonoMethodHeader *) 
171                         mono_image_alloc0 (image, MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *));
172
173                 header->code = mono_image_alloc (image, mb->pos);
174                 memcpy ((char*)header->code, mb->code, mb->pos);
175
176                 for (i = 0, l = mb->locals_list; l; l = l->next, i++) {
177                         header->locals [i] = (MonoType *)l->data;
178                 }
179         }
180
181         if (max_stack < 8)
182                 max_stack = 8;
183
184         header->max_stack = max_stack;
185
186         method->signature = signature;
187
188         header->code_size = mb->pos;
189         header->num_locals = mb->locals;
190         header->init_locals = TRUE;
191
192         header->num_clauses = mb->num_clauses;
193         header->clauses = mb->clauses;
194
195         method->skip_visibility = mb->skip_visibility;
196
197         i = g_list_length (mw->method_data);
198         if (i) {
199                 GList *tmp;
200                 void **data;
201                 l = g_list_reverse (mw->method_data);
202                 if (method->dynamic)
203                         data = g_malloc (sizeof (gpointer) * (i + 1));
204                 else
205                         data = mono_image_alloc (image, sizeof (gpointer) * (i + 1));
206                 /* store the size in the first element */
207                 data [0] = GUINT_TO_POINTER (i);
208                 i = 1;
209                 for (tmp = l; tmp; tmp = tmp->next) {
210                         data [i++] = tmp->data;
211                 }
212                 g_list_free (l);
213
214                 mw->method_data = data;
215         }
216         /*{
217                 static int total_code = 0;
218                 static int total_alloc = 0;
219                 total_code += mb->pos;
220                 total_alloc += mb->code_size;
221                 g_print ("code size: %d of %d (allocated: %d)\n", mb->pos, total_code, total_alloc);
222         }*/
223
224 #ifdef DEBUG_RUNTIME_CODE
225         printf ("RUNTIME CODE FOR %s\n", mono_method_full_name (method, TRUE));
226         printf ("%s\n", mono_disasm_code (&marshal_dh, method, mb->code, mb->code + mb->pos));
227 #endif
228
229         mono_loader_unlock ();
230         return method;
231 }
232
233 guint32
234 mono_mb_add_data (MonoMethodBuilder *mb, gpointer data)
235 {
236         MonoMethodWrapper *mw;
237
238         g_assert (mb != NULL);
239
240         mw = (MonoMethodWrapper *)mb->method;
241
242         /* one O(n) is enough */
243         mw->method_data = g_list_prepend (mw->method_data, data);
244
245         return g_list_length (mw->method_data);
246 }
247
248 void
249 mono_mb_patch_addr (MonoMethodBuilder *mb, int pos, int value)
250 {
251         mb->code [pos] = value & 0xff;
252         mb->code [pos + 1] = (value >> 8) & 0xff;
253         mb->code [pos + 2] = (value >> 16) & 0xff;
254         mb->code [pos + 3] = (value >> 24) & 0xff;
255 }
256
257 void
258 mono_mb_patch_addr_s (MonoMethodBuilder *mb, int pos, gint8 value)
259 {
260         *((gint8 *)(&mb->code [pos])) = value;
261 }
262
263 void
264 mono_mb_emit_byte (MonoMethodBuilder *mb, guint8 op)
265 {
266         if (mb->pos >= mb->code_size) {
267                 mb->code_size += mb->code_size >> 1;
268                 mb->code = g_realloc (mb->code, mb->code_size);
269         }
270
271         mb->code [mb->pos++] = op;
272 }
273
274 void
275 mono_mb_emit_ldflda (MonoMethodBuilder *mb, gint32 offset)
276 {
277         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
278         mono_mb_emit_byte (mb, CEE_MONO_OBJADDR);
279
280         if (offset) {
281                 mono_mb_emit_icon (mb, offset);
282                 mono_mb_emit_byte (mb, CEE_ADD);
283         }
284 }
285
286 void
287 mono_mb_emit_i4 (MonoMethodBuilder *mb, gint32 data)
288 {
289         if ((mb->pos + 4) >= mb->code_size) {
290                 mb->code_size += mb->code_size >> 1;
291                 mb->code = g_realloc (mb->code, mb->code_size);
292         }
293
294         mono_mb_patch_addr (mb, mb->pos, data);
295         mb->pos += 4;
296 }
297
298 void
299 mono_mb_emit_i2 (MonoMethodBuilder *mb, gint16 data)
300 {
301         if ((mb->pos + 2) >= mb->code_size) {
302                 mb->code_size += mb->code_size >> 1;
303                 mb->code = g_realloc (mb->code, mb->code_size);
304         }
305
306         mb->code [mb->pos] = data & 0xff;
307         mb->code [mb->pos + 1] = (data >> 8) & 0xff;
308         mb->pos += 2;
309 }
310
311 void
312 mono_mb_emit_op (MonoMethodBuilder *mb, guint8 op, gpointer data)
313 {
314         mono_mb_emit_byte (mb, op);
315         mono_mb_emit_i4 (mb, mono_mb_add_data (mb, data));
316 }
317
318 void
319 mono_mb_emit_ldstr (MonoMethodBuilder *mb, char *str)
320 {
321         mono_mb_emit_op (mb, CEE_LDSTR, str);
322 }
323
324 void
325 mono_mb_emit_ldarg (MonoMethodBuilder *mb, guint argnum)
326 {
327         if (argnum < 4) {
328                 mono_mb_emit_byte (mb, CEE_LDARG_0 + argnum);
329         } else if (argnum < 256) {
330                 mono_mb_emit_byte (mb, CEE_LDARG_S);
331                 mono_mb_emit_byte (mb, argnum);
332         } else {
333                 mono_mb_emit_byte (mb, CEE_PREFIX1);
334                 mono_mb_emit_byte (mb, CEE_LDARG);
335                 mono_mb_emit_i2 (mb, argnum);
336         }
337 }
338
339 void
340 mono_mb_emit_ldarg_addr (MonoMethodBuilder *mb, guint argnum)
341 {
342         if (argnum < 256) {
343                 mono_mb_emit_byte (mb, CEE_LDARGA_S);
344                 mono_mb_emit_byte (mb, argnum);
345         } else {
346                 mono_mb_emit_byte (mb, CEE_PREFIX1);
347                 mono_mb_emit_byte (mb, CEE_LDARGA);
348                 mono_mb_emit_i2 (mb, argnum);
349         }
350 }
351
352 void
353 mono_mb_emit_ldloc_addr (MonoMethodBuilder *mb, guint locnum)
354 {
355         if (locnum < 256) {
356                 mono_mb_emit_byte (mb, CEE_LDLOCA_S);
357                 mono_mb_emit_byte (mb, locnum);
358         } else {
359                 mono_mb_emit_byte (mb, CEE_PREFIX1);
360                 mono_mb_emit_byte (mb, CEE_LDLOCA);
361                 mono_mb_emit_i2 (mb, locnum);
362         }
363 }
364
365 void
366 mono_mb_emit_ldloc (MonoMethodBuilder *mb, guint num)
367 {
368         if (num < 4) {
369                 mono_mb_emit_byte (mb, CEE_LDLOC_0 + num);
370         } else if (num < 256) {
371                 mono_mb_emit_byte (mb, CEE_LDLOC_S);
372                 mono_mb_emit_byte (mb, num);
373         } else {
374                 mono_mb_emit_byte (mb, CEE_PREFIX1);
375                 mono_mb_emit_byte (mb, CEE_LDLOC);
376                 mono_mb_emit_i2 (mb, num);
377         }
378 }
379
380 void
381 mono_mb_emit_stloc (MonoMethodBuilder *mb, guint num)
382 {
383         if (num < 4) {
384                 mono_mb_emit_byte (mb, CEE_STLOC_0 + num);
385         } else if (num < 256) {
386                 mono_mb_emit_byte (mb, CEE_STLOC_S);
387                 mono_mb_emit_byte (mb, num);
388         } else {
389                 mono_mb_emit_byte (mb, CEE_PREFIX1);
390                 mono_mb_emit_byte (mb, CEE_STLOC);
391                 mono_mb_emit_i2 (mb, num);
392         }
393 }
394
395 void
396 mono_mb_emit_icon (MonoMethodBuilder *mb, gint32 value)
397 {
398         if (value >= -1 && value < 8) {
399                 mono_mb_emit_byte (mb, CEE_LDC_I4_0 + value);
400         } else if (value >= -128 && value <= 127) {
401                 mono_mb_emit_byte (mb, CEE_LDC_I4_S);
402                 mono_mb_emit_byte (mb, value);
403         } else {
404                 mono_mb_emit_byte (mb, CEE_LDC_I4);
405                 mono_mb_emit_i4 (mb, value);
406         }
407 }
408
409 int
410 mono_mb_get_label (MonoMethodBuilder *mb)
411 {
412         return mb->pos;
413 }
414
415 int
416 mono_mb_get_pos (MonoMethodBuilder *mb)
417 {
418         return mb->pos;
419 }
420
421 guint32
422 mono_mb_emit_branch (MonoMethodBuilder *mb, guint8 op)
423 {
424         guint32 res;
425         mono_mb_emit_byte (mb, op);
426         res = mb->pos;
427         mono_mb_emit_i4 (mb, 0);
428         return res;
429 }
430
431 guint32
432 mono_mb_emit_short_branch (MonoMethodBuilder *mb, guint8 op)
433 {
434         guint32 res;
435         mono_mb_emit_byte (mb, op);
436         res = mb->pos;
437         mono_mb_emit_byte (mb, 0);
438
439         return res;
440 }
441
442 void
443 mono_mb_emit_branch_label (MonoMethodBuilder *mb, guint8 op, guint32 label)
444 {
445         mono_mb_emit_byte (mb, op);
446         mono_mb_emit_i4 (mb, label - (mb->pos + 4));
447 }
448
449 void
450 mono_mb_patch_branch (MonoMethodBuilder *mb, guint32 pos)
451 {
452         mono_mb_patch_addr (mb, pos, mb->pos - (pos + 4));
453 }
454
455 void
456 mono_mb_patch_short_branch (MonoMethodBuilder *mb, guint32 pos)
457 {
458         mono_mb_patch_addr_s (mb, pos, mb->pos - (pos + 1));
459 }
460
461 void
462 mono_mb_emit_ptr (MonoMethodBuilder *mb, gpointer ptr)
463 {
464         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
465         mono_mb_emit_op (mb, CEE_MONO_LDPTR, ptr);
466 }
467
468 void
469 mono_mb_emit_calli (MonoMethodBuilder *mb, MonoMethodSignature *sig)
470 {
471         mono_mb_emit_op (mb, CEE_CALLI, sig);
472 }
473
474 void
475 mono_mb_emit_managed_call (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *opt_sig)
476 {
477         mono_mb_emit_op (mb, CEE_CALL, method);
478 }
479
480 void
481 mono_mb_emit_native_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, gpointer func)
482 {
483         mono_mb_emit_ptr (mb, func);
484         mono_mb_emit_calli (mb, sig);
485 }
486
487 void
488 mono_mb_emit_icall (MonoMethodBuilder *mb, gpointer func)
489 {
490         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
491         mono_mb_emit_op (mb, CEE_MONO_ICALL, func);
492 }
493
494 void
495 mono_mb_emit_exception_full (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg)
496 {
497         MonoMethod *ctor = NULL;
498
499         MonoClass *mme = mono_class_from_name (mono_defaults.corlib, exc_nspace, exc_name);
500         mono_class_init (mme);
501         ctor = mono_class_get_method_from_name (mme, ".ctor", 0);
502         g_assert (ctor);
503         mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
504         if (msg != NULL) {
505                 mono_mb_emit_byte (mb, CEE_DUP);
506                 mono_mb_emit_ldflda (mb, G_STRUCT_OFFSET (MonoException, message));
507                 mono_mb_emit_ldstr (mb, (char*)msg);
508                 mono_mb_emit_byte (mb, CEE_STIND_REF);
509         }
510         mono_mb_emit_byte (mb, CEE_THROW);
511 }
512
513 void
514 mono_mb_emit_exception (MonoMethodBuilder *mb, const char *exc_name, const char *msg)
515 {
516         mono_mb_emit_exception_full (mb, "System", exc_name, msg);
517 }
518
519 void
520 mono_mb_emit_add_to_local (MonoMethodBuilder *mb, guint16 local, gint32 incr)
521 {
522         mono_mb_emit_ldloc (mb, local); 
523         mono_mb_emit_icon (mb, incr);
524         mono_mb_emit_byte (mb, CEE_ADD);
525         mono_mb_emit_stloc (mb, local); 
526 }
527
528 void
529 mono_mb_set_clauses (MonoMethodBuilder *mb, int num_clauses, MonoExceptionClause *clauses)
530 {
531         mb->num_clauses = num_clauses;
532         mb->clauses = clauses;
533 }