Merge pull request #2847 from vargaz/get-type-reflection
authormonojenkins <jo.shields+jenkins@xamarin.com>
Thu, 7 Apr 2016 07:20:22 +0000 (08:20 +0100)
committermonojenkins <jo.shields+jenkins@xamarin.com>
Thu, 7 Apr 2016 07:20:22 +0000 (08:20 +0100)
[runtime] Fix type_from_parsed_name () to work in the presence of inl…

…ining done by LLVM.

13 files changed:
mono/metadata/appdomain.c
mono/metadata/attach.c
mono/metadata/exception.c
mono/metadata/gc.c
mono/metadata/threadpool-ms.c
mono/metadata/threads-types.h
mono/metadata/threads.c
mono/mini/aot-compiler.c
mono/mini/debugger-agent.c
mono/mini/mini-arm64.h
mono/profiler/proflog.c
mono/utils/mono-error-internals.h
mono/utils/mono-error.c

index aaeab57d56729239d8b9bcb99d7405cb72360375..71bfc07952fd368df01fbf19665f8e22a7fa0121 100644 (file)
@@ -2431,7 +2431,8 @@ unload_thread_main (void *arg)
        /* Force it to be attached to avoid racing during shutdown. */
        thread = mono_thread_attach_full (mono_get_root_domain (), TRUE, &error);
        mono_error_raise_exception (&error); /* FIXME don't raise here */
-       mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Domain unloader"), TRUE);
+       mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Domain unloader"), TRUE, &error);
+       mono_error_raise_exception (&error); /* FIXME don't raise here */
 
        /* 
         * FIXME: Abort our parent thread last, so we can return a failure 
index 688f816a407a841dd228169b151abd27ad24794f..6b65259b962b437f20e121671fb189b8486219c3 100644 (file)
@@ -475,6 +475,7 @@ transport_start_receive (void)
 static guint32 WINAPI
 receiver_thread (void *arg)
 {
+       MonoError error;
        int res, content_len;
        guint8 buffer [256];
        guint8 *p, *p_end;
@@ -493,7 +494,8 @@ receiver_thread (void *arg)
                printf ("attach: Connected.\n");
 
                MonoThread *thread = mono_thread_attach (mono_get_root_domain ());
-               mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Attach receiver"), TRUE);
+               mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "Attach receiver"), TRUE, &error);
+               mono_error_assert_ok (&error);
                /* Ask the runtime to not abort this thread */
                //mono_thread_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
                /* Ask the runtime to not wait for this thread */
index 332ac557d1ee77e5e5eb86213eb59ae6e47beda5..e2e162f702a1efeb69e0226a5a4ab8eedb4d52d4 100644 (file)
@@ -940,8 +940,10 @@ ves_icall_Mono_Runtime_GetNativeStackTrace (MonoException *exc)
 {
        char *trace;
        MonoString *res;
-       if (!exc)
-               mono_raise_exception (mono_get_exception_argument_null ("exception"));
+       if (!exc) {
+               mono_set_pending_exception (mono_get_exception_argument_null ("exception"));
+               return NULL;
+       }
 
        trace = mono_exception_get_native_backtrace (exc);
        res = mono_string_new (mono_domain_get (), trace);
index edba423b8a6b237d802d484de46611da1f7e04d5..bf02f2892b2e8ff3f8c0f544e2c3a08cf4dfa2b5 100644 (file)
@@ -748,7 +748,9 @@ finalize_domain_objects (DomainFinalizationReq *req)
 static guint32
 finalizer_thread (gpointer unused)
 {
-       mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE);
+       MonoError error;
+       mono_thread_set_name_internal (mono_thread_internal_current (), mono_string_new (mono_get_root_domain (), "Finalizer"), FALSE, &error);
+       mono_error_assert_ok (&error);
 
        gboolean wait = TRUE;
 
index eef559e9926c0d2dc18578c602ab5a5d376031a7..14ebdf648ceb2ff052f46f2115b62b87830ded6a 100644 (file)
@@ -592,7 +592,8 @@ worker_thread (gpointer data)
        thread = mono_thread_internal_current ();
        g_assert (thread);
 
-       mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Threadpool worker"), FALSE);
+       mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Threadpool worker"), FALSE, &error);
+       mono_error_assert_ok (&error);
 
        mono_coop_mutex_lock (&threadpool->active_threads_lock);
        g_ptr_array_add (threadpool->working_threads, thread);
index b970e96e038a2fd0944ccb0c01295aa048b4e7f7..f767247435aa25f7ebe39c62836cf651692fa93e 100644 (file)
@@ -218,7 +218,7 @@ MONO_API MonoException* mono_thread_get_undeniable_exception (void);
 
 MonoException* mono_thread_get_and_clear_pending_exception (void);
 
-void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed);
+void mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed, MonoError *error);
 
 void mono_runtime_set_has_tls_get (gboolean val);
 gboolean mono_runtime_has_tls_get (void);
index 0246491124d24e947d208c29737290f0fc627509..856f0abf8797153ac0049e640442c89ba8097713 100644 (file)
@@ -618,23 +618,25 @@ create_internal_thread (MonoError *error)
        return thread;
 }
 
-static void
-init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate)
+static gboolean
+init_root_domain_thread (MonoInternalThread *thread, MonoThread *candidate, MonoError *error)
 {
-       MonoError error;
        MonoDomain *domain = mono_get_root_domain ();
 
+       mono_error_init (error);
        if (!candidate || candidate->obj.vtable->domain != domain) {
-               candidate = new_thread_with_internal (domain, thread, &error);
-               mono_error_raise_exception (&error); /* FIXME don't raise here */
+               candidate = new_thread_with_internal (domain, thread, error);
+               return_val_if_nok (error, FALSE);
        }
        set_current_thread_for_domain (domain, thread, candidate);
        g_assert (!thread->root_domain_thread);
        MONO_OBJECT_SETREF (thread, root_domain_thread, candidate);
+       return TRUE;
 }
 
 static guint32 WINAPI start_wrapper_internal(void *data)
 {
+       MonoError error;
        MonoThreadInfo *info;
        StartInfo *start_info = (StartInfo *)data;
        guint32 (*start_func)(void *);
@@ -683,7 +685,8 @@ static guint32 WINAPI start_wrapper_internal(void *data)
        /* We have to do this here because mono_thread_new_init()
           requires that root_domain_thread is set up. */
        thread_adjust_static_data (internal);
-       init_root_domain_thread (internal, start_info->obj);
+       init_root_domain_thread (internal, start_info->obj, &error);
+       mono_error_raise_exception (&error); /* FIXME don't raise here */
 
        /* This MUST be called before any managed code can be
         * executed, as it calls the callback function that (for the
@@ -790,7 +793,7 @@ static guint32 WINAPI start_wrapper(void *data)
  */
 static gboolean
 create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *start_info, gboolean threadpool_thread, guint32 stack_size,
-                          gboolean throw_on_failure)
+                          MonoError *error)
 {
        HANDLE thread_handle;
        MonoNativeThreadId tid;
@@ -802,6 +805,8 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star
         */
        mono_threads_join_threads ();
 
+       mono_error_init (error);
+
        mono_threads_lock ();
        if (shutting_down) {
                g_free (start_info);
@@ -837,15 +842,12 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, StartInfo *star
                                                                                                stack_size, create_flags, &tid);
 
        if (thread_handle == NULL) {
-               /* The thread couldn't be created, so throw an exception */
+               /* The thread couldn't be created, so set an exception */
                mono_threads_lock ();
                mono_g_hash_table_remove (threads_starting_up, thread);
                mono_threads_unlock ();
                g_free (start_info);
-               if (throw_on_failure)
-                       mono_raise_exception (mono_get_exception_execution_engine ("Couldn't create thread"));
-               else
-                       g_warning ("%s: CreateThread error 0x%x", __func__, GetLastError ());
+               mono_error_set_execution_engine (error, "Couldn't create thread. Error 0x%x", GetLastError());
                return FALSE;
        }
        THREAD_DEBUG (g_message ("%s: Started thread ID %"G_GSIZE_FORMAT" (handle %p)", __func__, tid, thread_handle));
@@ -934,9 +936,11 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
        start_info->obj = thread;
        start_info->start_arg = arg;
 
-       res = create_thread (thread, internal, start_info, threadpool_thread, stack_size, TRUE);
-       if (!res)
+       res = create_thread (thread, internal, start_info, threadpool_thread, stack_size, &error);
+       if (!res) {
+               mono_error_raise_exception (&error); /* FIXME don't raise here */
                return NULL;
+       }
 
        /* Check that the managed and unmanaged layout of MonoInternalThread matches */
 #ifndef MONO_CROSS_COMPILE
@@ -1022,7 +1026,9 @@ mono_thread_attach_full (MonoDomain *domain, gboolean force_attach, MonoError *e
 
        thread_adjust_static_data (thread);
 
-       init_root_domain_thread (thread, current_thread);
+       init_root_domain_thread (thread, current_thread, error);
+       return_val_if_nok (error, NULL);
+
        if (domain != mono_get_root_domain ())
                set_current_thread_for_domain (domain, thread, current_thread);
 
@@ -1128,6 +1134,7 @@ HANDLE
 ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
                                                                                                   MonoObject *start)
 {
+       MonoError error;
        StartInfo *start_info;
        MonoInternalThread *internal;
        gboolean res;
@@ -1158,8 +1165,9 @@ ves_icall_System_Threading_Thread_Thread_internal (MonoThread *this_obj,
        start_info->obj = this_obj;
        g_assert (this_obj->obj.vtable->domain == mono_domain_get ());
 
-       res = create_thread (this_obj, internal, start_info, FALSE, 0, FALSE);
+       res = create_thread (this_obj, internal, start_info, FALSE, 0, &error);
        if (!res) {
+               mono_error_cleanup (&error);
                UNLOCK_THREAD (internal);
                return NULL;
        }
@@ -1350,14 +1358,16 @@ ves_icall_System_Threading_Thread_GetName_internal (MonoInternalThread *this_obj
 }
 
 void 
-mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed)
+mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, gboolean managed, MonoError *error)
 {
        LOCK_THREAD (this_obj);
 
+       mono_error_init (error);
+
        if ((this_obj->flags & MONO_THREAD_FLAG_NAME_SET) && !this_obj->threadpool_thread) {
                UNLOCK_THREAD (this_obj);
                
-               mono_raise_exception (mono_get_exception_invalid_operation ("Thread.Name can only be set once."));
+               mono_error_set_invalid_operation (error, "Thread.Name can only be set once.");
                return;
        }
        if (this_obj->name) {
@@ -1388,7 +1398,9 @@ mono_thread_set_name_internal (MonoInternalThread *this_obj, MonoString *name, g
 void 
 ves_icall_System_Threading_Thread_SetName_internal (MonoInternalThread *this_obj, MonoString *name)
 {
-       mono_thread_set_name_internal (this_obj, name, TRUE);
+       MonoError error;
+       mono_thread_set_name_internal (this_obj, name, TRUE, &error);
+       mono_error_set_pending_exception (&error);
 }
 
 /*
index f0f79d7c40d170416ea662b3832a938e380cfe43..f65b3a6ee68a58a0b22aae2c31695b1d46471843 100644 (file)
@@ -7865,8 +7865,10 @@ compile_thread_main (gpointer *user_data)
        GPtrArray *methods = (GPtrArray *)user_data [2];
        int i;
 
+       MonoError error;
        MonoThread *thread = mono_thread_attach (domain);
-       mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "AOT compiler"), TRUE);
+       mono_thread_set_name_internal (thread->internal_thread, mono_string_new (mono_get_root_domain (), "AOT compiler"), TRUE, &error);
+       mono_error_assert_ok (&error);
 
        for (i = 0; i < methods->len; ++i)
                compile_method (acfg, (MonoMethod *)g_ptr_array_index (methods, i));
index ba127514b1bacda0a733af3ee9b110c4326ecd5a..c7e18a0f0d9af49c4a515e255662d5781568a4e1 100644 (file)
@@ -9675,6 +9675,7 @@ wait_for_attach (void)
 static guint32 WINAPI
 debugger_thread (void *arg)
 {
+       MonoError error;
        int res, len, id, flags, command = 0;
        CommandSet command_set = (CommandSet)0;
        guint8 header [HEADER_LENGTH];
@@ -9691,7 +9692,8 @@ debugger_thread (void *arg)
 
        attach_cookie = mono_jit_thread_attach (mono_get_root_domain (), &attach_dummy);
        MonoInternalThread *thread = mono_thread_internal_current ();
-       mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE);
+       mono_thread_set_name_internal (thread, mono_string_new (mono_get_root_domain (), "Debugger agent"), TRUE, &error);
+       mono_error_assert_ok (&error);
 
        thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
 
index 01cd3b7b4184af2850d5385c7f40158d8ade07e3..e3e90eb6ea59bd350346001e0c5c22a065e4349e 100644 (file)
@@ -175,7 +175,7 @@ typedef struct {
 
 #endif
 
-#if defined(TARGET_APPLETVOS)
+#if defined(TARGET_APPLETVOS) || defined(TARGET_IOS)
 #define MONO_ARCH_HAVE_UNWIND_BACKTRACE 1
 #endif
 
index 63c1fd8f9a0da336067eaacdd42b288f745887e8..3d9079f45f73bcea0397a83985bbd5dc445a68de 100644 (file)
@@ -515,6 +515,7 @@ struct _MonoProfiler {
 #endif
        volatile gint32 run_writer_thread;
        MonoLockFreeQueue writer_queue;
+       MonoSemType writer_queue_sem;
        MonoConcurrentHashTable *method_table;
        mono_mutex_t method_table_mutex;
        BinaryObject *binary_objects;
@@ -894,6 +895,7 @@ send_buffer (MonoProfiler *prof, GPtrArray *methods, LogBuffer *buffer)
        entry->methods = methods;
        entry->buffer = buffer;
        mono_lock_free_queue_enqueue (&prof->writer_queue, &entry->node);
+       mono_os_sem_post (&prof->writer_queue_sem);
 }
 
 static void
@@ -3981,8 +3983,11 @@ log_shutdown (MonoProfiler *prof)
        TLS_SET (tlsmethodlist, NULL);
 
        InterlockedWrite (&prof->run_writer_thread, 0);
+       mono_os_sem_post (&prof->writer_queue_sem);
        pthread_join (prof->writer_thread, &res);
 
+       mono_os_sem_destroy (&prof->writer_queue_sem);
+
 #if defined (HAVE_SYS_ZLIB)
        if (prof->gzfile)
                gzclose (prof->gzfile);
@@ -4269,96 +4274,111 @@ start_helper_thread (MonoProfiler* prof)
 }
 #endif
 
-static void *
-writer_thread (void *arg)
+static gboolean
+handle_writer_queue_entry (MonoProfiler *prof)
 {
-       MonoProfiler *prof = (MonoProfiler *)arg;
+       WriterQueueEntry *entry;
 
-       mono_threads_attach_tools_thread ();
-       mono_thread_info_set_name (mono_native_thread_id_get (), "Profiler writer");
+       if ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
+               LogBuffer *method_buffer = NULL;
+               gboolean new_methods = FALSE;
 
-       dump_header (prof);
+               if (entry->methods->len)
+                       method_buffer = create_buffer ();
 
-       while (InterlockedRead (&prof->run_writer_thread)) {
-               WriterQueueEntry *entry;
+               /*
+                * Encode the method events in a temporary log buffer that we
+                * flush to disk before the main buffer, ensuring that all
+                * methods have metadata emitted before they're referenced.
+                */
+               for (guint i = 0; i < entry->methods->len; i++) {
+                       MethodInfo *info = (MethodInfo *)g_ptr_array_index (entry->methods, i);
 
-               while ((entry = (WriterQueueEntry *) mono_lock_free_queue_dequeue (&prof->writer_queue))) {
-                       LogBuffer *method_buffer = NULL;
-                       gboolean new_methods = FALSE;
+                       if (mono_conc_hashtable_lookup (prof->method_table, info->method))
+                               continue;
 
-                       if (entry->methods->len)
-                               method_buffer = create_buffer ();
+                       new_methods = TRUE;
 
                        /*
-                        * Encode the method events in a temporary log buffer that we
-                        * flush to disk before the main buffer, ensuring that all
-                        * methods have metadata emitted before they're referenced.
+                        * Other threads use this hash table to get a general
+                        * idea of whether a method has already been emitted to
+                        * the stream. Due to the way we add to this table, it
+                        * can easily happen that multiple threads queue up the
+                        * same methods, but that's OK since eventually all
+                        * methods will be in this table and the thread-local
+                        * method lists will just be empty for the rest of the
+                        * app's lifetime.
                         */
-                       for (guint i = 0; i < entry->methods->len; i++) {
-                               MethodInfo *info = (MethodInfo *)g_ptr_array_index (entry->methods, i);
+                       mono_os_mutex_lock (&prof->method_table_mutex);
+                       mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
+                       mono_os_mutex_unlock (&prof->method_table_mutex);
+
+                       char *name = mono_method_full_name (info->method, 1);
+                       int nlen = strlen (name) + 1;
+                       void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
+                       int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
+
+                       method_buffer = ensure_logbuf_inner (method_buffer,
+                               EVENT_SIZE /* event */ +
+                               LEB128_SIZE /* time */ +
+                               LEB128_SIZE /* method */ +
+                               LEB128_SIZE /* start */ +
+                               LEB128_SIZE /* size */ +
+                               nlen /* name */
+                       );
+
+                       emit_byte (method_buffer, TYPE_JIT | TYPE_METHOD);
+                       emit_time (method_buffer, info->time);
+                       emit_method_inner (method_buffer, info->method);
+                       emit_ptr (method_buffer, cstart);
+                       emit_value (method_buffer, csize);
+
+                       memcpy (method_buffer->cursor, name, nlen);
+                       method_buffer->cursor += nlen;
+
+                       mono_free (name);
+                       free (info);
+               }
 
-                               if (mono_conc_hashtable_lookup (prof->method_table, info->method))
-                                       continue;
+               g_ptr_array_free (entry->methods, TRUE);
 
-                               new_methods = TRUE;
-
-                               /*
-                                * Other threads use this hash table to get a general
-                                * idea of whether a method has already been emitted to
-                                * the stream. Due to the way we add to this table, it
-                                * can easily happen that multiple threads queue up the
-                                * same methods, but that's OK since eventually all
-                                * methods will be in this table and the thread-local
-                                * method lists will just be empty for the rest of the
-                                * app's lifetime.
-                                */
-                               mono_os_mutex_lock (&prof->method_table_mutex);
-                               mono_conc_hashtable_insert (prof->method_table, info->method, info->method);
-                               mono_os_mutex_unlock (&prof->method_table_mutex);
-
-                               char *name = mono_method_full_name (info->method, 1);
-                               int nlen = strlen (name) + 1;
-                               void *cstart = info->ji ? mono_jit_info_get_code_start (info->ji) : NULL;
-                               int csize = info->ji ? mono_jit_info_get_code_size (info->ji) : 0;
-
-                               method_buffer = ensure_logbuf_inner (method_buffer,
-                                       EVENT_SIZE /* event */ +
-                                       LEB128_SIZE /* time */ +
-                                       LEB128_SIZE /* method */ +
-                                       LEB128_SIZE /* start */ +
-                                       LEB128_SIZE /* size */ +
-                                       nlen /* name */
-                               );
-
-                               emit_byte (method_buffer, TYPE_JIT | TYPE_METHOD);
-                               emit_time (method_buffer, info->time);
-                               emit_method_inner (method_buffer, info->method);
-                               emit_ptr (method_buffer, cstart);
-                               emit_value (method_buffer, csize);
-
-                               memcpy (method_buffer->cursor, name, nlen);
-                               method_buffer->cursor += nlen;
-
-                               mono_free (name);
-                               free (info);
-                       }
+               if (new_methods) {
+                       for (LogBuffer *iter = method_buffer; iter; iter = iter->next)
+                               iter->thread_id = 0;
+
+                       dump_buffer (prof, method_buffer);
+               } else if (method_buffer)
+                       free_buffer (method_buffer, method_buffer->size);
 
-                       g_ptr_array_free (entry->methods, TRUE);
+               dump_buffer (prof, entry->buffer);
 
-                       if (new_methods) {
-                               for (LogBuffer *iter = method_buffer; iter; iter = iter->next)
-                                       iter->thread_id = 0;
+               free (entry);
 
-                               dump_buffer (prof, method_buffer);
-                       } else if (method_buffer)
-                               free_buffer (method_buffer, method_buffer->size);
+               return TRUE;
+       }
 
-                       dump_buffer (prof, entry->buffer);
+       return FALSE;
+}
 
-                       free (entry);
-               }
+static void *
+writer_thread (void *arg)
+{
+       MonoProfiler *prof = (MonoProfiler *)arg;
+       WriterQueueEntry *entry;
+
+       mono_threads_attach_tools_thread ();
+       mono_thread_info_set_name (mono_native_thread_id_get (), "Profiler writer");
+
+       dump_header (prof);
+
+       while (InterlockedRead (&prof->run_writer_thread)) {
+               mono_os_sem_wait (&prof->writer_queue_sem, MONO_SEM_FLAGS_NONE);
+               handle_writer_queue_entry (prof);
        }
 
+       /* Drain any remaining entries on shutdown. */
+       while (handle_writer_queue_entry (prof));
+
        mono_thread_info_detach ();
 
        return NULL;
@@ -4469,6 +4489,8 @@ create_profiler (const char *filename, GPtrArray *filters)
 #endif
 
        mono_lock_free_queue_init (&prof->writer_queue);
+       mono_os_sem_init (&prof->writer_queue_sem, 1);
+
        mono_os_mutex_init (&prof->method_table_mutex);
        prof->method_table = mono_conc_hashtable_new (NULL, NULL);
 
index 31a2a4f5362a0bb5857a1c254a784f2946dcc0b4..71498c0d99b4e2e8148f5c73658aba7b934c2f0a 100644 (file)
@@ -102,6 +102,9 @@ mono_error_set_not_implemented (MonoError *error, const char *msg_format, ...);
 void
 mono_error_set_not_supported (MonoError *error, const char *msg_format, ...);
 
+void
+mono_error_set_invalid_operation (MonoError *error, const char *msg_format, ...);
+
 void
 mono_error_set_exception_instance (MonoError *error, MonoException *exc);
 
index 7cbce5cf765d3a0e8730327fa626bee8158ce54f..84d04781e708927500ec5fc106ec46066cacfda4 100644 (file)
@@ -393,7 +393,7 @@ mono_error_set_execution_engine (MonoError *oerror, const char *msg_format, ...)
 }
 
 /**
- * mono_error_set_execution_engine:
+ * mono_error_set_not_supported:
  *
  * System.NotSupportedException
  */
@@ -406,6 +406,20 @@ mono_error_set_not_supported (MonoError *oerror, const char *msg_format, ...)
        va_end (args);
 }
 
+/**
+ * mono_error_set_invalid_operation:
+ *
+ * System.InvalidOperationException
+ */
+void
+mono_error_set_invalid_operation (MonoError *oerror, const char *msg_format, ...)
+{
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, "System", "InvalidOperationException", msg_format, args);
+       va_end (args);
+}
+
 void
 mono_error_set_exception_instance (MonoError *oerror, MonoException *exc)
 {