Created
July 19, 2012 05:21
-
-
Save rpgmaker/3140937 to your computer and use it in GitHub Desktop.
XmlSerializer Generator (Everything(include deserialize classes with properties) work except for Deserializing List/Dictionary/Array
This file contains hidden or 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 System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Collections.Concurrent; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
using System.Xml; | |
using System.IO; | |
using System.Collections; | |
using System.Text.RegularExpressions; | |
using System.Security; | |
namespace PServiceBus.Serializer.Xml { | |
public static class Generator { | |
const int DefaultStringBuilderCapacity = 1024 * 2; | |
const TypeAttributes TypeAttribute = | |
TypeAttributes.Public | TypeAttributes.Serializable | TypeAttributes.Sealed; | |
const BindingFlags PropertyBinding = BindingFlags.Instance | BindingFlags.Public; | |
const BindingFlags MethodBinding = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; | |
const MethodAttributes MethodAttribute = | |
MethodAttributes.Public | |
| MethodAttributes.Virtual | |
| MethodAttributes.Final | |
| MethodAttributes.HideBySig | |
| MethodAttributes.NewSlot | |
| MethodAttributes.SpecialName; | |
static readonly ConcurrentDictionary<Type, Type> _types = | |
new ConcurrentDictionary<Type, Type>(); | |
static readonly string _xmlSerializerStr = "XmlSerializer", | |
_classStr = "Class", _dllStr = ".dll", | |
_quote = "`", _iListStr = "IList`1", | |
iDictStr = "IDictionary`2", | |
_xmlSerializeStr = "IXmlSerializer.Serialize", | |
_xmlDeserializeStr = "IXmlSerializer.Deserialize", | |
_serializeStr = "Serialize", _deserializeStr = "Deserialize", | |
_writeStr = "Write", _readStr = "Read", _arrayLiteral = "[]", | |
_arrayStr = "Array", _anonymousBracketStr = "<>", | |
_typeStr = "type", _nilStr = "nil", | |
_anonymousStr = "_anonymoustype", _itemsStr = "Items", | |
_itemStr = "Item", _dictStr = "dict", _keyStr = "Key", _valueStr = "Value", | |
_capacityStr = "Capacity"; | |
static readonly Type _objectType = typeof(Object), | |
_serializerType = typeof(IXmlSerializer<>), | |
_stringType = typeof(String), | |
_xmlWriterType = typeof(XmlWriter), | |
_xmlWriterSettingType = typeof(XmlWriterSettings), | |
_stringBuilderType = typeof(StringBuilder), | |
_memoryStreamType = typeof(MemoryStream), | |
_streamWriterType = typeof(StreamWriter), | |
_voidType = typeof(void), | |
_intType = typeof(int), | |
_xmlReaderType = typeof(XmlReader), | |
_nullableType = typeof(Nullable<>), | |
_dateTimeType = typeof(DateTime), | |
_timeSpanType = typeof(TimeSpan), | |
_decimalType = typeof(decimal), | |
_byteArray = typeof(byte[]), | |
_listType = typeof(IList), | |
_dictType = typeof(IDictionary), | |
_genericDictType = typeof(Dictionary<,>), | |
_genericListType = typeof(List<>), | |
_genericKeyValuePairType = typeof(KeyValuePair<,>), | |
_genericDictionaryEnumerator = | |
Type.GetType("System.Collections.Generic.Dictionary`2+Enumerator"), | |
_genericListEnumerator = | |
Type.GetType("System.Collections.Generic.List`1+Enumerator"), | |
_typeType = typeof(Type), | |
_generatorType = typeof(Generator); | |
static readonly MethodInfo _stringBuilderToString = | |
_stringBuilderType.GetMethod("ToString", Type.EmptyTypes), | |
_iDisposableDispose = typeof(IDisposable).GetMethod("Dispose"), | |
_objectToString = _objectType.GetMethod("ToString"), | |
_objectGetType = _objectType.GetMethod("GetType"), | |
_typeName = _typeType.GetProperty("Name").GetGetMethod(), | |
_generatorWriteObject = _generatorType.GetMethod("WriteObject", MethodBinding), | |
_generatorReadObject = _generatorType.GetMethod("ReadObject", MethodBinding), | |
_generatorFix = _generatorType.GetMethod("Fix", MethodBinding), | |
_generatorIsByteArrayType = _generatorType.GetMethod("IsByteArrayType", MethodBinding), | |
_generatorIsPrimitiveType = _generatorType.GetMethod("IsPrimitiveType", MethodBinding), | |
_generatorIsClassType = _generatorType.GetMethod("IsClassType", MethodBinding), | |
_convertToBase64String = typeof(Convert).GetMethod("ToBase64String", new []{ _byteArray }), | |
_xmlWriterCreate = _xmlWriterType.GetMethod("Create", | |
MethodBinding, null, new[] { _stringBuilderType, _xmlWriterSettingType }, null), | |
_xmlWriterFlush = _xmlWriterType.GetMethod("Flush"), | |
_xmlWriterWriteValue = _xmlWriterType.GetMethod("WriteValue", new []{ _stringType }), | |
_xmlWriterClose = _xmlWriterType.GetMethod("Close"), | |
_xmlWriterWriteStartElement = _xmlWriterType.GetMethod("WriteStartElement", new []{ _stringType }), | |
_xmlWriterWriteAttributeString = _xmlWriterType.GetMethod("WriteAttributeString", new []{ _stringType, _stringType}), | |
_xmlWriterWriteEndElement = _xmlWriterType.GetMethod("WriteEndElement"), | |
_streamWriterWrite = _streamWriterType.GetMethod("Write", new[] { _stringType }), | |
_streamWriterFlush = _streamWriterType.GetMethod("Flush"), | |
_streamWriterClose = _streamWriterType.GetMethod("Close"), | |
_memoryStreamClose = typeof(Stream).GetMethod("Close"), | |
_memoryStreamPosition = _memoryStreamType.GetProperty("Position").GetSetMethod(), | |
_xmlReaderRead = _xmlReaderType.GetMethod("Read"), | |
_xmlReaderClose = _xmlReaderType.GetMethod("Close"), | |
_xmlReaderCreate = _xmlReaderType.GetMethod("Create", MethodBinding, null, new[] { typeof(Stream) }, null), | |
_xmlWriterSettingOmitXmlDeclaration = | |
_xmlWriterSettingType.GetProperty("OmitXmlDeclaration").GetSetMethod(); | |
static readonly ConstructorInfo _stringBuilderCtor = | |
_stringBuilderType.GetConstructor(new[] { typeof(int) }), | |
_memoryStreamCtor = _memoryStreamType.GetConstructor(Type.EmptyTypes), | |
_streamWriterCtor = _streamWriterType.GetConstructor(new[] { typeof(Stream) }), | |
_xmlWriterSettingCtor = _xmlWriterSettingType.GetConstructor(Type.EmptyTypes); | |
static readonly Type[] _stringParamTypes = | |
new[] { _stringType }; | |
static readonly Regex _invalidChar = new Regex(@"(>|<|'|""|&)", RegexOptions.Compiled | | |
RegexOptions.IgnoreCase | RegexOptions.Multiline); | |
static readonly ConcurrentDictionary<string, MethodBuilder> _writeMethodBuilders = | |
new ConcurrentDictionary<string, MethodBuilder>(); | |
static readonly ConcurrentDictionary<string, MethodBuilder> _readMethodBuilders = | |
new ConcurrentDictionary<string, MethodBuilder>(); | |
static readonly ConcurrentDictionary<Type, bool> _primitiveTypes = | |
new ConcurrentDictionary<Type, bool>(); | |
static readonly ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> _typeProperties = | |
new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>(); | |
public static string Fix(this string name) { | |
return name.Replace(_quote, string.Empty).Replace(_arrayLiteral, _arrayStr).Replace(_anonymousBracketStr, string.Empty); | |
} | |
public static bool IsPrimitiveType(this Type type) { | |
return _primitiveTypes.GetOrAdd(type, key => | |
{ | |
if (key.IsGenericType && | |
key.GetGenericTypeDefinition() == _nullableType) | |
key = key.GetGenericArguments()[0]; | |
return key == _stringType || | |
key.IsPrimitive || key == _dateTimeType || | |
key == _decimalType || key == _timeSpanType || | |
key.IsEnum || key == _byteArray; | |
}); | |
} | |
internal static object ChangeType(this string value, Type type) { | |
return | |
type.IsEnum ? Enum.Parse(type, value) : | |
type == typeof(byte[]) ? Convert.FromBase64String(value) : | |
type == typeof(Guid) ? new Guid(value) : | |
type == typeof(TimeSpan) ? TimeSpan.Parse(value) : | |
Convert.ChangeType(value, type); | |
} | |
internal static string Escape(this string str) { | |
if (_invalidChar.IsMatch(str)) | |
return SecurityElement.Escape(str); | |
return str; | |
} | |
public static void WriteObject(object obj, string name, XmlWriter writer) { | |
writer.WriteStartElement(name); | |
if (obj == null) writer.WriteAttributeString(_typeStr, _nilStr); | |
else { | |
var buffer = obj as byte[]; | |
var str = obj as string; | |
var value = buffer != null ? Convert.ToBase64String(buffer) : | |
str != null ? str.Escape() : obj.ToString(); | |
writer.WriteValue(value); | |
} | |
writer.WriteEndElement(); | |
} | |
public static T ReadObject<T>(XmlReader reader) { | |
reader.Read(); | |
return (T)reader.ReadString().ChangeType(typeof(T)); | |
} | |
internal static bool IsListType(this Type type) { | |
return _listType.IsAssignableFrom(type) || type.Name == _iListStr; | |
} | |
internal static bool IsDictionaryType(this Type type) { | |
return _dictType.IsAssignableFrom(type) || type.Name == iDictStr; | |
} | |
public static bool IsCollectionType(this Type type) { | |
return type.IsListType() || type.IsDictionaryType(); | |
} | |
public static bool IsClassType(this Type type) { | |
return !type.IsCollectionType() && !type.IsPrimitiveType(); | |
} | |
internal static IEnumerable<PropertyInfo> GetTypeProperties(this Type type) { | |
return _typeProperties.GetOrAdd(type, key => key.GetProperties(PropertyBinding)); | |
} | |
public static Type Generate(Type objType) { | |
var returnType = default(Type); | |
if (_types.TryGetValue(objType, out returnType)) | |
return returnType; | |
var isPrimitive = objType.IsPrimitiveType(); | |
var genericType = _serializerType.MakeGenericType(objType); | |
var objTypeName = objType.IsGenericType ? | |
String.Concat(objType.Name, | |
String.Join(string.Empty, | |
objType.GetGenericArguments().Select(x => x.FullName))) : | |
objType.Name; | |
var typeName = String.Concat(objTypeName.Fix(), _xmlSerializerStr); | |
var asmName = String.Concat(typeName, _classStr); | |
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly( | |
new AssemblyName(asmName) { | |
Version = new Version(1, 0, 0, 0) }, | |
AssemblyBuilderAccess.RunAndSave); | |
var module = assembly.DefineDynamicModule(String.Concat(typeName, _dllStr)); | |
var type = module.DefineType(typeName, TypeAttribute, _objectType, new[] { genericType }); | |
var serializeMethod = type.DefineMethod(_xmlSerializeStr, MethodAttribute, | |
_stringType, new[] { objType }); | |
var deserializeMethod = type.DefineMethod(_xmlDeserializeStr, MethodAttribute, objType, | |
_stringParamTypes); | |
var serializeIL = serializeMethod.GetILGenerator(); | |
var deserializeIL = deserializeMethod.GetILGenerator(); | |
//Serialize | |
var sbLocal = serializeIL.DeclareLocal(_stringBuilderType); | |
var writerLocal = serializeIL.DeclareLocal(_xmlWriterType); | |
var writerSettingLocal = serializeIL.DeclareLocal(_xmlWriterSettingType); | |
serializeIL.Emit(OpCodes.Ldc_I4, DefaultStringBuilderCapacity); | |
serializeIL.Emit(OpCodes.Newobj, _stringBuilderCtor); | |
serializeIL.Emit(OpCodes.Stloc, sbLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Newobj, _xmlWriterSettingCtor); | |
serializeIL.Emit(OpCodes.Stloc, writerSettingLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Ldloc, writerSettingLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Ldc_I4_1); | |
serializeIL.Emit(OpCodes.Callvirt, _xmlWriterSettingOmitXmlDeclaration); | |
serializeIL.Emit(OpCodes.Ldloc, sbLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Ldloc, writerSettingLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Call, _xmlWriterCreate); | |
serializeIL.Emit(OpCodes.Stloc, writerLocal.LocalIndex); | |
if (!isPrimitive) serializeIL.Emit(OpCodes.Ldarg_0); | |
serializeIL.Emit(OpCodes.Ldarg_1); | |
serializeIL.Emit(OpCodes.Ldarg_1); | |
serializeIL.Emit(OpCodes.Call, _objectGetType); | |
serializeIL.Emit(OpCodes.Callvirt, _typeName); | |
serializeIL.Emit(OpCodes.Call, _generatorFix); | |
serializeIL.Emit(OpCodes.Ldloc, writerLocal.LocalIndex); | |
serializeIL.Emit(isPrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
WriteSerializeMethodFor(type, objType)); | |
serializeIL.Emit(OpCodes.Ldloc, writerLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Callvirt, _xmlWriterFlush); | |
serializeIL.Emit(OpCodes.Ldloc, writerLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Callvirt, _xmlWriterClose); | |
serializeIL.Emit(OpCodes.Ldloc, sbLocal.LocalIndex); | |
serializeIL.Emit(OpCodes.Callvirt, _stringBuilderToString); | |
serializeIL.Emit(OpCodes.Ret); | |
//Deserialize | |
var msLocal = deserializeIL.DeclareLocal(_memoryStreamType); | |
var swLocal = deserializeIL.DeclareLocal(_streamWriterType); | |
var readerLocal = deserializeIL.DeclareLocal(_xmlReaderType); | |
var returnLocal = deserializeIL.DeclareLocal(objType); | |
deserializeIL.Emit(OpCodes.Newobj, _memoryStreamCtor); | |
deserializeIL.Emit(OpCodes.Stloc, msLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ldloc, msLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Newobj, _streamWriterCtor); | |
deserializeIL.Emit(OpCodes.Stloc, swLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ldloc, swLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ldarg_1); | |
deserializeIL.Emit(OpCodes.Callvirt, _streamWriterWrite); | |
deserializeIL.Emit(OpCodes.Ldloc, swLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Callvirt, _streamWriterFlush); | |
deserializeIL.Emit(OpCodes.Ldloc, msLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ldc_I8, 0L); | |
deserializeIL.Emit(OpCodes.Callvirt, _memoryStreamPosition); | |
deserializeIL.Emit(OpCodes.Ldloc, msLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Call, _xmlReaderCreate); | |
deserializeIL.Emit(OpCodes.Stloc, readerLocal.LocalIndex); | |
if (!isPrimitive) deserializeIL.Emit(OpCodes.Ldarg_0); | |
deserializeIL.Emit(OpCodes.Ldloc, readerLocal.LocalIndex); | |
deserializeIL.Emit(isPrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
WriteDeserializeMethodFor(type, objType)); | |
deserializeIL.Emit(OpCodes.Stloc, returnLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ldloc, swLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Callvirt, _streamWriterClose); | |
deserializeIL.Emit(OpCodes.Ldloc, msLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Callvirt, _memoryStreamClose); | |
deserializeIL.Emit(OpCodes.Ldloc, readerLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Callvirt, _xmlReaderClose); | |
deserializeIL.Emit(OpCodes.Ldloc, returnLocal.LocalIndex); | |
deserializeIL.Emit(OpCodes.Ret); | |
type.DefineMethodOverride(serializeMethod, | |
genericType.GetMethod(_serializeStr)); | |
type.DefineMethodOverride(deserializeMethod, | |
genericType.GetMethod(_deserializeStr)); | |
returnType = type.CreateType(); | |
_types[objType] = returnType; | |
assembly.Save(String.Concat(typeName, _dllStr)); | |
return returnType; | |
} | |
internal static MethodInfo WriteSerializeMethodFor(TypeBuilder typeBuilder, Type type) { | |
if (type.IsPrimitiveType()) return _generatorWriteObject; | |
MethodBuilder method; | |
var key = type.FullName; | |
var typeName = type.IsGenericType ? | |
String.Concat(type.Name, String.Join(string.Empty, | |
type.GetGenericArguments().Select(x => x.FullName))) : | |
type.Name; | |
typeName = typeName.Fix(); | |
if (_writeMethodBuilders.TryGetValue(key, out method)) | |
return method; | |
var methodName = String.Concat(_writeStr, typeName); | |
method = typeBuilder.DefineMethod(methodName, MethodAttribute, | |
_voidType, new []{ type, _stringType, _xmlWriterType }); | |
_writeMethodBuilders[key] = method; | |
var methodIL = method.GetILGenerator(); | |
WriteSerializeFor(typeBuilder, type, methodIL); | |
methodIL.Emit(OpCodes.Ret); | |
return method; | |
} | |
internal static MethodInfo WriteDeserializeMethodFor(TypeBuilder typeBuilder, Type type) { | |
if (type.IsPrimitiveType()) | |
return _generatorReadObject.MakeGenericMethod(type); | |
MethodBuilder method; | |
var key = type.FullName; | |
var typeName = type.IsGenericType ? | |
String.Concat(type.Name, String.Join(string.Empty, | |
type.GetGenericArguments().Select(x => x.FullName))) : | |
type.Name; | |
typeName = typeName.Fix(); | |
if (_readMethodBuilders.TryGetValue(key, out method)) | |
return method; | |
var methodName = String.Concat(_readStr, typeName); | |
method = typeBuilder.DefineMethod(methodName, MethodAttribute, | |
type, new[] { _xmlReaderType }); | |
_readMethodBuilders[key] = method; | |
var methodIL = method.GetILGenerator(); | |
WriteDeserializeFor(typeBuilder, type, methodIL); | |
methodIL.Emit(OpCodes.Ret); | |
return method; | |
} | |
internal static void ReadPropertiesFor(TypeBuilder typeBuilder, Type type, LocalBuilder valueLocal, ILGenerator methodIL) { | |
var isClass = type.IsClass; | |
var props = type.GetTypeProperties(); | |
foreach (var prop in props) { | |
var name = prop.Name; | |
var propType = prop.PropertyType; | |
var isPrimitive = propType.IsPrimitiveType(); | |
var method = WriteDeserializeMethodFor(typeBuilder, propType); | |
methodIL.Emit(isClass ? OpCodes.Ldloc : OpCodes.Ldloca, | |
valueLocal); | |
if (!isPrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit( | |
isPrimitive ? OpCodes.Call : OpCodes.Callvirt, method); | |
methodIL.Emit(isClass ? OpCodes.Callvirt : OpCodes.Call, | |
prop.GetSetMethod()); | |
} | |
} | |
internal static void WritePropertiesFor(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldarg_2); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteStartElement); | |
var props = type.GetTypeProperties(); | |
foreach (var prop in props) { | |
var name = prop.Name; | |
var propType = prop.PropertyType; | |
var isPrimitive = propType.IsPrimitiveType(); | |
var method = WriteSerializeMethodFor(typeBuilder, propType); | |
if (!isPrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Callvirt, prop.GetGetMethod()); | |
if (propType.IsValueType) | |
methodIL.Emit(OpCodes.Box, propType); | |
methodIL.Emit(OpCodes.Ldstr, name); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit( | |
isPrimitive ? OpCodes.Call : OpCodes.Callvirt, method); | |
} | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteEndElement); | |
} | |
internal static void WriteDictionary(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
var arguments = type.GetGenericArguments(); | |
var keyType = arguments[0]; | |
var valueType = arguments[1]; | |
var isKeyPrimitive = keyType.IsPrimitiveType(); | |
var isValuePrimitive = valueType.IsPrimitiveType(); | |
var keyValuePairType = _genericKeyValuePairType.MakeGenericType(keyType, valueType); | |
var enumeratorType = _genericDictionaryEnumerator.MakeGenericType(keyType, valueType); | |
var enumeratorLocal = methodIL.DeclareLocal(enumeratorType); | |
var entryLocal = methodIL.DeclareLocal(keyValuePairType); | |
var startEnumeratorLabel = methodIL.DefineLabel(); | |
var moveNextLabel = methodIL.DefineLabel(); | |
var endEnumeratorLabel = methodIL.DefineLabel(); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Callvirt, | |
_genericDictType.MakeGenericType(keyType, valueType).GetMethod("GetEnumerator")); | |
methodIL.Emit(OpCodes.Stloc_S, enumeratorLocal.LocalIndex); | |
methodIL.BeginExceptionBlock(); | |
methodIL.Emit(OpCodes.Br, startEnumeratorLabel); | |
methodIL.MarkLabel(moveNextLabel); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, | |
enumeratorLocal.LocalType.GetProperty("Current") | |
.GetGetMethod()); | |
methodIL.Emit(OpCodes.Stloc, entryLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldstr, _itemStr); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteStartElement); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldstr, _typeStr); | |
methodIL.Emit(OpCodes.Ldstr, _dictStr); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteAttributeString); | |
var keyMethod = WriteSerializeMethodFor(typeBuilder, keyType); | |
if (!isKeyPrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldloca_S, entryLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, keyValuePairType.GetProperty("Key").GetGetMethod()); | |
methodIL.Emit(OpCodes.Ldstr, _keyStr); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(isKeyPrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
keyMethod); | |
var valueMethod = WriteSerializeMethodFor(typeBuilder, valueType); | |
if (!isValuePrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldloca_S, entryLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, keyValuePairType.GetProperty("Value").GetGetMethod()); | |
methodIL.Emit(OpCodes.Ldstr, _valueStr); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(isValuePrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
valueMethod); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteEndElement); | |
methodIL.MarkLabel(startEnumeratorLabel); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, enumeratorType.GetMethod("MoveNext", MethodBinding)); | |
methodIL.Emit(OpCodes.Brtrue, moveNextLabel); | |
methodIL.Emit(OpCodes.Leave, endEnumeratorLabel); | |
methodIL.BeginFinallyBlock(); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Constrained, enumeratorLocal.LocalType); | |
methodIL.Emit(OpCodes.Callvirt, _iDisposableDispose); | |
methodIL.EndExceptionBlock(); | |
methodIL.MarkLabel(endEnumeratorLabel); | |
} | |
internal static void WriteArray(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
var itemType = type.GetElementType(); | |
var isPrimitive = itemType.IsPrimitiveType(); | |
var itemLocal = methodIL.DeclareLocal(itemType); | |
var indexLocal = methodIL.DeclareLocal(_intType); | |
var startLabel = methodIL.DefineLabel(); | |
var endLabel = methodIL.DefineLabel(); | |
methodIL.Emit(OpCodes.Ldc_I4_0); | |
methodIL.Emit(OpCodes.Stloc_S, indexLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Br, startLabel); | |
methodIL.MarkLabel(endLabel); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Ldloc_S, indexLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldelem, itemType); | |
methodIL.Emit(OpCodes.Stloc, itemLocal.LocalIndex); | |
var method = WriteSerializeMethodFor(typeBuilder, itemType); | |
if (!isPrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldloc, itemLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldstr, itemType.Name.Fix()); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(isPrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
method); | |
methodIL.Emit(OpCodes.Ldloc_S, indexLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldc_I4_1); | |
methodIL.Emit(OpCodes.Add); | |
methodIL.Emit(OpCodes.Stloc_S, indexLocal.LocalIndex); | |
methodIL.MarkLabel(startLabel); | |
methodIL.Emit(OpCodes.Ldloc_S, indexLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Ldlen); | |
methodIL.Emit(OpCodes.Conv_I4); | |
methodIL.Emit(OpCodes.Blt, endLabel); | |
} | |
internal static void WriteList(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
var arguments = type.GetGenericArguments(); | |
var listType = arguments.Length > 0 ? arguments[0] : _objectType; | |
var isPrimitive = listType.IsPrimitiveType(); | |
var genericListType = _genericListType.MakeGenericType(listType); | |
var enumeratorType = _genericListEnumerator.MakeGenericType(listType); | |
var enumeratorLocal = methodIL.DeclareLocal(enumeratorType); | |
var entryLocal = methodIL.DeclareLocal(listType); | |
var startEnumeratorLabel = methodIL.DefineLabel(); | |
var moveNextLabel = methodIL.DefineLabel(); | |
var endEnumeratorLabel = methodIL.DefineLabel(); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
if (type.Name == _iListStr) methodIL.Emit(OpCodes.Castclass, genericListType); | |
methodIL.Emit(OpCodes.Callvirt, | |
genericListType.GetMethod("GetEnumerator")); | |
methodIL.Emit(OpCodes.Stloc_S, enumeratorLocal.LocalIndex); | |
methodIL.BeginExceptionBlock(); | |
methodIL.Emit(OpCodes.Br, startEnumeratorLabel); | |
methodIL.MarkLabel(moveNextLabel); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, | |
enumeratorLocal.LocalType.GetProperty("Current") | |
.GetGetMethod()); | |
methodIL.Emit(OpCodes.Stloc, entryLocal.LocalIndex); | |
var method = WriteSerializeMethodFor(typeBuilder, listType); | |
if (!isPrimitive) methodIL.Emit(OpCodes.Ldarg_0); | |
methodIL.Emit(OpCodes.Ldloc, entryLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Ldstr, listType.Name.Fix()); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(isPrimitive ? OpCodes.Call : OpCodes.Callvirt, | |
method); | |
methodIL.MarkLabel(startEnumeratorLabel); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Call, enumeratorType.GetMethod("MoveNext", MethodBinding)); | |
methodIL.Emit(OpCodes.Brtrue, moveNextLabel); | |
methodIL.Emit(OpCodes.Leave, endEnumeratorLabel); | |
methodIL.BeginFinallyBlock(); | |
methodIL.Emit(OpCodes.Ldloca_S, enumeratorLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Constrained, enumeratorLocal.LocalType); | |
methodIL.Emit(OpCodes.Callvirt, _iDisposableDispose); | |
methodIL.EndExceptionBlock(); | |
methodIL.MarkLabel(endEnumeratorLabel); | |
} | |
internal static void WriteListArray(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
if (type.IsArray) WriteArray(typeBuilder, type, methodIL); | |
else WriteList(typeBuilder, type, methodIL); | |
} | |
internal static void WriteCollection(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldarg_2); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteStartElement); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldstr, _itemsStr); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteStartElement); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldstr, _typeStr); | |
methodIL.Emit(OpCodes.Ldstr, _itemsStr); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteAttributeString); | |
if (type.IsDictionaryType()) | |
WriteDictionary(typeBuilder, type, methodIL); | |
else WriteListArray(typeBuilder, type, methodIL); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteEndElement); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteEndElement); | |
} | |
internal static void WriteDeserializeFor(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
var valueLocal = methodIL.DeclareLocal(type); | |
var isClass = type.IsClass; | |
if (isClass) { | |
if (type.IsArray) methodIL.Emit(OpCodes.Newarr, type.GetElementType()); | |
else methodIL.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); | |
methodIL.Emit(OpCodes.Stloc, valueLocal.LocalIndex); | |
} else { | |
methodIL.Emit(OpCodes.Ldloca, valueLocal.LocalIndex); | |
methodIL.Emit(OpCodes.Initobj, type); | |
} | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Callvirt, _xmlReaderRead); | |
methodIL.Emit(OpCodes.Pop); | |
if (type.IsClassType()) | |
ReadPropertiesFor(typeBuilder, type, valueLocal, methodIL); | |
methodIL.Emit(isClass ? OpCodes.Ldloc : OpCodes.Ldloca, | |
valueLocal.LocalIndex); | |
} | |
internal static void WriteSerializeFor(TypeBuilder typeBuilder, Type type, ILGenerator methodIL) { | |
var conditionLabel = methodIL.DefineLabel(); | |
methodIL.Emit(OpCodes.Ldarg_1); | |
methodIL.Emit(OpCodes.Brtrue, conditionLabel); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldarg_2); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteStartElement); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Ldstr, _typeStr); | |
methodIL.Emit(OpCodes.Ldstr, _nilStr); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteAttributeString); | |
methodIL.Emit(OpCodes.Ldarg_3); | |
methodIL.Emit(OpCodes.Callvirt, _xmlWriterWriteEndElement); | |
methodIL.Emit(OpCodes.Ret); | |
methodIL.MarkLabel(conditionLabel); | |
if (type.IsClassType()) WritePropertiesFor(typeBuilder, type, methodIL); | |
else WriteCollection(typeBuilder, type, methodIL); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment