#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
}
}