/** * \file * Functions for creating IL methods at runtime. * * Author: * Paolo Molaro (lupus@ximian.com) * * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com) * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include "config.h" #include "loader.h" #include "mono/metadata/abi-details.h" #include "mono/metadata/method-builder.h" #include "mono/metadata/tabledefs.h" #include "mono/metadata/exception.h" #include "mono/metadata/appdomain.h" #include "mono/metadata/debug-helpers.h" #include "mono/metadata/metadata-internals.h" #include "mono/metadata/domain-internals.h" #include #include /* #define DEBUG_RUNTIME_CODE */ #define OPDEF(a,b,c,d,e,f,g,h,i,j) \ a = i, enum { #include "mono/cil/opcode.def" LAST = 0xff }; #undef OPDEF #ifdef DEBUG_RUNTIME_CODE static char* indenter (MonoDisHelper *dh, MonoMethod *method, guint32 ip_offset) { return g_strdup (" "); } static MonoDisHelper marshal_dh = { "\n", "IL_%04x: ", "IL_%04x", indenter, NULL, NULL }; #endif static MonoMethodBuilder * mono_mb_new_base (MonoClass *klass, MonoWrapperType type) { MonoMethodBuilder *mb; MonoMethod *m; g_assert (klass != NULL); mb = g_new0 (MonoMethodBuilder, 1); mb->method = m = (MonoMethod *)g_new0 (MonoMethodWrapper, 1); m->klass = klass; m->inline_info = 1; m->wrapper_type = type; #ifdef ENABLE_ILGEN mb->code_size = 40; mb->code = (unsigned char *)g_malloc (mb->code_size); mb->init_locals = TRUE; #endif /* placeholder for the wrapper always at index 1 */ mono_mb_add_data (mb, NULL); return mb; } MonoMethodBuilder * mono_mb_new_no_dup_name (MonoClass *klass, const char *name, MonoWrapperType type) { MonoMethodBuilder *mb = mono_mb_new_base (klass, type); mb->name = (char*)name; mb->no_dup_name = TRUE; return mb; } /** * mono_mb_new: */ MonoMethodBuilder * mono_mb_new (MonoClass *klass, const char *name, MonoWrapperType type) { MonoMethodBuilder *mb = mono_mb_new_base (klass, type); mb->name = g_strdup (name); return mb; } /** * mono_mb_free: */ void mono_mb_free (MonoMethodBuilder *mb) { #ifdef ENABLE_ILGEN GList *l; for (l = mb->locals_list; l; l = l->next) { /* Allocated in mono_mb_add_local () */ g_free (l->data); } g_list_free (mb->locals_list); if (!mb->dynamic) { g_free (mb->method); if (!mb->no_dup_name) g_free (mb->name); g_free (mb->code); } #else g_free (mb->method); if (!mb->no_dup_name) g_free (mb->name); #endif g_free (mb); } /** * mono_mb_create_method: * Create a \c MonoMethod from this method builder. * \returns the newly created method. */ MonoMethod * mono_mb_create_method (MonoMethodBuilder *mb, MonoMethodSignature *signature, int max_stack) { #ifdef ENABLE_ILGEN MonoMethodHeader *header; #endif MonoMethodWrapper *mw; MonoImage *image; MonoMethod *method; GList *l; int i; g_assert (mb != NULL); image = mb->method->klass->image; #ifdef ENABLE_ILGEN if (mb->dynamic) { method = mb->method; mw = (MonoMethodWrapper*)method; method->name = mb->name; method->dynamic = TRUE; mw->header = header = (MonoMethodHeader *) g_malloc0 (MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *)); header->code = mb->code; for (i = 0, l = mb->locals_list; l; l = l->next, i++) { header->locals [i] = (MonoType*)l->data; } } else #endif { /* Realloc the method info into a mempool */ method = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethodWrapper)); memcpy (method, mb->method, sizeof (MonoMethodWrapper)); mw = (MonoMethodWrapper*) method; if (mb->no_dup_name) method->name = mb->name; else method->name = mono_image_strdup (image, mb->name); #ifdef ENABLE_ILGEN mw->header = header = (MonoMethodHeader *) mono_image_alloc0 (image, MONO_SIZEOF_METHOD_HEADER + mb->locals * sizeof (MonoType *)); header->code = (const unsigned char *)mono_image_alloc (image, mb->pos); memcpy ((char*)header->code, mb->code, mb->pos); for (i = 0, l = mb->locals_list; l; l = l->next, i++) { header->locals [i] = (MonoType*)l->data; } #endif } #ifdef ENABLE_ILGEN /* Free the locals list so mono_mb_free () doesn't free the types twice */ g_list_free (mb->locals_list); mb->locals_list = NULL; #endif method->signature = signature; if (!signature->hasthis) method->flags |= METHOD_ATTRIBUTE_STATIC; #ifdef ENABLE_ILGEN if (max_stack < 8) max_stack = 8; header->max_stack = max_stack; header->code_size = mb->pos; header->num_locals = mb->locals; header->init_locals = mb->init_locals; header->num_clauses = mb->num_clauses; header->clauses = mb->clauses; method->skip_visibility = mb->skip_visibility; #endif i = g_list_length ((GList *)mw->method_data); if (i) { GList *tmp; void **data; l = g_list_reverse ((GList *)mw->method_data); if (method_is_dynamic (method)) data = (void **)g_malloc (sizeof (gpointer) * (i + 1)); else data = (void **)mono_image_alloc (image, sizeof (gpointer) * (i + 1)); /* store the size in the first element */ data [0] = GUINT_TO_POINTER (i); i = 1; for (tmp = l; tmp; tmp = tmp->next) { data [i++] = tmp->data; } g_list_free (l); mw->method_data = data; } #ifdef ENABLE_ILGEN /*{ static int total_code = 0; static int total_alloc = 0; total_code += mb->pos; total_alloc += mb->code_size; g_print ("code size: %d of %d (allocated: %d)\n", mb->pos, total_code, total_alloc); }*/ #ifdef DEBUG_RUNTIME_CODE printf ("RUNTIME CODE FOR %s\n", mono_method_full_name (method, TRUE)); printf ("%s\n", mono_disasm_code (&marshal_dh, method, mb->code, mb->code + mb->pos)); #endif if (mb->param_names) { char **param_names = (char **)mono_image_alloc0 (image, signature->param_count * sizeof (gpointer)); for (i = 0; i < signature->param_count; ++i) param_names [i] = mono_image_strdup (image, mb->param_names [i]); mono_image_lock (image); if (!image->wrapper_param_names) image->wrapper_param_names = g_hash_table_new (NULL, NULL); g_hash_table_insert (image->wrapper_param_names, method, param_names); mono_image_unlock (image); } #endif return method; } /** * mono_mb_add_data: */ guint32 mono_mb_add_data (MonoMethodBuilder *mb, gpointer data) { MonoMethodWrapper *mw; g_assert (mb != NULL); mw = (MonoMethodWrapper *)mb->method; /* one O(n) is enough */ mw->method_data = g_list_prepend ((GList *)mw->method_data, data); return g_list_length ((GList *)mw->method_data); } #ifdef ENABLE_ILGEN /** * mono_mb_add_local: */ int mono_mb_add_local (MonoMethodBuilder *mb, MonoType *type) { int res; MonoType *t; /* * Have to make a copy early since type might be sig->ret, * which is transient, see mono_metadata_signature_dup_internal_with_padding (). */ t = mono_metadata_type_dup (NULL, type); g_assert (mb != NULL); g_assert (type != NULL); res = mb->locals; mb->locals_list = g_list_append (mb->locals_list, t); mb->locals++; return res; } /** * mono_mb_patch_addr: */ void mono_mb_patch_addr (MonoMethodBuilder *mb, int pos, int value) { mb->code [pos] = value & 0xff; mb->code [pos + 1] = (value >> 8) & 0xff; mb->code [pos + 2] = (value >> 16) & 0xff; mb->code [pos + 3] = (value >> 24) & 0xff; } /** * mono_mb_patch_addr_s: */ void mono_mb_patch_addr_s (MonoMethodBuilder *mb, int pos, gint8 value) { *((gint8 *)(&mb->code [pos])) = value; } /** * mono_mb_emit_byte: */ void mono_mb_emit_byte (MonoMethodBuilder *mb, guint8 op) { if (mb->pos >= mb->code_size) { mb->code_size += mb->code_size >> 1; mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); } mb->code [mb->pos++] = op; } /** * mono_mb_emit_ldflda: */ void mono_mb_emit_ldflda (MonoMethodBuilder *mb, gint32 offset) { mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); mono_mb_emit_byte (mb, CEE_MONO_OBJADDR); if (offset) { mono_mb_emit_icon (mb, offset); mono_mb_emit_byte (mb, CEE_ADD); } } /** * mono_mb_emit_i4: */ void mono_mb_emit_i4 (MonoMethodBuilder *mb, gint32 data) { if ((mb->pos + 4) >= mb->code_size) { mb->code_size += mb->code_size >> 1; mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); } mono_mb_patch_addr (mb, mb->pos, data); mb->pos += 4; } void mono_mb_emit_i8 (MonoMethodBuilder *mb, gint64 data) { if ((mb->pos + 8) >= mb->code_size) { mb->code_size += mb->code_size >> 1; mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); } mono_mb_patch_addr (mb, mb->pos, data); mono_mb_patch_addr (mb, mb->pos + 4, data >> 32); mb->pos += 8; } /** * mono_mb_emit_i2: */ void mono_mb_emit_i2 (MonoMethodBuilder *mb, gint16 data) { if ((mb->pos + 2) >= mb->code_size) { mb->code_size += mb->code_size >> 1; mb->code = (unsigned char *)g_realloc (mb->code, mb->code_size); } mb->code [mb->pos] = data & 0xff; mb->code [mb->pos + 1] = (data >> 8) & 0xff; mb->pos += 2; } void mono_mb_emit_op (MonoMethodBuilder *mb, guint8 op, gpointer data) { mono_mb_emit_byte (mb, op); mono_mb_emit_i4 (mb, mono_mb_add_data (mb, data)); } /** * mono_mb_emit_ldstr: */ void mono_mb_emit_ldstr (MonoMethodBuilder *mb, char *str) { mono_mb_emit_op (mb, CEE_LDSTR, str); } /** * mono_mb_emit_ldarg: */ void mono_mb_emit_ldarg (MonoMethodBuilder *mb, guint argnum) { if (argnum < 4) { mono_mb_emit_byte (mb, CEE_LDARG_0 + argnum); } else if (argnum < 256) { mono_mb_emit_byte (mb, CEE_LDARG_S); mono_mb_emit_byte (mb, argnum); } else { mono_mb_emit_byte (mb, CEE_PREFIX1); mono_mb_emit_byte (mb, CEE_LDARG); mono_mb_emit_i2 (mb, argnum); } } /** * mono_mb_emit_ldarg_addr: */ void mono_mb_emit_ldarg_addr (MonoMethodBuilder *mb, guint argnum) { if (argnum < 256) { mono_mb_emit_byte (mb, CEE_LDARGA_S); mono_mb_emit_byte (mb, argnum); } else { mono_mb_emit_byte (mb, CEE_PREFIX1); mono_mb_emit_byte (mb, CEE_LDARGA); mono_mb_emit_i2 (mb, argnum); } } /** * mono_mb_emit_ldloc_addr: */ void mono_mb_emit_ldloc_addr (MonoMethodBuilder *mb, guint locnum) { if (locnum < 256) { mono_mb_emit_byte (mb, CEE_LDLOCA_S); mono_mb_emit_byte (mb, locnum); } else { mono_mb_emit_byte (mb, CEE_PREFIX1); mono_mb_emit_byte (mb, CEE_LDLOCA); mono_mb_emit_i2 (mb, locnum); } } /** * mono_mb_emit_ldloc: */ void mono_mb_emit_ldloc (MonoMethodBuilder *mb, guint num) { if (num < 4) { mono_mb_emit_byte (mb, CEE_LDLOC_0 + num); } else if (num < 256) { mono_mb_emit_byte (mb, CEE_LDLOC_S); mono_mb_emit_byte (mb, num); } else { mono_mb_emit_byte (mb, CEE_PREFIX1); mono_mb_emit_byte (mb, CEE_LDLOC); mono_mb_emit_i2 (mb, num); } } /** * mono_mb_emit_stloc: */ void mono_mb_emit_stloc (MonoMethodBuilder *mb, guint num) { if (num < 4) { mono_mb_emit_byte (mb, CEE_STLOC_0 + num); } else if (num < 256) { mono_mb_emit_byte (mb, CEE_STLOC_S); mono_mb_emit_byte (mb, num); } else { mono_mb_emit_byte (mb, CEE_PREFIX1); mono_mb_emit_byte (mb, CEE_STLOC); mono_mb_emit_i2 (mb, num); } } /** * mono_mb_emit_icon: */ void mono_mb_emit_icon (MonoMethodBuilder *mb, gint32 value) { if (value >= -1 && value < 8) { mono_mb_emit_byte (mb, CEE_LDC_I4_0 + value); } else if (value >= -128 && value <= 127) { mono_mb_emit_byte (mb, CEE_LDC_I4_S); mono_mb_emit_byte (mb, value); } else { mono_mb_emit_byte (mb, CEE_LDC_I4); mono_mb_emit_i4 (mb, value); } } void mono_mb_emit_icon8 (MonoMethodBuilder *mb, gint64 value) { mono_mb_emit_byte (mb, CEE_LDC_I8); mono_mb_emit_i8 (mb, value); } int mono_mb_get_label (MonoMethodBuilder *mb) { return mb->pos; } int mono_mb_get_pos (MonoMethodBuilder *mb) { return mb->pos; } /** * mono_mb_emit_branch: */ guint32 mono_mb_emit_branch (MonoMethodBuilder *mb, guint8 op) { guint32 res; mono_mb_emit_byte (mb, op); res = mb->pos; mono_mb_emit_i4 (mb, 0); return res; } guint32 mono_mb_emit_short_branch (MonoMethodBuilder *mb, guint8 op) { guint32 res; mono_mb_emit_byte (mb, op); res = mb->pos; mono_mb_emit_byte (mb, 0); return res; } void mono_mb_emit_branch_label (MonoMethodBuilder *mb, guint8 op, guint32 label) { mono_mb_emit_byte (mb, op); mono_mb_emit_i4 (mb, label - (mb->pos + 4)); } void mono_mb_patch_branch (MonoMethodBuilder *mb, guint32 pos) { mono_mb_patch_addr (mb, pos, mb->pos - (pos + 4)); } void mono_mb_patch_short_branch (MonoMethodBuilder *mb, guint32 pos) { mono_mb_patch_addr_s (mb, pos, mb->pos - (pos + 1)); } void mono_mb_emit_ptr (MonoMethodBuilder *mb, gpointer ptr) { mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); mono_mb_emit_op (mb, CEE_MONO_LDPTR, ptr); } void mono_mb_emit_calli (MonoMethodBuilder *mb, MonoMethodSignature *sig) { mono_mb_emit_op (mb, CEE_CALLI, sig); } /** * mono_mb_emit_managed_call: */ void mono_mb_emit_managed_call (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *opt_sig) { mono_mb_emit_op (mb, CEE_CALL, method); } /** * mono_mb_emit_native_call: */ void mono_mb_emit_native_call (MonoMethodBuilder *mb, MonoMethodSignature *sig, gpointer func) { mono_mb_emit_ptr (mb, func); mono_mb_emit_calli (mb, sig); } void mono_mb_emit_icall (MonoMethodBuilder *mb, gpointer func) { mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX); mono_mb_emit_op (mb, CEE_MONO_ICALL, func); } void mono_mb_emit_exception_full (MonoMethodBuilder *mb, const char *exc_nspace, const char *exc_name, const char *msg) { MonoMethod *ctor = NULL; MonoClass *mme = mono_class_load_from_name (mono_defaults.corlib, exc_nspace, exc_name); mono_class_init (mme); ctor = mono_class_get_method_from_name (mme, ".ctor", 0); g_assert (ctor); mono_mb_emit_op (mb, CEE_NEWOBJ, ctor); if (msg != NULL) { mono_mb_emit_byte (mb, CEE_DUP); mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoException, message)); mono_mb_emit_ldstr (mb, (char*)msg); mono_mb_emit_byte (mb, CEE_STIND_REF); } mono_mb_emit_byte (mb, CEE_THROW); } /** * mono_mb_emit_exception: */ void mono_mb_emit_exception (MonoMethodBuilder *mb, const char *exc_name, const char *msg) { mono_mb_emit_exception_full (mb, "System", exc_name, msg); } /** * mono_mb_emit_exception_for_error: */ void mono_mb_emit_exception_for_error (MonoMethodBuilder *mb, MonoError *error) { /* * If at some point there is need to support other types of errors, * the behaviour should conform with mono_error_prepare_exception(). */ g_assert (mono_error_get_error_code (error) == MONO_ERROR_GENERIC && "Unsupported error code."); mono_mb_emit_exception_full (mb, "System", mono_error_get_exception_name (error), mono_error_get_message (error)); } /** * mono_mb_emit_add_to_local: */ void mono_mb_emit_add_to_local (MonoMethodBuilder *mb, guint16 local, gint32 incr) { mono_mb_emit_ldloc (mb, local); mono_mb_emit_icon (mb, incr); mono_mb_emit_byte (mb, CEE_ADD); mono_mb_emit_stloc (mb, local); } void mono_mb_set_clauses (MonoMethodBuilder *mb, int num_clauses, MonoExceptionClause *clauses) { mb->num_clauses = num_clauses; mb->clauses = clauses; } /* * mono_mb_set_param_names: * * PARAM_NAMES should have length equal to the sig->param_count, the caller retains * ownership of the array, and its entries. */ void mono_mb_set_param_names (MonoMethodBuilder *mb, const char **param_names) { mb->param_names = param_names; } #endif /* DISABLE_JIT */