From ce7a733025ea5379d0e8651b744e7e1464c772c9 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Thu, 4 Dec 2025 11:53:30 -0800 Subject: [PATCH] Change the meaning of MethodAnalysisContext::Overrides It now specifically refers to methods that use `.override` in IL. --- Cpp2IL.Core.Tests/MethodOverridesTests.cs | 10 +- .../Model/Contexts/MethodAnalysisContext.cs | 95 +++++++++++-------- .../AsmResolverAssemblyPopulator.cs | 33 +++---- 3 files changed, 76 insertions(+), 62 deletions(-) diff --git a/Cpp2IL.Core.Tests/MethodOverridesTests.cs b/Cpp2IL.Core.Tests/MethodOverridesTests.cs index f3be0b76..dd5e0237 100644 --- a/Cpp2IL.Core.Tests/MethodOverridesTests.cs +++ b/Cpp2IL.Core.Tests/MethodOverridesTests.cs @@ -18,24 +18,24 @@ public void OverridesTests() { // Simple override Assert.That(@enum.GetMethod("ToString", 0).BaseMethod, Is.Not.Null); - Assert.That(@enum.GetMethod("ToString", 0).Overrides.Count(), Is.EqualTo(1)); + Assert.That(@enum.GetMethod("ToString", 0).Overrides, Is.Empty); // Simple interface implementation Assert.That(list.GetMethod("get_Count").BaseMethod, Is.Null); - Assert.That(list.GetMethod("get_Count").Overrides.Count(), Is.EqualTo(3)); // ICollection, ICollection, IReadOnlyCollection + Assert.That(list.GetMethod("get_Count").Overrides, Has.Count.EqualTo(3)); // ICollection, ICollection, IReadOnlyCollection // Explicit interface implementation - Assert.That(list.GetMethod("System.Collections.Generic.ICollection.get_IsReadOnly").Overrides.Count(), Is.EqualTo(1)); + Assert.That(list.GetMethod("System.Collections.Generic.ICollection.get_IsReadOnly").Overrides, Has.Count.EqualTo(1)); // No override - Assert.That(list.GetMethod("EnsureCapacity").Overrides.Count(), Is.EqualTo(0)); + Assert.That(list.GetMethod("EnsureCapacity").Overrides, Is.Empty); // Check that the base method can be found even if higher up in the inheritance chain. // OrdinalComparer inherits from StringComparer, but StringComparer doesn't override GetHashCode. Assert.That(ordinalComparer.GetMethod("GetHashCode", 0).BaseMethod?.DeclaringType?.FullName, Is.EqualTo("System.Object")); // Interface methods should not override anything - Assert.That(iList.Methods.Select(m => m.Overrides.Count()), Is.All.EqualTo(0)); + Assert.That(iList.Methods.Select(m => m.Overrides.Count), Is.All.EqualTo(0)); } } diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index 2393e4a9..6422da0d 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -136,12 +136,41 @@ public TypeAnalysisContext ReturnType protected Memory? rawMethodBody; - public MethodAnalysisContext? BaseMethod => Overrides.FirstOrDefault(m => m.DeclaringType?.IsInterface is false); + public MethodAnalysisContext? BaseMethod + { + get + { + if (Definition == null) + return null; + + var vtable = DeclaringType?.Definition?.VTable; + if (vtable == null) + return null; + + for (var i = 0; i < vtable.Length; ++i) + { + var vtableEntry = vtable[i]; + if (vtableEntry is null or { Type: not MetadataUsageType.MethodDef } || vtableEntry.AsMethod() != Definition) + continue; + + var baseType = DeclaringType?.DefaultBaseType; + while (baseType is not null) + { + if (TryGetMethodForSlot(baseType, i, out var method)) + { + return method; + } + baseType = baseType.DefaultBaseType; + } + } + return null; + } + } private List? _overrides; /// - /// The set of methods which this method overrides. + /// The set of interface methods which this method explicitly overrides. /// public List Overrides { @@ -167,31 +196,6 @@ private IEnumerable GetOverrides() return GetOverriddenMethods(declaringTypeDefinition, vtable); - bool TryGetMethodForSlot(TypeAnalysisContext declaringType, int slot, [NotNullWhen(true)] out MethodAnalysisContext? method) - { - if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType) - { - var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot); - if (genericMethod is not null) - { - method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments, []); - return true; - } - } - else - { - var baseMethod = declaringType.Methods.FirstOrDefault(m => m.Slot == slot); - if (baseMethod is not null) - { - method = baseMethod; - return true; - } - } - - method = null; - return false; - } - IEnumerable GetOverriddenMethods(Il2CppTypeDefinition declaringTypeDefinition, MetadataUsage?[] vtable) { for (var i = 0; i < vtable.Length; ++i) @@ -203,18 +207,6 @@ IEnumerable GetOverriddenMethods(Il2CppTypeDefinition dec if (vtableEntry.AsMethod() != Definition) continue; - // Normal inheritance - var baseType = DeclaringType?.DefaultBaseType; - while (baseType is not null) - { - if (TryGetMethodForSlot(baseType, i, out var method)) - { - yield return method; - break; // We only want direct overrides, not the entire inheritance chain. - } - baseType = baseType.DefaultBaseType; - } - // Interface inheritance foreach (var interfaceOffset in declaringTypeDefinition.InterfaceOffsets) { @@ -231,6 +223,31 @@ IEnumerable GetOverriddenMethods(Il2CppTypeDefinition dec } } + private static bool TryGetMethodForSlot(TypeAnalysisContext declaringType, int slot, [NotNullWhen(true)] out MethodAnalysisContext? method) + { + if (declaringType is GenericInstanceTypeAnalysisContext genericInstanceType) + { + var genericMethod = genericInstanceType.GenericType.Methods.FirstOrDefault(m => m.Slot == slot); + if (genericMethod is not null) + { + method = new ConcreteGenericMethodAnalysisContext(genericMethod, genericInstanceType.GenericArguments, []); + return true; + } + } + else + { + var baseMethod = declaringType.Methods.FirstOrDefault(m => m.Slot == slot); + if (baseMethod is not null) + { + method = baseMethod; + return true; + } + } + + method = null; + return false; + } + private static readonly List blockProcessors = [ new MetadataProcessor(), diff --git a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs index 49a599fa..63b2deb0 100644 --- a/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs +++ b/Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs @@ -533,26 +533,23 @@ private static void AddExplicitInterfaceImplementations(TypeDefinition type, Typ foreach (var overrideContext in methodContext.Overrides) { - if (overrideContext.DeclaringType?.IsInterface ?? false) + var interfaceMethod = (IMethodDefOrRef)overrideContext.ToMethodDescriptor(importer.TargetModule); + var method = methodContext.GetExtraData("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {methodContext}"); + type.MethodImplementations.Add(new MethodImplementation(interfaceMethod, method)); + var interfaceMethodResolved = interfaceMethod.Resolve(); + if (interfaceMethodResolved != null) { - var interfaceMethod = (IMethodDefOrRef)overrideContext.ToMethodDescriptor(importer.TargetModule); - var method = methodContext.GetExtraData("AsmResolverMethod") ?? throw new($"AsmResolver method not found in method analysis context for {methodContext}"); - type.MethodImplementations.Add(new MethodImplementation(interfaceMethod, method)); - var interfaceMethodResolved = interfaceMethod.Resolve(); - if (interfaceMethodResolved != null) + if (interfaceMethodResolved.IsGetMethod && !method.IsGetMethod) { - if (interfaceMethodResolved.IsGetMethod && !method.IsGetMethod) - { - getMethodsToCreate ??= []; - var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics)); - getMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method)); - } - else if (interfaceMethodResolved.IsSetMethod && !method.IsSetMethod) - { - setMethodsToCreate ??= []; - var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics)); - setMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method)); - } + getMethodsToCreate ??= []; + var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics)); + getMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method)); + } + else if (interfaceMethodResolved.IsSetMethod && !method.IsSetMethod) + { + setMethodsToCreate ??= []; + var interfacePropertyResolved = interfaceMethodResolved.DeclaringType!.Properties.First(p => p.Semantics.Contains(interfaceMethodResolved.Semantics)); + setMethodsToCreate.Add((interfacePropertyResolved, interfaceMethod.DeclaringType!.ToTypeSignature(), method)); } } }