Skip to content

Commit

Permalink
Core: Generic method call fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Nov 26, 2021
1 parent 31025b1 commit e01ac8a
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 14 deletions.
31 changes: 30 additions & 1 deletion Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldReadAction.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Cpp2IL.Core.Analysis.ResultModels;
using Cpp2IL.Core.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;

namespace Cpp2IL.Core.Analysis.Actions.Base
{
Expand All @@ -10,10 +13,36 @@ public abstract class AbstractFieldReadAction<T> : BaseAction<T>
public FieldUtils.FieldBeingAccessedData? FieldRead;
public LocalDefinition? LocalWritten;
protected LocalDefinition? ReadFrom;

protected TypeReference? ReadFromType;

protected AbstractFieldReadAction(MethodAnalysis<T> context, T instruction) : base(context, instruction)
{
}

protected void FixUpFieldRefForAnyPotentialGenericType(MethodAnalysis<T> context)
{
if(context.GetMethodDefinition() is not {} contextMethod)
return;

if(FieldRead == null)
return;

if (ReadFromType is null or TypeDefinition {HasGenericParameters: false})
return;

if (ReadFromType is TypeDefinition)
ReadFromType = ReadFromType.MakeGenericInstanceType(ReadFromType.GenericParameters.Cast<TypeReference>().ToArray());

if (FieldRead.ImpliedFieldLoad is { } impliedLoad)
{
var fieldRef = new FieldReference(impliedLoad.Name, impliedLoad.FieldType, ReadFromType);
FieldRead.ImpliedFieldLoad = contextMethod.Module.ImportFieldButCleanly(fieldRef);
} else if (FieldRead.FinalLoadInChain is { } finalLoad)
{
var fieldRef = new FieldReference(finalLoad.Name, finalLoad.FieldType, ReadFromType);
FieldRead.FinalLoadInChain = contextMethod.Module.ImportFieldButCleanly(fieldRef);
}
}

public override string ToPsuedoCode()
{
Expand Down
31 changes: 31 additions & 0 deletions Cpp2IL.Core/Analysis/Actions/Base/AbstractFieldWriteAction.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Cpp2IL.Core.Analysis.ResultModels;
using Cpp2IL.Core.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;

namespace Cpp2IL.Core.Analysis.Actions.Base
{
Expand All @@ -17,6 +20,34 @@ protected AbstractFieldWriteAction(MethodAnalysis<T> context, T instruction) : b
protected abstract string? GetValuePseudocode();
protected abstract Instruction[] GetIlToLoadValue(MethodAnalysis<T> context, ILProcessor processor);

protected void FixUpFieldRefForAnyPotentialGenericType(MethodAnalysis<T> context)
{
if(context.GetMethodDefinition() is not {} contextMethod)
return;

if(FieldWritten == null)
return;

if(InstanceBeingSetOn?.Type is not {} writtenOnType)
return;

if (writtenOnType is null or TypeDefinition {HasGenericParameters: false})
return;

if (writtenOnType is TypeDefinition)
writtenOnType = writtenOnType.MakeGenericInstanceType(writtenOnType.GenericParameters.Cast<TypeReference>().ToArray());

if (FieldWritten.ImpliedFieldLoad is { } impliedLoad)
{
var fieldRef = new FieldReference(impliedLoad.Name, impliedLoad.FieldType, writtenOnType);
FieldWritten.ImpliedFieldLoad = contextMethod.Module.ImportFieldButCleanly(fieldRef);
} else if (FieldWritten.FinalLoadInChain is { } finalLoad)
{
var fieldRef = new FieldReference(finalLoad.Name, finalLoad.FieldType, writtenOnType);
FieldWritten.FinalLoadInChain = contextMethod.Module.ImportFieldButCleanly(fieldRef);
}
}

public override Instruction[] ToILInstructions(MethodAnalysis<T> context, ILProcessor processor)
{
if (InstanceBeingSetOn == null || FieldWritten == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Cpp2IL.Core.Analysis.ResultModels;
using Cpp2IL.Core.Utils;
using Mono.Cecil;
using Instruction = Iced.Intel.Instruction;

namespace Cpp2IL.Core.Analysis.Actions.x86.Important
Expand All @@ -22,6 +23,11 @@ public CallVirtualMethodAction(MethodAnalysis<Instruction> context, Instruction
if (ManagedMethodBeingCalled == null) return;

InstanceBeingCalledOn = ManagedMethodBeingCalled.HasThis ? context.GetLocalInReg("rcx") : null;

if (ManagedMethodBeingCalled is MethodDefinition && InstanceBeingCalledOn?.Type is GenericInstanceType git)
{
ManagedMethodBeingCalled = ManagedMethodBeingCalled.MakeMethodOnGenericType(git.GenericArguments.ToArray());
}

if(!MethodUtils.CheckParameters(instruction, ManagedMethodBeingCalled, context, ManagedMethodBeingCalled.HasThis, out Arguments, InstanceBeingCalledOn?.Type, false))
AddComment("Arguments are incorrect?");
Expand Down
10 changes: 5 additions & 5 deletions Cpp2IL.Core/Analysis/Actions/x86/Important/FieldToLocalAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,33 @@ public FieldToLocalAction(MethodAnalysis<Instruction> context, Instruction instr
{
var sourceRegName = X86Utils.GetRegisterNameNew(instruction.MemoryBase);
_destRegName = X86Utils.GetRegisterNameNew(instruction.Op0Register);
var sourceFieldOffset = instruction.MemoryDisplacement;
var sourceFieldOffset = instruction.MemoryDisplacement32;

var readFrom = context.GetOperandInRegister(sourceRegName);

TypeReference readFromType;
if (readFrom is ConstantDefinition {Value: NewSafeCastResult<Instruction> result})
{
readFromType = result.castTo;
ReadFromType = result.castTo;
ReadFrom = result.original;
RegisterUsedLocal(ReadFrom, context);
}
else if(readFrom is LocalDefinition {IsMethodInfoParam: false} l && l.Type?.Resolve() != null)
{
ReadFrom = l;
readFromType = ReadFrom!.Type!;
ReadFromType = ReadFrom!.Type!;
RegisterUsedLocal(ReadFrom, context);
} else
{
AddComment($"This shouldn't be a field read? Op in reg {sourceRegName} is {context.GetOperandInRegister(sourceRegName)}, offset is {sourceFieldOffset} (0x{sourceFieldOffset:X})");
return;
}

FieldRead = FieldUtils.GetFieldBeingAccessed(readFromType, sourceFieldOffset, false);
FieldRead = FieldUtils.GetFieldBeingAccessed(ReadFromType, sourceFieldOffset, false);

if(FieldRead == null) return;

LocalWritten = context.MakeLocal(FieldRead.GetFinalType(), reg: _destRegName);
FixUpFieldRefForAnyPotentialGenericType(context);
RegisterDefinedLocalWithoutSideEffects(LocalWritten);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public RegToFieldAction(MethodAnalysis<Instruction> context, Instruction instruc
}

RegisterUsedLocal(InstanceBeingSetOn, context);

FieldWritten = FieldUtils.GetFieldBeingAccessed(InstanceBeingSetOn.Type, destFieldOffset, false);
FixUpFieldRefForAnyPotentialGenericType(context);
}

internal RegToFieldAction(MethodAnalysis<Instruction> context, Instruction instruction, FieldUtils.FieldBeingAccessedData fieldWritten, LocalDefinition instanceWrittenOn, LocalDefinition readFrom) : base(context, instruction)
Expand Down
9 changes: 7 additions & 2 deletions Cpp2IL.Core/Analysis/ResultModels/MethodAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using LibCpp2IL;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Instruction = Mono.Cecil.Cil.Instruction;

namespace Cpp2IL.Core.Analysis.ResultModels
Expand Down Expand Up @@ -100,6 +101,10 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia
_allInstructions = new(allInstructions);
EmptyRegConstant = MakeConstant(typeof(int), 0, "0");

var thisParamType = (TypeReference) method.DeclaringType;
if (thisParamType.HasGenericParameters)
thisParamType = thisParamType.MakeGenericInstanceType(thisParamType.GenericParameters.Cast<TypeReference>().ToArray());

var args = method.Parameters.ToList();
var haveHandledMethodInfoArg = false;
//Set up parameters in registers & as locals.
Expand All @@ -121,7 +126,7 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia
if (!method.IsStatic)
{
method.Body.ThisParameter.Name = "this";
FunctionArgumentLocals.Add(MakeLocal(method.DeclaringType, "this", _parameterDestRegList.RemoveAndReturn(0)).WithParameter(method.Body.ThisParameter));
FunctionArgumentLocals.Add(MakeLocal(thisParamType, "this", _parameterDestRegList.RemoveAndReturn(0)).WithParameter(method.Body.ThisParameter));
}

while (args.Count > 0)
Expand All @@ -140,7 +145,7 @@ internal MethodAnalysis(MethodDefinition method, ulong methodStart, ulong initia
{
//x86_32 and wasm are stack based
method.Body.ThisParameter.Name = "this";
FunctionArgumentLocals.Add(MakeLocal(method.DeclaringType, "this").WithParameter(method.Body.ThisParameter));
FunctionArgumentLocals.Add(MakeLocal(thisParamType, "this").WithParameter(method.Body.ThisParameter));
Stack.Push(FunctionArgumentLocals.First());
}

Expand Down
6 changes: 3 additions & 3 deletions Cpp2IL.Core/AttributeRestorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ public static List<CustomAttribute> GetCustomAttributesByAttributeIndex<T>(Il2Cp
var propertyMappings = GetSimplePropertyFieldMappings<T>(attr, keyFunctionAddresses);
foreach (var fieldWriteAction in fieldSets)
{
var field = fieldWriteAction.FieldWritten!.GetLast().FinalLoadInChain!;
var field = fieldWriteAction.FieldWritten!.GetLast().FinalLoadInChain!.Resolve()!;
if(!propertyMappings.TryGetValue(field, out var prop))
//TODO: This may be an actual field set, not a property set - add support
continue;
Expand Down Expand Up @@ -646,7 +646,7 @@ private static object AllocateArray(AllocatedArray array)
return null;
}

ret = fieldWrites!.Select(f => new FieldToParameterMapping(f.FieldWritten!.FinalLoadInChain!, ((LocalDefinition) f.SourceOperand!).ParameterDefinition!)).ToArray();
ret = fieldWrites!.Select(f => new FieldToParameterMapping(f.FieldWritten!.FinalLoadInChain!.Resolve(), ((LocalDefinition) f.SourceOperand!).ParameterDefinition!)).ToArray();

FieldToParameterMappings.TryAdd(constructor, ret);
return ret;
Expand Down Expand Up @@ -808,7 +808,7 @@ private static Dictionary<FieldDefinition, PropertyDefinition> GetSimpleProperty

var fieldBeingWritten = fieldWriteAction.FieldWritten.GetLast();

ret[fieldBeingWritten.FinalLoadInChain!] = propertyDefinition;
ret[fieldBeingWritten.FinalLoadInChain!.Resolve()] = propertyDefinition;
}

_simpleFieldToPropertyCache[type] = ret;
Expand Down
8 changes: 8 additions & 0 deletions Cpp2IL.Core/Utils/CecilUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ public static MethodReference ImportMethodButCleanly(this ModuleDefinition modul
return ret;
}

public static FieldReference ImportFieldButCleanly(this ModuleDefinition module, FieldReference field)
{
var declaringType = module.ImportTypeButCleanly(field.DeclaringType);
var fieldType = module.ImportTypeButCleanly(field.FieldType);

return new(field.Name, fieldType, declaringType);
}

public static TypeReference ImportTypeButCleanly(this ILProcessor processor, TypeReference reference) => processor.Body.Method.Module.ImportTypeButCleanly(reference);

public static MethodReference ImportMethodButCleanly(this ILProcessor processor, MethodReference reference) => processor.Body.Method.Module.ImportMethodButCleanly(reference);
Expand Down
4 changes: 2 additions & 2 deletions Cpp2IL.Core/Utils/FieldUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@ private static List<FieldInType> RecalculateFieldOffsetsForGenericType(TypeRefer
/// </summary>
public class FieldBeingAccessedData
{
public readonly FieldDefinition? ImpliedFieldLoad;
public readonly FieldDefinition? FinalLoadInChain;
public FieldReference? ImpliedFieldLoad;
public FieldReference? FinalLoadInChain;
public readonly FieldBeingAccessedData? NextChainLink;

private FieldBeingAccessedData(FieldDefinition? impliedFieldLoad, FieldDefinition? finalLoadInChain, FieldBeingAccessedData? nextChainLink)
Expand Down

0 comments on commit e01ac8a

Please sign in to comment.