<commit_before>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace Watsonia.Data
{
	internal static class DynamicProxyFactory
	{
		private static readonly AssemblyBuilder _assemblyBuilder = null;
		private static readonly ModuleBuilder _moduleBuilder = null;
		//private static readonly string _fileName = null;
		private static readonly Dictionary<string, Type> _cachedTypes = new Dictionary<string, Type>();

		static DynamicProxyFactory()
		{
			AssemblyName assemblyName = new AssemblyName();
			assemblyName.Name = "Watsonia.Data.DynamicProxies";

			_assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
			_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, false);

			// NOTE: We can also save the generated assembly to a file so that we can inspect it with Reflector
			//_assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
			//_fileName = _assemblyBuilder.GetName().Name + ".dll";
			//_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, _fileName);
		}

		internal static Type GetDynamicProxyType(Type parentType, Database database)
		{
			// A new type needs to be made for each type in each database
			// This is because each database may have different naming conventions and primary/foreign key types
			string proxyTypeName = database.DatabaseName + parentType.Name + "Proxy";
			if (!_cachedTypes.ContainsKey(proxyTypeName))
			{
				_cachedTypes.Add(proxyTypeName, CreateType(proxyTypeName, parentType, database));
			}
			return _cachedTypes[proxyTypeName];
		}

		internal static IDynamicProxy GetDynamicProxy(Type parentType, Database database)
		{
			Type proxyType = GetDynamicProxyType(parentType, database);
			IDynamicProxy proxy = (IDynamicProxy)proxyType.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
			proxy.StateTracker.Database = database;
			return proxy;
		}

		public static T GetDynamicProxy<T>(Database database)
		{
			Type parentType = typeof(T);
			IDynamicProxy proxy = GetDynamicProxy(parentType, database);
			return (T)proxy;
		}

		private static Type CreateType(string typeName, Type parent, Database database)
		{
			System.Diagnostics.Trace.WriteLine("Creating " + typeName, "Dynamic Proxy");

			DynamicProxyTypeMembers members = new DynamicProxyTypeMembers();
			members.PrimaryKeyColumnName = database.Configuration.GetPrimaryKeyColumnName(parent);
			members.PrimaryKeyColumnType = database.Configuration.GetPrimaryKeyColumnType(parent);

			TypeBuilder type = _moduleBuilder.DefineType(typeName,
				TypeAttributes.Public |
				TypeAttributes.Class |
				TypeAttributes.AutoClass |
				TypeAttributes.AnsiClass |
				TypeAttributes.BeforeFieldInit |
				TypeAttributes.AutoLayout,
				parent);
			type.AddInterfaceImplementation(typeof(IDynamicProxy));

			// Implement INotifyPropertyChanging
			FieldBuilder propertyChangingEventField = CreatePropertyChangingEvent(type);
			members.OnPropertyChangingMethod = CreateOnPropertyChangingMethod(type, propertyChangingEventField);

			// Implement INotifyPropertyChanged
			FieldBuilder propertyChangedEventField = CreatePropertyChangedEvent(type);
			members.OnPropertyChangedMethod = CreateOnPropertyChangedMethod(type, propertyChangedEventField);

			// Add the properties
			AddProperties(type, parent, members, database);

			// Add some methods
			CreateSetValuesFromReaderMethod(type, members);
			CreateGetHashCodeMethod(type, members);
			CreateEqualsMethod(type, members);

			// Implement IDataErrorInfo
			CreateErrorProperty(type, members);
			CreateErrorItemProperty(type, members);

			// Add the constructor
			AddConstructor(type, parent, members);

			Type t = type.CreateType();

			// NOTE: We can also save the generated assembly to a file so that we can inspect it with Reflector
			//_assemblyBuilder.Save(_fileName);

			return t;
		}

		private static void AddConstructor(TypeBuilder type, Type parent, DynamicProxyTypeMembers members)
		{
			ConstructorInfo c = parent.GetConstructor(
				BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
				null,
				Type.EmptyTypes,
				null);
			if (c == null || c.IsPrivate)
			{
				throw new InvalidOperationException(
					string.Format("An accessible empty constructor was not found on type {0}", parent.FullName));
			}

			ConstructorBuilder constructor = type.DefineConstructor(
				c.Attributes | MethodAttributes.Public,		// Make it public, dammit
				c.CallingConvention,
				Type.EmptyTypes);

			MethodInfo stateTrackerIsLoadingMethod = typeof(DynamicProxyStateTracker).GetMethod(
				"set_IsLoading", new Type[]{ typeof(Boolean) });

			ILGenerator gen = constructor.GetILGenerator();

			// base.ctor()
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, c);

			// this.StateTracker.IsLoading = true;
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, members.GetStateTrackerMethod);
			gen.Emit(OpCodes.Ldc_I4_1);
			gen.Emit(OpCodes.Callvirt, stateTrackerIsLoadingMethod);

			// Set the default values
			foreach (string propertyName in members.DefaultValues.Keys)
			{
				object value = members.DefaultValues[propertyName];
				bool isNullable = false;
				Type propertyType = members.GetPropertyMethods[propertyName].ReturnType;
				if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
				{
					isNullable = true;
					propertyType = propertyType.GetGenericArguments()[0];
				}

				if (propertyType == typeof(string))
				{
					gen.Emit(OpCodes.Ldarg_0);
					gen.Emit(OpCodes.Ldstr, (string)value);
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else if (propertyType == typeof(DateTime))
				{
					ConstructorInfo dateTimeConstructor = typeof(DateTime).GetConstructor(
						new Type[] { typeof(long), typeof(DateTimeKind) });

					DateTime dateTimeValue = (DateTime)value;

					gen.Emit(OpCodes.Ldarg_0);
					gen.Emit(OpCodes.Ldc_I8, dateTimeValue.Ticks);
					gen.Emit(OpCodes.Ldc_I4, (int)dateTimeValue.Kind);
					gen.Emit(OpCodes.Newobj, dateTimeConstructor);
					if (isNullable)
					{
						ConstructorInfo nullableDateTimeConstructor = typeof(Nullable<>).MakeGenericType(typeof(DateTime)).GetConstructor(
							new Type[] { typeof(DateTime) });
						gen.Emit(OpCodes.Newobj, nullableDateTimeConstructor);
					}
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else if (propertyType.IsValueType)
				{
					gen.Emit(OpCodes.Ldarg_0);
					switch (Type.GetTypeCode(propertyType))
					{
						case TypeCode.Byte:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((byte)value));
							break;
						}
						case TypeCode.SByte:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((sbyte)value));
							break;
						}
						case TypeCode.Int16:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((short)value));
							break;
						}
						case TypeCode.UInt16:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((ushort)value));
							break;
						}
						case TypeCode.Int32:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)value);
							break;
						}
						case TypeCode.UInt32:
						{
							gen.Emit(OpCodes.Ldc_I4, unchecked((int)((uint)value)));
							break;
						}
						case TypeCode.Int64:
						{
							gen.Emit(OpCodes.Ldc_I8, Convert.ToInt64(value));
							break;
						}
						case TypeCode.UInt64:
						{
							gen.Emit(OpCodes.Ldc_I8, unchecked((long)((ulong)value)));
							break;
						}
						case TypeCode.Boolean:
						{
							if ((bool)value)
							{
								gen.Emit(OpCodes.Ldc_I4_1);
							}
							else
							{
								gen.Emit(OpCodes.Ldc_I4_0);
							}
							break;
						}
						case TypeCode.Char:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((char)value));
							break;
						}
						case TypeCode.Single:
						{
							gen.Emit(OpCodes.Ldc_R4, (float)value);
							break;
						}
						case TypeCode.Double:
						{
							gen.Emit(OpCodes.Ldc_R8, (double)value);
							break;
						}
						case TypeCode.Decimal:
						{
							Decimal decimalValue = Convert.ToDecimal(value);
							int[] words = Decimal.GetBits(decimalValue);
							int power = (words[3] >> 16) & 0xff;
							int sign = words[3] >> 31;

							if (power == 0 && decimalValue <= int.MaxValue && decimalValue >= int.MinValue)
							{
								ConstructorInfo decimalConstructor = typeof(Decimal).GetConstructor(
									new Type[1] { typeof(int) });

								gen.Emit(OpCodes.Ldc_I4, (int)decimalValue);
								gen.Emit(OpCodes.Newobj, decimalConstructor);
							}
							else
							{
								ConstructorInfo decimalConstructor = typeof(Decimal).GetConstructor(
									new Type[5] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) });

								gen.Emit(OpCodes.Ldc_I4, words[0]);
								gen.Emit(OpCodes.Ldc_I4, words[1]);
								gen.Emit(OpCodes.Ldc_I4, words[2]);
								gen.Emit(OpCodes.Ldc_I4, sign);
								gen.Emit(OpCodes.Ldc_I4, power);
								gen.Emit(OpCodes.Newobj, decimalConstructor);
							}
							break;
						}
					}
					if (isNullable)
					{
						ConstructorInfo nullableConstructor = typeof(Nullable<>).MakeGenericType(propertyType).GetConstructor(
							new Type[] { propertyType });
						gen.Emit(OpCodes.Newobj, nullableConstructor);
					}
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else
				{
					// Not much we can do here, we just have to silently ignore the troublesome default value
					// We don't want to throw an exception because then users would have to remove the default value attribute
					// to get things to work
					System.Diagnostics.Trace.WriteLine(string.Format("Unsupported default value type for {0}: {1}", propertyName, propertyType));
				}
			}

			// this.StateTracker.IsLoading = false;
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, members.GetStateTrackerMethod);
			gen.Emit(OpCodes.Ldc_I4_0);
			gen.Emit(OpCodes.Callvirt, stateTrackerIsLoadingMethod);

			// return;<commit_msg>Only set default values if they haven't already<commit_after>﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

namespace Watsonia.Data
{
	internal static class DynamicProxyFactory
	{
		private static readonly AssemblyBuilder _assemblyBuilder = null;
		private static readonly ModuleBuilder _moduleBuilder = null;
		//private static readonly string _fileName = null;
		private static readonly Dictionary<string, Type> _cachedTypes = new Dictionary<string, Type>();

		static DynamicProxyFactory()
		{
			AssemblyName assemblyName = new AssemblyName();
			assemblyName.Name = "Watsonia.Data.DynamicProxies";

			_assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
			_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, false);

			// NOTE: We can also save the generated assembly to a file so that we can inspect it with Reflector
			//_assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
			//_fileName = _assemblyBuilder.GetName().Name + ".dll";
			//_moduleBuilder = _assemblyBuilder.DefineDynamicModule(_assemblyBuilder.GetName().Name, _fileName);
		}

		internal static Type GetDynamicProxyType(Type parentType, Database database)
		{
			// A new type needs to be made for each type in each database
			// This is because each database may have different naming conventions and primary/foreign key types
			string proxyTypeName = database.DatabaseName + parentType.Name + "Proxy";
			if (!_cachedTypes.ContainsKey(proxyTypeName))
			{
				_cachedTypes.Add(proxyTypeName, CreateType(proxyTypeName, parentType, database));
			}
			return _cachedTypes[proxyTypeName];
		}

		internal static IDynamicProxy GetDynamicProxy(Type parentType, Database database)
		{
			Type proxyType = GetDynamicProxyType(parentType, database);
			IDynamicProxy proxy = (IDynamicProxy)proxyType.GetConstructor(Type.EmptyTypes).Invoke(Type.EmptyTypes);
			proxy.StateTracker.Database = database;
			return proxy;
		}

		public static T GetDynamicProxy<T>(Database database)
		{
			Type parentType = typeof(T);
			IDynamicProxy proxy = GetDynamicProxy(parentType, database);
			return (T)proxy;
		}

		private static Type CreateType(string typeName, Type parent, Database database)
		{
			System.Diagnostics.Trace.WriteLine("Creating " + typeName, "Dynamic Proxy");

			DynamicProxyTypeMembers members = new DynamicProxyTypeMembers();
			members.PrimaryKeyColumnName = database.Configuration.GetPrimaryKeyColumnName(parent);
			members.PrimaryKeyColumnType = database.Configuration.GetPrimaryKeyColumnType(parent);

			TypeBuilder type = _moduleBuilder.DefineType(typeName,
				TypeAttributes.Public |
				TypeAttributes.Class |
				TypeAttributes.AutoClass |
				TypeAttributes.AnsiClass |
				TypeAttributes.BeforeFieldInit |
				TypeAttributes.AutoLayout,
				parent);
			type.AddInterfaceImplementation(typeof(IDynamicProxy));

			// Implement INotifyPropertyChanging
			FieldBuilder propertyChangingEventField = CreatePropertyChangingEvent(type);
			members.OnPropertyChangingMethod = CreateOnPropertyChangingMethod(type, propertyChangingEventField);

			// Implement INotifyPropertyChanged
			FieldBuilder propertyChangedEventField = CreatePropertyChangedEvent(type);
			members.OnPropertyChangedMethod = CreateOnPropertyChangedMethod(type, propertyChangedEventField);

			// Add the properties
			AddProperties(type, parent, members, database);

			// Add some methods
			CreateSetValuesFromReaderMethod(type, members);
			CreateGetHashCodeMethod(type, members);
			CreateEqualsMethod(type, members);

			// Implement IDataErrorInfo
			CreateErrorProperty(type, members);
			CreateErrorItemProperty(type, members);

			// Add the constructor
			AddConstructor(type, parent, members);

			Type t = type.CreateType();

			// NOTE: We can also save the generated assembly to a file so that we can inspect it with Reflector
			//_assemblyBuilder.Save(_fileName);

			return t;
		}

		private static void AddConstructor(TypeBuilder type, Type parent, DynamicProxyTypeMembers members)
		{
			ConstructorInfo c = parent.GetConstructor(
				BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
				null,
				Type.EmptyTypes,
				null);
			if (c == null || c.IsPrivate)
			{
				throw new InvalidOperationException(
					string.Format("An accessible empty constructor was not found on type {0}", parent.FullName));
			}

			ConstructorBuilder constructor = type.DefineConstructor(
				c.Attributes | MethodAttributes.Public,		// Make it public, dammit
				c.CallingConvention,
				Type.EmptyTypes);

			MethodInfo stateTrackerIsLoadingMethod = typeof(DynamicProxyStateTracker).GetMethod(
				"set_IsLoading", new Type[] { typeof(bool) });

			MethodInfo stateTrackerChangedFieldsMethod = typeof(DynamicProxyStateTracker).GetMethod(
				"get_ChangedFields", Type.EmptyTypes);

			MethodInfo stringListContainsMethod = typeof(List<>).MakeGenericType(typeof(string)).GetMethod(
				"Contains", new Type[] { typeof(string) });

			ILGenerator gen = constructor.GetILGenerator();

			// base.ctor()
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, c);

			// this.StateTracker.IsLoading = true;
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, members.GetStateTrackerMethod);
			gen.Emit(OpCodes.Ldc_I4_1);
			gen.Emit(OpCodes.Callvirt, stateTrackerIsLoadingMethod);

			// Set the default values
			foreach (string propertyName in members.DefaultValues.Keys)
			{
				Label endIfShouldSetDefaultValueLabel = gen.DefineLabel();

				// if (!this.StateTracker.ChangedFields.Contains("Property"))
				gen.Emit(OpCodes.Ldarg_0);
				gen.Emit(OpCodes.Call, members.GetStateTrackerMethod);
				gen.Emit(OpCodes.Callvirt, stateTrackerChangedFieldsMethod);
				gen.Emit(OpCodes.Ldstr, propertyName);
				gen.Emit(OpCodes.Callvirt, stringListContainsMethod);
				gen.Emit(OpCodes.Brtrue_S, endIfShouldSetDefaultValueLabel);

				object value = members.DefaultValues[propertyName];
				bool isNullable = false;
				Type propertyType = members.GetPropertyMethods[propertyName].ReturnType;
				if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
				{
					isNullable = true;
					propertyType = propertyType.GetGenericArguments()[0];
				}

				if (propertyType == typeof(string))
				{
					gen.Emit(OpCodes.Ldarg_0);
					gen.Emit(OpCodes.Ldstr, (string)value);
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else if (propertyType == typeof(DateTime))
				{
					ConstructorInfo dateTimeConstructor = typeof(DateTime).GetConstructor(
						new Type[] { typeof(long), typeof(DateTimeKind) });

					DateTime dateTimeValue = (DateTime)value;

					gen.Emit(OpCodes.Ldarg_0);
					gen.Emit(OpCodes.Ldc_I8, dateTimeValue.Ticks);
					gen.Emit(OpCodes.Ldc_I4, (int)dateTimeValue.Kind);
					gen.Emit(OpCodes.Newobj, dateTimeConstructor);
					if (isNullable)
					{
						ConstructorInfo nullableDateTimeConstructor = typeof(Nullable<>).MakeGenericType(typeof(DateTime)).GetConstructor(
							new Type[] { typeof(DateTime) });
						gen.Emit(OpCodes.Newobj, nullableDateTimeConstructor);
					}
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else if (propertyType.IsValueType)
				{
					gen.Emit(OpCodes.Ldarg_0);
					switch (Type.GetTypeCode(propertyType))
					{
						case TypeCode.Byte:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((byte)value));
							break;
						}
						case TypeCode.SByte:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((sbyte)value));
							break;
						}
						case TypeCode.Int16:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((short)value));
							break;
						}
						case TypeCode.UInt16:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((ushort)value));
							break;
						}
						case TypeCode.Int32:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)value);
							break;
						}
						case TypeCode.UInt32:
						{
							gen.Emit(OpCodes.Ldc_I4, unchecked((int)((uint)value)));
							break;
						}
						case TypeCode.Int64:
						{
							gen.Emit(OpCodes.Ldc_I8, Convert.ToInt64(value));
							break;
						}
						case TypeCode.UInt64:
						{
							gen.Emit(OpCodes.Ldc_I8, unchecked((long)((ulong)value)));
							break;
						}
						case TypeCode.Boolean:
						{
							if ((bool)value)
							{
								gen.Emit(OpCodes.Ldc_I4_1);
							}
							else
							{
								gen.Emit(OpCodes.Ldc_I4_0);
							}
							break;
						}
						case TypeCode.Char:
						{
							gen.Emit(OpCodes.Ldc_I4, (int)((char)value));
							break;
						}
						case TypeCode.Single:
						{
							gen.Emit(OpCodes.Ldc_R4, (float)value);
							break;
						}
						case TypeCode.Double:
						{
							gen.Emit(OpCodes.Ldc_R8, (double)value);
							break;
						}
						case TypeCode.Decimal:
						{
							Decimal decimalValue = Convert.ToDecimal(value);
							int[] words = Decimal.GetBits(decimalValue);
							int power = (words[3] >> 16) & 0xff;
							int sign = words[3] >> 31;

							if (power == 0 && decimalValue <= int.MaxValue && decimalValue >= int.MinValue)
							{
								ConstructorInfo decimalConstructor = typeof(Decimal).GetConstructor(
									new Type[1] { typeof(int) });

								gen.Emit(OpCodes.Ldc_I4, (int)decimalValue);
								gen.Emit(OpCodes.Newobj, decimalConstructor);
							}
							else
							{
								ConstructorInfo decimalConstructor = typeof(Decimal).GetConstructor(
									new Type[5] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(byte) });

								gen.Emit(OpCodes.Ldc_I4, words[0]);
								gen.Emit(OpCodes.Ldc_I4, words[1]);
								gen.Emit(OpCodes.Ldc_I4, words[2]);
								gen.Emit(OpCodes.Ldc_I4, sign);
								gen.Emit(OpCodes.Ldc_I4, power);
								gen.Emit(OpCodes.Newobj, decimalConstructor);
							}
							break;
						}
					}
					if (isNullable)
					{
						ConstructorInfo nullableConstructor = typeof(Nullable<>).MakeGenericType(propertyType).GetConstructor(
							new Type[] { propertyType });
						gen.Emit(OpCodes.Newobj, nullableConstructor);
					}
					gen.Emit(OpCodes.Callvirt, members.SetPropertyMethods[propertyName]);
				}
				else
				{
					// Not much we can do here, we just have to silently ignore the troublesome default value
					// We don't want to throw an exception because then users would have to remove the default value attribute
					// to get things to work
					System.Diagnostics.Trace.WriteLine(string.Format("Unsupported default value type for {0}: {1}", propertyName, propertyType));
				}

				gen.MarkLabel(endIfShouldSetDefaultValueLabel);
			}

			// this.StateTracker.IsLoading = false;
			gen.Emit(OpCodes.Ldarg_0);
			gen.Emit(OpCodes.Call, members.GetStateTrackerMethod);
			gen.Emit(OpCodes.Ldc_I4_0);
			gen.Emit(OpCodes.Callvirt, stateTrackerIsLoadingMethod);

			// return;
{'fraction_non_alphanumeric': 0.10621798185834469, 'fraction_numerical': 0.0060628848051887015, 'mean_word_length': 3.52434616202424, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6595603', 'n_tokens_mistral': 8218, 'n_tokens_neox': 6374, 'n_words': 1243}