From 87074a606421ecb0163ae9727b8409f9824216b2 Mon Sep 17 00:00:00 2001 From: CCat <63068823+CedaryCat@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:12:46 +0800 Subject: [PATCH 1/3] Relinker: handle variables & parameters --- ModFramework/Relinker/ArrayToCollectionRelinker.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ModFramework/Relinker/ArrayToCollectionRelinker.cs b/ModFramework/Relinker/ArrayToCollectionRelinker.cs index 1a73fbc..a4c642e 100644 --- a/ModFramework/Relinker/ArrayToCollectionRelinker.cs +++ b/ModFramework/Relinker/ArrayToCollectionRelinker.cs @@ -98,6 +98,18 @@ public override void Relink(PropertyDefinition property) property.PropertyType = ICollectionGen; } + public override void Relink(MethodDefinition method, VariableDefinition variable) + { + if (variable.VariableType is ArrayType arrayType && arrayType.ElementType.FullName == this.Type.FullName) + variable.VariableType = ICollectionGen; + } + + public override void Relink(MethodDefinition method, ParameterDefinition parameter) + { + if (parameter.ParameterType is ArrayType arrayType && arrayType.ElementType.FullName == this.Type.FullName) + parameter.ParameterType = ICollectionGen; + } + public override void Relink(MethodBody body, Instruction instr) { if (body.Method.ReturnType is ArrayType arrayType && arrayType.ElementType.FullName == this.Type.FullName) From 8786238bfb315cc16b0ae105f56886fc8c0ce079 Mon Sep 17 00:00:00 2001 From: CCat <63068823+CedaryCat@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:44:11 +0800 Subject: [PATCH 2/3] Handle generic constraints and method overrides in relinker --- ModFramework/Relinker/TypeRelinker.cs | 69 +++++++++++++++------------ 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/ModFramework/Relinker/TypeRelinker.cs b/ModFramework/Relinker/TypeRelinker.cs index 0fc56a0..9b8118f 100644 --- a/ModFramework/Relinker/TypeRelinker.cs +++ b/ModFramework/Relinker/TypeRelinker.cs @@ -101,15 +101,14 @@ bool CheckType(TRef type, Action? update = null) } else { - // TODO determine if this is needed anymore. causes recursion issues but i dont see evidence its needed anymore - //if (type.HasGenericParameters) - // for (int i = 0; i < type.GenericParameters.Count; i++) - // { - // changed |= CheckType( - // type.GenericParameters[i], - // nr => type.GenericParameters[i] = nr - // ); - // } + foreach (var gpm in type.GenericParameters) + { + foreach (var ct in gpm.Constraints) + { + FixAttributes(ct.CustomAttributes); + changed |= CheckType(ct.ConstraintType, ntype => ct.ConstraintType = ntype); + } + } changed |= RelinkType(ref type); } @@ -125,34 +124,39 @@ bool CheckType(TRef type, Action? update = null) return changed; } - public abstract bool RelinkType(ref TRef typeReference) where TRef : TypeReference; - - public void Relink(Instruction instr) + private void CheckMethodRef(MethodReference mref) { - if (instr.Operand is MethodReference mref) + if (mref is GenericInstanceMethod gim) { - if (mref is GenericInstanceMethod gim) - { - CheckType(gim.ElementMethod.DeclaringType, nt => gim.ElementMethod.DeclaringType = nt); + CheckType(gim.ElementMethod.DeclaringType, nt => gim.ElementMethod.DeclaringType = nt); - for (var x = 0; x < gim.GenericArguments.Count; x++) - { - CheckType(gim.GenericArguments[x], nt => gim.GenericArguments[x] = nt); + for (var x = 0; x < gim.GenericArguments.Count; x++) + { + CheckType(gim.GenericArguments[x], nt => gim.GenericArguments[x] = nt); - if (gim.GenericArguments[x].DeclaringType is not null) - CheckType(gim.GenericArguments[x].DeclaringType, nt => gim.GenericArguments[x].DeclaringType = nt); - } + if (gim.GenericArguments[x].DeclaringType is not null) + CheckType(gim.GenericArguments[x].DeclaringType, nt => gim.GenericArguments[x].DeclaringType = nt); } - else - CheckType(mref.DeclaringType, nt => mref.DeclaringType = nt); + } + else + CheckType(mref.DeclaringType, nt => mref.DeclaringType = nt); - CheckType(mref.ReturnType, nt => mref.ReturnType = nt); + CheckType(mref.ReturnType, nt => mref.ReturnType = nt); - foreach (var prm in mref.Parameters) - { - CheckType(prm.ParameterType, nt => prm.ParameterType = nt); - FixAttributes(prm.CustomAttributes); - } + foreach (var prm in mref.Parameters) + { + CheckType(prm.ParameterType, nt => prm.ParameterType = nt); + FixAttributes(prm.CustomAttributes); + } + } + + public abstract bool RelinkType(ref TRef typeReference) where TRef : TypeReference; + + public void Relink(Instruction instr) + { + if (instr.Operand is MethodReference mref) + { + CheckMethodRef(mref); } else if (instr.Operand is FieldReference fref) { @@ -298,6 +302,11 @@ public override void Relink(MethodDefinition method) FixAttributes(prm.CustomAttributes); } + foreach (var ovrd in method.Overrides) + { + CheckMethodRef(ovrd); + } + FixAttributes(method.CustomAttributes); CheckType(method.MethodReturnType.ReturnType, nt => method.MethodReturnType.ReturnType = nt); From fb7030d723476af894d0f70bd62a5a0605e2c624 Mon Sep 17 00:00:00 2001 From: CCat <63068823+CedaryCat@users.noreply.github.com> Date: Sat, 31 Jan 2026 20:55:26 +0800 Subject: [PATCH 3/3] Fix by-ref parameter handling in hook/delegate emission MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve by-ref params via ByReferenceType.ElementType, avoiding GetElementType() over-unwrapping (ref int[] → int) Carry over RefKind + In/Out metadata to emitted delegate parameters Adjust IL emission and generic declaring-type relinking --- ModFramework/Emitters/HookEmitter.cs | 27 ++++++++++--------- .../Extensions/Replacement.Extensions.cs | 6 +++++ ModFramework/Relinker/TypeRelinker.cs | 12 ++++++++- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ModFramework/Emitters/HookEmitter.cs b/ModFramework/Emitters/HookEmitter.cs index 8495a33..cfd3dcf 100644 --- a/ModFramework/Emitters/HookEmitter.cs +++ b/ModFramework/Emitters/HookEmitter.cs @@ -91,8 +91,8 @@ static string GetUniqueName(MethodDefinition method) name += "_" + string.Join("_", method.Parameters.Select(y => { var name = y.ParameterType.Name; - if (y.ParameterType.IsByReference) - name = $"{y.ParameterType.GetElementType().Name}ByRef"; + if (y.ParameterType is ByReferenceType byref) + name = $"{byref.ElementType.Name}ByRef"; return name; })); } @@ -133,7 +133,7 @@ static TypeDefinition CreateHookEventArgs(TypeDefinition hookType, MethodDefinit // for each parameter in the method, create a field foreach (var param in hookDefinition.Parameters) { - var paramType = param.ParameterType.IsByReference ? param.ParameterType.GetElementType() : param.ParameterType; + var paramType = param.ParameterType is ByReferenceType byref ? byref.ElementType : param.ParameterType; FieldDefinition paramField = new(param.Name, FieldAttributes.Public, paramType); hookEvent.Fields.Add(paramField); } @@ -254,7 +254,7 @@ public static TypeDefinition GetOrCreateOriginalMethodDelegate(MonoModder modder }; foreach (var param in originalDefinition.Parameters) - invoke.Parameters.Add(new(param.Name, ParameterAttributes.None, param.ParameterType)); + invoke.Parameters.Add(param.ClonePreservingInOut()); delegateType.Methods.Add(invoke); @@ -266,7 +266,7 @@ public static TypeDefinition GetOrCreateOriginalMethodDelegate(MonoModder modder IsRuntime = true }; foreach (var param in originalDefinition.Parameters) - beginInvoke.Parameters.Add(new(param.Name, ParameterAttributes.None, param.ParameterType)); + beginInvoke.Parameters.Add(param.ClonePreservingInOut()); beginInvoke.Parameters.Add(new("callback", ParameterAttributes.None, iAsyncCallback)); beginInvoke.Parameters.Add(new("object", ParameterAttributes.None, delegateType.Module.TypeSystem.Object)); delegateType.Methods.Add(beginInvoke); @@ -495,7 +495,7 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin // if any out parameters, initialise them with default values foreach (var param in methodDefinition.Parameters.Where(x => x.IsOut)) { - var type = param.ParameterType.GetElementType(); + var type = ((ByReferenceType)param.ParameterType).ElementType; il.Emit(OpCodes.Ldarg_S, param); var defaultValue = CreateDefaultValueInstruction(type); il.Append(defaultValue); @@ -510,14 +510,15 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin il.Emit(OpCodes.Ldftn, original); il.Emit(OpCodes.Newobj, originalMethodField.FieldType.Resolve().Methods.Single(x => x.Name == ".ctor")); - for (int i = 0; i < methodDefinition.Parameters.Count; i++) + foreach (var param in methodDefinition.Parameters) { - var isByRef = methodDefinition.Parameters[i].ParameterType.IsByReference; - var opCode = isByRef ? OpCodes.Ldarg_S : OpCodes.Ldarg; - il.Emit(opCode, methodDefinition.Parameters[i]); - if (isByRef) - il.Append(CreateLoadIndirectInstruction(methodDefinition.Parameters[i].ParameterType.GetElementType())); + il.Emit(OpCodes.Ldarg_S, param); + if (param.ParameterType is ByReferenceType byRefType) + { + il.Append(CreateLoadIndirectInstruction(byRefType.ElementType)); + } } + il.Emit(OpCodes.Call, eventInvoke); // store the event args in a local variable @@ -530,7 +531,7 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin il.Emit(OpCodes.Ldarg_S, param); il.Emit(OpCodes.Ldloc, eventArgsVariable); il.Emit(OpCodes.Ldfld, field); - il.Append(CreateStoreIndirectFunction(field.FieldType.GetElementType())); + il.Append(CreateStoreIndirectFunction(field.FieldType)); } // use ContinueExecutionName to determine whether to continue or not diff --git a/ModFramework/Extensions/Replacement.Extensions.cs b/ModFramework/Extensions/Replacement.Extensions.cs index c2ec28e..9d81248 100644 --- a/ModFramework/Extensions/Replacement.Extensions.cs +++ b/ModFramework/Extensions/Replacement.Extensions.cs @@ -43,4 +43,10 @@ public static PropertyDefinition Clone(this PropertyDefinition property) return prop; } + + public static ParameterDefinition ClonePreservingInOut(this ParameterDefinition p) + { + var attrs = p.Attributes & (ParameterAttributes.In | ParameterAttributes.Out); + return new ParameterDefinition(p.Name, attrs, p.ParameterType); + } } diff --git a/ModFramework/Relinker/TypeRelinker.cs b/ModFramework/Relinker/TypeRelinker.cs index 9b8118f..b227be6 100644 --- a/ModFramework/Relinker/TypeRelinker.cs +++ b/ModFramework/Relinker/TypeRelinker.cs @@ -135,7 +135,17 @@ private void CheckMethodRef(MethodReference mref) CheckType(gim.GenericArguments[x], nt => gim.GenericArguments[x] = nt); if (gim.GenericArguments[x].DeclaringType is not null) - CheckType(gim.GenericArguments[x].DeclaringType, nt => gim.GenericArguments[x].DeclaringType = nt); + CheckType(gim.GenericArguments[x].DeclaringType, nt => + { + if (gim.GenericArguments[x] is GenericParameter gp) + { + gim.GenericArguments[x] = nt.GenericParameters[gp.Position]; + } + else + { + gim.GenericArguments[x].DeclaringType = nt; + } + }); } } else