Skip to content

Commit fb7030d

Browse files
committed
Fix by-ref parameter handling in hook/delegate emission
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
1 parent 8786238 commit fb7030d

3 files changed

Lines changed: 31 additions & 14 deletions

File tree

ModFramework/Emitters/HookEmitter.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ static string GetUniqueName(MethodDefinition method)
9191
name += "_" + string.Join("_", method.Parameters.Select(y =>
9292
{
9393
var name = y.ParameterType.Name;
94-
if (y.ParameterType.IsByReference)
95-
name = $"{y.ParameterType.GetElementType().Name}ByRef";
94+
if (y.ParameterType is ByReferenceType byref)
95+
name = $"{byref.ElementType.Name}ByRef";
9696
return name;
9797
}));
9898
}
@@ -133,7 +133,7 @@ static TypeDefinition CreateHookEventArgs(TypeDefinition hookType, MethodDefinit
133133
// for each parameter in the method, create a field
134134
foreach (var param in hookDefinition.Parameters)
135135
{
136-
var paramType = param.ParameterType.IsByReference ? param.ParameterType.GetElementType() : param.ParameterType;
136+
var paramType = param.ParameterType is ByReferenceType byref ? byref.ElementType : param.ParameterType;
137137
FieldDefinition paramField = new(param.Name, FieldAttributes.Public, paramType);
138138
hookEvent.Fields.Add(paramField);
139139
}
@@ -254,7 +254,7 @@ public static TypeDefinition GetOrCreateOriginalMethodDelegate(MonoModder modder
254254
};
255255

256256
foreach (var param in originalDefinition.Parameters)
257-
invoke.Parameters.Add(new(param.Name, ParameterAttributes.None, param.ParameterType));
257+
invoke.Parameters.Add(param.ClonePreservingInOut());
258258

259259
delegateType.Methods.Add(invoke);
260260

@@ -266,7 +266,7 @@ public static TypeDefinition GetOrCreateOriginalMethodDelegate(MonoModder modder
266266
IsRuntime = true
267267
};
268268
foreach (var param in originalDefinition.Parameters)
269-
beginInvoke.Parameters.Add(new(param.Name, ParameterAttributes.None, param.ParameterType));
269+
beginInvoke.Parameters.Add(param.ClonePreservingInOut());
270270
beginInvoke.Parameters.Add(new("callback", ParameterAttributes.None, iAsyncCallback));
271271
beginInvoke.Parameters.Add(new("object", ParameterAttributes.None, delegateType.Module.TypeSystem.Object));
272272
delegateType.Methods.Add(beginInvoke);
@@ -495,7 +495,7 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin
495495
// if any out parameters, initialise them with default values
496496
foreach (var param in methodDefinition.Parameters.Where(x => x.IsOut))
497497
{
498-
var type = param.ParameterType.GetElementType();
498+
var type = ((ByReferenceType)param.ParameterType).ElementType;
499499
il.Emit(OpCodes.Ldarg_S, param);
500500
var defaultValue = CreateDefaultValueInstruction(type);
501501
il.Append(defaultValue);
@@ -510,14 +510,15 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin
510510
il.Emit(OpCodes.Ldftn, original);
511511
il.Emit(OpCodes.Newobj, originalMethodField.FieldType.Resolve().Methods.Single(x => x.Name == ".ctor"));
512512

513-
for (int i = 0; i < methodDefinition.Parameters.Count; i++)
513+
foreach (var param in methodDefinition.Parameters)
514514
{
515-
var isByRef = methodDefinition.Parameters[i].ParameterType.IsByReference;
516-
var opCode = isByRef ? OpCodes.Ldarg_S : OpCodes.Ldarg;
517-
il.Emit(opCode, methodDefinition.Parameters[i]);
518-
if (isByRef)
519-
il.Append(CreateLoadIndirectInstruction(methodDefinition.Parameters[i].ParameterType.GetElementType()));
515+
il.Emit(OpCodes.Ldarg_S, param);
516+
if (param.ParameterType is ByReferenceType byRefType)
517+
{
518+
il.Append(CreateLoadIndirectInstruction(byRefType.ElementType));
519+
}
520520
}
521+
521522
il.Emit(OpCodes.Call, eventInvoke);
522523

523524
// store the event args in a local variable
@@ -530,7 +531,7 @@ static MethodDefinition CreateReplacement(MethodDefinition original, MethodDefin
530531
il.Emit(OpCodes.Ldarg_S, param);
531532
il.Emit(OpCodes.Ldloc, eventArgsVariable);
532533
il.Emit(OpCodes.Ldfld, field);
533-
il.Append(CreateStoreIndirectFunction(field.FieldType.GetElementType()));
534+
il.Append(CreateStoreIndirectFunction(field.FieldType));
534535
}
535536

536537
// use ContinueExecutionName to determine whether to continue or not

ModFramework/Extensions/Replacement.Extensions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,10 @@ public static PropertyDefinition Clone(this PropertyDefinition property)
4343

4444
return prop;
4545
}
46+
47+
public static ParameterDefinition ClonePreservingInOut(this ParameterDefinition p)
48+
{
49+
var attrs = p.Attributes & (ParameterAttributes.In | ParameterAttributes.Out);
50+
return new ParameterDefinition(p.Name, attrs, p.ParameterType);
51+
}
4652
}

ModFramework/Relinker/TypeRelinker.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,17 @@ private void CheckMethodRef(MethodReference mref)
135135
CheckType(gim.GenericArguments[x], nt => gim.GenericArguments[x] = nt);
136136

137137
if (gim.GenericArguments[x].DeclaringType is not null)
138-
CheckType(gim.GenericArguments[x].DeclaringType, nt => gim.GenericArguments[x].DeclaringType = nt);
138+
CheckType(gim.GenericArguments[x].DeclaringType, nt =>
139+
{
140+
if (gim.GenericArguments[x] is GenericParameter gp)
141+
{
142+
gim.GenericArguments[x] = nt.GenericParameters[gp.Position];
143+
}
144+
else
145+
{
146+
gim.GenericArguments[x].DeclaringType = nt;
147+
}
148+
});
139149
}
140150
}
141151
else

0 commit comments

Comments
 (0)