Skip to content

Instantly share code, notes, and snippets.

@spacechase0
Last active August 19, 2021 15:02
Show Gist options
  • Save spacechase0/a30369905a4ae28ea1d7b93a60b2159b to your computer and use it in GitHub Desktop.
Save spacechase0/a30369905a4ae28ea1d7b93a60b2159b to your computer and use it in GitHub Desktop.
Adding fields dynamically to XMLSerializer test
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