2006-05-31 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / loader.c
index 6d2a4165fcc36aac9ffe4666001fa367a7e05a3b..b8d5a0d6e51f11b0fc961a99e861ad47b35c277a 100644 (file)
@@ -34,6 +34,7 @@
 #include <mono/metadata/debug-helpers.h>
 #include <mono/metadata/reflection.h>
 #include <mono/utils/mono-logger.h>
+#include <mono/metadata/exception.h>
 
 MonoDefaults mono_defaults;
 
@@ -57,6 +58,14 @@ mono_loader_init ()
        loader_error_thread_id = TlsAlloc ();
 }
 
+void
+mono_loader_cleanup (void)
+{
+       TlsFree (loader_error_thread_id);
+
+       DeleteCriticalSection (&loader_mutex);
+}
+
 /*
  * Handling of type load errors should be done as follows:
  *
@@ -75,27 +84,24 @@ set_loader_error (MonoLoaderError *error)
        TlsSetValue (loader_error_thread_id, error);
 }
 
-/*
+
+/**
  * mono_loader_set_error_type_load:
  *
- *   Set the loader error for this thread. CLASS_NAME and ASSEMBLY_NAME should be
- * dynamically allocated strings whose ownership is passed to this function.
+ * Set the loader error for this thread. 
  */
 void
-mono_loader_set_error_type_load (char *class_name, char *assembly_name)
+mono_loader_set_error_type_load (const char *class_name, const char *assembly_name)
 {
        MonoLoaderError *error;
 
-       if (mono_loader_get_last_error ()) {
-               g_free (class_name);
-               g_free (assembly_name);
+       if (mono_loader_get_last_error ()) 
                return;
-       }
 
        error = g_new0 (MonoLoaderError, 1);
        error->kind = MONO_LOADER_ERROR_TYPE;
-       error->class_name = class_name;
-       error->assembly_name = assembly_name;
+       error->class_name = g_strdup (class_name);
+       error->assembly_name = g_strdup (assembly_name);
 
        /* 
         * This is not strictly needed, but some (most) of the loader code still
@@ -114,7 +120,7 @@ mono_loader_set_error_type_load (char *class_name, char *assembly_name)
  * inside metadata.
  */
 void
-mono_loader_set_error_method_load (MonoClass *klass, const char *member_name)
+mono_loader_set_error_method_load (const char *class_name, const char *member_name)
 {
        MonoLoaderError *error;
 
@@ -124,7 +130,7 @@ mono_loader_set_error_method_load (MonoClass *klass, const char *member_name)
 
        error = g_new0 (MonoLoaderError, 1);
        error->kind = MONO_LOADER_ERROR_METHOD;
-       error->klass = klass;
+       error->class_name = g_strdup (class_name);
        error->member_name = member_name;
 
        set_loader_error (error);
@@ -133,7 +139,7 @@ mono_loader_set_error_method_load (MonoClass *klass, const char *member_name)
 /*
  * mono_loader_set_error_field_load:
  *
- *   Set the loader error for this thread. MEMBER_NAME should point to a string
+ * Set the loader error for this thread. MEMBER_NAME should point to a string
  * inside metadata.
  */
 void
@@ -165,20 +171,85 @@ mono_loader_get_last_error (void)
        return (MonoLoaderError*)TlsGetValue (loader_error_thread_id);
 }
 
+/**
+ * mono_loader_clear_error:
+ *
+ * Disposes any loader error messages on this thread
+ */
 void
 mono_loader_clear_error (void)
 {
        MonoLoaderError *ex = (MonoLoaderError*)TlsGetValue (loader_error_thread_id);
 
-       g_assert (ex);  
-
-       g_free (ex->class_name);
-       g_free (ex->assembly_name);
+        g_free (ex->class_name);
+        g_free (ex->assembly_name);
        g_free (ex);
-
+       
        TlsSetValue (loader_error_thread_id, NULL);
 }
 
+/**
+ * mono_loader_error_prepare_exception:
+ * @error: The MonoLoaderError to turn into an exception
+ *
+ * This turns a MonoLoaderError into an exception that can be thrown
+ * and resets the Mono Loader Error state during this process.
+ *
+ */
+MonoException *
+mono_loader_error_prepare_exception (MonoLoaderError *error)
+{
+        MonoException *ex = NULL;
+
+        switch (error->kind) {
+        case MONO_LOADER_ERROR_TYPE: {
+               char *cname = g_strdup (error->class_name);
+               char *aname = g_strdup (error->assembly_name);
+               MonoString *class_name;
+               
+               mono_loader_clear_error ();
+               
+               class_name = mono_string_new (mono_domain_get (), cname);
+
+                ex = mono_get_exception_type_load (class_name, aname);
+               g_free (cname);
+               g_free (aname);
+                break;
+        }
+        case MONO_LOADER_ERROR_METHOD: {
+               char *cname = g_strdup (error->class_name);
+               char *aname = g_strdup (error->member_name);
+               
+               mono_loader_clear_error ();
+               ex = mono_get_exception_missing_method (cname, aname);
+               g_free (cname);
+               g_free (aname);
+               break;
+       }
+               
+        case MONO_LOADER_ERROR_FIELD: {
+               char *cnspace = g_strdup (*error->klass->name_space ? error->klass->name_space : "");
+               char *cname = g_strdup (error->klass->name);
+               char *cmembername = g_strdup (error->member_name);
+                char *class_name;
+
+               mono_loader_clear_error ();
+               class_name = g_strdup_printf ("%s%s%s", cnspace, cnspace ? "." : "", cname);
+               
+               ex = mono_get_exception_missing_field (class_name, cmembername);
+                g_free (class_name);
+               g_free (cname);
+               g_free (cmembername);
+               g_free (cnspace);
+                break;
+        }
+        default:
+                g_assert_not_reached ();
+        }
+
+        return ex;
+}
+
 static MonoClassField*
 field_from_memberref (MonoImage *image, guint32 token, MonoClass **retklass,
                      MonoGenericContext *context)
@@ -324,14 +395,14 @@ mono_metadata_signature_vararg_match (MonoMethodSignature *sig1, MonoMethodSigna
 }
 
 static MonoMethod *
-find_method_in_class (MonoClass *klass, const char *name, const char *qname, const char *fqname,
-                     MonoMethodSignature *sig)
+find_method_in_class (MonoClass *in_class, const char *name, const char *qname, const char *fqname,
+                     MonoMethodSignature *sig, MonoClass *from_class)
 {
        int i;
 
-       mono_class_setup_methods (klass);
-       for (i = 0; i < klass->method.count; ++i) {
-               MonoMethod *m = klass->methods [i];
+       mono_class_setup_methods (in_class);
+       for (i = 0; i < in_class->method.count; ++i) {
+               MonoMethod *m = in_class->methods [i];
 
                if (!((fqname && !strcmp (m->name, fqname)) ||
                      (qname && !strcmp (m->name, qname)) || !strcmp (m->name, name)))
@@ -339,25 +410,30 @@ find_method_in_class (MonoClass *klass, const char *name, const char *qname, con
 
                if (sig->call_convention == MONO_CALL_VARARG) {
                        if (mono_metadata_signature_vararg_match (sig, mono_method_signature (m)))
-                               return m;
+                               break;
                } else {
                        if (mono_metadata_signature_equal (sig, mono_method_signature (m)))
-                               return m;
+                               break;
                }
        }
 
+       if (i < in_class->method.count) {
+               mono_class_setup_methods (from_class);
+               g_assert (from_class->method.count == in_class->method.count);
+               return from_class->methods [i];
+       }
        return NULL;
 }
 
 static MonoMethod *
-find_method (MonoClass *klass, MonoClass *ic, const char* name, MonoMethodSignature *sig)
+find_method (MonoClass *in_class, MonoClass *ic, const char* name, MonoMethodSignature *sig, MonoClass *from_class)
 {
        int i;
        char *qname, *fqname, *class_name;
        gboolean is_interface;
        MonoMethod *result = NULL;
 
-       is_interface = MONO_CLASS_IS_INTERFACE (klass);
+       is_interface = MONO_CLASS_IS_INTERFACE (in_class);
 
        if (ic) {
                class_name = mono_type_get_name_full (&ic->byval_arg, MONO_TYPE_NAME_FORMAT_IL);
@@ -370,27 +446,32 @@ find_method (MonoClass *klass, MonoClass *ic, const char* name, MonoMethodSignat
        } else
                class_name = qname = fqname = NULL;
 
-       while (klass) {
-               result = find_method_in_class (klass, name, qname, fqname, sig);
+       while (in_class) {
+               g_assert (from_class);
+               result = find_method_in_class (in_class, name, qname, fqname, sig, from_class);
                if (result)
                        goto out;
 
                if (name [0] == '.' && (!strcmp (name, ".ctor") || !strcmp (name, ".cctor")))
                        break;
 
-               for (i = 0; i < klass->interface_count; i++) {
-                       MonoClass *ic = klass->interfaces [i];
+               g_assert (from_class->interface_count == in_class->interface_count);
+               for (i = 0; i < in_class->interface_count; i++) {
+                       MonoClass *ic = in_class->interfaces [i];
+                       MonoClass *from_ic = from_class->interfaces [i];
 
-                       result = find_method_in_class (ic, name, qname, fqname, sig);
+                       result = find_method_in_class (ic, name, qname, fqname, sig, from_ic);
                        if (result)
                                goto out;
                }
 
-               klass = klass->parent;
+               in_class = in_class->parent;
+               from_class = from_class->parent;
        }
+       g_assert (!in_class == !from_class);
 
        if (is_interface)
-               result = find_method_in_class (mono_defaults.object_class, name, qname, fqname, sig);
+               result = find_method_in_class (mono_defaults.object_class, name, qname, fqname, sig, mono_defaults.object_class);
 
  out:
        g_free (class_name);
@@ -538,6 +619,7 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
                if (!klass) {
                        char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_REF | nindex);
                        g_warning ("Missing method %s in assembly %s, type %s", mname, image->name, name);
+                       mono_loader_set_error_method_load (name, mname);
                        g_free (name);
                        return NULL;
                }
@@ -550,6 +632,7 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
                if (!klass) {
                        char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_SPEC | nindex);
                        g_warning ("Missing method %s in assembly %s, type %s", mname, image->name, name);
+                       mono_loader_set_error_method_load (name, mname);
                        g_free (name);
                        return NULL;
                }
@@ -559,15 +642,22 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
                if (!klass) {
                        char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_DEF | nindex);
                        g_warning ("Missing method %s in assembly %s, type %s", mname, image->name, name);
+                       mono_loader_set_error_method_load (name, mname);
                        g_free (name);
                        return NULL;
                }
                break;
        case MONO_MEMBERREF_PARENT_METHODDEF:
                return mono_get_method (image, MONO_TOKEN_METHOD_DEF | nindex, NULL);
+               
        default:
-               g_error ("Memberref parent unknown: class: %d, index %d", class, nindex);
-               g_assert_not_reached ();
+               {
+                       /* This message leaks */
+                       char *message = g_strdup_printf ("Memberref parent unknown: class: %d, index %d", class, nindex);
+                       mono_loader_set_error_method_load ("", message);
+                       return NULL;
+               }
+
        }
        g_assert (klass);
        mono_class_init (klass);
@@ -576,11 +666,13 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
        mono_metadata_decode_blob_size (ptr, &ptr);
 
        sig = mono_metadata_parse_method_signature (image, 0, ptr, NULL);
+       if (sig == NULL)
+               return NULL;
 
        switch (class) {
        case MONO_MEMBERREF_PARENT_TYPEREF:
        case MONO_MEMBERREF_PARENT_TYPEDEF:
-               method = find_method (klass, NULL, mname, sig);
+               method = find_method (klass, NULL, mname, sig, klass);
                break;
 
        case MONO_MEMBERREF_PARENT_TYPESPEC: {
@@ -591,18 +683,14 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
 
                if (type->type != MONO_TYPE_ARRAY && type->type != MONO_TYPE_SZARRAY) {
                        MonoClass *in_class = klass->generic_class ? klass->generic_class->container_class : klass;
-                       method = find_method (in_class, NULL, mname, sig);
-                       if (method && klass->generic_class) {
-                               MonoClass *klass_hint = (in_class == method->klass) ? klass : NULL;
-                               method = mono_class_inflate_generic_method_full (method, klass_hint, klass->generic_class->context);
-                               method = mono_get_inflated_method (method);
-                       }
+                       method = find_method (in_class, NULL, mname, sig, klass);
                        break;
                }
 
                result = (MonoMethod *)g_new0 (MonoMethodPInvoke, 1);
                result->klass = klass;
                result->iflags = METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL;
+               result->flags = METHOD_ATTRIBUTE_PUBLIC;
                result->signature = sig;
                result->name = mname;
 
@@ -642,6 +730,7 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
 
        if (!method) {
                char *msig = mono_signature_get_desc (sig, FALSE);
+               char * class_name = mono_type_get_name (&klass->byval_arg);
                GString *s = g_string_new (mname);
                if (sig->generic_param_count)
                        g_string_append_printf (s, "<[%d]>", sig->generic_param_count);
@@ -651,9 +740,10 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp
 
                g_warning (
                        "Missing method %s::%s in assembly %s, referenced in assembly %s",
-                       mono_type_get_name (&klass->byval_arg), msig, klass->image->name, image->name);
+                       class_name, msig, klass->image->name, image->name);
+               mono_loader_set_error_method_load (class_name, mname);
                g_free (msig);
-               mono_loader_set_error_method_load (klass, mname);
+               g_free (class_name);
        }
        mono_metadata_free_method_signature (sig);
 
@@ -1168,13 +1258,15 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass,
 
        if ((cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
            (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))
-               result = (MonoMethod *)g_new0 (MonoMethodPInvoke, 1);
+               result = (MonoMethod *)mono_mempool_alloc0 (image->mempool, sizeof (MonoMethodPInvoke));
        else
-               result = (MonoMethod *)g_new0 (MonoMethodNormal, 1);
+               result = (MonoMethod *)mono_mempool_alloc0 (image->mempool, sizeof (MonoMethodNormal));
 
        if (!klass) {
                guint32 type = mono_metadata_typedef_from_method (image, token);
                klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | type);
+               if (klass == NULL)
+                       return NULL;
        }
 
        result->slot = -1;
@@ -1219,7 +1311,7 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass,
        }
 
        /* FIXME: lazyness for generics too, but how? */
-       if (!result->klass->dummy && !(result->flags & METHOD_ATTRIBUTE_ABSTRACT) &&
+       if (!(result->flags & METHOD_ATTRIBUTE_ABSTRACT) &&
            !(cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) &&
            !(result->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) && container) {
                gpointer loc = mono_image_rva_map (image, cols [0]);
@@ -1276,7 +1368,8 @@ mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constra
 {
        MonoMethod *method, *result;
        MonoClass *ic = NULL;
-       MonoGenericClass *gclass = NULL;
+       MonoGenericContext *class_context = NULL, *method_context = NULL;
+       MonoMethodSignature *sig;
 
        mono_loader_lock ();
 
@@ -1288,20 +1381,32 @@ mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constra
 
        mono_class_init (constrained_class);
        method = mono_get_inflated_method (method);
+       sig = mono_method_signature (method);
+
+       if (method->is_inflated && sig->generic_param_count) {
+               MonoMethodInflated *imethod = (MonoMethodInflated *) method;
+               sig = mono_method_signature (imethod->declaring);
+               method_context = imethod->context;
+       }
 
        if ((constrained_class != method->klass) && (method->klass->interface_id != 0))
                ic = method->klass;
 
        if (constrained_class->generic_class)
-               gclass = constrained_class->generic_class;
+               class_context = constrained_class->generic_class->context;
 
-       result = find_method (constrained_class, ic, method->name, mono_method_signature (method));
-       if (!result)
-               g_warning ("Missing method %s in assembly %s token %x", method->name,
-                          image->name, token);
+       result = find_method (constrained_class, ic, method->name, sig, constrained_class);
+       if (!result) {
+               g_warning ("Missing method %s.%s.%s in assembly %s token %x", method->klass->name_space,
+                          method->klass->name, method->name, image->name, token);
+               mono_loader_unlock ();
+               return NULL;
+       }
 
-       if (gclass)
-               result = mono_class_inflate_generic_method (result, gclass->context);
+       if (class_context)
+               result = mono_class_inflate_generic_method (result, class_context);
+       if (method_context)
+               result = mono_class_inflate_generic_method (result, method_context);
 
        mono_loader_unlock ();
        return result;
@@ -1316,25 +1421,26 @@ mono_free_method  (MonoMethod *method)
                 * locals are shared.
                 */
                /* mono_metadata_free_method_signature (method->signature); */
-               g_free (method->signature);
+               /* g_free (method->signature); */
        }
-
+       
        if (method->dynamic) {
                MonoMethodWrapper *mw = (MonoMethodWrapper*)method;
-
+               
                g_free ((char*)method->name);
                if (mw->method.header)
                        g_free ((char*)mw->method.header->code);
                g_free (mw->method_data);
        }
 
-       if (!(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && ((MonoMethodNormal *)method)->header) {
+       if (method->dynamic && !(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && ((MonoMethodNormal *)method)->header) {
                /* FIXME: Ditto */
                /* mono_metadata_free_mh (((MonoMethodNormal *)method)->header); */
                g_free (((MonoMethodNormal*)method)->header);
        }
 
-       g_free (method);
+       if (method->dynamic)
+               g_free (method);
 }
 
 void
@@ -1593,7 +1699,9 @@ mono_method_signature (MonoMethod *m)
        int size;
        MonoImage* img;
        const char *sig;
+       gboolean can_cache_signature;
        MonoGenericContainer *container;
+       int *pattrs;
 
        if (m->signature)
                return m->signature;
@@ -1620,14 +1728,36 @@ mono_method_signature (MonoMethod *m)
        img = m->klass->image;
 
        sig = mono_metadata_blob_heap (img, mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_SIGNATURE));
-       size = mono_metadata_decode_blob_size (sig, &sig);
 
        g_assert (!m->klass->generic_class);
        container = m->generic_container;
        if (!container)
                container = m->klass->generic_container;
 
-       m->signature = mono_metadata_parse_method_signature_full (img, container, idx, sig, NULL);
+       /* Generic signatures depend on the container so they cannot be cached */
+       /* icall/pinvoke signatures cannot be cached cause we modify them below */
+       can_cache_signature = !(m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && !(m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && !container;
+
+       /* If the method has parameter attributes, that can modify the signature */
+       pattrs = mono_metadata_get_param_attrs (img, idx);
+       if (pattrs) {
+               can_cache_signature = FALSE;
+               g_free (pattrs);
+       }
+
+       if (can_cache_signature)
+               m->signature = g_hash_table_lookup (img->method_signatures, sig);
+
+       if (!m->signature) {
+               const char *sig_body;
+
+               size = mono_metadata_decode_blob_size (sig, &sig_body);
+
+               m->signature = mono_metadata_parse_method_signature_full (img, container, idx, sig_body, NULL);
+
+               if (can_cache_signature)
+                       g_hash_table_insert (img->method_signatures, (gpointer)sig, m->signature);
+       }
 
        /* Verify metadata consistency */
        if (m->signature->generic_param_count) {
@@ -1703,7 +1833,7 @@ mono_method_get_header (MonoMethod *method)
        gpointer loc;
        MonoMethodNormal* mn = (MonoMethodNormal*) method;
 
-       if (method->klass->dummy || (method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
+       if ((method->flags & METHOD_ATTRIBUTE_ABSTRACT) || (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
                return NULL;
 
 #ifdef G_LIKELY