From b62f1c39e6c122857a7dcd452cd4b928a32fc15d Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:20:54 -0700 Subject: [PATCH 1/3] Interface methods should not have a base method --- Cpp2IL.Core.Tests/MethodOverridesTests.cs | 59 ++++++++++++++++++- .../Model/Contexts/MethodAnalysisContext.cs | 6 +- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Cpp2IL.Core.Tests/MethodOverridesTests.cs b/Cpp2IL.Core.Tests/MethodOverridesTests.cs index 7d9c2c49..f3be0b76 100644 --- a/Cpp2IL.Core.Tests/MethodOverridesTests.cs +++ b/Cpp2IL.Core.Tests/MethodOverridesTests.cs @@ -1,5 +1,4 @@ using System.Linq; -using Cpp2IL.Core.Model.Contexts; namespace Cpp2IL.Core.Tests; @@ -39,4 +38,62 @@ public void OverridesTests() Assert.That(iList.Methods.Select(m => m.Overrides.Count()), Is.All.EqualTo(0)); } } + + [Test] + public void InterfaceMethodsShouldNotOverrideAnything() + { + var appContext = TestGameLoader.LoadSimple2019Game(); + + using (Assert.EnterMultipleScope()) + { + var count = 0; + foreach (var assembly in appContext.Assemblies) + { + foreach (var type in assembly.Types) + { + if (!type.IsInterface) + continue; + + foreach (var method in type.Methods) + { + if (!method.IsVirtual && !method.IsAbstract) + continue; + + if (method.IsStatic || !method.IsNewSlot) + continue; + + Assert.That(method.Overrides, Is.Empty); + count++; + } + } + } + Assert.That(count, Is.GreaterThan(0)); + } + } + + [Test] + public void InterfaceMethodsShouldHaveNoBaseMethod() + { + var appContext = TestGameLoader.LoadSimple2019Game(); + + using (Assert.EnterMultipleScope()) + { + var count = 0; + foreach (var assembly in appContext.Assemblies) + { + foreach (var type in assembly.Types) + { + if (!type.IsInterface) + continue; + + foreach (var method in type.Methods) + { + Assert.That(method.BaseMethod, Is.Null); + count++; + } + } + } + Assert.That(count, Is.GreaterThan(0)); + } + } } diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index e20e75a7..e22a0520 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -64,6 +64,10 @@ public class MethodAnalysisContext : HasGenericParameters, IMethodInfoProvider public bool IsVirtual => (Attributes & MethodAttributes.Virtual) != 0; + public bool IsAbstract => (Attributes & MethodAttributes.Abstract) != 0; + + public bool IsNewSlot => (Attributes & MethodAttributes.NewSlot) != 0; + protected override int CustomAttributeIndex => Definition?.customAttributeIndex ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeIndex if they have custom attributes"); public override AssemblyAnalysisContext CustomAttributeAssembly => DeclaringType?.DeclaringAssembly ?? throw new("Subclasses of MethodAnalysisContext should override CustomAttributeAssembly if they have custom attributes"); @@ -179,7 +183,7 @@ IEnumerable GetOverriddenMethods(Il2CppTypeDefinition dec continue; // Normal inheritance - var baseType = DeclaringType?.BaseType; + var baseType = DeclaringType is { IsInterface: true } ? null : DeclaringType?.BaseType; while (baseType is not null) { if (TryGetMethodForSlot(baseType, i, out var method)) From 0497b84c89a7a13dc19468ee35d501750f0a1814 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:22:12 -0700 Subject: [PATCH 2/3] Fix interface base type --- Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs | 28 +++++++++++++++++++ .../Model/Contexts/MethodAnalysisContext.cs | 2 +- .../Model/Contexts/TypeAnalysisContext.cs | 2 +- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs diff --git a/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs b/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs new file mode 100644 index 00000000..8786b6b6 --- /dev/null +++ b/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs @@ -0,0 +1,28 @@ +namespace Cpp2IL.Core.Tests; + +public class TypeAnalysisContextTests +{ + [Test] + public void InterfacesHaveNoBaseType() + { + var appContext = TestGameLoader.LoadSimple2019Game(); + + using (Assert.EnterMultipleScope()) + { + var count = 0; + foreach (var assembly in appContext.Assemblies) + { + foreach (var type in assembly.Types) + { + if (!type.IsInterface) + continue; + + Assert.That(type.DefaultBaseType, Is.Null); + Assert.That(type.BaseType, Is.Null); + count++; + } + } + Assert.That(count, Is.GreaterThan(0)); + } + } +} diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index e22a0520..4be56dce 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -183,7 +183,7 @@ IEnumerable GetOverriddenMethods(Il2CppTypeDefinition dec continue; // Normal inheritance - var baseType = DeclaringType is { IsInterface: true } ? null : DeclaringType?.BaseType; + var baseType = DeclaringType?.BaseType; while (baseType is not null) { if (TryGetMethodForSlot(baseType, i, out var method)) diff --git a/Cpp2IL.Core/Model/Contexts/TypeAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/TypeAnalysisContext.cs index 1bb0e9ff..a7f2aeec 100644 --- a/Cpp2IL.Core/Model/Contexts/TypeAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/TypeAnalysisContext.cs @@ -71,7 +71,7 @@ public class TypeAnalysisContext : HasGenericParameters, ITypeInfoProvider, ICSh public TypeAttributes Attributes => OverrideAttributes ?? DefaultAttributes; - public virtual TypeAnalysisContext? DefaultBaseType => Definition == null ? null : DeclaringAssembly.ResolveIl2CppType(Definition.RawBaseType); + public virtual TypeAnalysisContext? DefaultBaseType => Definition == null || DefaultAttributes.HasFlag(TypeAttributes.Interface) ? null : DeclaringAssembly.ResolveIl2CppType(Definition.RawBaseType); public TypeAnalysisContext? OverrideBaseType { get; set; } From cadae41d0d97727beb92e813bc6c9eb3335553f8 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:30:57 -0700 Subject: [PATCH 3/3] Add test for static classes --- Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs b/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs index 8786b6b6..e9d36ca6 100644 --- a/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs +++ b/Cpp2IL.Core.Tests/TypeAnalysisContextTests.cs @@ -25,4 +25,28 @@ public void InterfacesHaveNoBaseType() Assert.That(count, Is.GreaterThan(0)); } } + + [Test] + public void StaticClassesHaveObjectBaseType() + { + var appContext = TestGameLoader.LoadSimple2019Game(); + + using (Assert.EnterMultipleScope()) + { + var count = 0; + foreach (var assembly in appContext.Assemblies) + { + foreach (var type in assembly.Types) + { + if (!type.IsStatic) + continue; + + Assert.That(type.DefaultBaseType, Is.EqualTo(appContext.SystemTypes.SystemObjectType)); + Assert.That(type.BaseType, Is.EqualTo(appContext.SystemTypes.SystemObjectType)); + count++; + } + } + Assert.That(count, Is.GreaterThan(0)); + } + } }