Open xADDBx opened 2 months ago
I just tried testing the different possible combinations of IsLiteral, IsInitOnly and IsStatic. At the same time I noticed that static readonly fields will also cause an exception:
FieldModifiers | IsLiteral | IsInitOnly | IsStatic | Causes Exception |
---|---|---|---|---|
False | False | False | No | |
readonly | False | True | False | No |
static | False | False | True | No |
static readonly | False | True | True | Yes |
const | True | False | True | Yes |
Updated Test class:
public class Test {
public const string MyConstField = "Don't set me";
public string MyField = "Set me";
public static string MyStaticField = "Set me";
public readonly string MyReadonlyField = "Set me";
public static readonly string MyStaticReadonlyField = "Don't set me";
}
MakeDeepCopy will throw for both MyStaticReadonlyField
and MyConstField
. Changing the Traverse.SetValue to the following seemed to work for my test case:
[HarmonyPatch]
public static class Patches {
[HarmonyPatch(typeof(Traverse), nameof(Traverse.SetValue)), HarmonyPrefix]
public static bool SetValue(object value, ref Traverse __result, Traverse __instance) {
var infoField = AccessTools.Field(typeof(Traverse), "_info");
var methodField = AccessTools.Field(typeof(Traverse), "_method");
var rootField = AccessTools.Field(typeof(Traverse), "_root");
var paramsField = AccessTools.Field(typeof(Traverse), "_params");
MemberInfo _info = infoField.GetValue(__instance) as MemberInfo;
MethodBase _method = methodField.GetValue(__instance) as MethodBase;
object[] _params = paramsField.GetValue(__instance) as object[];
object _root = rootField.GetValue(__instance);
if (_info is FieldInfo fi) {
bool isConst = fi.IsLiteral && !fi.IsInitOnly && fi.IsStatic;
bool isStaticReadonly = !fi.IsLiteral && fi.IsInitOnly && fi.IsStatic;
if (!isConst && !isStaticReadonly)
((FieldInfo)_info).SetValue(_root, value, AccessTools.all, null, CultureInfo.CurrentCulture);
}
if (_info is PropertyInfo)
((PropertyInfo)_info).SetValue(_root, value, AccessTools.all, null, _params, CultureInfo.CurrentCulture);
if (_method is not null)
throw new Exception($"cannot set value of method {_method.FullDescription()}");
__result = __instance;
return false;
}
}
Actually, is there a reason it tries to set static Fields at all? While it works on normal static fields, does the method really need to set static fields which should be shared across instances anyway?
In total I see three different approaches:
static readonly
or const
as shown above and don't set those fieldsstatic
field and don't set those (depending on where Traverse.SetValue is used, this might not be a good idea)?Edit: To make sure this doesn't only apply to public string, I also tested for private int fields, which showed exactly the same behavior.
Describe the bug When using AccessTools.MakeDeepCopy on a class which contains a constant field, Harmony will cause a
System.FieldAccessException: Cannot set a constant field
.To Reproduce
Expected behavior Const fields should be ignored when creating a DeepCopy
Screenshots / Code
Runtime environment (please complete the following information):
OS:
Windows 10, 64bit.NET Version:
I've managed to reproduce this error in .NET Framework 4.8.1 (Unity and non-Unity runtime), .NET 6.0 and .NET 8.0. I assume it's an issue on all versionsHarmony Version:
Tested on Harmony 2.3.3 and Harmony 2.3.1.1.Runtime:
I've reproduced it in bothPathfinder: Wrath of the Righteous
andWarhammer 40,000: Rogue Trader
additionally I can reproduce this in a simple console application