diff --git a/runtime/runtime.m b/runtime/runtime.m index 2ab99e13c2fa..2db8a577526c 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1001,11 +1001,27 @@ -(void) xamarinSetGCHandle: (int) gc_handle; * Registration map */ +static int +compare_mtclassmap (const void *a, const void *b) +{ + MTClassMap *mapa = (MTClassMap *) a; + MTClassMap *mapb = (MTClassMap *) b; + if (mapa->handle == mapb->handle) + return 0; + if ((intptr_t) mapa->handle < (intptr_t) mapb->handle) + return -1; + return 1; +} + void xamarin_add_registration_map (struct MTRegistrationMap *map) { // COOP: no managed memory access: any mode options.RegistrationData = map; + + // Sort the type map according to Class + qsort (map->map, map->map_count - map->custom_type_count, sizeof (MTClassMap), compare_mtclassmap); + qsort (&map->map [map->map_count - map->custom_type_count], map->custom_type_count, sizeof (MTClassMap), compare_mtclassmap); } /* diff --git a/runtime/xamarin/runtime.h b/runtime/xamarin/runtime.h index 765f05d8d64a..df5e85ca8579 100644 --- a/runtime/xamarin/runtime.h +++ b/runtime/xamarin/runtime.h @@ -75,7 +75,7 @@ typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) { uint32_t /* MTTokenReference */ skipped_reference; - uint32_t /* index into MTRegistrationMap->map */ index; + uint32_t /* MTTokenReference */ actual_reference; } MTManagedClassMap; typedef struct __attribute__((packed)) { diff --git a/src/ObjCRuntime/Class.cs b/src/ObjCRuntime/Class.cs index 761e100b1e1a..200819b31f24 100644 --- a/src/ObjCRuntime/Class.cs +++ b/src/ObjCRuntime/Class.cs @@ -251,10 +251,13 @@ unsafe static IntPtr FindClass (Type type, out bool is_custom_type) if (!CompareTokenReference (asm_name, mod_token, type_token, token_reference)) continue; - // This is a skipped type, we now got the index into the normal table. - var idx = map->skipped_map [i].index; - is_custom_type = idx >= (map->map_count - map->custom_type_count); - return map->map [idx].handle; + // This is a skipped type, we now got the actual type reference of the type we're looking for, + // so go look for it in the type map. + var actual_reference = map->skipped_map [i].actual_reference; + for (int k = 0; k < map->map_count; k++) { + if (map->map [k].type_reference == actual_reference) + return map->map [k].handle; + } } return IntPtr.Zero; @@ -293,10 +296,26 @@ unsafe static bool CompareTokenReference (string asm_name, int mod_token, int ty return Runtime.StringEquals (assembly_name, asm_name); } + static unsafe int FindMapIndex (Runtime.MTClassMap *array, int lo, int hi, IntPtr @class) + { + if (hi >= lo) { + int mid = lo + (hi - lo) / 2; + + if (array [mid].handle == @class) + return mid; + + if (array [mid].handle.ToInt64 () > @class.ToInt64 ()) + return FindMapIndex (array, lo, mid - 1, @class); + + return FindMapIndex (array, mid + 1, hi, @class); + } + + return -1; + } + internal unsafe static Type FindType (IntPtr @class, out bool is_custom_type) { var map = Runtime.options->RegistrationMap; - Runtime.MTClassMap? entry = null; is_custom_type = false; @@ -308,19 +327,13 @@ internal unsafe static Type FindType (IntPtr @class, out bool is_custom_type) } // Find the ObjC class pointer in our map - // Potential improvement: order the type handles after loading them, which means we could do a binary search here. - // A binary search will likely be faster than a dictionary for any real-world scenario (and if slower, not much slower), - // but it would need a lot less memory (very little when sorting, could probably use stack memory, and then nothing at all afterwards). - for (int i = 0; i < map->map_count; i++) { - if (map->map [i].handle != @class) - continue; - - entry = map->map [i]; - is_custom_type = i >= (map->map_count - map->custom_type_count); - break; + var mapIndex = FindMapIndex (map->map, 0, map->map_count - map->custom_type_count - 1, @class); + if (mapIndex == -1) { + mapIndex = FindMapIndex (map->map, map->map_count - map->custom_type_count, map->map_count - 1, @class); + is_custom_type = true; } - if (!entry.HasValue) { + if (mapIndex == -1) { #if LOG_TYPELOAD Console.WriteLine ($"FindType (0x{@class:X} = {Marshal.PtrToStringAuto (class_getName (@class))}) => found no type."); #endif @@ -328,14 +341,15 @@ internal unsafe static Type FindType (IntPtr @class, out bool is_custom_type) } // Resolve the map entry we found to a managed type - var member = ResolveTokenReference (entry.Value.type_reference, 0x02000000); + var type_reference = map->map [mapIndex].type_reference; + var member = ResolveTokenReference (type_reference, 0x02000000); var type = member as Type; if (type == null && member != null) - throw ErrorHelper.CreateError (8022, $"Expected the token reference 0x{entry.Value.type_reference:X} to be a type, but it's a {member.GetType ().Name}. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new."); + throw ErrorHelper.CreateError (8022, $"Expected the token reference 0x{type_reference:X} to be a type, but it's a {member.GetType ().Name}. Please file a bug report at https://github.com/xamarin/xamarin-macios/issues/new."); #if LOG_TYPELOAD - Console.WriteLine ($"FindType (0x{@class:X} = {Marshal.PtrToStringAuto (class_getName (@class))}) => {type.FullName}; is custom: {is_custom_type} (token reference: 0x{entry.Value.type_reference:X})."); + Console.WriteLine ($"FindType (0x{@class:X} = {Marshal.PtrToStringAuto (class_getName (@class))}) => {type.FullName}; is custom: {is_custom_type} (token reference: 0x{type_reference:X})."); #endif return type; diff --git a/src/ObjCRuntime/Runtime.cs b/src/ObjCRuntime/Runtime.cs index 1f7a41c2d385..12501fe4731e 100644 --- a/src/ObjCRuntime/Runtime.cs +++ b/src/ObjCRuntime/Runtime.cs @@ -80,7 +80,7 @@ internal struct MTClassMap { internal struct MTManagedClassMap { public uint skipped_reference; // implied token type: TypeDef - public uint index; // index into MTRegistrationMap.map + public uint actual_reference; // implied token type: TypeDef } [StructLayout (LayoutKind.Sequential, Pack = 1)] diff --git a/tools/common/StaticRegistrar.cs b/tools/common/StaticRegistrar.cs index 883b677e0f32..92f1bb605070 100644 --- a/tools/common/StaticRegistrar.cs +++ b/tools/common/StaticRegistrar.cs @@ -2636,6 +2636,7 @@ class SkippedType public TypeReference Skipped; public ObjCType Actual; public uint SkippedTokenReference; + public uint ActualTokenReference; } List skipped_types = new List (); protected override void OnSkipType (TypeReference type, ObjCType registered_type) @@ -3031,16 +3032,13 @@ void Specialize (AutoIndentStringBuilder sb) if (skipped_types.Count > 0) { map.AppendLine ("static const MTManagedClassMap __xamarin_skipped_map [] = {"); - foreach (var skipped in skipped_types) + foreach (var skipped in skipped_types) { skipped.SkippedTokenReference = CreateTokenReference (skipped.Skipped, TokenType.TypeDef); - - foreach (var skipped in skipped_types.OrderBy ((v) => v.SkippedTokenReference)) { - if (map_dict.TryGetValue (skipped.Actual, out var index)) { - map.AppendLine ("{{ 0x{0:X}, {1} /* '{2}' => '{3}' */ }},", skipped.SkippedTokenReference, map_dict [skipped.Actual], skipped.Skipped.FullName, skipped.Actual.Type.FullName); - } else { - throw ErrorHelper.CreateError (99, $"Internal error: could not find the native type for {skipped.Skipped.FullName} (failed to find {skipped.Actual.Type.FullName}). Please file a bug report with a test case (https://github.com/xamarin/xamarin-macios/issues/new)."); - } + skipped.ActualTokenReference = CreateTokenReference (skipped.Actual.Type, TokenType.TypeDef); } + + foreach (var skipped in skipped_types.OrderBy ((v) => v.SkippedTokenReference)) + map.AppendLine ("{{ 0x{0:X}, 0x{1:X} /* '{2}' => '{3}' */ }},", skipped.SkippedTokenReference, skipped.ActualTokenReference, skipped.Skipped.FullName, skipped.Actual.Type.FullName); map.AppendLine ("};"); map.AppendLine (); }