**** Merged from MCS ****
[mono-project.git] / mcs / class / Mono.C5 / Builder.cs
blob50510acd20f2bb63c569549ee3d4b8f3013d1c7c
1 #if NET_2_0
2 /*
3 Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
23 using C5;
24 using System;
25 using System.Reflection;
26 using System.Reflection.Emit;
27 using System.Diagnostics;
28 namespace C5.ComparerBuilder
30 /// <summary>
31 /// A default item comparer for an item type that is either generic (IComparable&lt;T&gt;)
32 /// or ordinarily (System.IComparable) comparable.
33 /// </summary>
34 public class FromComparable<T>
36 static Type naturalComparerO = typeof(NaturalComparerO<>);
38 static Type naturalComparer = typeof(NaturalComparer<>);
41 /// <summary>
42 /// Create a default comparer
43 /// <exception cref="ArgumentException"/> if T is not comparable.
44 /// </summary>
45 /// <returns>The comparer</returns>
46 [Tested]
47 public static IComparer<T> Examine()
49 Type t = typeof(T);
51 if (t.Equals(typeof(int)))
52 return (IComparer<T>)(new IC());
54 if (typeof(IComparable<T>).IsAssignableFrom(t))
56 Type c = naturalComparer.BindGenericParameters(new Type[] { t });
58 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
61 if (t.GetInterface("System.IComparable") != null)
63 Type c = naturalComparerO.BindGenericParameters(new Type[] { t });
65 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
68 throw new ArgumentException(String.Format("Cannot make IComparer<{0}>", t));
72 namespace C5.HasherBuilder
74 /// <summary>
75 /// Prototype for an sequenced hasher for IIndexed[W]
76 /// This will use the IIndexed[W] specific operations
77 /// </summary>
78 public class SequencedHasher<S, W> : IHasher<S>
79 where S : ISequenced<W>
81 /// <summary>
82 /// Get the hash code with respect to this sequenced hasher
83 /// </summary>
84 /// <param name="item">The item</param>
85 /// <returns>The hash code</returns>
86 [Tested]
87 public int GetHashCode(S item) { return item.GetHashCode(); }
90 /// <summary>
91 /// Check if two items are equal with respect to this sequenced hasher
92 /// </summary>
93 /// <param name="i1">first item</param>
94 /// <param name="i2">second item</param>
95 /// <returns>True if equal</returns>
96 [Tested]
97 public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }
102 /// <summary>
103 /// Prototype for an unsequenced hasher for ICollection[W]
104 /// This will use the ICollection[W] specific operations
105 /// </summary>
106 public class UnsequencedHasher<S, W> : IHasher<S>
107 where S : ICollection<W>
109 /// <summary>
110 /// Get the hash code with respect to this unsequenced hasher
111 /// </summary>
112 /// <param name="item">The item</param>
113 /// <returns>The hash code</returns>
114 [Tested]
115 public int GetHashCode(S item) { return item.GetHashCode(); }
118 /// <summary>
119 /// Check if two items are equal with respect to this unsequenced hasher
120 /// </summary>
121 /// <param name="i1">first item</param>
122 /// <param name="i2">second item</param>
123 /// <returns>True if equal</returns>
124 [Tested]
125 public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }
130 /// <summary>
131 /// Create a hasher for T that is DefaultValueTypeHasher[T]
132 /// or DefaultReferenceTypeHasher[T] unless T has been
133 /// instatiated to a type of the exact form IIndexed[W] or ICollection[W]
134 /// in which case Examine will return Sequenced- repectively UnsequencedHasher.
135 /// </summary>
136 public class ByPrototype<T>
138 static Type isequenced = typeof(ISequenced<>);
140 static Type ieditable = typeof(ICollection<>);
142 static Type orderedhasher = typeof(HasherBuilder.SequencedHasher<,>);
144 static Type unorderedhasher = typeof(HasherBuilder.UnsequencedHasher<,>);
146 static Type isequenced = Type.GetType("C5.ISequenced");
148 static Type ieditable = Type.GetType("C5.ICollection");
150 static Type orderedhasher = Type.GetType("C5.HasherBuilder.SequencedHasher");
152 static Type unorderedhasher = Type.GetType("C5.HasherBuilder.UnsequencedHasher");
155 /// <summary>
156 /// See class description
157 /// </summary>
158 /// <returns>The hasher</returns>
159 [Tested]
160 public static IHasher<T> Examine()
162 Type t = typeof(T);
164 if (!t.HasGenericArguments)
166 if (t.Equals(typeof(int)))
167 return (IHasher<T>)(new IntHasher());
168 else if (t.IsValueType)
169 return new DefaultValueTypeHasher<T>();
170 else
171 return new DefaultReferenceTypeHasher<T>();
174 Type s = t.GetGenericTypeDefinition();
175 Type[] v = t.GetGenericArguments();
176 Type b;
178 if (s.Equals(isequenced))
179 b = orderedhasher;
180 else if (s.Equals(ieditable))
181 b = unorderedhasher;
182 else if (t.IsValueType)
183 return new DefaultValueTypeHasher<T>();
184 else
185 return new DefaultReferenceTypeHasher<T>();
187 Type c = b.BindGenericParameters(new Type[] { t, v[0] });
189 return (IHasher<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));
194 #if !EXPERIMENTAL
196 /// <summary>
197 /// IHasher factory class: examines at instatiation time if T is an
198 /// interface implementing "int GetHashCode()" and "bool Equals(T)".
199 /// If those are not present, MakeHasher will return a default hasher,
200 /// else this class will implement Ihasher[T] via Invoke() on the
201 /// reflected method infos.
202 /// </summary>
203 public class ByInvoke<T> : IHasher<T>
205 internal static readonly System.Reflection.MethodInfo hinfo, einfo;
208 static ByInvoke()
210 Type t = typeof(T);
212 if (!t.IsInterface) return;
214 BindingFlags f = BindingFlags.Public | BindingFlags.Instance;
216 hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null);
217 einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null);
221 private ByInvoke() { }
223 /// <summary>
224 ///
225 /// </summary>
226 /// <returns></returns>
227 public static IHasher<T> MakeHasher()
229 if (hinfo != null && einfo != null)
230 return new ByInvoke<T>();
231 else
232 return new DefaultReferenceTypeHasher<T>();
235 /// <summary>
236 ///
237 /// </summary>
238 /// <param name="item"></param>
239 /// <returns></returns>
240 public int GetHashCode(T item)
242 return (int)(hinfo.Invoke(item, null));
245 /// <summary>
246 ///
247 /// </summary>
248 /// <param name="i1"></param>
249 /// <param name="i2"></param>
250 /// <returns></returns>
251 public bool Equals(T i1, T i2)
253 return (bool)(einfo.Invoke(i1, new object[1] { i2 }));
259 /// <summary>
260 /// Like ByInvoke, but tries to build a hasher by RTCG to
261 /// avoid the Invoke() overhead. Does not work as intended
262 /// because of a Whidbey RTCG bug.
263 /// </summary>
264 public class ByRTCG
266 private static ModuleBuilder moduleBuilder;
268 private static AssemblyBuilder assemblyBuilder;
270 private static int uid = 0;
273 /// <summary>
274 ///
275 /// </summary>
276 /// <param name="hinfo"></param>
277 /// <param name="einfo"></param>
278 /// <returns></returns>
279 public static /*ObjectHasher */ IHasher<T> CreateHasher<T>(MethodInfo hinfo, MethodInfo einfo)
281 if (moduleBuilder == null)
283 string assmname = "LeFake";
284 string filename = assmname + ".dll";
285 AssemblyName assemblyName = new AssemblyName("LeFake");
286 AppDomain appdomain = AppDomain.CurrentDomain;
287 AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave;
289 assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc);
290 moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename);
293 Type t = typeof(/*object*/ T);
294 Type o_t = typeof(object);
295 Type h_t = typeof(/*ObjectHasher*/ IHasher<T>);
296 Type i_t = typeof(int);
297 //TODO: protect uid for thread safety!
298 string name = "C5.Dynamic.Hasher_" + uid++;
299 TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;
300 TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t });
301 MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual;
302 MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t });
303 ILGenerator ilg = mb.GetILGenerator();
305 ilg.Emit(OpCodes.Ldarg_1);
306 ilg.Emit(OpCodes.Callvirt, hinfo);
307 ilg.Emit(OpCodes.Ret);
308 mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t });
309 ilg = mb.GetILGenerator();
310 ilg.Emit(OpCodes.Ldarg_1);
311 ilg.Emit(OpCodes.Ldarg_2);
312 ilg.Emit(OpCodes.Callvirt, einfo);
313 ilg.Emit(OpCodes.Ret);
315 Type hasher_t = tb.CreateType();
316 object hasher = hasher_t.GetConstructor(new Type[0]).Invoke(null);
318 return (IHasher<T>)hasher;
321 /// <summary>
322 ///
323 /// </summary>
324 /// <typeparam name="T"></typeparam>
325 /// <returns></returns>
326 public static IHasher<T> build<T>()
328 MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo;
330 if (hinfo != null && einfo != null)
331 return CreateHasher<T>(hinfo, einfo);
332 else
333 return ByPrototype<T>.Examine();
336 /// <summary>
337 ///
338 /// </summary>
339 public void dump()
341 assemblyBuilder.Save("LeFake.dll");
344 #endif
346 #endif