X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;ds=sidebyside;f=mono%2Fmetadata%2Floader.c;h=fa9db19419fbb778e8d1f401cd93f46c3e9eaf03;hb=8a764ef32a6dd6d465d916dd7166a0dcfd3d4a7b;hp=681ab5a828b88b098cba3072516cbf709cb4de17;hpb=5c697547f83f47ad44698ace4f1563e9ff1c1011;p=mono.git diff --git a/mono/metadata/loader.c b/mono/metadata/loader.c index 681ab5a828b..fa9db19419f 100644 --- a/mono/metadata/loader.c +++ b/mono/metadata/loader.c @@ -6,8 +6,8 @@ * Miguel de Icaza (miguel@ximian.com) * Patrik Torstensson (patrik.torstensson@labs2.com) * - * (C) 2001 Ximian, Inc. - * Copyright (C) 2002-2006 Novell, Inc. + * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com) + * Copyright 2004-2009 Novell, Inc (http://www.novell.com) * * This file is used by the interpreter and the JIT engine to locate * assemblies. Used to load AssemblyRef and later to resolve various @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -34,18 +33,27 @@ #include #include #include +#include +#include +#include #include #include -#include +#include +#include MonoDefaults mono_defaults; /* * This lock protects the hash tables inside MonoImage used by the metadata * loading functions in class.c and loader.c. + * + * See domain-internals.h for locking policy in combination with the + * domain lock. */ static CRITICAL_SECTION loader_mutex; +/* Statistics */ +static guint32 inflated_signatures_size; /* * This TLS variable contains the last type load error encountered by the loader. @@ -55,9 +63,18 @@ guint32 loader_error_thread_id; void mono_loader_init () { - InitializeCriticalSection (&loader_mutex); + static gboolean inited; + + if (!inited) { + InitializeCriticalSection (&loader_mutex); - loader_error_thread_id = TlsAlloc (); + loader_error_thread_id = TlsAlloc (); + + mono_counters_register ("Inflated signatures size", + MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &inflated_signatures_size); + + inited = TRUE; + } } void @@ -76,7 +93,7 @@ mono_loader_cleanup (void) * with the appropriate arguments, then return NULL to report the failure. The error * should be propagated until it reaches code which can throw managed exceptions. At that * point, an exception should be thrown based on the information returned by - * mono_loader_get_error (). Then the error should be cleared by calling + * mono_loader_get_last_error (). Then the error should be cleared by calling * mono_loader_clear_error (). */ @@ -100,7 +117,7 @@ mono_loader_set_error_assembly_load (const char *assembly_name, gboolean ref_onl return; error = g_new0 (MonoLoaderError, 1); - error->kind = MONO_LOADER_ERROR_ASSEMBLY; + error->exception_type = MONO_EXCEPTION_FILE_NOT_FOUND; error->assembly_name = g_strdup (assembly_name); error->ref_only = ref_only; @@ -131,7 +148,7 @@ mono_loader_set_error_type_load (const char *class_name, const char *assembly_na return; error = g_new0 (MonoLoaderError, 1); - error->kind = MONO_LOADER_ERROR_TYPE; + error->exception_type = MONO_EXCEPTION_TYPE_LOAD; error->class_name = g_strdup (class_name); error->assembly_name = g_strdup (assembly_name); @@ -161,7 +178,7 @@ mono_loader_set_error_method_load (const char *class_name, const char *member_na return; error = g_new0 (MonoLoaderError, 1); - error->kind = MONO_LOADER_ERROR_METHOD; + error->exception_type = MONO_EXCEPTION_MISSING_METHOD; error->class_name = g_strdup (class_name); error->member_name = member_name; @@ -184,13 +201,34 @@ mono_loader_set_error_field_load (MonoClass *klass, const char *member_name) return; error = g_new0 (MonoLoaderError, 1); - error->kind = MONO_LOADER_ERROR_FIELD; + error->exception_type = MONO_EXCEPTION_MISSING_FIELD; error->klass = klass; error->member_name = member_name; set_loader_error (error); } +/* + * mono_loader_set_error_bad_image: + * + * Set the loader error for this thread. + */ +void +mono_loader_set_error_bad_image (char *msg) +{ + MonoLoaderError *error; + + if (mono_loader_get_last_error ()) + return; + + error = g_new0 (MonoLoaderError, 1); + error->exception_type = MONO_EXCEPTION_BAD_IMAGE; + error->msg = msg; + + set_loader_error (error); +} + + /* * mono_loader_get_last_error: * @@ -216,6 +254,7 @@ mono_loader_clear_error (void) if (ex) { g_free (ex->class_name); g_free (ex->assembly_name); + g_free (ex->msg); g_free (ex); TlsSetValue (loader_error_thread_id, NULL); @@ -235,8 +274,8 @@ mono_loader_error_prepare_exception (MonoLoaderError *error) { MonoException *ex = NULL; - switch (error->kind) { - case MONO_LOADER_ERROR_TYPE: { + switch (error->exception_type) { + case MONO_EXCEPTION_TYPE_LOAD: { char *cname = g_strdup (error->class_name); char *aname = g_strdup (error->assembly_name); MonoString *class_name; @@ -245,12 +284,12 @@ mono_loader_error_prepare_exception (MonoLoaderError *error) class_name = mono_string_new (mono_domain_get (), cname); - ex = mono_get_exception_type_load (class_name, aname); + ex = mono_get_exception_type_load (class_name, aname); g_free (cname); g_free (aname); - break; + break; } - case MONO_LOADER_ERROR_METHOD: { + case MONO_EXCEPTION_MISSING_METHOD: { char *cname = g_strdup (error->class_name); char *aname = g_strdup (error->member_name); @@ -261,7 +300,7 @@ mono_loader_error_prepare_exception (MonoLoaderError *error) break; } - case MONO_LOADER_ERROR_FIELD: { + case MONO_EXCEPTION_MISSING_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); @@ -271,27 +310,38 @@ mono_loader_error_prepare_exception (MonoLoaderError *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 (class_name); g_free (cname); g_free (cmembername); g_free (cnspace); - break; + break; } - case MONO_LOADER_ERROR_ASSEMBLY: { + case MONO_EXCEPTION_FILE_NOT_FOUND: { char *msg; + char *filename; if (error->ref_only) msg = g_strdup_printf ("Cannot resolve dependency to assembly '%s' 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.", error->assembly_name); else msg = g_strdup_printf ("Could not load file or assembly '%s' or one of its dependencies.", error->assembly_name); + filename = g_strdup (error->assembly_name); + /* Has to call this before calling anything which might call mono_class_init () */ + mono_loader_clear_error (); + ex = mono_get_exception_file_not_found2 (msg, mono_string_new (mono_domain_get (), filename)); + g_free (msg); + g_free (filename); + break; + } + case MONO_EXCEPTION_BAD_IMAGE: { + char *msg = g_strdup (error->msg); mono_loader_clear_error (); - ex = mono_get_exception_file_not_found2 (msg, mono_string_new (mono_domain_get (), error->assembly_name)); + ex = mono_get_exception_bad_image_format (msg); g_free (msg); break; } - + default: g_assert_not_reached (); } @@ -299,25 +349,62 @@ mono_loader_error_prepare_exception (MonoLoaderError *error) return ex; } +/* + * find_cached_memberref_sig: + * + * Return a cached copy of the memberref signature identified by SIG_IDX. + * We use a gpointer since the cache stores both MonoTypes and MonoMethodSignatures. + * A cache is needed since the type/signature parsing routines allocate everything + * from a mempool, so without a cache, multiple requests for the same signature would + * lead to unbounded memory growth. For normal methods/fields this is not a problem + * since the resulting methods/fields are cached, but inflated methods/fields cannot + * be cached. + * LOCKING: Acquires the loader lock. + */ +static gpointer +find_cached_memberref_sig (MonoImage *image, guint32 sig_idx) +{ + gpointer res; + + mono_loader_lock (); + res = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (sig_idx)); + mono_loader_unlock (); + + return res; +} + +static gpointer +cache_memberref_sig (MonoImage *image, guint32 sig_idx, gpointer sig) +{ + gpointer prev_sig; + + mono_loader_lock (); + prev_sig = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (sig_idx)); + if (prev_sig) { + /* Somebody got in before us */ + sig = prev_sig; + } + else + g_hash_table_insert (image->memberref_signatures, GUINT_TO_POINTER (sig_idx), sig); + mono_loader_unlock (); + + return sig; +} + static MonoClassField* field_from_memberref (MonoImage *image, guint32 token, MonoClass **retklass, MonoGenericContext *context) { MonoClass *klass; - MonoClassField *field; + MonoClassField *field, *sig_field = NULL; MonoTableInfo *tables = image->tables; + MonoType *sig_type; guint32 cols[6]; guint32 nindex, class; const char *fname; const char *ptr; guint32 idx = mono_metadata_token_index (token); - if (image->dynamic) { - MonoClassField *result = mono_lookup_dynamic_token (image, token); - *retklass = result->parent; - return result; - } - mono_metadata_decode_row (&tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); nindex = cols [MONO_MEMBERREF_CLASS] >> MONO_MEMBERREF_PARENT_BITS; class = cols [MONO_MEMBERREF_CLASS] & MONO_MEMBERREF_PARENT_MASK; @@ -328,52 +415,57 @@ field_from_memberref (MonoImage *image, guint32 token, MonoClass **retklass, mono_metadata_decode_blob_size (ptr, &ptr); /* we may want to check the signature here... */ + if (*ptr++ != 0x6) { + mono_loader_set_error_bad_image (g_strdup_printf ("Bad field signature class token %08x field name %s token %08x", class, fname, token)); + return NULL; + } + /* FIXME: This needs a cache, especially for generic instances, since + * mono_metadata_parse_type () allocates everything from a mempool. + */ + sig_type = find_cached_memberref_sig (image, cols [MONO_MEMBERREF_SIGNATURE]); + if (!sig_type) { + sig_type = mono_metadata_parse_type (image, MONO_PARSE_TYPE, 0, ptr, &ptr); + sig_type = cache_memberref_sig (image, cols [MONO_MEMBERREF_SIGNATURE], sig_type); + } + switch (class) { case MONO_MEMBERREF_PARENT_TYPEDEF: klass = mono_class_get (image, MONO_TOKEN_TYPE_DEF | nindex); if (!klass) { - char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_REF | nindex); - g_warning ("Missing field %s in class %s (typeref index %d)", fname, name, nindex); + char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_DEF | nindex); + g_warning ("Missing field %s in class %s (typedef index %d)", fname, name, nindex); + mono_loader_set_error_type_load (name, image->assembly_name); g_free (name); return NULL; } mono_class_init (klass); if (retklass) *retklass = klass; - field = mono_class_get_field_from_name (klass, fname); + sig_field = field = mono_class_get_field_from_name (klass, fname); break; case MONO_MEMBERREF_PARENT_TYPEREF: klass = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | nindex); if (!klass) { char *name = mono_class_name_from_token (image, MONO_TOKEN_TYPE_REF | nindex); - g_warning ("Missing field %s in class %s (typeref index %d)", fname, name, nindex); + g_warning ("missing field %s in class %s (typeref index %d)", fname, name, nindex); + mono_loader_set_error_type_load (name, image->assembly_name); g_free (name); return NULL; } mono_class_init (klass); if (retklass) *retklass = klass; - field = mono_class_get_field_from_name (klass, fname); + sig_field = field = mono_class_get_field_from_name (klass, fname); break; case MONO_MEMBERREF_PARENT_TYPESPEC: { - /*guint32 bcols [MONO_TYPESPEC_SIZE]; - guint32 len; - MonoType *type; - - mono_metadata_decode_row (&tables [MONO_TABLE_TYPESPEC], nindex - 1, - bcols, MONO_TYPESPEC_SIZE); - ptr = mono_metadata_blob_heap (image, bcols [MONO_TYPESPEC_SIGNATURE]); - len = mono_metadata_decode_value (ptr, &ptr); - type = mono_metadata_parse_type (image, MONO_PARSE_TYPE, 0, ptr, &ptr); - - klass = mono_class_from_mono_type (type); - mono_class_init (klass); - g_print ("type in sig: %s\n", klass->name);*/ klass = mono_class_get_full (image, MONO_TOKEN_TYPE_SPEC | nindex, context); + //FIXME can't klass be null? mono_class_init (klass); if (retklass) *retklass = klass; field = mono_class_get_field_from_name (klass, fname); + if (field) + sig_field = mono_metadata_get_corresponding_field_from_generic_type_definition (field); break; } default: @@ -383,6 +475,10 @@ field_from_memberref (MonoImage *image, guint32 token, MonoClass **retklass, if (!field) mono_loader_set_error_field_load (klass, fname); + else if (sig_field && !mono_metadata_type_equal_full (sig_type, sig_field->type, TRUE)) { + mono_loader_set_error_field_load (klass, fname); + return NULL; + } return field; } @@ -396,7 +492,16 @@ mono_field_from_token (MonoImage *image, guint32 token, MonoClass **retklass, MonoClassField *field; if (image->dynamic) { - MonoClassField *result = mono_lookup_dynamic_token (image, token); + MonoClassField *result; + MonoClass *handle_class; + + *retklass = NULL; + result = mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context); + // This checks the memberref type as well + if (result && handle_class != mono_defaults.fieldhandle_class) { + mono_loader_set_error_bad_image (g_strdup ("Bad field token.")); + return NULL; + } *retklass = result->parent; return result; } @@ -425,7 +530,7 @@ mono_field_from_token (MonoImage *image, guint32 token, MonoClass **retklass, } mono_loader_lock (); - if (field && !field->parent->generic_class) + if (field && !field->parent->generic_class && !field->parent->generic_container) g_hash_table_insert (image->field_cache, GUINT_TO_POINTER (token), field); mono_loader_unlock (); return field; @@ -457,17 +562,42 @@ mono_metadata_signature_vararg_match (MonoMethodSignature *sig1, MonoMethodSigna } static MonoMethod * -find_method_in_class (MonoClass *in_class, const char *name, const char *qname, const char *fqname, +find_method_in_class (MonoClass *klass, const char *name, const char *qname, const char *fqname, MonoMethodSignature *sig, MonoClass *from_class) { - int i; + int i; + + /* Search directly in the metadata to avoid calling setup_methods () */ + + /* FIXME: !from_class->generic_class condition causes test failures. */ + if (klass->type_token && !klass->image->dynamic && !klass->methods && !klass->rank && klass == from_class && !from_class->generic_class) { + for (i = 0; i < klass->method.count; ++i) { + guint32 cols [MONO_METHOD_SIZE]; + MonoMethod *method; + const char *m_name; - mono_class_setup_methods (in_class); - for (i = 0; i < in_class->method.count; ++i) { - MonoMethod *m = in_class->methods [i]; + mono_metadata_decode_table_row (klass->image, MONO_TABLE_METHOD, klass->method.first + i, cols, MONO_METHOD_SIZE); + + m_name = mono_metadata_string_heap (klass->image, cols [MONO_METHOD_NAME]); + + if (!((fqname && !strcmp (m_name, fqname)) || + (qname && !strcmp (m_name, qname)) || + (name && !strcmp (m_name, name)))) + continue; + + method = mono_get_method (klass->image, MONO_TOKEN_METHOD_DEF | (klass->method.first + i + 1), klass); + if (method && (sig->call_convention != MONO_CALL_VARARG) && mono_metadata_signature_equal (sig, mono_method_signature (method))) + return method; + } + } + + mono_class_setup_methods (klass); + for (i = 0; i < klass->method.count; ++i) { + MonoMethod *m = klass->methods [i]; if (!((fqname && !strcmp (m->name, fqname)) || - (qname && !strcmp (m->name, qname)) || !strcmp (m->name, name))) + (qname && !strcmp (m->name, qname)) || + (name && !strcmp (m->name, name)))) continue; if (sig->call_convention == MONO_CALL_VARARG) { @@ -479,11 +609,8 @@ find_method_in_class (MonoClass *in_class, const char *name, const char *qname, } } - 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]; - } + if (i < klass->method.count) + return mono_class_get_method_by_index (from_class, i); return NULL; } @@ -517,12 +644,23 @@ find_method (MonoClass *in_class, MonoClass *ic, const char* name, MonoMethodSig if (name [0] == '.' && (!strcmp (name, ".ctor") || !strcmp (name, ".cctor"))) break; - 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]; + g_assert (from_class->interface_offsets_count == in_class->interface_offsets_count); + for (i = 0; i < in_class->interface_offsets_count; i++) { + MonoClass *in_ic = in_class->interfaces_packed [i]; + MonoClass *from_ic = from_class->interfaces_packed [i]; + char *ic_qname, *ic_fqname, *ic_class_name; + + ic_class_name = mono_type_get_name_full (&in_ic->byval_arg, MONO_TYPE_NAME_FORMAT_IL); + ic_qname = g_strconcat (ic_class_name, ".", name, NULL); + if (in_ic->name_space && in_ic->name_space [0]) + ic_fqname = g_strconcat (in_ic->name_space, ".", ic_class_name, ".", name, NULL); + else + ic_fqname = NULL; - result = find_method_in_class (ic, name, qname, fqname, sig, from_ic); + result = find_method_in_class (in_ic, ic ? name : NULL, ic_qname, ic_fqname, sig, from_ic); + g_free (ic_class_name); + g_free (ic_fqname); + g_free (ic_qname); if (result) goto out; } @@ -552,7 +690,9 @@ inflate_generic_signature (MonoImage *image, MonoMethodSignature *sig, MonoGener if (!context) return sig; - res = mono_metadata_signature_alloc (image, sig->param_count); + res = g_malloc0 (sizeof (MonoMethodSignature) + ((gint32)sig->param_count - MONO_ZERO_LEN_ARRAY) * sizeof (MonoType*)); + res->param_count = sig->param_count; + res->sentinelpos = -1; res->ret = mono_class_inflate_generic_type (sig->ret, context); is_open = mono_class_is_open_constructed_type (res->ret); for (i = 0; i < sig->param_count; ++i) { @@ -590,11 +730,9 @@ inflate_generic_header (MonoMethodHeader *header, MonoGenericContext *context) res->clauses = g_memdup (header->clauses, sizeof (MonoExceptionClause) * res->num_clauses); for (i = 0; i < header->num_clauses; ++i) { MonoExceptionClause *clause = &res->clauses [i]; - MonoType *t; if (clause->flags != MONO_EXCEPTION_CLAUSE_NONE) continue; - t = mono_class_inflate_generic_type (&clause->data.catch_class->byval_arg, context); - clause->data.catch_class = mono_class_from_mono_type (t); + clause->data.catch_class = mono_class_inflate_generic_class (clause->data.catch_class, context); } } return res; @@ -608,8 +746,9 @@ mono_method_get_signature_full (MonoMethod *method, MonoImage *image, guint32 to { int table = mono_metadata_token_table (token); int idx = mono_metadata_token_index (token); + int sig_idx; guint32 cols [MONO_MEMBERREF_SIZE]; - MonoMethodSignature *sig, *prev_sig; + MonoMethodSignature *sig; const char *ptr; /* !table is for wrappers: we should really assign their own token to them */ @@ -618,7 +757,6 @@ mono_method_get_signature_full (MonoMethod *method, MonoImage *image, guint32 to if (table == MONO_TABLE_METHODSPEC) { g_assert (!(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && - !(method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && mono_method_signature (method)); g_assert (method->is_inflated); @@ -632,29 +770,30 @@ mono_method_get_signature_full (MonoMethod *method, MonoImage *image, guint32 to /* FIXME: This might be incorrect for vararg methods */ return mono_method_signature (method); - mono_loader_lock (); - sig = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (token)); - mono_loader_unlock (); - if (!sig) { - mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); + mono_metadata_decode_row (&image->tables [MONO_TABLE_MEMBERREF], idx-1, cols, MONO_MEMBERREF_SIZE); + sig_idx = cols [MONO_MEMBERREF_SIGNATURE]; - ptr = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + sig = find_cached_memberref_sig (image, sig_idx); + if (!sig) { + ptr = mono_metadata_blob_heap (image, sig_idx); mono_metadata_decode_blob_size (ptr, &ptr); sig = mono_metadata_parse_method_signature (image, 0, ptr, NULL); - mono_loader_lock (); - prev_sig = g_hash_table_lookup (image->memberref_signatures, GUINT_TO_POINTER (token)); - if (prev_sig) { - /* Somebody got in before us */ - /* FIXME: Free sig */ - sig = prev_sig; - } - else - g_hash_table_insert (image->memberref_signatures, GUINT_TO_POINTER (token), sig); - mono_loader_unlock (); + sig = cache_memberref_sig (image, sig_idx, sig); } - sig = inflate_generic_signature (image, sig, context); + if (context) { + MonoMethodSignature *cached; + + /* This signature is not owned by a MonoMethod, so need to cache */ + sig = inflate_generic_signature (image, sig, context); + cached = mono_metadata_get_inflated_signature (sig, context); + if (cached != sig) + mono_metadata_free_inflated_signature (sig); + else + inflated_signatures_size += mono_metadata_signature_size (cached); + sig = cached; + } return sig; } @@ -666,10 +805,13 @@ mono_method_get_signature (MonoMethod *method, MonoImage *image, guint32 token) } /* this is only for the typespec array methods */ -static MonoMethod* -search_in_array_class (MonoClass *klass, const char *name, MonoMethodSignature *sig) +MonoMethod* +mono_method_search_in_array_class (MonoClass *klass, const char *name, MonoMethodSignature *sig) { int i; + + mono_class_setup_methods (klass); + for (i = 0; i < klass->method.count; ++i) { MonoMethod *method = klass->methods [i]; if (strcmp (method->name, name) == 0 && sig->param_count == method->signature->param_count) @@ -679,13 +821,14 @@ search_in_array_class (MonoClass *klass, const char *name, MonoMethodSignature * } static MonoMethod * -method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typespec_context) +method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typespec_context, + gboolean *used_context) { MonoClass *klass = NULL; MonoMethod *method = NULL; MonoTableInfo *tables = image->tables; guint32 cols[6]; - guint32 nindex, class; + guint32 nindex, class, sig_idx; const char *mname; MonoMethodSignature *sig; const char *ptr; @@ -698,13 +841,21 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp mname = mono_metadata_string_heap (image, cols [MONO_MEMBERREF_NAME]); + /* + * Whether we actually used the `typespec_context' or not. + * This is used to tell our caller whether or not it's safe to insert the returned + * method into a cache. + */ + if (used_context) + *used_context = class == MONO_MEMBERREF_PARENT_TYPESPEC; + switch (class) { case MONO_MEMBERREF_PARENT_TYPEREF: klass = mono_class_from_typeref (image, MONO_TOKEN_TYPE_REF | nindex); 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); + mono_loader_set_error_type_load (name, image->assembly_name); g_free (name); return NULL; } @@ -717,7 +868,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); + mono_loader_set_error_type_load (name, image->assembly_name); g_free (name); return NULL; } @@ -727,7 +878,7 @@ 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); + mono_loader_set_error_type_load (name, image->assembly_name); g_free (name); return NULL; } @@ -747,12 +898,19 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp g_assert (klass); mono_class_init (klass); - ptr = mono_metadata_blob_heap (image, cols [MONO_MEMBERREF_SIGNATURE]); + sig_idx = cols [MONO_MEMBERREF_SIGNATURE]; + + ptr = mono_metadata_blob_heap (image, sig_idx); mono_metadata_decode_blob_size (ptr, &ptr); - sig = mono_metadata_parse_method_signature (image, 0, ptr, NULL); - if (sig == NULL) - return NULL; + sig = find_cached_memberref_sig (image, sig_idx); + if (!sig) { + sig = mono_metadata_parse_method_signature (image, 0, ptr, NULL); + if (sig == NULL) + return NULL; + + sig = cache_memberref_sig (image, sig_idx, sig); + } switch (class) { case MONO_MEMBERREF_PARENT_TYPEREF: @@ -773,7 +931,7 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp } /* we're an array and we created these methods already in klass in mono_class_init () */ - result = search_in_array_class (klass, mname, sig); + result = mono_method_search_in_array_class (klass, mname, sig); if (result) return result; @@ -802,7 +960,6 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp g_free (msig); g_free (class_name); } - mono_metadata_free_method_signature (sig); return method; } @@ -810,11 +967,11 @@ method_from_memberref (MonoImage *image, guint32 idx, MonoGenericContext *typesp static MonoMethod * method_from_methodspec (MonoImage *image, MonoGenericContext *context, guint32 idx) { - MonoMethod *method, *inflated; + MonoMethod *method; + MonoClass *klass; MonoTableInfo *tables = image->tables; - MonoGenericContext *new_context = NULL; - MonoGenericMethod *gmethod; - MonoGenericContainer *container = NULL; + MonoGenericContext new_context; + MonoGenericInst *inst; const char *ptr; guint32 cols [MONO_METHODSPEC_SIZE]; guint32 token, nindex, param_count; @@ -830,115 +987,44 @@ method_from_methodspec (MonoImage *image, MonoGenericContext *context, guint32 i param_count = mono_metadata_decode_value (ptr, &ptr); g_assert (param_count); - /* - * Be careful with the two contexts here: - * - * ---------------------------------------- - * class Foo { - * static void Hello (S s, T t) { } - * - * static void Test (U u) { - * Foo.Hello (u, "World"); - * } - * } - * ---------------------------------------- - * - * Let's assume we're currently JITing Foo.Test - * (ie. `S' is instantiated as `int' and `U' is instantiated as `long'). - * - * The call to Hello() is encoded with a MethodSpec with a TypeSpec as parent - * (MONO_MEMBERREF_PARENT_TYPESPEC). - * - * The TypeSpec is encoded as `Foo', so we need to parse it in the current - * context (S=int, U=long) to get the correct `Foo'. - * - * After that, we parse the memberref signature in the new context - * (S=int, T=uninstantiated) and get the open generic method `Foo.Hello'. - * - */ + inst = mono_metadata_parse_generic_inst (image, NULL, param_count, ptr, &ptr); + if (context && inst->is_open) + inst = mono_metadata_inflate_generic_inst (inst, context); + if ((token & MONO_METHODDEFORREF_MASK) == MONO_METHODDEFORREF_METHODDEF) method = mono_get_method_full (image, MONO_TOKEN_METHOD_DEF | nindex, NULL, context); else - method = method_from_memberref (image, nindex, context); - - method = mono_get_inflated_method (method); - - container = method->generic_container; - g_assert (container); - - gmethod = g_new0 (MonoGenericMethod, 1); - if (method->klass->generic_class) - gmethod->class_inst = method->klass->generic_class->inst; - gmethod->container = container; - - new_context = g_new0 (MonoGenericContext, 1); - new_context->gmethod = gmethod; - /* FIXME: Is this correct? */ - if (container->parent) - new_context->class_inst = container->parent->context.class_inst; + method = method_from_memberref (image, nindex, context, NULL); - /* - * When parsing the methodspec signature, we're in the old context again: - * - * ---------------------------------------- - * class Foo { - * static void Hello (T t) { } - * - * static void Test (U u) { - * Foo.Hello (u); - * } - * } - * ---------------------------------------- - * - * Let's assume we're currently JITing "Foo.Test". - * - * In this case, we already parsed the memberref as "Foo.Hello" and the methodspec - * signature is "". This means that we must instantiate the method type parameter - * `T' from the new method with the method type parameter `U' from the current context; - * ie. instantiate the method as `Foo.Hello. - */ - - gmethod->inst = mono_metadata_parse_generic_inst (image, NULL, param_count, ptr, &ptr); - - if (context && gmethod->inst->is_open) - gmethod->inst = mono_metadata_inflate_generic_inst (gmethod->inst, context); + if (!method) + return NULL; - if (!container->method_hash) - container->method_hash = g_hash_table_new ( - (GHashFunc)mono_metadata_generic_method_hash, (GEqualFunc)mono_metadata_generic_method_equal); + klass = method->klass; - inflated = g_hash_table_lookup (container->method_hash, gmethod); - if (inflated) { - g_free (gmethod); - g_free (new_context); - return inflated; + if (klass->generic_class) { + g_assert (method->is_inflated); + method = ((MonoMethodInflated *) method)->declaring; } - context = new_context; - - mono_stats.generics_metadata_size += sizeof (MonoGenericMethod) + - sizeof (MonoGenericContext) + param_count * sizeof (MonoType); - - inflated = mono_class_inflate_generic_method_full (method, method->klass, new_context); - g_hash_table_insert (container->method_hash, gmethod, inflated); + new_context.class_inst = klass->generic_class ? klass->generic_class->context.class_inst : NULL; + new_context.method_inst = inst; - return inflated; + return mono_class_inflate_generic_method_full (method, klass, &new_context); } -typedef struct MonoDllMap MonoDllMap; - -struct MonoDllMap { - char *name; - char *target; +struct _MonoDllMap { char *dll; + char *target; + char *func; + char *target_func; MonoDllMap *next; }; -static GHashTable *global_dll_map; +static MonoDllMap *global_dll_map; static int -mono_dllmap_lookup_hash (GHashTable *dll_map, const char *dll, const char* func, const char **rdll, const char **rfunc) { - MonoDllMap *map, *tmp; +mono_dllmap_lookup_list (MonoDllMap *dll_map, const char *dll, const char* func, const char **rdll, const char **rfunc) { + int found = 0; *rdll = dll; @@ -947,25 +1033,33 @@ mono_dllmap_lookup_hash (GHashTable *dll_map, const char *dll, const char* func, mono_loader_lock (); - map = g_hash_table_lookup (dll_map, dll); - if (!map) { - mono_loader_unlock (); - return 0; - } - *rdll = map->target? map->target: dll; - - for (tmp = map->next; tmp; tmp = tmp->next) { - if (strcmp (func, tmp->name) == 0) { - *rfunc = tmp->name; - if (tmp->dll) - *rdll = tmp->dll; - mono_loader_unlock (); - return 1; + /* + * we use the first entry we find that matches, since entries from + * the config file are prepended to the list and we document that the + * later entries win. + */ + for (; dll_map; dll_map = dll_map->next) { + if (dll_map->dll [0] == 'i' && dll_map->dll [1] == ':') { + if (g_ascii_strcasecmp (dll_map->dll + 2, dll)) + continue; + } else if (strcmp (dll_map->dll, dll)) { + continue; + } + if (!found && dll_map->target) { + *rdll = dll_map->target; + found = 1; + /* we don't quit here, because we could find a full + * entry that matches also function and that has priority. + */ + } + if (dll_map->func && strcmp (dll_map->func, func) == 0) { + *rfunc = dll_map->target_func; + break; } } - *rfunc = func; + mono_loader_unlock (); - return 1; + return found; } static int @@ -973,47 +1067,46 @@ mono_dllmap_lookup (MonoImage *assembly, const char *dll, const char* func, cons { int res; if (assembly && assembly->dll_map) { - res = mono_dllmap_lookup_hash (assembly->dll_map, dll, func, rdll, rfunc); + res = mono_dllmap_lookup_list (assembly->dll_map, dll, func, rdll, rfunc); if (res) return res; } - return mono_dllmap_lookup_hash (global_dll_map, dll, func, rdll, rfunc); + return mono_dllmap_lookup_list (global_dll_map, dll, func, rdll, rfunc); } +/* + * mono_dllmap_insert: + * + * LOCKING: Acquires the loader lock. + * + * NOTE: This can be called before the runtime is initialized, for example from + * mono_config_parse (). + */ void -mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc) { - MonoDllMap *map, *entry; - GHashTable *dll_map = NULL; +mono_dllmap_insert (MonoImage *assembly, const char *dll, const char *func, const char *tdll, const char *tfunc) +{ + MonoDllMap *entry; + + mono_loader_init (); mono_loader_lock (); if (!assembly) { - if (!global_dll_map) - global_dll_map = g_hash_table_new (g_str_hash, g_str_equal); - dll_map = global_dll_map; + entry = g_malloc0 (sizeof (MonoDllMap)); + entry->dll = dll? g_strdup (dll): NULL; + entry->target = tdll? g_strdup (tdll): NULL; + entry->func = func? g_strdup (func): NULL; + entry->target_func = tfunc? g_strdup (tfunc): NULL; + entry->next = global_dll_map; + global_dll_map = entry; } else { - if (!assembly->dll_map) - assembly->dll_map = g_hash_table_new (g_str_hash, g_str_equal); - dll_map = assembly->dll_map; - } - - map = g_hash_table_lookup (dll_map, dll); - if (!map) { - map = g_new0 (MonoDllMap, 1); - map->dll = g_strdup (dll); - if (tdll) - map->target = g_strdup (tdll); - g_hash_table_insert (dll_map, map->dll, map); - } - if (func) { - entry = g_new0 (MonoDllMap, 1); - entry->name = g_strdup (func); - if (tfunc) - entry->target = g_strdup (tfunc); - if (tdll && map->target && strcmp (map->target, tdll)) - entry->dll = g_strdup (tdll); - entry->next = map->next; - map->next = entry; + entry = mono_image_alloc0 (assembly, sizeof (MonoDllMap)); + entry->dll = dll? mono_image_strdup (assembly, dll): NULL; + entry->target = tdll? mono_image_strdup (assembly, tdll): NULL; + entry->func = func? mono_image_strdup (assembly, func): NULL; + entry->target_func = tfunc? mono_image_strdup (assembly, tfunc): NULL; + entry->next = assembly->dll_map; + assembly->dll_map = entry; } mono_loader_unlock (); @@ -1309,20 +1402,12 @@ mono_lookup_pinvoke_call (MonoMethod *method, const char **exc_class, const char return piinfo->addr; } -MonoGenericMethod * -mono_get_shared_generic_method (MonoGenericContainer *container) -{ - MonoGenericMethod *gmethod = g_new0 (MonoGenericMethod, 1); - gmethod->container = container; - gmethod->class_inst = container->context.class_inst; - gmethod->inst = mono_get_shared_generic_inst (container); - - return gmethod; -} - +/* + * LOCKING: assumes the loader lock to be taken. + */ static MonoMethod * mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, - MonoGenericContext *context) + MonoGenericContext *context, gboolean *used_context) { MonoMethod *result; int table = mono_metadata_token_table (token); @@ -1330,30 +1415,46 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, MonoTableInfo *tables = image->tables; MonoGenericContainer *generic_container = NULL, *container = NULL; const char *sig = NULL; - int size, i; + int size; guint32 cols [MONO_TYPEDEF_SIZE]; - if (image->dynamic) - return mono_lookup_dynamic_token (image, token); + if (image->dynamic) { + MonoClass *handle_class; + + result = mono_lookup_dynamic_token_class (image, token, TRUE, &handle_class, context); + // This checks the memberref type as well + if (result && handle_class != mono_defaults.methodhandle_class) { + mono_loader_set_error_bad_image (g_strdup ("Bad method token.")); + return NULL; + } + return result; + } if (table != MONO_TABLE_METHOD) { - if (table == MONO_TABLE_METHODSPEC) + if (table == MONO_TABLE_METHODSPEC) { + if (used_context) *used_context = TRUE; return method_from_methodspec (image, context, idx); + } if (table != MONO_TABLE_MEMBERREF) g_print("got wrong token: 0x%08x\n", token); g_assert (table == MONO_TABLE_MEMBERREF); - result = method_from_memberref (image, idx, context); + return method_from_memberref (image, idx, context, used_context); + } - return result; + if (used_context) *used_context = FALSE; + + if (idx > image->tables [MONO_TABLE_METHOD].rows) { + mono_loader_set_error_bad_image (g_strdup ("Bad method token.")); + return NULL; } mono_metadata_decode_row (&image->tables [MONO_TABLE_METHOD], idx - 1, cols, 6); if ((cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) || (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) - result = (MonoMethod *)mono_mempool_alloc0 (image->mempool, sizeof (MonoMethodPInvoke)); + result = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethodPInvoke)); else - result = (MonoMethod *)mono_mempool_alloc0 (image->mempool, sizeof (MonoMethodNormal)); + result = (MonoMethod *)mono_image_alloc0 (image, sizeof (MonoMethodNormal)); mono_stats.method_count ++; @@ -1374,22 +1475,15 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, container = klass->generic_container; generic_container = mono_metadata_load_generic_params (image, token, container); if (generic_container) { - MonoGenericContext *context; - + result->is_generic = TRUE; generic_container->owner.method = result; - context = &generic_container->context; - if (container) - context->class_inst = container->context.class_inst; - context->gmethod = mono_get_shared_generic_method (generic_container); mono_metadata_load_generic_param_constraints (image, token, generic_container); - for (i = 0; i < generic_container->type_argc; i++) - mono_class_from_generic_parameter (&generic_container->type_params [i], image, TRUE); - container = generic_container; } + if (!sig) /* already taken from the methodref */ sig = mono_metadata_blob_heap (image, cols [4]); size = mono_metadata_decode_blob_size (sig, &sig); @@ -1397,24 +1491,24 @@ mono_get_method_from_token (MonoImage *image, guint32 token, MonoClass *klass, if (cols [1] & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) { if (result->klass == mono_defaults.string_class && !strcmp (result->name, ".ctor")) result->string_ctor = 1; - } else if ((cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) && (!(cols [1] & METHOD_IMPL_ATTRIBUTE_NATIVE))) { + } else if (cols [2] & METHOD_ATTRIBUTE_PINVOKE_IMPL) { MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)result; - MonoTableInfo *im = &tables [MONO_TABLE_IMPLMAP]; +#ifdef PLATFORM_WIN32 + /* IJW is P/Invoke with a predefined function pointer. */ + if (image->is_module_handle && (cols [1] & METHOD_IMPL_ATTRIBUTE_NATIVE)) { + piinfo->addr = mono_image_rva_map (image, cols [0]); + g_assert (piinfo->addr); + } +#endif piinfo->implmap_idx = mono_metadata_implmap_from_method (image, idx - 1); - piinfo->piflags = mono_metadata_decode_row_col (im, piinfo->implmap_idx - 1, MONO_IMPLMAP_FLAGS); - } - - /* FIXME: lazyness for generics too, but how? */ - 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]); - g_assert (loc); - ((MonoMethodNormal *) result)->header = mono_metadata_parse_mh_full (image, container, loc); + /* Native methods can have no map. */ + if (piinfo->implmap_idx) + piinfo->piflags = mono_metadata_decode_row_col (&tables [MONO_TABLE_IMPLMAP], piinfo->implmap_idx - 1, MONO_IMPLMAP_FLAGS); } - result->generic_container = generic_container; + if (generic_container) + mono_method_set_generic_container (result, generic_container); return result; } @@ -1425,27 +1519,60 @@ mono_get_method (MonoImage *image, guint32 token, MonoClass *klass) return mono_get_method_full (image, token, klass, NULL); } +static gpointer +get_method_token (gpointer value) +{ + MonoMethod *m = (MonoMethod*)value; + + return GUINT_TO_POINTER (m->token); +} + MonoMethod * mono_get_method_full (MonoImage *image, guint32 token, MonoClass *klass, MonoGenericContext *context) { MonoMethod *result; + gboolean used_context = FALSE; /* We do everything inside the lock to prevent creation races */ mono_loader_lock (); - if ((result = g_hash_table_lookup (image->method_cache, GINT_TO_POINTER (token)))) { + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) { + if (!image->method_cache) + image->method_cache = mono_value_hash_table_new (NULL, NULL, get_method_token); + result = mono_value_hash_table_lookup (image->method_cache, GINT_TO_POINTER (token)); + } else { + if (!image->methodref_cache) + image->methodref_cache = g_hash_table_new (NULL, NULL); + result = g_hash_table_lookup (image->methodref_cache, GINT_TO_POINTER (token)); + } + if (result) { mono_loader_unlock (); return result; } - result = mono_get_method_from_token (image, token, klass, context); + result = mono_get_method_from_token (image, token, klass, context, &used_context); //printf ("GET: %s\n", mono_method_full_name (result, TRUE)); - if (!(result && result->is_inflated)) - g_hash_table_insert (image->method_cache, GINT_TO_POINTER (token), result); +#if 0 + g_message (G_STRLOC ": %s - %d - %d", mono_method_full_name (result, TRUE), + result->is_inflated, used_context); +#endif + + /* + * `used_context' specifies whether or not mono_get_method_from_token() actually + * used the `context' to get the method. See bug #80969. + */ + + if (!used_context && !(result && result->is_inflated) && result) { + if (mono_metadata_token_table (token) == MONO_TABLE_METHOD) { + mono_value_hash_table_insert (image->method_cache, GINT_TO_POINTER (token), result); + } else { + g_hash_table_insert (image->methodref_cache, GINT_TO_POINTER (token), result); + } + } mono_loader_unlock (); @@ -1467,34 +1594,48 @@ mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constra { MonoMethod *method, *result; MonoClass *ic = NULL; - MonoGenericContext *class_context = NULL, *method_context = NULL; - MonoMethodSignature *sig; + MonoGenericContext *method_context = NULL; + MonoMethodSignature *sig, *original_sig; mono_loader_lock (); - *cil_method = mono_get_method_from_token (image, token, NULL, context); + *cil_method = mono_get_method_from_token (image, token, NULL, context, NULL); if (!*cil_method) { mono_loader_unlock (); return NULL; } mono_class_init (constrained_class); - method = mono_get_inflated_method (*cil_method); - sig = mono_method_signature (method); + method = *cil_method; + original_sig = 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 = mono_method_get_context (method); + + original_sig = sig; + /* + * We must inflate the signature with the class instantiation to work on + * cases where a class inherit from a generic type and the override replaces + * and type argument which a concrete type. See #325283. + */ + if (method_context->class_inst) { + MonoGenericContext ctx; + ctx.method_inst = NULL; + ctx.class_inst = method_context->class_inst; + + sig = inflate_generic_signature (method->klass->image, sig, &ctx); + } } - if ((constrained_class != method->klass) && (method->klass->interface_id != 0)) + if ((constrained_class != method->klass) && (MONO_CLASS_IS_INTERFACE (method->klass))) ic = method->klass; - if (constrained_class->generic_class) - class_context = mono_class_get_context (constrained_class); - result = find_method (constrained_class, ic, method->name, sig, constrained_class); + if (sig != original_sig) + mono_metadata_free_inflated_signature (sig); + 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); @@ -1502,8 +1643,6 @@ mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constra return NULL; } - if (class_context) - result = mono_class_inflate_generic_method (result, class_context); if (method_context) result = mono_class_inflate_generic_method (result, method_context); @@ -1514,6 +1653,10 @@ mono_get_method_constrained (MonoImage *image, guint32 token, MonoClass *constra void mono_free_method (MonoMethod *method) { + if (mono_profiler_get_events () & MONO_PROFILE_METHOD_EVENTS) + mono_profiler_method_free (method); + + /* FIXME: This hack will go away when the profiler will support freeing methods */ if (mono_profiler_get_events () != MONO_PROFILE_NONE) return; @@ -1528,38 +1671,47 @@ mono_free_method (MonoMethod *method) if (method->dynamic) { MonoMethodWrapper *mw = (MonoMethodWrapper*)method; - + int i; + + mono_marshal_free_dynamic_wrappers (method); + + mono_loader_lock (); + mono_property_hash_remove_object (method->klass->image->property_hash, method); + mono_loader_unlock (); + g_free ((char*)method->name); - if (mw->method.header) + if (mw->method.header) { g_free ((char*)mw->method.header->code); + for (i = 0; i < mw->method.header->num_locals; ++i) + g_free (mw->method.header->locals [i]); + g_free (mw->method.header->clauses); + g_free (mw->method.header); + } g_free (mw->method_data); - } - - 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); - } - - if (method->dynamic) + g_free (method->signature); g_free (method); + } } void mono_method_get_param_names (MonoMethod *method, const char **names) { int i, lastp; - MonoClass *klass = method->klass; + MonoClass *klass; MonoTableInfo *methodt; MonoTableInfo *paramt; guint32 idx; + if (method->is_inflated) + method = ((MonoMethodInflated *) method)->declaring; + if (!mono_method_signature (method)->param_count) return; for (i = 0; i < mono_method_signature (method)->param_count; ++i) names [i] = ""; - if (klass->generic_class || klass->rank) /* copy the names later */ + klass = method->klass; + if (klass->rank) return; mono_class_init (klass); @@ -1605,9 +1757,6 @@ mono_method_get_param_token (MonoMethod *method, int index) MonoTableInfo *methodt; guint32 idx; - if (klass->generic_class) - g_assert_not_reached (); - mono_class_init (klass); if (klass->image->dynamic) { @@ -1619,7 +1768,11 @@ mono_method_get_param_token (MonoMethod *method, int index) if (idx > 0) { guint param_index = mono_metadata_decode_row_col (methodt, idx - 1, MONO_METHOD_PARAMLIST); - return mono_metadata_make_token (MONO_TABLE_PARAM, param_index + index); + if (index == -1) + /* Return value */ + return mono_metadata_make_token (MONO_TABLE_PARAM, 0); + else + return mono_metadata_make_token (MONO_TABLE_PARAM, param_index + index); } return 0; @@ -1810,8 +1963,11 @@ mono_method_signature (MonoMethod *m) const char *sig; gboolean can_cache_signature; MonoGenericContainer *container; + MonoMethodSignature *signature = NULL; int *pattrs; + /* We need memory barriers below because of the double-checked locking pattern */ + if (m->signature) return m->signature; @@ -1824,10 +1980,14 @@ mono_method_signature (MonoMethod *m) if (m->is_inflated) { MonoMethodInflated *imethod = (MonoMethodInflated *) m; - MonoMethodSignature *signature; /* the lock is recursive */ signature = mono_method_signature (imethod->declaring); - m->signature = inflate_generic_signature (imethod->declaring->klass->image, signature, mono_method_get_context (m)); + signature = inflate_generic_signature (imethod->declaring->klass->image, signature, mono_method_get_context (m)); + + inflated_signatures_size += mono_metadata_signature_size (signature); + + mono_memory_barrier (); + m->signature = signature; mono_loader_unlock (); return m->signature; } @@ -1839,7 +1999,7 @@ mono_method_signature (MonoMethod *m) sig = mono_metadata_blob_heap (img, mono_metadata_decode_row_col (&img->tables [MONO_TABLE_METHOD], idx - 1, MONO_METHOD_SIGNATURE)); g_assert (!m->klass->generic_class); - container = m->generic_container; + container = mono_method_get_generic_container (m); if (!container) container = m->klass->generic_container; @@ -1855,39 +2015,39 @@ mono_method_signature (MonoMethod *m) } if (can_cache_signature) - m->signature = g_hash_table_lookup (img->method_signatures, sig); + signature = g_hash_table_lookup (img->method_signatures, sig); - if (!m->signature) { + if (!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 (!m->signature) { + signature = mono_metadata_parse_method_signature_full (img, container, idx, sig_body, NULL); + if (!signature) { mono_loader_unlock (); return NULL; } if (can_cache_signature) - g_hash_table_insert (img->method_signatures, (gpointer)sig, m->signature); + g_hash_table_insert (img->method_signatures, (gpointer)sig, signature); } /* Verify metadata consistency */ - if (m->signature->generic_param_count) { + if (signature->generic_param_count) { if (!container || !container->is_method) g_error ("Signature claims method has generic parameters, but generic_params table says it doesn't"); - if (container->type_argc != m->signature->generic_param_count) + if (container->type_argc != signature->generic_param_count) g_error ("Inconsistent generic parameter count. Signature says %d, generic_params table says %d", - m->signature->generic_param_count, container->type_argc); + signature->generic_param_count, container->type_argc); } else if (container && container->is_method && container->type_argc) g_error ("generic_params table claims method has generic parameters, but signature says it doesn't"); if (m->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) - m->signature->pinvoke = 1; - else if ((m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && (!(m->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE))) { + signature->pinvoke = 1; + else if (m->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { MonoCallConvention conv = 0; MonoMethodPInvoke *piinfo = (MonoMethodPInvoke *)m; - m->signature->pinvoke = 1; + signature->pinvoke = 1; switch (piinfo->piflags & PINVOKE_ATTRIBUTE_CALL_CONV_MASK) { case 0: /* no call conv, so using default */ @@ -1912,9 +2072,12 @@ mono_method_signature (MonoMethod *m) g_warning ("unsupported calling convention : 0x%04x", piinfo->piflags); g_assert_not_reached (); } - m->signature->call_convention = conv; + signature->call_convention = conv; } + mono_memory_barrier (); + m->signature = signature; + mono_loader_unlock (); return m->signature; } @@ -1945,6 +2108,7 @@ mono_method_get_header (MonoMethod *method) MonoImage* img; gpointer loc; MonoMethodNormal* mn = (MonoMethodNormal*) method; + MonoMethodHeader *header; 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; @@ -1956,23 +2120,28 @@ mono_method_get_header (MonoMethod *method) #endif return mn->header; - mono_loader_lock (); - - if (mn->header) { - mono_loader_unlock (); - return mn->header; - } - if (method->is_inflated) { MonoMethodInflated *imethod = (MonoMethodInflated *) method; MonoMethodHeader *header; - /* the lock is recursive */ + header = mono_method_get_header (imethod->declaring); + + mono_loader_lock (); + + if (mn->header) { + mono_loader_unlock (); + return mn->header; + } + mn->header = inflate_generic_header (header, mono_method_get_context (method)); mono_loader_unlock (); return mn->header; } + /* + * Do most of the work outside the loader lock, to avoid assembly loader hook + * deadlocks. + */ g_assert (mono_metadata_token_table (method->token) == MONO_TABLE_METHOD); idx = mono_metadata_token_index (method->token); img = method->klass->image; @@ -1981,7 +2150,19 @@ mono_method_get_header (MonoMethod *method) g_assert (loc); - mn->header = mono_metadata_parse_mh_full (img, method->generic_container, loc); + header = mono_metadata_parse_mh_full (img, mono_method_get_generic_container (method), loc); + + mono_loader_lock (); + + if (mn->header) { + /* header is allocated from the image mempool, no need to free it */ + mono_loader_unlock (); + return mn->header; + } + + mono_memory_barrier (); + + mn->header = header; mono_loader_unlock (); return mn->header;