[runtime] Don't insta-fail when a faulty COM type is encountered. (#5616)
[mono.git] / mono / utils / mono-error.c
index 7e186b6b717ce06b2f4103bb2ab3abe22e5ce2df..27f2b62fb248ff784d2f6dbc9b350f98a7613d07 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * mono-error.c: Error handling code
+/**
+ * \file
+ * Error handling code
  *
  * Authors:
  *     Rodrigo Kumpera    (rkumpera@novell.com)
@@ -38,6 +39,12 @@ is_managed_exception (MonoErrorInternal *error)
        return (error->error_code == MONO_ERROR_EXCEPTION_INSTANCE);
 }
 
+static gboolean
+is_boxed (MonoErrorInternal *error)
+{
+       return ((error->flags & MONO_ERROR_MEMPOOL_BOXED) != 0);
+}
+
 static void
 mono_error_prepare (MonoErrorInternal *error)
 {
@@ -95,10 +102,9 @@ mono_error_init_flags (MonoError *oerror, unsigned short flags)
 
 /**
  * mono_error_init:
- * @error: Pointer to MonoError struct to initialize
- *
- * Any function which takes a MonoError for purposes of reporting an error
- * is required to call either this or mono_error_init_flags on entry.
+ * \param error Pointer to \c MonoError struct to initialize
+ * Any function which takes a \c MonoError for purposes of reporting an error
+ * is required to call either this or \c mono_error_init_flags on entry.
  */
 void
 mono_error_init (MonoError *error)
@@ -116,6 +122,8 @@ mono_error_cleanup (MonoError *oerror)
 
        /* Two cleanups in a row without an intervening init. */
        g_assert (orig_error_code != MONO_ERROR_CLEANUP_CALLED_SENTINEL);
+       /* Mempool stored error shouldn't be cleaned up */
+       g_assert (!is_boxed (error));
 
        /* Mark it as cleaned up. */
        error->error_code = MONO_ERROR_CLEANUP_CALLED_SENTINEL;
@@ -168,6 +176,17 @@ mono_error_get_error_code (MonoError *error)
        return error->error_code;
 }
 
+const char*
+mono_error_get_exception_name (MonoError *oerror)
+{
+       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
+
+       if (error->error_code == MONO_ERROR_NONE)
+               return NULL;
+
+       return error->exception_name;
+}
+
 /*Return a pointer to the internal error message, might be NULL.
 Caller should not release it.*/
 const char*
@@ -289,18 +308,27 @@ mono_error_set_assembly_load_simple (MonoError *oerror, const char *assembly_nam
        if (refection_only)
                mono_error_set_assembly_load (oerror, assembly_name, "Cannot resolve dependency to assembly because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.");
        else
-               mono_error_set_assembly_load (oerror, assembly_name, "Could not load file or assembly or one of its dependencies.");
+               mono_error_set_assembly_load (oerror, assembly_name, "Could not load file or assembly '%s' or one of its dependencies.", assembly_name);
 }
 
 void
 mono_error_set_type_load_class (MonoError *oerror, MonoClass *klass, const char *msg_format, ...)
+{
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_vset_type_load_class (oerror, klass, msg_format, args);
+       va_end (args);
+}
+
+void
+mono_error_vset_type_load_class (MonoError *oerror, MonoClass *klass, const char *msg_format, va_list args)
 {
        MonoErrorInternal *error = (MonoErrorInternal*)oerror;
        mono_error_prepare (error);
 
        error->error_code = MONO_ERROR_TYPE_LOAD;
        mono_error_set_class (oerror, klass);
-       set_error_message ();
+       set_error_messagev ();
 }
 
 /*
@@ -442,6 +470,20 @@ mono_error_set_invalid_operation (MonoError *oerror, const char *msg_format, ...
        va_end (args);
 }
 
+/**
+ * mono_error_set_file_not_found:
+ *
+ * System.IO.FileNotFoundException
+ */
+void
+mono_error_set_file_not_found (MonoError *oerror, const char *msg_format, ...)
+{
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, "System.IO", "FileNotFoundException", msg_format, args);
+       va_end (args);
+}
+
 void
 mono_error_set_invalid_program (MonoError *oerror, const char *msg_format, ...)
 {
@@ -453,6 +495,17 @@ mono_error_set_invalid_program (MonoError *oerror, const char *msg_format, ...)
        set_error_message ();
 }
 
+/**
+ * mono_error_set_invalid_cast:
+ *
+ * System.InvalidCastException
+ */
+void
+mono_error_set_invalid_cast (MonoError *oerror)
+{
+        mono_error_set_generic_error (oerror, "System", "InvalidCastException", "");
+}
+
 void
 mono_error_set_exception_instance (MonoError *oerror, MonoException *exc)
 {
@@ -463,6 +516,16 @@ mono_error_set_exception_instance (MonoError *oerror, MonoException *exc)
        error->exn.instance_handle = mono_gchandle_new (exc ? &exc->object : NULL, FALSE);
 }
 
+void
+mono_error_set_exception_handle (MonoError *oerror, MonoExceptionHandle exc)
+{
+       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
+
+       mono_error_prepare (error);
+       error->error_code = MONO_ERROR_EXCEPTION_INSTANCE;
+       error->exn.instance_handle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject, exc), FALSE);
+}
+
 void
 mono_error_set_out_of_memory (MonoError *oerror, const char *msg_format, ...)
 {
@@ -514,20 +577,30 @@ mono_error_set_not_verifiable (MonoError *oerror, MonoMethod *method, const char
 }
 
 
+/* Used by mono_error_prepare_exception - it sets its own error on mono_string_new_checked failure. */
+static MonoString*
+string_new_cleanup (MonoDomain *domain, const char *text)
+{
+       MonoError ignored_err;
+       MonoString *result = mono_string_new_checked (domain, text, &ignored_err);
+       mono_error_cleanup (&ignored_err);
+       return result;
+}
+
 static MonoString*
 get_type_name_as_mono_string (MonoErrorInternal *error, MonoDomain *domain, MonoError *error_out)
 {
        MonoString* res = NULL;
 
        if (error->type_name) {
-               res = mono_string_new (domain, error->type_name);
+               res = string_new_cleanup (domain, error->type_name);
                
        } else {
                MonoClass *klass = get_class (error);
                if (klass) {
                        char *name = mono_type_full_name (&klass->byval_arg);
                        if (name) {
-                               res = mono_string_new (domain, name);
+                               res = string_new_cleanup (domain, name);
                                g_free (name);
                        }
                }
@@ -540,7 +613,7 @@ get_type_name_as_mono_string (MonoErrorInternal *error, MonoDomain *domain, Mono
 static void
 set_message_on_exception (MonoException *exception, MonoErrorInternal *error, MonoError *error_out)
 {
-       MonoString *msg = mono_string_new (mono_domain_get (), error->full_message);
+       MonoString *msg = string_new_cleanup (mono_domain_get (), error->full_message);
        if (msg)
                MONO_OBJECT_SETREF (exception, message, msg);
        else
@@ -557,7 +630,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
        MonoString *assembly_name = NULL, *type_name = NULL, *method_name = NULL, *field_name = NULL, *msg = NULL;
        MonoDomain *domain = mono_domain_get ();
 
-       mono_error_init (error_out);
+       error_init (error_out);
 
        switch (error->error_code) {
        case MONO_ERROR_NONE:
@@ -569,7 +642,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                        if (!mono_error_ok (error_out))
                                break;
 
-                       method_name = mono_string_new (domain, error->member_name);
+                       method_name = string_new_cleanup (domain, error->member_name);
                        if (!method_name) {
                                mono_error_set_out_of_memory (error_out, "Could not allocate method name");
                                break;
@@ -589,7 +662,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                        if (!mono_error_ok (error_out))
                                break;
                        
-                       field_name = mono_string_new (domain, error->member_name);
+                       field_name = string_new_cleanup (domain, error->member_name);
                        if (!field_name) {
                                mono_error_set_out_of_memory (error_out, "Could not allocate field name");
                                break;
@@ -610,15 +683,17 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                                break;
 
                        if (error->assembly_name) {
-                               assembly_name = mono_string_new (domain, error->assembly_name);
+                               assembly_name = string_new_cleanup (domain, error->assembly_name);
                                if (!assembly_name) {
                                        mono_error_set_out_of_memory (error_out, "Could not allocate assembly name");
                                        break;
                                }
+                       } else {
+                               assembly_name = mono_string_empty (domain);
                        }
 
                        exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", "TypeLoadException", type_name, assembly_name, error_out);
-                       if (exception)
+                       if (exception && error->full_message != NULL && strcmp (error->full_message, ""))
                                set_message_on_exception (exception, error, error_out);
                } else {
                        exception = mono_exception_from_name_msg (mono_defaults.corlib, "System", "TypeLoadException", error->full_message);
@@ -628,14 +703,14 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
        case MONO_ERROR_FILE_NOT_FOUND:
        case MONO_ERROR_BAD_IMAGE:
                if (error->assembly_name) {
-                       msg = mono_string_new (domain, error->full_message);
+                       msg = string_new_cleanup (domain, error->full_message);
                        if (!msg) {
                                mono_error_set_out_of_memory (error_out, "Could not allocate message");
                                break;
                        }
 
                        if (error->assembly_name) {
-                               assembly_name = mono_string_new (domain, error->assembly_name);
+                               assembly_name = string_new_cleanup (domain, error->assembly_name);
                                if (!assembly_name) {
                                        mono_error_set_out_of_memory (error_out, "Could not allocate assembly name");
                                        break;
@@ -730,6 +805,9 @@ mono_error_convert_to_exception (MonoError *target_error)
        MonoError error;
        MonoException *ex;
 
+       /* Mempool stored error shouldn't be cleaned up */
+       g_assert (!is_boxed ((MonoErrorInternal*)target_error));
+
        if (mono_error_ok (target_error))
                return NULL;
 
@@ -750,5 +828,95 @@ void
 mono_error_move (MonoError *dest, MonoError *src)
 {
        memcpy (dest, src, sizeof (MonoErrorInternal));
-       mono_error_init (src);
+       error_init (src);
+}
+
+/**
+ * mono_error_box:
+ * \param ierror The input error that will be boxed.
+ * \param image The mempool of this image will hold the boxed error.
+ * Creates a new boxed error in the given mempool from \c MonoError.
+ * It does not alter \p ierror, so you still have to clean it up with
+ * \c mono_error_cleanup or \c mono_error_convert_to_exception or another such function.
+ * \returns the boxed error, or NULL if the mempool could not allocate.
+ */
+MonoErrorBoxed*
+mono_error_box (const MonoError *ierror, MonoImage *image)
+{
+       MonoErrorInternal *from = (MonoErrorInternal*)ierror;
+       /* Don't know how to box a gchandle */
+       g_assert (!is_managed_exception (from));
+       MonoErrorBoxed* box = mono_image_alloc (image, sizeof (MonoErrorBoxed));
+       box->image = image;
+       mono_error_init_flags (&box->error, MONO_ERROR_MEMPOOL_BOXED);
+       MonoErrorInternal *to = (MonoErrorInternal*)&box->error;
+
+#define DUP_STR(field) do {                                            \
+               if (from->field) {                                      \
+                       if (!(to->field = mono_image_strdup (image, from->field))) \
+                               to->flags |= MONO_ERROR_INCOMPLETE;     \
+               } else {                                                \
+                       to->field = NULL;                               \
+               }                                                       \
+       } while (0)
+
+       to->error_code = from->error_code;
+       DUP_STR (type_name);
+       DUP_STR (assembly_name);
+       DUP_STR (member_name);
+       DUP_STR (exception_name_space);
+       DUP_STR (exception_name);
+       DUP_STR (full_message);
+       DUP_STR (full_message_with_fields);
+       DUP_STR (first_argument);
+       to->exn.klass = from->exn.klass;
+
+#undef DUP_STR
+       
+       return box;
+}
+
+
+/**
+ * mono_error_set_from_boxed:
+ * \param oerror The error that will be set to the contents of the box.
+ * \param box A mempool-allocated error.
+ * Sets the error condition in the oerror from the contents of the
+ * given boxed error.  Does not alter the boxed error, so it can be
+ * used in a future call to \c mono_error_set_from_boxed as needed.  The
+ * \p oerror should've been previously initialized with \c mono_error_init,
+ * as usual.
+ * \returns TRUE on success or FALSE on failure.
+ */
+gboolean
+mono_error_set_from_boxed (MonoError *oerror, const MonoErrorBoxed *box)
+{
+       MonoErrorInternal* to = (MonoErrorInternal*)oerror;
+       MonoErrorInternal* from = (MonoErrorInternal*)&box->error;
+       g_assert (!is_managed_exception (from));
+
+       mono_error_prepare (to);
+       to->flags |= MONO_ERROR_FREE_STRINGS;
+#define DUP_STR(field) do {                                            \
+               if (from->field) {                                      \
+                       if (!(to->field = g_strdup (from->field)))      \
+                               to->flags |= MONO_ERROR_INCOMPLETE;     \
+               } else {                                                \
+                       to->field = NULL;                               \
+               }                                                       \
+       } while (0)
+
+       to->error_code = from->error_code;
+       DUP_STR (type_name);
+       DUP_STR (assembly_name);
+       DUP_STR (member_name);
+       DUP_STR (exception_name_space);
+       DUP_STR (exception_name);
+       DUP_STR (full_message);
+       DUP_STR (full_message_with_fields);
+       DUP_STR (first_argument);
+       to->exn.klass = from->exn.klass;
+                 
+#undef DUP_STR
+       return (to->flags & MONO_ERROR_INCOMPLETE) == 0 ;
 }