Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions Cpp2IL.Core.Tests/MethodOverridesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>, IReadOnlyCollection<T>
Assert.That(list.GetMethod("get_Count").Overrides, Has.Count.EqualTo(3)); // ICollection, ICollection<T>, IReadOnlyCollection<T>

// Explicit interface implementation
Assert.That(list.GetMethod("System.Collections.Generic.ICollection<T>.get_IsReadOnly").Overrides.Count(), Is.EqualTo(1));
Assert.That(list.GetMethod("System.Collections.Generic.ICollection<T>.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));
}
}

Expand Down
95 changes: 56 additions & 39 deletions Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,41 @@ public TypeAnalysisContext ReturnType

protected Memory<byte>? 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<MethodAnalysisContext>? _overrides;

/// <summary>
/// The set of methods which this method overrides.
/// The set of interface methods which this method explicitly overrides.
/// </summary>
public List<MethodAnalysisContext> Overrides
{
Expand All @@ -167,31 +196,6 @@ private IEnumerable<MethodAnalysisContext> 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<MethodAnalysisContext> GetOverriddenMethods(Il2CppTypeDefinition declaringTypeDefinition, MetadataUsage?[] vtable)
{
for (var i = 0; i < vtable.Length; ++i)
Expand All @@ -203,18 +207,6 @@ IEnumerable<MethodAnalysisContext> 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)
{
Expand All @@ -231,6 +223,31 @@ IEnumerable<MethodAnalysisContext> 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<IBlockProcessor> blockProcessors =
[
new MetadataProcessor(),
Expand Down
33 changes: 15 additions & 18 deletions Cpp2IL.Core/Utils/AsmResolver/AsmResolverAssemblyPopulator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MethodDefinition>("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<MethodDefinition>("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));
}
}
}
Expand Down
Loading