#region License Information /* HeuristicLab * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using HeuristicLab.Persistence.Default.CompositeSerializers.Storable; using System.Reflection.Emit; namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable.Descriptors { public static class TypeSerializationCompiler { #region Reflection Constants private static readonly ConstructorInfo DictionaryConstructor = typeof(Dictionary).GetConstructor(Type.EmptyTypes); private static readonly MethodInfo DictionaryAdd = typeof(Dictionary).GetMethod("Add"); private static readonly MethodInfo DictionaryTryGetValue = typeof(Dictionary).GetMethod("TryGetValue"); #endregion #region interface methods /// /// Generate a decompose method delegate for the given type. /// /// The type descriptor. /// A lambda function to decompose the given type. public static Func> GenerateDecomposeMethod(TypeDescriptor typeDescriptor) { DynamicMethod dm = new DynamicMethod("", typeof(Dictionary), new Type[] { typeof(object) }, typeDescriptor.Handle, true); ILGenerator ilgen = dm.GetILGenerator(); CreateDictionary(ilgen); foreach (HookDescriptor hook in typeDescriptor.Hooks.Where(h => h.HookType == HookType.BeforeSerialization)) CallHook(ilgen, hook); foreach (var field in typeDescriptor.Fields) GetField(ilgen, field); foreach (var property in typeDescriptor.Properties) GetProperty(ilgen, property); ilgen.Emit(OpCodes.Ldloc_0); ilgen.Emit(OpCodes.Ret); return (Func>)dm.CreateDelegate(typeof(Func>)); } /// /// Generate a populate method delegate for the given type. /// /// The type descriptor. /// A lambda function to populate the given type. public static Action> GeneratePopulateMethod(TypeDescriptor typeDescriptor) { DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(Dictionary), typeof(object[])}, typeDescriptor.Handle, true); dm.InitLocals = false; ILGenerator ilgen = dm.GetILGenerator(); ilgen.DeclareLocal(typeof(object)); var defaultValues = new List(); foreach (var field in typeDescriptor.Fields) { SetField(ilgen, field, defaultValues.Count); if (field.DefaultValue != null) defaultValues.Add(field.DefaultValue); } foreach (var property in typeDescriptor.Properties) { SetProperty(ilgen, property, defaultValues.Count); if (property.DefaultValue != null) defaultValues.Add(property.DefaultValue); } foreach (HookDescriptor hook in typeDescriptor.Hooks.Where(h => h.HookType == HookType.AfterDeserialization)) CallHook(ilgen, hook); ilgen.Emit(OpCodes.Ret); var action = (Action, object[]>)dm.CreateDelegate(typeof(Action, object[]>)); return new Action>((instance, values) => { action(instance, values, defaultValues.ToArray()); }); } /// /// Generate a create instance method delegete to instantiate the given type. /// /// The type descriptor. /// A lambda function to instantiate the given type. public static Func GenerateCreateInstanceMethod(TypeDescriptor typeDescriptor) { DynamicMethod dm = new DynamicMethod("", typeof(object), null, typeDescriptor.Handle, true); ILGenerator ilgen = dm.GetILGenerator(); if (typeDescriptor.HasStorableConstructor) { ilgen.Emit(OpCodes.Ldc_I4_1); // load true ilgen.Emit(OpCodes.Newobj, typeDescriptor.StorableConstructor); } else if (typeDescriptor.HasDefaultConstructor) { ilgen.Emit(OpCodes.Newobj, typeDescriptor.DefaultConstructor); } else { throw new InvalidOperationException("Cannot create instance: No StorableConstructor and no default constructor"); } ilgen.Emit(OpCodes.Ret); return (Func)dm.CreateDelegate(typeof(Func)); } #endregion #region auxiliary functions /// /// Generate var values = new Dictionary();. /// private static void CreateDictionary(ILGenerator ilgen) { ilgen.DeclareLocal(typeof(Dictionary)); ilgen.Emit(OpCodes.Newobj, DictionaryConstructor); ilgen.Emit(OpCodes.Stloc_0); } /// /// Generate obj.hook();. /// private static void CallHook(ILGenerator ilgen, HookDescriptor hook) { ilgen.Emit(OpCodes.Ldarg_0); // load this ilgen.Emit(OpCodes.Castclass, hook.DeclaringType.Handle); ilgen.Emit(OpCodes.Callvirt, hook.Handle); // (()obj).() } /// /// Generate values.add(field.Name, obj.field);. /// private static void GetField(ILGenerator ilgen, FieldDescriptor field) { ilgen.Emit(OpCodes.Ldloc_0); // load dict ilgen.Emit(OpCodes.Ldstr, field.Name); ilgen.Emit(OpCodes.Ldarg_0); // load this ilgen.Emit(OpCodes.Castclass, field.DeclaringType.Handle); ilgen.Emit(OpCodes.Ldfld, field.Handle); if (field.Handle.FieldType.IsValueType) ilgen.Emit(OpCodes.Box, field.Handle.FieldType); ilgen.Emit(OpCodes.Callvirt, DictionaryAdd); } /// /// Generate values.add(property.name, obj.Property);. /// private static void GetProperty(ILGenerator ilgen, PropertyDescriptor property) { if (!property.CanRead) return; ilgen.Emit(OpCodes.Ldloc_0); ilgen.Emit(OpCodes.Ldstr, property.Name); ilgen.Emit(OpCodes.Ldarg_0); ilgen.Emit(OpCodes.Castclass, property.Handle.DeclaringType); ilgen.Emit(OpCodes.Callvirt, property.Handle.GetGetMethod(true)); if (property.Handle.PropertyType.IsValueType) ilgen.Emit(OpCodes.Box, property.Handle.PropertyType); ilgen.Emit(OpCodes.Callvirt, DictionaryAdd); } /// /// Set the value of a field using the dictionary parameter. /// /// The descriptor of the field to set. /// The index of the default value. private static void SetField(ILGenerator ilgen, FieldDescriptor field, int index) { ilgen.Emit(OpCodes.Ldarg_0); // load this ilgen.Emit(OpCodes.Castclass, field.DeclaringType.Handle); ilgen.Emit(OpCodes.Ldarg_1); // load dict ilgen.Emit(OpCodes.Ldstr, field.Name); ilgen.Emit(OpCodes.Ldloca_S, (byte)0); // load pointer to 'value' ilgen.Emit(OpCodes.Callvirt, DictionaryTryGetValue); if (field.DefaultValue == null) { ilgen.Emit(OpCodes.Pop); } else { var endLabel = ilgen.DefineLabel(); ilgen.Emit(OpCodes.Brtrue_S, endLabel); ilgen.Emit(OpCodes.Ldarg_2); // load defaultValues ilgen.Emit(OpCodes.Ldc_I4, index); ilgen.Emit(OpCodes.Ldelem, typeof(object)); ilgen.Emit(OpCodes.Stloc_0); ilgen.MarkLabel(endLabel); } ilgen.Emit(OpCodes.Ldloc_0); // load value ilgen.Emit(field.Handle.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.Handle.FieldType); ilgen.Emit(OpCodes.Stfld, field.Handle); } /// /// Set the value of a property using the dictionary parameter. /// /// The descriptor of the property. /// The index of the default value. private static void SetProperty(ILGenerator ilgen, PropertyDescriptor property, int index) { if (!property.CanWrite) return; ilgen.Emit(OpCodes.Ldarg_0); // load this ilgen.Emit(OpCodes.Castclass, property.DeclaringType.Handle); ilgen.Emit(OpCodes.Ldarg_1); // load dict ilgen.Emit(OpCodes.Ldstr, property.Name); ilgen.Emit(OpCodes.Ldloca_S, (byte)0); // load pointer to 'value' ilgen.Emit(OpCodes.Callvirt, DictionaryTryGetValue); if (property.DefaultValue == null) { ilgen.Emit(OpCodes.Pop); } else { var endLabel = ilgen.DefineLabel(); ilgen.Emit(OpCodes.Brtrue_S, endLabel); ilgen.Emit(OpCodes.Ldarg_2); // load defaultValues ilgen.Emit(OpCodes.Ldc_I4, index); ilgen.Emit(OpCodes.Ldelem, typeof(object)); ilgen.Emit(OpCodes.Stloc_0); ilgen.MarkLabel(endLabel); } ilgen.Emit(OpCodes.Ldloc_0); // load value ilgen.Emit(property.Handle.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, property.Handle.PropertyType); ilgen.Emit(OpCodes.Callvirt, property.Handle.GetSetMethod(true)); } #endregion } }