[runtime] Add more system assemblies remapping
[mono.git] / mono / utils / mono-error.c
index c37fc3d16cb8518dddfc115c9bf89c9512bbd599..55f84ffd8e8f1de87aab2c64464ebab9d3955ea6 100644 (file)
@@ -4,35 +4,51 @@
  * Authors:
  *     Rodrigo Kumpera    (rkumpera@novell.com)
  * Copyright 2009 Novell, Inc (http://www.novell.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 #include <glib.h>
 
+#include <config.h>
 #include "mono-error.h"
 #include "mono-error-internals.h"
 
 #include <mono/metadata/exception.h>
-#include <mono/metadata/class-internals.h>
+#include <mono/metadata/exception-internals.h>
 #include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/object.h>
 #include <mono/metadata/object-internals.h>
 
+#define set_error_messagev() do { \
+       if (!(error->full_message = g_strdup_vprintf (msg_format, args))) \
+                       error->flags |= MONO_ERROR_INCOMPLETE; \
+} while (0)
+
 #define set_error_message() do { \
        va_list args; \
        va_start (args, msg_format); \
-       if (!(error->full_message = g_strdup_vprintf (msg_format, args))) \
-                       error->flags |= MONO_ERROR_INCOMPLETE; \
+       set_error_messagev();        \
        va_end (args); \
 } while (0)
 
+static void
+mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args);
+
 static gboolean
 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)
 {
+       /* mono_error_set_* after a mono_error_cleanup without an intervening init */
+       g_assert (error->error_code != MONO_ERROR_CLEANUP_CALLED_SENTINEL);
        if (error->error_code != MONO_ERROR_NONE)
                return;
 
@@ -83,6 +99,13 @@ mono_error_init_flags (MonoError *oerror, unsigned short flags)
        error->flags = 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.
+ */
 void
 mono_error_init (MonoError *error)
 {
@@ -93,15 +116,32 @@ void
 mono_error_cleanup (MonoError *oerror)
 {
        MonoErrorInternal *error = (MonoErrorInternal*)oerror;
-       if (error->error_code == MONO_ERROR_NONE)
+       short int orig_error_code = error->error_code;
+       gboolean free_strings = error->flags & MONO_ERROR_FREE_STRINGS;
+       gboolean has_instance_handle = is_managed_exception (error);
+
+       /* 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;
+       error->flags = 0;
+
+       if (orig_error_code == MONO_ERROR_NONE)
                return;
 
-       if (is_managed_exception (error))
+
+       if (has_instance_handle)
                mono_gchandle_free (error->exn.instance_handle);
 
+
        g_free ((char*)error->full_message);
        g_free ((char*)error->full_message_with_fields);
-       if (!(error->flags & MONO_ERROR_FREE_STRINGS)) //no memory was allocated
+       error->full_message = NULL;
+       error->full_message_with_fields = NULL;
+       if (!free_strings) //no memory was allocated
                return;
 
        g_free ((char*)error->type_name);
@@ -110,6 +150,9 @@ mono_error_cleanup (MonoError *oerror)
        g_free ((char*)error->exception_name_space);
        g_free ((char*)error->exception_name);
        g_free ((char*)error->first_argument);
+       error->type_name = error->assembly_name = error->member_name = error->exception_name_space = error->exception_name = error->first_argument = NULL;
+       error->exn.klass = NULL;
+
 }
 
 gboolean
@@ -254,18 +297,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 ();
 }
 
 /*
@@ -332,130 +384,110 @@ mono_error_set_bad_image (MonoError *oerror, MonoImage *image, const char *msg_f
 }
 
 void
-mono_error_set_generic_error (MonoError *oerror, const char * name_space, const char *name, const char *msg_format, ...)
+mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args)
 {
        MonoErrorInternal *error = (MonoErrorInternal*)oerror;
        mono_error_prepare (error);
 
        error->error_code = MONO_ERROR_GENERIC;
        mono_error_set_corlib_exception (oerror, name_space, name);
-       set_error_message ();
+       set_error_messagev ();
 }
 
 void
-mono_error_set_exception_instance (MonoError *oerror, MonoException *exc)
+mono_error_set_generic_error (MonoError *oerror, const char * name_space, const char *name, const char *msg_format, ...)
 {
-       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
-
-       mono_error_prepare (error);
-       error->error_code = MONO_ERROR_EXCEPTION_INSTANCE;
-       error->exn.instance_handle = mono_gchandle_new (exc ? &exc->object : NULL, FALSE);
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, name_space, name, msg_format, args);
+       va_end (args);
 }
 
+/**
+ * mono_error_set_not_implemented:
+ *
+ * System.NotImplementedException
+ */
 void
-mono_error_set_from_loader_error (MonoError *oerror)
+mono_error_set_not_implemented (MonoError *oerror, const char *msg_format, ...)
 {
-       MonoLoaderError *loader_error = mono_loader_get_last_error ();
-       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
-       gboolean dup_strings = TRUE;
-
-       mono_error_prepare (error);
-
-       if (!loader_error) {
-               mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from an empty loader-error");
-               return;
-       }
-
-       switch (loader_error->exception_type) {
-       case MONO_EXCEPTION_NONE:
-               mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from a non-error loader-error");
-               break;
-
-       case MONO_EXCEPTION_INVALID_PROGRAM:
-               mono_error_set_generic_error (oerror, "System", "InvalidProgramException", "Failed for unknown reasons.");
-               break;
-
-       case MONO_EXCEPTION_UNVERIFIABLE_IL:
-               mono_error_set_generic_error (oerror, "System.Security", "VerificationException", "Failed for unknown reasons.");
-               break;
-
-       case MONO_EXCEPTION_MISSING_METHOD:
-               error->error_code = MONO_ERROR_MISSING_METHOD;
-               mono_error_set_type_name (oerror, loader_error->class_name);
-               mono_error_set_member_name (oerror, loader_error->member_name);
-               error->full_message = g_strdup ("Failed for unknown reasons.");
-               break;
-
-       case MONO_EXCEPTION_MISSING_FIELD:
-               mono_error_set_field_load (oerror, loader_error->klass, loader_error->member_name, "Failed for unknown reasons.");
-               break;
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, "System", "NotImplementedException", msg_format, args);
+       va_end (args);
+}
 
-       case MONO_EXCEPTION_TYPE_LOAD:
-               mono_error_set_type_load_name (oerror, g_strdup (loader_error->class_name), g_strdup (loader_error->assembly_name), "Failed for unknown reasons.");
-               dup_strings = FALSE;
-               break;
-       
-       case MONO_EXCEPTION_FILE_NOT_FOUND:
-               mono_error_set_assembly_load_simple (oerror, loader_error->assembly_name, loader_error->ref_only);
-               break;
+/**
+ * mono_error_set_execution_engine:
+ *
+ * System.ExecutionEngineException
+ */
+void
+mono_error_set_execution_engine (MonoError *oerror, const char *msg_format, ...)
+{
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, "System", "ExecutionEngineException", msg_format, args);
+       va_end (args);
+}
 
-       case MONO_EXCEPTION_METHOD_ACCESS:
-               mono_error_set_generic_error (oerror, "System", "MethodAccessException", "Failed for unknown reasons.");
-               break;
+/**
+ * mono_error_set_not_supported:
+ *
+ * System.NotSupportedException
+ */
+void
+mono_error_set_not_supported (MonoError *oerror, const char *msg_format, ...)
+{
+       va_list args;
+       va_start (args, msg_format);
+       mono_error_set_generic_errorv (oerror, "System", "NotSupportedException", msg_format, args);
+       va_end (args);
+}
 
-       case MONO_EXCEPTION_FIELD_ACCESS:
-               mono_error_set_generic_error (oerror, "System", "FieldAccessException", "Failed for unknown reasons.");
-               break;
+/**
+ * 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);
+}
 
-       case MONO_EXCEPTION_OBJECT_SUPPLIED:
-       case MONO_EXCEPTION_GENERIC_SHARING_FAILED:
-               mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce a mono-error from JIT internal error %d", loader_error->exception_type);
-               break;
+void
+mono_error_set_invalid_program (MonoError *oerror, const char *msg_format, ...)
+{
+       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
 
-       case MONO_EXCEPTION_BAD_IMAGE:
-               mono_error_set_bad_image_name (oerror, "<unknown>", "%s", loader_error->msg);
-               break;
+       mono_error_prepare (error);
+       error->error_code = MONO_ERROR_INVALID_PROGRAM;
 
-       case MONO_EXCEPTION_OUT_OF_MEMORY:
-               mono_error_set_out_of_memory (oerror, "Failed for unknown reasons.");
-               break;
+       set_error_message ();
+}
 
-       default:
-               mono_error_set_generic_error (oerror, "System", "ExecutionEngineException", "Runtime tried to produce an unknown loader-error %d", loader_error->exception_type);
-               break;
-       }
+void
+mono_error_set_exception_instance (MonoError *oerror, MonoException *exc)
+{
+       MonoErrorInternal *error = (MonoErrorInternal*)oerror;
 
-       mono_error_dup_strings (oerror, dup_strings);
-       mono_loader_clear_error ();
+       mono_error_prepare (error);
+       error->error_code = MONO_ERROR_EXCEPTION_INSTANCE;
+       error->exn.instance_handle = mono_gchandle_new (exc ? &exc->object : NULL, FALSE);
 }
 
 void
-mono_loader_set_error_from_mono_error (MonoError *oerror)
+mono_error_set_exception_handle (MonoError *oerror, MonoExceptionHandle exc)
 {
        MonoErrorInternal *error = (MonoErrorInternal*)oerror;
 
-       switch (error->error_code) {
-       case MONO_ERROR_MISSING_METHOD:
-               mono_loader_set_error_method_load (get_type_name (error), error->member_name);
-               break;
-       case MONO_ERROR_MISSING_FIELD:
-               mono_loader_set_error_field_load (error->exn.klass, error->member_name);
-               break;
-       case MONO_ERROR_TYPE_LOAD:
-               mono_loader_set_error_type_load (get_type_name (error), get_assembly_name (error));
-               break;
-       case MONO_ERROR_FILE_NOT_FOUND:
-               /* XXX can't recover if it's ref only or not */
-               mono_loader_set_error_assembly_load (get_assembly_name (error), FALSE);
-               break;
-       case MONO_ERROR_BAD_IMAGE:
-               mono_loader_set_error_bad_image (g_strdup (error->full_message));
-               break;
-       case MONO_ERROR_EXCEPTION_INSTANCE:
-               mono_loader_set_error_bad_image (g_strdup_printf ("Non translatable error"));
-       default:
-               mono_loader_set_error_bad_image (g_strdup_printf ("Non translatable error: %s", error->full_message));
-       }
+       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
@@ -570,7 +602,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                                break;
                        }
 
-                       exception = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "MissingMethodException", type_name, method_name);
+                       exception = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "MissingMethodException", type_name, method_name, error_out);
                        if (exception)
                                set_message_on_exception (exception, error, error_out);
                } else {
@@ -590,7 +622,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                                break;
                        }
                        
-                       exception = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "MissingFieldException", type_name, field_name);
+                       exception = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "MissingFieldException", type_name, field_name, error_out);
                        if (exception)
                                set_message_on_exception (exception, error, error_out);
                } else {
@@ -599,7 +631,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                break;
 
        case MONO_ERROR_TYPE_LOAD:
-               if (error->type_name && error->assembly_name) {
+               if ((error->type_name && error->assembly_name) || error->exn.klass) {
                        type_name = get_type_name_as_mono_string (error, domain, error_out);
                        if (!mono_error_ok (error_out))
                                break;
@@ -612,8 +644,8 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                                }
                        }
 
-                       exception = mono_exception_from_name_two_strings (mono_get_corlib (), "System", "TypeLoadException", type_name, assembly_name);
-                       if (exception)
+                       exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System", "TypeLoadException", type_name, assembly_name, error_out);
+                       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);
@@ -638,9 +670,9 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                        }
 
                        if (error->error_code == MONO_ERROR_FILE_NOT_FOUND)
-                               exception = mono_exception_from_name_two_strings (mono_get_corlib (), "System.IO", "FileNotFoundException", msg, assembly_name);
+                               exception = mono_exception_from_name_two_strings_checked (mono_get_corlib (), "System.IO", "FileNotFoundException", msg, assembly_name, error_out);
                        else
-                               exception = mono_exception_from_name_two_strings (mono_defaults.corlib, "System", "BadImageFormatException", msg, assembly_name);
+                               exception = mono_exception_from_name_two_strings_checked (mono_defaults.corlib, "System", "BadImageFormatException", msg, assembly_name, error_out);
                } else {
                        if (error->error_code == MONO_ERROR_FILE_NOT_FOUND)
                                exception = mono_exception_from_name_msg (mono_get_corlib (), "System.IO", "FileNotFoundException", error->full_message);
@@ -683,7 +715,7 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
        }
        case MONO_ERROR_GENERIC:
                if (!error->exception_name_space || !error->exception_name)
-                       mono_error_set_generic_error (error_out, "System", "ExecutionEngineException", "MonoError with generic error but no exception name was supplied");
+                       mono_error_set_execution_engine (error_out, "MonoError with generic error but no exception name was supplied");
                else
                        exception = mono_exception_from_name_msg (mono_defaults.corlib, error->exception_name_space, error->exception_name, error->full_message);
                break;
@@ -692,8 +724,19 @@ mono_error_prepare_exception (MonoError *oerror, MonoError *error_out)
                exception = (MonoException*) mono_gchandle_get_target (error->exn.instance_handle);
                break;
 
+       case MONO_ERROR_CLEANUP_CALLED_SENTINEL:
+               mono_error_set_execution_engine (error_out, "MonoError reused after mono_error_cleanup");
+               break;
+
+       case MONO_ERROR_INVALID_PROGRAM: {
+               gboolean lacks_message = error->flags & MONO_ERROR_INCOMPLETE;
+               if (lacks_message)
+                       return mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", "");
+               else
+                       return mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", error->full_message);
+       }
        default:
-               mono_error_set_generic_error (error_out, "System", "ExecutionEngineException", "Invalid error-code %d", error->error_code);
+               mono_error_set_execution_engine (error_out, "Invalid error-code %d", error->error_code);
        }
 
        if (!mono_error_ok (error_out))
@@ -714,6 +757,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;
 
@@ -729,3 +775,104 @@ mono_error_convert_to_exception (MonoError *target_error)
        mono_error_cleanup (target_error);
        return ex;
 }
+
+void
+mono_error_move (MonoError *dest, MonoError *src)
+{
+       memcpy (dest, src, sizeof (MonoErrorInternal));
+       mono_error_init (src);
+}
+
+/**
+ * mono_error_box:
+ * @ierror: The input error that will be boxed.
+ * @image: The mempool of this image will hold the boxed error.
+ *
+ * Creates a new boxed error in the given mempool from MonoError.
+ * It does not alter ierror, so you still have to clean it up with
+ * mono_error_cleanup or 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:
+ * @oerror: The error that will be set to the contents of the box.
+ * @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 mono_error_set_from_boxed as needed.  The
+ * oerror should've been previously initialized with 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 ;
+}