Free cookie consent management tool by TermsFeed Policy Generator

source: branches/PersistenceSpeedUp/HeuristicLab.Persistence/3.3/Default/CompositeSerializers/Storable/Descriptors/TypeSerializationCompiler.cs @ 18242

Last change on this file since 18242 was 6214, checked in by epitzer, 13 years ago

Streamline persistence reflection and store into persistable data structure for caching (#1530)

File size: 9.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Reflection;
26using System.Text;
27using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
28using System.Reflection.Emit;
29
30namespace HeuristicLab.Persistence.Default.CompositeSerializers.Storable.Descriptors {
31
32  public static class TypeSerializationCompiler {
33
34    #region Reflection Constants
35    private static readonly ConstructorInfo DictionaryConstructor =
36      typeof(Dictionary<string, object>).GetConstructor(Type.EmptyTypes);
37
38    private static readonly MethodInfo DictionaryAdd =
39      typeof(Dictionary<string, object>).GetMethod("Add");
40
41    private static readonly MethodInfo DictionaryTryGetValue =
42      typeof(Dictionary<string, object>).GetMethod("TryGetValue");
43    #endregion
44
45    #region interface methods
46    /// <summary>
47    /// Generate a decompose method delegate for the given type.
48    /// </summary>
49    /// <param name="typeDescriptor">The type descriptor.</param>
50    /// <returns>A lambda function to decompose the given type.</returns>
51    public static Func<object, Dictionary<string, object>> GenerateDecomposeMethod(TypeDescriptor typeDescriptor) {
52      DynamicMethod dm = new DynamicMethod("", typeof(Dictionary<string, object>), new Type[] { typeof(object) }, typeDescriptor.Handle, true);
53      ILGenerator ilgen = dm.GetILGenerator();
54      CreateDictionary(ilgen);
55      foreach (HookDescriptor hook in typeDescriptor.Hooks.Where(h => h.HookType == HookType.BeforeSerialization))
56        CallHook(ilgen, hook);
57      foreach (var field in typeDescriptor.Fields)
58         GetField(ilgen, field);
59      foreach (var property in typeDescriptor.Properties)
60        GetProperty(ilgen, property);
61      ilgen.Emit(OpCodes.Ldloc_0);
62      ilgen.Emit(OpCodes.Ret);
63      return (Func<object, Dictionary<string, object>>)dm.CreateDelegate(typeof(Func<object, Dictionary<string, object>>));
64    }
65
66    /// <summary>
67    /// Generate a populate method delegate for the given type.
68    /// </summary>
69    /// <param name="typeDescriptor">The type descriptor.</param>
70    /// <returns>A lambda function to populate the given type.</returns>
71    public static Action<object, Dictionary<string, object>> GeneratePopulateMethod(TypeDescriptor typeDescriptor) {
72      DynamicMethod dm = new DynamicMethod("", null, new Type[] { typeof(object), typeof(Dictionary<string, object>), typeof(object[])}, typeDescriptor.Handle, true);
73      dm.InitLocals = false;
74      ILGenerator ilgen = dm.GetILGenerator();
75      ilgen.DeclareLocal(typeof(object));
76      var defaultValues = new List<object>();
77      foreach (var field in typeDescriptor.Fields) {
78        SetField(ilgen, field, defaultValues.Count);
79        if (field.DefaultValue != null)
80          defaultValues.Add(field.DefaultValue);
81      }
82      foreach (var property in typeDescriptor.Properties) {
83        SetProperty(ilgen, property, defaultValues.Count);
84        if (property.DefaultValue != null)
85          defaultValues.Add(property.DefaultValue);
86      }
87      foreach (HookDescriptor hook in typeDescriptor.Hooks.Where(h => h.HookType == HookType.AfterDeserialization))
88        CallHook(ilgen, hook);
89      ilgen.Emit(OpCodes.Ret);
90      var action = (Action<object, Dictionary<string, object>, object[]>)dm.CreateDelegate(typeof(Action<object, Dictionary<string, object>, object[]>));
91      return new Action<object, Dictionary<string, object>>((instance, values) => {
92        action(instance, values, defaultValues.ToArray());
93      });
94    }
95
96    /// <summary>
97    /// Generate a create instance method delegete to instantiate the given type.
98    /// </summary>
99    /// <param name="typeDescriptor">The type descriptor.</param>
100    /// <returns>A lambda function to instantiate the given type.</returns>
101    public static Func<object> GenerateCreateInstanceMethod(TypeDescriptor typeDescriptor) {
102      DynamicMethod dm = new DynamicMethod("", typeof(object), null, typeDescriptor.Handle, true);
103      ILGenerator ilgen = dm.GetILGenerator();
104      if (typeDescriptor.HasStorableConstructor) {
105        ilgen.Emit(OpCodes.Ldc_I4_1); // load true
106        ilgen.Emit(OpCodes.Newobj, typeDescriptor.StorableConstructor);
107      } else if (typeDescriptor.HasDefaultConstructor) {
108        ilgen.Emit(OpCodes.Newobj, typeDescriptor.DefaultConstructor);
109      } else {
110        throw new InvalidOperationException("Cannot create instance: No StorableConstructor and no default constructor");
111      }
112      ilgen.Emit(OpCodes.Ret);
113      return (Func<object>)dm.CreateDelegate(typeof(Func<object>));
114    }
115    #endregion
116
117    #region auxiliary functions
118    /// <summary>
119    /// Generate <code>var values = new Dictionary<string, object>();</code>.
120    /// </summary>
121    private static void CreateDictionary(ILGenerator ilgen) {
122      ilgen.DeclareLocal(typeof(Dictionary<string, object>));
123      ilgen.Emit(OpCodes.Newobj, DictionaryConstructor);
124      ilgen.Emit(OpCodes.Stloc_0);
125    }
126
127    /// <summary>
128    /// Generate <code>obj.hook();</code>.
129    /// </summary>
130    private static void CallHook(ILGenerator ilgen, HookDescriptor hook) {
131      ilgen.Emit(OpCodes.Ldarg_0); // load this
132      ilgen.Emit(OpCodes.Castclass, hook.DeclaringType.Handle);
133      ilgen.Emit(OpCodes.Callvirt, hook.Handle); // ((<type>)obj).<method>()
134    }
135
136    /// <summary>
137    /// Generate <code>values.add(field.Name, obj.field);</code>.
138    /// </summary>
139    private static void GetField(ILGenerator ilgen, FieldDescriptor field) {
140      ilgen.Emit(OpCodes.Ldloc_0); // load dict
141      ilgen.Emit(OpCodes.Ldstr, field.Name);
142      ilgen.Emit(OpCodes.Ldarg_0); // load this
143      ilgen.Emit(OpCodes.Castclass, field.DeclaringType.Handle);
144      ilgen.Emit(OpCodes.Ldfld, field.Handle);
145      if (field.Handle.FieldType.IsValueType)
146        ilgen.Emit(OpCodes.Box, field.Handle.FieldType);
147      ilgen.Emit(OpCodes.Callvirt, DictionaryAdd);
148    }
149
150    /// <summary>
151    /// Generate <code>values.add(property.name, obj.Property);</code>.
152    /// </summary>
153    private static void GetProperty(ILGenerator ilgen, PropertyDescriptor property) {
154      if (!property.CanRead) return;
155      ilgen.Emit(OpCodes.Ldloc_0);
156      ilgen.Emit(OpCodes.Ldstr, property.Name);
157      ilgen.Emit(OpCodes.Ldarg_0);
158      ilgen.Emit(OpCodes.Castclass, property.Handle.DeclaringType);
159      ilgen.Emit(OpCodes.Callvirt, property.Handle.GetGetMethod(true));
160      if (property.Handle.PropertyType.IsValueType)
161        ilgen.Emit(OpCodes.Box, property.Handle.PropertyType);
162      ilgen.Emit(OpCodes.Callvirt, DictionaryAdd);
163    }
164
165    /// <summary>
166    /// Set the value of a field using the dictionary parameter.
167    /// </summary>
168    /// <param name="field">The descriptor of the field to set.</param>
169    /// <param name="index">The index of the default value.</param>
170    private static void SetField(ILGenerator ilgen, FieldDescriptor field, int index) {
171      ilgen.Emit(OpCodes.Ldarg_0); // load this
172      ilgen.Emit(OpCodes.Castclass, field.DeclaringType.Handle);
173      ilgen.Emit(OpCodes.Ldarg_1); // load dict
174      ilgen.Emit(OpCodes.Ldstr, field.Name);
175      ilgen.Emit(OpCodes.Ldloca_S, (byte)0); // load pointer to 'value'
176      ilgen.Emit(OpCodes.Callvirt, DictionaryTryGetValue);
177      if (field.DefaultValue == null) {
178        ilgen.Emit(OpCodes.Pop);
179      } else {
180        var endLabel = ilgen.DefineLabel();
181        ilgen.Emit(OpCodes.Brtrue_S, endLabel);
182        ilgen.Emit(OpCodes.Ldarg_2); // load defaultValues
183        ilgen.Emit(OpCodes.Ldc_I4, index);
184        ilgen.Emit(OpCodes.Ldelem, typeof(object));
185        ilgen.Emit(OpCodes.Stloc_0);
186        ilgen.MarkLabel(endLabel);
187      }
188      ilgen.Emit(OpCodes.Ldloc_0); // load value
189      ilgen.Emit(field.Handle.FieldType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, field.Handle.FieldType);
190      ilgen.Emit(OpCodes.Stfld, field.Handle);
191    }
192
193    /// <summary>
194    /// Set the value of a property using the dictionary parameter.
195    /// </summary>
196    /// <param name="property">The descriptor of the property.</param>
197    /// <param name="index">The index of the default value.</param>
198    private static void SetProperty(ILGenerator ilgen, PropertyDescriptor property, int index) {
199      if (!property.CanWrite) return;
200      ilgen.Emit(OpCodes.Ldarg_0); // load this
201      ilgen.Emit(OpCodes.Castclass, property.DeclaringType.Handle);
202      ilgen.Emit(OpCodes.Ldarg_1); // load dict
203      ilgen.Emit(OpCodes.Ldstr, property.Name);
204      ilgen.Emit(OpCodes.Ldloca_S, (byte)0); // load pointer to 'value'
205      ilgen.Emit(OpCodes.Callvirt, DictionaryTryGetValue);
206      if (property.DefaultValue == null) {
207        ilgen.Emit(OpCodes.Pop);
208      } else {
209        var endLabel = ilgen.DefineLabel();
210        ilgen.Emit(OpCodes.Brtrue_S, endLabel);
211        ilgen.Emit(OpCodes.Ldarg_2); // load defaultValues
212        ilgen.Emit(OpCodes.Ldc_I4, index);
213        ilgen.Emit(OpCodes.Ldelem, typeof(object));
214        ilgen.Emit(OpCodes.Stloc_0);
215        ilgen.MarkLabel(endLabel);
216      }
217      ilgen.Emit(OpCodes.Ldloc_0); // load value
218      ilgen.Emit(property.Handle.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, property.Handle.PropertyType);
219      ilgen.Emit(OpCodes.Callvirt, property.Handle.GetSetMethod(true));
220    }
221    #endregion
222  }
223}
Note: See TracBrowser for help on using the repository browser.