Make case-insensitive class lookup work correctly with exported types (mono/mono...
authorRyan Lucia <rylucia@microsoft.com>
Wed, 18 Sep 2019 21:18:30 +0000 (17:18 -0400)
committerGitHub <noreply@github.com>
Wed, 18 Sep 2019 21:18:30 +0000 (17:18 -0400)
* Make case-insensitive class lookup work correctly with exported types

* Search all matching namespaces

* Skip AOT cache for case-insensitive lookups

Commit migrated from https://github.com/mono/mono/commit/529a11cf5b7557851472d7cb646559e0d3b02763

src/mono/mono/metadata/class.c
src/mono/netcore/CoreFX.issues.rsp

index 6bcf500..419e880 100644 (file)
@@ -65,6 +65,9 @@ static gboolean can_access_type (MonoClass *access_klass, MonoClass *member_klas
 static char* mono_assembly_name_from_token (MonoImage *image, guint32 type_token);
 static guint32 mono_field_resolve_flags (MonoClassField *field);
 
+static MonoClass *
+mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, const char *name, GHashTable* visited_images, gboolean case_sensitive, MonoError *error);
+
 GENERATE_GET_CLASS_WITH_CACHE (valuetype, "System", "ValueType")
 GENERATE_TRY_GET_CLASS_WITH_CACHE (handleref, "System.Runtime.InteropServices", "HandleRef")
 
@@ -2943,6 +2946,20 @@ mono_image_add_to_name_cache (MonoImage *image, const char *nspace,
 
 typedef struct {
        gconstpointer key;
+       GSList *values;
+} FindAllUserData;
+
+static void
+find_all_nocase (gpointer key, gpointer value, gpointer user_data)
+{
+       char *name = (char*)key;
+       FindAllUserData *data = (FindAllUserData*)user_data;
+       if (mono_utf8_strcasecmp (name, (char*)data->key) == 0)
+               data->values = g_slist_prepend (data->values, value);
+}
+
+typedef struct {
+       gconstpointer key;
        gpointer value;
 } FindUserData;
 
@@ -2995,66 +3012,20 @@ mono_class_from_name_case (MonoImage *image, const char* name_space, const char
 MonoClass *
 mono_class_from_name_case_checked (MonoImage *image, const char *name_space, const char *name, MonoError *error)
 {
-       MonoTableInfo  *t = &image->tables [MONO_TABLE_TYPEDEF];
-       guint32 cols [MONO_TYPEDEF_SIZE];
-       const char *n;
-       const char *nspace;
-       guint32 i, visib;
-
-       error_init (error);
-
-       if (image_is_dynamic (image)) {
-               guint32 token = 0;
-               FindUserData user_data;
-
-               mono_image_init_name_cache (image);
-               mono_image_lock (image);
-
-               user_data.key = name_space;
-               user_data.value = NULL;
-               g_hash_table_foreach (image->name_cache, find_nocase, &user_data);
-
-               if (user_data.value) {
-                       GHashTable *nspace_table = (GHashTable*)user_data.value;
-
-                       user_data.key = name;
-                       user_data.value = NULL;
+       MonoClass *klass;
+       GHashTable *visited_images;
 
-                       g_hash_table_foreach (nspace_table, find_nocase, &user_data);
-                       
-                       if (user_data.value)
-                               token = GPOINTER_TO_UINT (user_data.value);
-               }
+       visited_images = g_hash_table_new (g_direct_hash, g_direct_equal);
 
-               mono_image_unlock (image);
-               
-               if (token)
-                       return mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | token, error);
-               else
-                       return NULL;
+       klass = mono_class_from_name_checked_aux (image, name_space, name, visited_images, FALSE, error);
 
-       }
+       g_hash_table_destroy (visited_images);
 
-       /* add a cache if needed */
-       for (i = 1; i <= t->rows; ++i) {
-               mono_metadata_decode_row (t, i - 1, cols, MONO_TYPEDEF_SIZE);
-               visib = cols [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK;
-               /*
-                * Nested types are accessed from the nesting name.  We use the fact that nested types use different visibility flags
-                * than toplevel types, thus avoiding the need to grovel through the NESTED_TYPE table
-                */
-               if (visib >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visib <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM)
-                       continue;
-               n = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAME]);
-               nspace = mono_metadata_string_heap (image, cols [MONO_TYPEDEF_NAMESPACE]);
-               if (mono_utf8_strcasecmp (n, name) == 0 && mono_utf8_strcasecmp (nspace, name_space) == 0)
-                       return mono_class_get_checked (image, MONO_TOKEN_TYPE_DEF | i, error);
-       }
-       return NULL;
+       return klass;
 }
 
 static MonoClass*
-return_nested_in (MonoClass *klass, char *nested)
+return_nested_in (MonoClass *klass, char *nested, gboolean case_sensitive)
 {
        MonoClass *found;
        char *s = strchr (nested, '/');
@@ -3066,9 +3037,16 @@ return_nested_in (MonoClass *klass, char *nested)
        }
 
        while ((found = mono_class_get_nested_types (klass, &iter))) {
-               if (strcmp (m_class_get_name (found), nested) == 0) {
+               const char *name = m_class_get_name (found);
+               gint strcmp_result;
+               if (case_sensitive)
+                       strcmp_result = strcmp (name, nested);
+               else
+                       strcmp_result = mono_utf8_strcasecmp (name, nested);
+
+               if (strcmp_result == 0) {
                        if (s)
-                               return return_nested_in (found, s);
+                               return return_nested_in (found, s, case_sensitive);
                        return found;
                }
        }
@@ -3076,7 +3054,7 @@ return_nested_in (MonoClass *klass, char *nested)
 }
 
 static MonoClass*
-search_modules (MonoImage *image, const char *name_space, const char *name, MonoError *error)
+search_modules (MonoImage *image, const char *name_space, const char *name, gboolean case_sensitive, MonoError *error)
 {
        MonoTableInfo *file_table = &image->tables [MONO_TABLE_FILE];
        MonoImage *file_image;
@@ -3099,7 +3077,11 @@ search_modules (MonoImage *image, const char *name_space, const char *name, Mono
 
                file_image = mono_image_load_file_for_image_checked (image, i + 1, error);
                if (file_image) {
-                       klass = mono_class_from_name_checked (file_image, name_space, name, error);
+                       if (case_sensitive)
+                               klass = mono_class_from_name_checked (file_image, name_space, name, error);
+                       else
+                               klass = mono_class_from_name_case_checked (file_image, name_space, name, error);
+
                        if (klass || !is_ok (error))
                                return klass;
                }
@@ -3109,10 +3091,10 @@ search_modules (MonoImage *image, const char *name_space, const char *name, Mono
 }
 
 static MonoClass *
-mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, const char *name, GHashTable* visited_images, MonoError *error)
+mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, const char *name, GHashTable* visited_images, gboolean case_sensitive, MonoError *error)
 {
-       GHashTable *nspace_table;
-       MonoImage *loaded_image;
+       GHashTable *nspace_table = NULL;
+       MonoImage *loaded_image = NULL;
        guint32 token = 0;
        int i;
        MonoClass *klass;
@@ -3139,16 +3121,17 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
        }
 
        /* FIXME: get_class_from_name () can't handle types in the EXPORTEDTYPE table */
-       if (get_class_from_name && image->tables [MONO_TABLE_EXPORTEDTYPE].rows == 0) {
+       // The AOT cache in get_class_from_name is case-sensitive, so don't bother with it for case-insensitive lookups
+       if (get_class_from_name && image->tables [MONO_TABLE_EXPORTEDTYPE].rows == 0 && case_sensitive) {
                gboolean res = get_class_from_name (image, name_space, name, &klass);
                if (res) {
                        if (!klass) {
-                               klass = search_modules (image, name_space, name, error);
+                               klass = search_modules (image, name_space, name, case_sensitive, error);
                                if (!is_ok (error))
                                        return NULL;
                        }
                        if (nested)
-                               return klass ? return_nested_in (klass, nested) : NULL;
+                               return klass ? return_nested_in (klass, nested, case_sensitive) : NULL;
                        else
                                return klass;
                }
@@ -3157,10 +3140,32 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
        mono_image_init_name_cache (image);
        mono_image_lock (image);
 
-       nspace_table = (GHashTable *)g_hash_table_lookup (image->name_cache, name_space);
+       if (case_sensitive) {
+               nspace_table = (GHashTable *)g_hash_table_lookup (image->name_cache, name_space);
+
+               if (nspace_table)
+                       token = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, name));
+       } else {
+               FindAllUserData all_user_data = { name_space, NULL };
+               FindUserData user_data = { name, NULL };
+               GSList *values;
 
-       if (nspace_table)
-               token = GPOINTER_TO_UINT (g_hash_table_lookup (nspace_table, name));
+               // We're forced to check all matching namespaces, not just the first one found,
+               // because our desired type could be in any of the ones that match case-insensitively.
+               g_hash_table_foreach (image->name_cache, find_all_nocase, &all_user_data);
+
+               values = all_user_data.values;
+               while (values && !user_data.value) {
+                       nspace_table = (GHashTable*)values->data;
+                       g_hash_table_foreach (nspace_table, find_nocase, &user_data);
+                       values = values->next;
+               }
+
+               g_slist_free (all_user_data.values);
+
+               if (user_data.value)
+                       token = GPOINTER_TO_UINT (user_data.value);
+       }
 
        mono_image_unlock (image);
 
@@ -3169,14 +3174,18 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
                for (i = 0; i < image->module_count; ++i) {
                        MonoImage *module = image->modules [i];
 
-                       klass = mono_class_from_name_checked (module, name_space, name, error);
+                       if (case_sensitive)
+                               klass = mono_class_from_name_checked (module, name_space, name, error);
+                       else
+                               klass = mono_class_from_name_case_checked (module, name_space, name, error);
+
                        if (klass || !is_ok (error))
                                return klass;
                }
        }
 
        if (!token) {
-               klass = search_modules (image, name_space, name, error);
+               klass = search_modules (image, name_space, name, case_sensitive, error);
                if (klass || !is_ok (error))
                        return klass;
                return NULL;
@@ -3196,9 +3205,9 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
                        loaded_image = mono_assembly_load_module_checked (image->assembly, impl >> MONO_IMPLEMENTATION_BITS, error);
                        if (!loaded_image)
                                return NULL;
-                       klass = mono_class_from_name_checked_aux (loaded_image, name_space, name, visited_images, error);
+                       klass = mono_class_from_name_checked_aux (loaded_image, name_space, name, visited_images, case_sensitive, error);
                        if (nested)
-                               return klass ? return_nested_in (klass, nested) : NULL;
+                               return klass ? return_nested_in (klass, nested, case_sensitive) : NULL;
                        return klass;
                } else if ((impl & MONO_IMPLEMENTATION_MASK) == MONO_IMPLEMENTATION_ASSEMBLYREF) {
                        guint32 assembly_idx;
@@ -3209,9 +3218,9 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
                        g_assert (image->references [assembly_idx - 1]);
                        if (image->references [assembly_idx - 1] == (gpointer)-1)
                                return NULL;                    
-                       klass = mono_class_from_name_checked_aux (image->references [assembly_idx - 1]->image, name_space, name, visited_images, error);
+                       klass = mono_class_from_name_checked_aux (image->references [assembly_idx - 1]->image, name_space, name, visited_images, case_sensitive, error);
                        if (nested)
-                               return klass ? return_nested_in (klass, nested) : NULL;
+                               return klass ? return_nested_in (klass, nested, case_sensitive) : NULL;
                        return klass;
                } else {
                        g_assert_not_reached ();
@@ -3222,7 +3231,7 @@ mono_class_from_name_checked_aux (MonoImage *image, const char* name_space, cons
 
        klass = mono_class_get_checked (image, token, error);
        if (nested)
-               return return_nested_in (klass, nested);
+               return return_nested_in (klass, nested, case_sensitive);
        return klass;
 }
 
@@ -3246,7 +3255,7 @@ mono_class_from_name_checked (MonoImage *image, const char* name_space, const ch
 
        visited_images = g_hash_table_new (g_direct_hash, g_direct_equal);
 
-       klass = mono_class_from_name_checked_aux (image, name_space, name, visited_images, error);
+       klass = mono_class_from_name_checked_aux (image, name_space, name, visited_images, TRUE, error);
 
        g_hash_table_destroy (visited_images);
 
index d18b0f4..1526907 100644 (file)
 # GC.GetGCMemoryInfo is not implemented
 -nomethod System.Tests.GCTests.GetGCMemoryInfo
 
-# Could not load type 'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
-# https://github.com/mono/mono/issues/15153
--nomethod System.Tests.ActivatorTests.TestingCreateInstanceObjectHandleFullSignature
-
 # flaky test, System.ArgumentNullException : Key cannot be null. Parameter name: key
 -nomethod System.Tests.GetEnvironmentVariable.EnvironmentVariablesAreHashtable