Last active
August 19, 2021 15:02
-
-
Save spacechase0/a30369905a4ae28ea1d7b93a60b2159b to your computer and use it in GitHub Desktop.
Adding fields dynamically to XMLSerializer test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using HarmonyLib; | |
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Globalization; | |
using System.IO; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Xml.Serialization; | |
namespace DynamicFieldsTest | |
{ | |
public class Root | |
{ | |
public int A; | |
public double B { get; set; } | |
} | |
public static class RootC | |
{ | |
internal class Holder { public string Value { get; set; } } | |
internal static ConditionalWeakTable< Root, Holder > values = new ConditionalWeakTable<Root, Holder>(); | |
public static void SetC( this Root root, string value ) | |
{ | |
values.GetOrCreateValue( root ).Value = value; | |
} | |
public static string GetC( this Root root ) | |
{ | |
return values.GetOrCreateValue( root ).Value; | |
} | |
} | |
public class RootCPropertyInfo : PropertyInfo | |
{ | |
public override Type PropertyType => typeof( string ); | |
public override PropertyAttributes Attributes => PropertyAttributes.None; | |
public override bool CanRead => true; | |
public override bool CanWrite => true; | |
public override string Name => "C"; | |
public override Type DeclaringType => typeof( Root ); | |
public override Type ReflectedType => typeof( Root ); | |
public override MethodInfo[] GetAccessors( bool nonPublic ) | |
{ | |
return new MethodInfo[] | |
{ | |
typeof( RootC ).GetMethod( "GetC" ), | |
typeof( RootC ).GetMethod( "SetC" ), | |
typeof( RootC ).GetMethod( "GetC" ), | |
typeof( RootC ).GetMethod( "SetC" ), | |
}; | |
} | |
public override object[] GetCustomAttributes( bool inherit ) | |
{ | |
return new object[0]; | |
} | |
public override object[] GetCustomAttributes( Type attributeType, bool inherit ) | |
{ | |
return new object[ 0 ]; | |
} | |
public override MethodInfo GetGetMethod( bool nonPublic ) | |
{ | |
return GetAccessors( nonPublic )[ 0 ]; | |
} | |
public override ParameterInfo[] GetIndexParameters() | |
{ | |
return new ParameterInfo[ 0 ]; | |
} | |
public override MethodInfo GetSetMethod( bool nonPublic ) | |
{ | |
return GetAccessors( nonPublic )[ 1 ]; | |
} | |
public override object GetValue( object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture ) | |
{ | |
return RootC.GetC( obj as Root ); | |
} | |
public override bool IsDefined( Type attributeType, bool inherit ) | |
{ | |
return false; | |
} | |
public override void SetValue( object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture ) | |
{ | |
RootC.SetC( obj as Root, value as string ); | |
} | |
} | |
class Program | |
{ | |
static void Main( string[] args ) | |
{ | |
var harmony = new Harmony( "spacechase0.xmltest" ); | |
{ | |
var type = AccessTools.TypeByName( "System.Xml.Serialization.StructModel" ); | |
harmony.Patch( AccessTools.Method( type, "GetMemberInfos" ), postfix: new HarmonyMethod( AccessTools.Method( typeof( Program ), nameof( Program.StructModel_GetMemberInfos_Postfix ) ) ) ); | |
harmony.Patch( AccessTools.Method(type,"GetPropertyModel"), postfix: new HarmonyMethod( AccessTools.Method( typeof( Program ), nameof( Program.StructModel_GetPropertyModel_Postfix ) ) ) ); | |
} | |
var root1 = new Root(); | |
root1.A = 1; | |
root1.B = 2; | |
root1.SetC( "3" ); | |
var serializer = new XmlSerializer( typeof( Root ) ); | |
using ( StringWriter writer = new StringWriter() ) | |
{ | |
serializer.Serialize( writer, root1 ); | |
string str = writer.ToString(); | |
Console.WriteLine( str ); | |
var root2 = ( Root ) serializer.Deserialize( new StringReader( str ) ); | |
Console.WriteLine( $"root1: A={root1.A} B={root1.B} C={root1.GetC()}" ); | |
Console.WriteLine( $"root2: A={root2.A} B={root2.B} C={root2.GetC()}" ); | |
} | |
Console.ReadKey(); | |
} | |
private static void StructModel_GetMemberInfos_Postfix( object __instance, ref MemberInfo[] __result ) | |
{ | |
var type = ( Type ) AccessTools.Field( __instance.GetType(), "type" ).GetValue( __instance ); | |
if ( type != typeof( Root ) ) | |
return; | |
var rootC = new RootCPropertyInfo(); | |
var ret = new List<MemberInfo>( __result ); | |
ret.Add( rootC ); | |
__result = ret.ToArray(); | |
} | |
private static void StructModel_GetPropertyModel_Postfix( object __instance, PropertyInfo propertyInfo, ref object __result ) | |
{ | |
// This patch is necessary because it doesn't like static methods for the property getter | |
// TODO: Test transpiling the above check (in CheckPropertyRead) out? | |
if ( __result == null && propertyInfo.DeclaringType == typeof( Root ) && propertyInfo.Name == "C" ) | |
{ | |
var type = AccessTools.TypeByName( "System.Xml.Serialization.FieldModel" ); | |
var typeCParam = AccessTools.TypeByName( "System.Xml.Serialization.TypeDesc" ); | |
var modelScope = AccessTools.Property( __instance.GetType(), "ModelScope" ).GetValue( __instance ); | |
var typeScope = AccessTools.Property( modelScope.GetType(), "TypeScope" ).GetValue( modelScope ); | |
var typeDesc = AccessTools.Method( typeScope.GetType(), "GetTypeDesc", | |
new Type[] { typeof( Type ), typeof( MemberInfo ), typeof( bool ), typeof( bool ) } ) | |
.Invoke( typeScope, new object[] { propertyInfo.PropertyType, propertyInfo, true, false } ); | |
__result = AccessTools.Constructor( type, new Type[] { typeof( MemberInfo ), typeof( Type ), typeCParam } ).Invoke( new object[] { propertyInfo, typeof( string ), typeDesc } ); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment